Merge branch 'dev'

pull/930/merge v1.21.0
lyswhut 2022-05-22 11:17:47 +08:00
commit f2f2008d5d
80 changed files with 2634 additions and 4021 deletions

View File

@ -6,6 +6,32 @@ Project versioning adheres to [Semantic Versioning](http://semver.org/).
Commit convention is based on [Conventional Commits](http://conventionalcommits.org). Commit convention is based on [Conventional Commits](http://conventionalcommits.org).
Change log format is based on [Keep a Changelog](http://keepachangelog.com/). Change log format is based on [Keep a Changelog](http://keepachangelog.com/).
## [1.21.0](https://github.com/lyswhut/lx-music-desktop/compare/v1.20.0...v1.21.0) - 2022-05-22
### 新增
- 新增设置-播放设置-显示歌词罗马音,默认关闭,注:目前只有网易源能获取到罗马音歌词(得益于 Binaryify/NeteaseCloudMusicApi/pull/1523如果你知道其他源的歌词罗马音获取方式欢迎PR或开issue交流
### 优化
- 同时删除一首歌以上时将需要二次确认删除
- 禁用透明窗口时右侧不再偏移5px距离在win7、Ubuntu等系统上测试发现不偏移也不影响滚动条的拖动了
- 删除未下载完成的任务时,只同时尝试删除已有下载进度的本地文件
- 在全屏状态下使用`Esc`键可以退出全屏(#827
### 修复
- 修复某些情况下歌曲播放出错时不会自动切歌的问题
- 修复关闭“显示切换动画”设置后,在应用启动时该设置没有被应用的问题
- 修复原始歌词存在偏移时,歌词偏移设置的重置未按预期工作的问题
- 修复长度大于一行的歌词在使用歌词调整播放进度时的时间不准问题
- 修复潜在歌单更新失败的问题
### 文档
- 将歌曲添加“稍后播放”后,它们会被放在一个优先级最高的特殊队列中,点击“下一曲”时会消耗该队列中的歌曲,并且无法通过“上一曲”功能播放该队列的上一首歌曲
- 在切歌时若不是通过“上一曲”、“下一曲”功能切歌(例如直接点击“排行榜列表”、“我的列表”中的歌曲切歌),“稍后播放”队列将会被清空
## [1.20.0](https://github.com/lyswhut/lx-music-desktop/compare/v1.19.0...v1.20.0) - 2022-04-17 ## [1.20.0](https://github.com/lyswhut/lx-music-desktop/compare/v1.19.0...v1.20.0) - 2022-04-17
特别说明Scheme URL其实是支持Linux系统的但好像需要deb之类的安装包创建出`.desktop`文件才行。 特别说明Scheme URL其实是支持Linux系统的但好像需要deb之类的安装包创建出`.desktop`文件才行。

4
FAQ.md
View File

@ -14,6 +14,8 @@
4. 对于歌单详情列表除了可以使用第2条的方式播放外你可以点击详情页上面的播放按钮临时播放当前歌单或点击收藏将当前歌单收藏到“我的列表”后再去播放 4. 对于歌单详情列表除了可以使用第2条的方式播放外你可以点击详情页上面的播放按钮临时播放当前歌单或点击收藏将当前歌单收藏到“我的列表”后再去播放
5. 对于排行榜详情列表除了可以使用第2条的方式播放外你可以在右击排行榜名字后弹出的菜单中播放或收藏整个排行榜这与第四条的歌单中的播放、与收藏按钮功能一致 5. 对于排行榜详情列表除了可以使用第2条的方式播放外你可以在右击排行榜名字后弹出的菜单中播放或收藏整个排行榜这与第四条的歌单中的播放、与收藏按钮功能一致
6. v1.18.0及之后新增了“双击列表里的歌曲时自动切换到当前列表播放”设置,默认关闭,此功能仅对歌单、排行榜有效 6. v1.18.0及之后新增了“双击列表里的歌曲时自动切换到当前列表播放”设置,默认关闭,此功能仅对歌单、排行榜有效
7. 将歌曲添加“稍后播放”后,它们会被放在一个优先级最高的特殊队列中,点击“下一曲”时会消耗该队列中的歌曲,并且无法通过“上一曲”功能播放该队列的上一首歌曲
8. 在切歌时若不是通过“上一曲”、“下一曲”功能切歌(例如直接点击“排行榜列表”、“我的列表”中的歌曲切歌),“稍后播放”队列将会被清空
## 可用的鼠标、键盘快捷操作 ## 可用的鼠标、键盘快捷操作
@ -165,7 +167,7 @@
### Windows 7 下界面异常 ### Windows 7 下界面异常
由于软件默认使用了透明窗口根据Electron官方文档的[说明](https://electronjs.org/docs/api/frameless-window#%E5%B1%80%E9%99%90%E6%80%A7) 由于软件默认使用了透明窗口根据Electron官方文档的[说明](https://www.electronjs.org/docs/latest/tutorial/window-customization#limitations)
> 在 windows 操作系统上, 当 DWM 被禁用时, 透明窗口将无法工作。 > 在 windows 操作系统上, 当 DWM 被禁用时, 透明窗口将无法工作。
因此,当 win7 没有使用**Aero**主题时界面将会显示异常开启AERO的方法请自行百度`win7开启Aero效果`(开启后可看到任务栏变透明)。<br> 因此,当 win7 没有使用**Aero**主题时界面将会显示异常开启AERO的方法请自行百度`win7开启Aero效果`(开启后可看到任务栏变透明)。<br>

View File

@ -6,7 +6,7 @@ module.exports = {
output: { output: {
filename: '[name].js', filename: '[name].js',
libraryTarget: 'commonjs2', libraryTarget: 'commonjs2',
path: path.join(__dirname, '../../dist/electron'), path: path.join(__dirname, '../../dist'),
}, },
resolve: { resolve: {
alias: { alias: {
@ -25,9 +25,6 @@ module.exports = {
}, },
], ],
}, },
performance: {
maxEntrypointSize: 300000,
},
plugins: [ plugins: [
new ESLintPlugin(), new ESLintPlugin(),
], ],

View File

@ -19,4 +19,8 @@ module.exports = merge(baseConfig, {
__userApi: `"${path.join(__dirname, '../../src/main/modules/userApi').replace(/\\/g, '\\\\')}"`, __userApi: `"${path.join(__dirname, '../../src/main/modules/userApi').replace(/\\/g, '\\\\')}"`,
}), }),
], ],
performance: {
maxEntrypointSize: 1024 * 1024 * 50,
maxAssetSize: 1024 * 1024 * 30,
},
}) })

View File

@ -26,11 +26,11 @@ module.exports = merge(baseConfig, {
patterns: [ patterns: [
{ {
from: path.join(__dirname, '../../src/main/modules/userApi/renderer'), from: path.join(__dirname, '../../src/main/modules/userApi/renderer'),
to: path.join(__dirname, '../../dist/electron/userApi/renderer'), to: path.join(__dirname, '../../dist/userApi/renderer'),
}, },
{ {
from: path.join(__dirname, '../../src/main/modules/userApi/rendererEvent/name.js'), from: path.join(__dirname, '../../src/main/modules/userApi/rendererEvent/name.js'),
to: path.join(__dirname, '../../dist/electron/userApi/rendererEvent/name.js'), to: path.join(__dirname, '../../dist/userApi/rendererEvent/name.js'),
}, },
], ],
}), }),
@ -40,6 +40,10 @@ module.exports = merge(baseConfig, {
}, },
}), }),
], ],
performance: {
maxEntrypointSize: 1024 * 1024 * 10,
maxAssetSize: 1024 * 1024 * 20,
},
optimization: { optimization: {
minimize: false, minimize: false,
}, },

View File

@ -14,7 +14,7 @@ const okayLog = chalk.bgGreen.white(' OKAY ') + ' '
function build() { function build() {
del.sync(['dist/electron/**', 'build/**']) del.sync(['dist/**', 'build/**'])
const spinners = new Spinnies({ color: 'blue' }) const spinners = new Spinnies({ color: 'blue' })
spinners.add('main', { text: 'main building' }) spinners.add('main', { text: 'main building' })

View File

@ -17,7 +17,7 @@ module.exports = {
output: { output: {
filename: '[name].js', filename: '[name].js',
libraryTarget: 'commonjs2', libraryTarget: 'commonjs2',
path: path.join(__dirname, '../../dist/electron'), path: path.join(__dirname, '../../dist'),
publicPath: 'auto', publicPath: 'auto',
}, },
resolve: { resolve: {
@ -127,9 +127,6 @@ module.exports = {
}, },
], ],
}, },
performance: {
maxEntrypointSize: 300000,
},
plugins: [ plugins: [
new HTMLPlugin({ new HTMLPlugin({
filename: 'lyric.html', filename: 'lyric.html',

View File

@ -1,8 +1,7 @@
const path = require('path') // const path = require('path')
const webpack = require('webpack') const webpack = require('webpack')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { merge } = require('webpack-merge') const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.config.base') const baseConfig = require('./webpack.config.base')
@ -20,14 +19,6 @@ module.exports = merge(baseConfig, {
...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)), ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)),
], ],
plugins: [ plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: path.join(__dirname, '../../src/static'),
to: path.join(__dirname, '../../dist/electron/static'),
},
],
}),
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env': { 'process.env': {
NODE_ENV: '"production"', NODE_ENV: '"production"',
@ -44,6 +35,8 @@ module.exports = merge(baseConfig, {
], ],
}, },
performance: { performance: {
maxEntrypointSize: 1024 * 1024 * 10,
maxAssetSize: 1024 * 1024 * 20,
hints: 'warning', hints: 'warning',
}, },
node: { node: {

View File

@ -17,7 +17,7 @@ module.exports = {
output: { output: {
filename: '[name].js', filename: '[name].js',
libraryTarget: 'commonjs2', libraryTarget: 'commonjs2',
path: path.join(__dirname, '../../dist/electron'), path: path.join(__dirname, '../../dist'),
publicPath: 'auto', publicPath: 'auto',
}, },
resolve: { resolve: {
@ -127,9 +127,6 @@ module.exports = {
}, },
], ],
}, },
performance: {
maxEntrypointSize: 300000,
},
plugins: [ plugins: [
new HTMLPlugin({ new HTMLPlugin({
filename: 'index.html', filename: 'index.html',

View File

@ -23,7 +23,7 @@ module.exports = merge(baseConfig, {
patterns: [ patterns: [
{ {
from: path.join(__dirname, '../../src/static'), from: path.join(__dirname, '../../src/static'),
to: path.join(__dirname, '../../dist/electron/static'), to: path.join(__dirname, '../../dist/static'),
}, },
], ],
}), }),
@ -43,6 +43,8 @@ module.exports = merge(baseConfig, {
], ],
}, },
performance: { performance: {
maxEntrypointSize: 1024 * 1024 * 10,
maxAssetSize: 1024 * 1024 * 20,
hints: 'warning', hints: 'warning',
}, },
node: { node: {

View File

@ -151,7 +151,7 @@ function startElectron() {
let args = [ let args = [
'--inspect=5858', '--inspect=5858',
// 'NODE_ENV=development', // 'NODE_ENV=development',
path.join(__dirname, '../dist/electron/main.js'), path.join(__dirname, '../dist/main.js'),
] ]
// detect yarn or npm and process commandline args accordingly // detect yarn or npm and process commandline args accordingly

5701
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
{ {
"name": "lx-music-desktop", "name": "lx-music-desktop",
"version": "1.20.0", "version": "1.21.0",
"description": "一个免费的音乐查找助手", "description": "一个免费的音乐查找助手",
"main": "./dist/electron/main.js", "main": "./dist/main.js",
"productName": "lx-music-desktop", "productName": "lx-music-desktop",
"scripts": { "scripts": {
"pack": "node build-config/pack.js && npm run pack:win:setup:x64", "pack": "node build-config/pack.js && npm run pack:win:setup:x64",
@ -57,7 +57,7 @@
"publish:linux:rpm": "cross-env ARCH=x64 electron-builder -l=rpm --x64 -p onTagOrDraft", "publish:linux:rpm": "cross-env ARCH=x64 electron-builder -l=rpm --x64 -p onTagOrDraft",
"publish:linux:pacman": "cross-env ARCH=x64 electron-builder -l=pacman --x64 -p onTagOrDraft", "publish:linux:pacman": "cross-env ARCH=x64 electron-builder -l=pacman --x64 -p onTagOrDraft",
"dev": "node build-config/runner-dev.js", "dev": "node build-config/runner-dev.js",
"clean:electron": "rimraf dist/electron", "clean:electron": "rimraf dist",
"clean": "rimraf dist && rimraf build", "clean": "rimraf dist && rimraf build",
"build:src": "node build-config/pack.js", "build:src": "node build-config/pack.js",
"build:main": "cross-env NODE_ENV=production webpack --config build-config/main/webpack.config.prod.js --progress", "build:main": "cross-env NODE_ENV=production webpack --config build-config/main/webpack.config.prod.js --progress",
@ -74,7 +74,7 @@
], ],
"engines": { "engines": {
"node": ">= 16", "node": ">= 16",
"npm": ">=8.3.0" "npm": ">=8.5.2"
}, },
"build": { "build": {
"appId": "cn.toside.music.desktop", "appId": "cn.toside.music.desktop",
@ -89,7 +89,7 @@
"output": "./build" "output": "./build"
}, },
"files": [ "files": [
"dist/electron/**/*" "dist/**/*"
], ],
"asar": { "asar": {
"smartUnpack": false "smartUnpack": false
@ -178,33 +178,33 @@
}, },
"homepage": "https://github.com/lyswhut/lx-music-desktop#readme", "homepage": "https://github.com/lyswhut/lx-music-desktop#readme",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.17.9", "@babel/core": "^7.18.0",
"@babel/eslint-parser": "^7.17.0", "@babel/eslint-parser": "^7.17.0",
"@babel/plugin-proposal-class-properties": "^7.16.7", "@babel/plugin-proposal-class-properties": "^7.17.12",
"@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-modules-umd": "^7.16.7", "@babel/plugin-transform-modules-umd": "^7.18.0",
"@babel/plugin-transform-runtime": "^7.17.0", "@babel/plugin-transform-runtime": "^7.18.0",
"@babel/polyfill": "^7.12.1", "@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.16.11", "@babel/preset-env": "^7.18.0",
"babel-loader": "^8.2.4", "babel-loader": "^8.2.5",
"babel-preset-minify": "^0.5.1", "babel-preset-minify": "^0.5.2",
"browserslist": "^4.20.2", "browserslist": "^4.20.3",
"chalk": "^4.1.2", "chalk": "^4.1.2",
"changelog-parser": "^2.8.1", "changelog-parser": "^2.8.1",
"copy-webpack-plugin": "^10.2.4", "copy-webpack-plugin": "^11.0.0",
"core-js": "^3.22.0", "core-js": "^3.22.5",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"css-loader": "^6.7.1", "css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^3.4.1", "css-minimizer-webpack-plugin": "^4.0.0",
"del": "^6.0.0", "del": "^6.1.0",
"electron": "^13.6.9", "electron": "^13.6.9",
"electron-builder": "^23.0.6", "electron-builder": "^23.0.9",
"electron-debug": "^3.2.0", "electron-debug": "^3.2.0",
"electron-devtools-installer": "^3.2.0", "electron-devtools-installer": "^3.2.0",
"electron-to-chromium": "^1.4.111", "electron-to-chromium": "^1.4.137",
"electron-updater": "^5.0.2", "electron-updater": "^5.0.4",
"eslint": "^8.13.0", "eslint": "^8.16.0",
"eslint-config-standard": "^16.0.3", "eslint-config-standard": "^17.0.0",
"eslint-formatter-friendly": "git+https://github.com/lyswhut/eslint-friendly-formatter.git#2170d1320e2fad13615a9dcf229669f0bb473a53", "eslint-formatter-friendly": "git+https://github.com/lyswhut/eslint-friendly-formatter.git#2170d1320e2fad13615a9dcf229669f0bb473a53",
"eslint-plugin-html": "^6.2.0", "eslint-plugin-html": "^6.2.0",
"eslint-plugin-import": "^2.26.0", "eslint-plugin-import": "^2.26.0",
@ -215,12 +215,11 @@
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"html-webpack-plugin": "^5.5.0", "html-webpack-plugin": "^5.5.0",
"less": "^4.1.2", "less": "^4.1.2",
"less-loader": "^10.2.0", "less-loader": "^11.0.0",
"markdown-it": "^12.3.2",
"mini-css-extract-plugin": "^2.6.0", "mini-css-extract-plugin": "^2.6.0",
"node-loader": "^2.0.0", "node-loader": "^2.0.0",
"postcss": "^8.4.12", "postcss": "^8.4.14",
"postcss-loader": "^6.2.1", "postcss-loader": "^7.0.0",
"postcss-pxtorem": "^6.0.0", "postcss-pxtorem": "^6.0.0",
"pug": "^3.0.2", "pug": "^3.0.2",
"pug-loader": "^2.4.0", "pug-loader": "^2.4.0",
@ -235,16 +234,16 @@
"url-loader": "^4.1.1", "url-loader": "^4.1.1",
"vue-loader": "^17.0.0", "vue-loader": "^17.0.0",
"vue-template-compiler": "^2.6.14", "vue-template-compiler": "^2.6.14",
"webpack": "^5.72.0", "webpack": "^5.72.1",
"webpack-cli": "^4.9.2", "webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.8.1", "webpack-dev-server": "^4.9.0",
"webpack-hot-middleware": "git+https://github.com/lyswhut/webpack-hot-middleware.git#329c4375134b89d39da23a56a94db651247c74a1", "webpack-hot-middleware": "git+https://github.com/lyswhut/webpack-hot-middleware.git#329c4375134b89d39da23a56a94db651247c74a1",
"webpack-merge": "^5.8.0" "webpack-merge": "^5.8.0"
}, },
"dependencies": { "dependencies": {
"bufferutil": "^4.0.6", "bufferutil": "^4.0.6",
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"electron-log": "^4.4.6", "electron-log": "^4.4.7",
"electron-store": "^8.0.1", "electron-store": "^8.0.1",
"font-list": "git+https://github.com/lyswhut/node-font-list.git#4edbb1933b49a9bac1eedd63a31da16b487fe57d", "font-list": "git+https://github.com/lyswhut/node-font-list.git#4edbb1933b49a9bac1eedd63a31da16b487fe57d",
"http-terminator": "^3.2.0", "http-terminator": "^3.2.0",
@ -256,13 +255,13 @@
"needle": "^3.1.0", "needle": "^3.1.0",
"node-id3": "^0.2.3", "node-id3": "^0.2.3",
"request": "^2.88.2", "request": "^2.88.2",
"socket.io": "^4.4.1", "socket.io": "^4.5.1",
"sortablejs": "^1.15.0", "sortablejs": "^1.15.0",
"tunnel": "^0.0.6", "tunnel": "^0.0.6",
"utf-8-validate": "^5.0.9", "utf-8-validate": "^5.0.9",
"vue": "^3.2.33", "vue": "^3.2.35",
"vue-i18n": "^9.2.0-beta.35", "vue-i18n": "^9.2.0-beta.35",
"vue-router": "^4.0.14", "vue-router": "^4.1.0-aabe509",
"vuex": "^4.0.2" "vuex": "^4.0.2"
}, },
"overrides": { "overrides": {

View File

@ -1,36 +1,23 @@
特别说明Scheme URL其实是支持Linux系统的但好像需要deb之类的安装包创建出`.desktop`文件才行。
### 新增 ### 新增
- 新增播放详情页歌词右键菜单,原来设置-播放详情页设置的字体重置已迁移到此菜单内 - 新增设置-播放设置-显示歌词罗马音,默认关闭,注:目前只有网易源能获取到罗马音歌词(得益于 Binaryify/NeteaseCloudMusicApi/pull/1523如果你知道其他源的歌词罗马音获取方式欢迎PR或开issue交流
- 新增歌词偏移设置,可以在播放详情页歌词右键菜单中使用
- 新增设置-播放设置-播放错误时自动切换歌曲设置默认开启原来的行为若你不想在遇到音频加载失败、url获取失败等错误时自动切歌可以关闭此设置
- 新增设置-桌面歌词设置-自动刷新歌词置顶(当歌词置顶后仍被某些程序遮挡时可尝试启用此设置)
- 新增列表更新管理,可以在鼠标移入“我的列表”标题时出现的按钮中进入,这可以用来设置启动软件时需要自动从原平台更新的列表
### 优化 ### 优化
- 优化播放详情页背景显示,现在有背景图片的主题可以在播放详情页显示它的图片了 - 同时删除一首歌以上时将需要二次确认删除
- 播放详情页在全屏状态下仍会显示退出播放详情页按钮,同时在其旁边添加退出全屏按钮 - 禁用透明窗口时右侧不再偏移5px距离在win7、Ubuntu等系统上测试发现不偏移也不影响滚动条的拖动了
- 播放详情页在全屏状态下鼠标在空白处静止不动3秒后自动将其隐藏 - 删除未下载完成的任务时,只同时尝试删除已有下载进度的本地文件
- 在全屏状态下使用`Esc`键可以退出全屏(#827
### 修复 ### 修复
- 修复Linux无法全屏的问题 - 修复某些情况下歌曲播放出错时不会自动切歌的问题
- 修复播放下载列表的歌曲时使用Windows任务栏缩略图工具栏控制按钮的收藏按钮收藏歌曲时的异常问题 - 修复关闭“显示切换动画”设置后,在应用启动时该设置没有被应用的问题
- 修复启用搜索历史但不启用热门搜索时,搜索历史不显示的问题 - 修复原始歌词存在偏移时,歌词偏移设置的重置未按预期工作的问题
- 修复窗口尺寸设置对应的字体大小在启动后不生效的问题 - 修复长度大于一行的歌词在使用歌词调整播放进度时的时间不准问题
- 修复wy源搜索某些歌曲时第一页之后的歌曲无法加载的问题 - 修复潜在歌单更新失败的问题
- 修复使用Scheme URL搜索歌曲时不会自动关闭播放详情页若处于打开状态的问题
- 修复换源失败时的处理问题
- 修复启用代理时https请求可能被挂起或被转为http的问题
- 修复正在下载的歌曲暂停任务后,再开始会导致程序卡死的问题
### 变更 ### 文档
- 播放详情页的任意地方右键双击隐藏详情页的行为,“任意区域”改为在“非歌词区域” - 将歌曲添加“稍后播放”后,它们会被放在一个优先级最高的特殊队列中,点击“下一曲”时会消耗该队列中的歌曲,并且无法通过“上一曲”功能播放该队列的上一首歌曲
- 在切歌时若不是通过“上一曲”、“下一曲”功能切歌(例如直接点击“排行榜列表”、“我的列表”中的歌曲切歌),“稍后播放”队列将会被清空
### 移除
- 移除设置-播放详情页设置-歌词字体重置,此设置项已迁移到播放详情页的歌词菜单中
- 移除播放详情页使用+-快捷键调整字体大小的功能,改用歌词右键菜单的字体大小调整功能

View File

@ -9,12 +9,12 @@ const version_bak = JSON.stringify(version, null, 2)
const parseChangelog = require('changelog-parser') const parseChangelog = require('changelog-parser')
const changelogPath = jp('../../CHANGELOG.md') const changelogPath = jp('../../CHANGELOG.md')
const md_renderer = markdownStr => new (require('markdown-it'))({ // const md_renderer = markdownStr => new (require('markdown-it'))({
html: true, // html: true,
linkify: true, // linkify: true,
typographer: true, // typographer: true,
breaks: true, // breaks: true,
}).render(markdownStr) // }).render(markdownStr)
const getPrevVer = () => parseChangelog(changelogPath).then(res => { const getPrevVer = () => parseChangelog(changelogPath).then(res => {
if (!res.versions.length) throw new Error('CHANGELOG 无法解析到版本号') if (!res.versions.length) throw new Error('CHANGELOG 无法解析到版本号')
@ -28,7 +28,7 @@ const updateChangeLog = async(newVerNum, newChangeLog) => {
fs.writeFileSync(changelogPath, changeLog.replace(new RegExp('(## [?0.1.1]?)'), log + '\n$1'), 'utf-8') fs.writeFileSync(changelogPath, changeLog.replace(new RegExp('(## [?0.1.1]?)'), log + '\n$1'), 'utf-8')
} }
const renderChangeLog = md => md_renderer(md) // const renderChangeLog = md => md_renderer(md)
module.exports = async newVerNum => { module.exports = async newVerNum => {
@ -38,7 +38,7 @@ module.exports = async newVerNum => {
newVerNum = verArr.join('.') newVerNum = verArr.join('.')
} }
const newMDChangeLog = fs.readFileSync(jp('../changeLog.md'), 'utf-8') const newMDChangeLog = fs.readFileSync(jp('../changeLog.md'), 'utf-8')
const newChangeLog = renderChangeLog(newMDChangeLog) // const newChangeLog = renderChangeLog(newMDChangeLog)
version.history.unshift({ version.history.unshift({
version: version.version, version: version.version,
desc: version.desc, desc: version.desc,
@ -58,7 +58,7 @@ module.exports = async newVerNum => {
return { return {
pkg_bak, pkg_bak,
version_bak, version_bak,
changeLog: newChangeLog, // changeLog: newChangeLog,
} }
} }

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@ const path = require('path')
const os = require('os') const os = require('os')
const defaultSetting = { const defaultSetting = {
version: '1.0.56', version: '1.0.57',
player: { player: {
togglePlayMethod: 'listLoop', togglePlayMethod: 'listLoop',
highQuality: false, highQuality: false,
@ -12,6 +12,7 @@ const defaultSetting = {
mediaDeviceId: 'default', mediaDeviceId: 'default',
isMediaDeviceRemovedStopPlay: false, isMediaDeviceRemovedStopPlay: false,
isShowLyricTranslation: false, isShowLyricTranslation: false,
isShowLyricRoma: false,
isS2t: false, // 是否将歌词从简体转换为繁体 isS2t: false, // 是否将歌词从简体转换为繁体
isPlayLxlrc: true, isPlayLxlrc: true,
isSavePlayTime: false, isSavePlayTime: false,

View File

@ -120,6 +120,7 @@
"lists__new_list_btn": "Create list", "lists__new_list_btn": "Create list",
"lists__new_list_input": "New list...", "lists__new_list_input": "New list...",
"lists__remove": "Remove", "lists__remove": "Remove",
"lists__remove music_tip": "Do you really want to remove the selected {len} songs?",
"lists__remove_tip": "Do you really want to remove {name}?", "lists__remove_tip": "Do you really want to remove {name}?",
"lists__remove_tip_button": "Yes, that's right", "lists__remove_tip_button": "Yes, that's right",
"lists__rename": "Rename", "lists__rename": "Rename",
@ -355,7 +356,8 @@
"setting__play_detail_font_size_reset": "Reset", "setting__play_detail_font_size_reset": "Reset",
"setting__play_detail_font_zoom": "Zoom the currently playing lyrics", "setting__play_detail_font_zoom": "Zoom the currently playing lyrics",
"setting__play_detail_lyric_progress": "Allows to adjust playback progress by lyrics", "setting__play_detail_lyric_progress": "Allows to adjust playback progress by lyrics",
"setting__play_lyric_lxlrc": "Use Karaoke-style lyrics playback (if supported)", "setting__play_lyric_lxlrc": "Play with karaoke-style lyrics (if available)",
"setting__play_lyric_roma": "Show lyrics roman",
"setting__play_lyric_s2t": "Convert the playing and downloading lyrics to Traditional Chinese", "setting__play_lyric_s2t": "Convert the playing and downloading lyrics to Traditional Chinese",
"setting__play_lyric_transition": "Show lyrics translation", "setting__play_lyric_transition": "Show lyrics translation",
"setting__play_mediaDevice": "Audio output", "setting__play_mediaDevice": "Audio output",
@ -363,7 +365,7 @@
"setting__play_mediaDevice_title": "Select a media device for audio output", "setting__play_mediaDevice_title": "Select a media device for audio output",
"setting__play_media_device_error_tip": "This function conflicts with the audio visualization function. You have enabled audio visualization when you started the software this time. This setting is temporarily unavailable. Please restart the software and then modify this setting.", "setting__play_media_device_error_tip": "This function conflicts with the audio visualization function. You have enabled audio visualization when you started the software this time. This setting is temporarily unavailable. Please restart the software and then modify this setting.",
"setting__play_media_device_tip": "This feature conflicts with Audio Visualization, both cannot be enabled at the same time, would you like to turn Audio Visualization off and apply the selected audio output settings?", "setting__play_media_device_tip": "This feature conflicts with Audio Visualization, both cannot be enabled at the same time, would you like to turn Audio Visualization off and apply the selected audio output settings?",
"setting__play_quality": "Play 320K quality songs first (if supported)", "setting__play_quality": "Priority playback of 320K quality songs (if available)",
"setting__play_save_play_time": "Remember playback progress", "setting__play_save_play_time": "Remember playback progress",
"setting__play_task_bar": "Show playing progress on the taskbar", "setting__play_task_bar": "Show playing progress on the taskbar",
"setting__play_timeout": "Timed pause", "setting__play_timeout": "Timed pause",
@ -432,6 +434,7 @@
"sync__title": "Choose how to synchronize the list with {name}", "sync__title": "Choose how to synchronize the list with {name}",
"tag__high_quality": "HQ", "tag__high_quality": "HQ",
"tag__lossless": "SQ", "tag__lossless": "SQ",
"tag__lossless_24bit": "24bit",
"theme_auto": "Auto", "theme_auto": "Auto",
"theme_auto_tip": "Right-click to open the light and dark theme settings window", "theme_auto_tip": "Right-click to open the light and dark theme settings window",
"theme_black": "Black", "theme_black": "Black",

View File

@ -120,6 +120,7 @@
"lists__new_list_btn": "新建列表", "lists__new_list_btn": "新建列表",
"lists__new_list_input": "新列表...", "lists__new_list_input": "新列表...",
"lists__remove": "删除", "lists__remove": "删除",
"lists__remove music_tip": "你真的要移除所选的 {len} 首歌曲吗?",
"lists__remove_tip": "你真的想要移除 {name} 吗?", "lists__remove_tip": "你真的想要移除 {name} 吗?",
"lists__remove_tip_button": "是的 没错", "lists__remove_tip_button": "是的 没错",
"lists__rename": "重命名", "lists__rename": "重命名",
@ -355,15 +356,16 @@
"setting__play_detail_font_size_reset": "重置", "setting__play_detail_font_size_reset": "重置",
"setting__play_detail_font_zoom": "缩放当前正在播放的歌词", "setting__play_detail_font_zoom": "缩放当前正在播放的歌词",
"setting__play_detail_lyric_progress": "允许通过歌词调整播放进度", "setting__play_detail_lyric_progress": "允许通过歌词调整播放进度",
"setting__play_lyric_lxlrc": "使用卡拉OK式歌词播放如果支持", "setting__play_lyric_lxlrc": "使用卡拉OK式歌词播放如果可用",
"setting__play_lyric_roma": "显示歌词罗马音(如果可用)",
"setting__play_lyric_s2t": "将播放与下载的歌词转换为繁体中文", "setting__play_lyric_s2t": "将播放与下载的歌词转换为繁体中文",
"setting__play_lyric_transition": "显示歌词翻译", "setting__play_lyric_transition": "显示歌词翻译(如果可用)",
"setting__play_mediaDevice": "音频输出", "setting__play_mediaDevice": "音频输出",
"setting__play_mediaDevice_remove_stop_play": "当前的声音输出设备被改变时暂停播放歌曲", "setting__play_mediaDevice_remove_stop_play": "当前的声音输出设备被改变时暂停播放歌曲",
"setting__play_mediaDevice_title": "选择声音输出的媒体设备", "setting__play_mediaDevice_title": "选择声音输出的媒体设备",
"setting__play_media_device_error_tip": "此功能与音频可视化功能冲突,你本次启动软件时已启用过音频可视化,此设置暂不可用,请 重启 软件后,再来修改此设置。", "setting__play_media_device_error_tip": "此功能与音频可视化功能冲突,你本次启动软件时已启用过音频可视化,此设置暂不可用,请 重启 软件后,再来修改此设置。",
"setting__play_media_device_tip": "此功能与音频可视化功能冲突,两者无法同时启用,是否将音频可视化关闭 并 应用所选音频输出设置?", "setting__play_media_device_tip": "此功能与音频可视化功能冲突,两者无法同时启用,是否将音频可视化关闭 并 应用所选音频输出设置?",
"setting__play_quality": "优先播放320K品质的歌曲如果支持", "setting__play_quality": "优先播放320K品质的歌曲如果可用",
"setting__play_save_play_time": "记住播放进度", "setting__play_save_play_time": "记住播放进度",
"setting__play_task_bar": "在任务栏上显示当前歌曲播放进度", "setting__play_task_bar": "在任务栏上显示当前歌曲播放进度",
"setting__play_timeout": "定时暂停", "setting__play_timeout": "定时暂停",
@ -432,6 +434,7 @@
"sync__title": "选择与 {name} 的列表同步方式", "sync__title": "选择与 {name} 的列表同步方式",
"tag__high_quality": "HQ", "tag__high_quality": "HQ",
"tag__lossless": "SQ", "tag__lossless": "SQ",
"tag__lossless_24bit": "24bit",
"theme_auto": "道法自然", "theme_auto": "道法自然",
"theme_auto_tip": "鼠标 右击 可打开亮、暗主题设置窗口", "theme_auto_tip": "鼠标 右击 可打开亮、暗主题设置窗口",
"theme_black": "黑灯瞎火", "theme_black": "黑灯瞎火",

View File

@ -120,6 +120,7 @@
"lists__new_list_btn": "新建列表", "lists__new_list_btn": "新建列表",
"lists__new_list_input": "新列表...", "lists__new_list_input": "新列表...",
"lists__remove": "刪除", "lists__remove": "刪除",
"lists__remove music_tip": "你真的要移除所選的 {len} 首歌曲嗎?",
"lists__remove_tip": "你真的想要移除 {name} 嗎?", "lists__remove_tip": "你真的想要移除 {name} 嗎?",
"lists__remove_tip_button": "是的 沒錯", "lists__remove_tip_button": "是的 沒錯",
"lists__rename": "重命名", "lists__rename": "重命名",
@ -355,15 +356,16 @@
"setting__play_detail_font_size_reset": "重置", "setting__play_detail_font_size_reset": "重置",
"setting__play_detail_font_zoom": "縮放當前正在播放的歌詞", "setting__play_detail_font_zoom": "縮放當前正在播放的歌詞",
"setting__play_detail_lyric_progress": "允許通過歌詞調整播放進度", "setting__play_detail_lyric_progress": "允許通過歌詞調整播放進度",
"setting__play_lyric_lxlrc": "使用卡拉OK式歌詞播放如果支持", "setting__play_lyric_lxlrc": "使用卡拉OK式歌詞播放如果可用",
"setting__play_lyric_roma": "顯示歌詞羅馬音(如果可用)",
"setting__play_lyric_s2t": "將播放與下載的歌詞轉換為繁體中文", "setting__play_lyric_s2t": "將播放與下載的歌詞轉換為繁體中文",
"setting__play_lyric_transition": "顯示歌詞翻譯", "setting__play_lyric_transition": "顯示歌詞翻譯(如果可用)",
"setting__play_mediaDevice": "音頻輸出", "setting__play_mediaDevice": "音頻輸出",
"setting__play_mediaDevice_remove_stop_play": "當前的聲音輸出設備被改變時暫停播放歌曲", "setting__play_mediaDevice_remove_stop_play": "當前的聲音輸出設備被改變時暫停播放歌曲",
"setting__play_mediaDevice_title": "選擇聲音輸出的媒體設備", "setting__play_mediaDevice_title": "選擇聲音輸出的媒體設備",
"setting__play_media_device_error_tip": "此功能與音頻可視化功能衝突,你本次啟動軟件時已啟用過音頻可視化,此設置暫不可用,請 重啟 軟件後,再來修改此設置。", "setting__play_media_device_error_tip": "此功能與音頻可視化功能衝突,你本次啟動軟件時已啟用過音頻可視化,此設置暫不可用,請 重啟 軟件後,再來修改此設置。",
"setting__play_media_device_tip": "此功能與音頻可視化功能衝突,兩者無法同時啟用,是否將音頻可視化關閉 並 應用所選音頻輸出設置?", "setting__play_media_device_tip": "此功能與音頻可視化功能衝突,兩者無法同時啟用,是否將音頻可視化關閉 並 應用所選音頻輸出設置?",
"setting__play_quality": "優先播放320K品質的歌曲如果支持", "setting__play_quality": "優先播放320K品質的歌曲如果可用",
"setting__play_save_play_time": "記住播放進度", "setting__play_save_play_time": "記住播放進度",
"setting__play_task_bar": "在任務欄上顯示當前歌曲播放進度", "setting__play_task_bar": "在任務欄上顯示當前歌曲播放進度",
"setting__play_timeout": "定時暫停", "setting__play_timeout": "定時暫停",
@ -432,6 +434,7 @@
"sync__title": "選擇與 {name} 的列表同步方式", "sync__title": "選擇與 {name} 的列表同步方式",
"tag__high_quality": "HQ", "tag__high_quality": "HQ",
"tag__lossless": "SQ", "tag__lossless": "SQ",
"tag__lossless_24bit": "24bit",
"theme_auto": "道法自然", "theme_auto": "道法自然",
"theme_auto_tip": "鼠標 右擊 可打開亮、暗主題設置窗口", "theme_auto_tip": "鼠標 右擊 可打開亮、暗主題設置窗口",
"theme_black": "黑燈瞎火", "theme_black": "黑燈瞎火",

View File

@ -67,11 +67,10 @@ exports.createWindow = async userApi => {
event.preventDefault() event.preventDefault()
}) })
} }
global.modules.userApiWindow.webContents.session.setPermissionRequestHandler((webContents, permission, callback) => { global.modules.userApiWindow.webContents.session.setPermissionRequestHandler((webContents, permission, resolve) => {
if (webContents === global.modules.mainWindow.webContents) return callback(true) if (webContents === global.modules.mainWindow.webContents) return resolve(true)
// eslint-disable-next-line node/no-callback-literal resolve(false)
callback(false)
}) })
global.modules.userApiWindow.webContents.setWindowOpenHandler(() => { global.modules.userApiWindow.webContents.setWindowOpenHandler(() => {
return { action: 'deny' } return { action: 'deny' }

View File

@ -165,7 +165,7 @@ contextBridge.exposeInMainWorld('lx', {
headers: resp.headers, headers: resp.headers,
bytes: resp.bytes, bytes: resp.bytes,
raw: resp.raw, raw: resp.raw,
body: body, body,
}, body) }, body)
}).request }).request
@ -212,7 +212,7 @@ contextBridge.exposeInMainWorld('lx', {
}, },
rsaEncrypt(buffer, key) { rsaEncrypt(buffer, key) {
buffer = Buffer.concat([Buffer.alloc(128 - buffer.length), buffer]) buffer = Buffer.concat([Buffer.alloc(128 - buffer.length), buffer])
return publicEncrypt({ key: key, padding: constants.RSA_NO_PADDING }, buffer) return publicEncrypt({ key, padding: constants.RSA_NO_PADDING }, buffer)
}, },
randomBytes(size) { randomBytes(size) {
return randomBytes(size) return randomBytes(size)

View File

@ -41,6 +41,7 @@ const setLrcConfig = () => {
config: desktopLyric, config: desktopLyric,
languageId: global.appSetting.langId, languageId: global.appSetting.langId,
isShowLyricTranslation: global.appSetting.player.isShowLyricTranslation, isShowLyricTranslation: global.appSetting.player.isShowLyricTranslation,
isShowLyricRoma: global.appSetting.player.isShowLyricRoma,
isPlayLxlrc: global.appSetting.player.isPlayLxlrc, isPlayLxlrc: global.appSetting.player.isPlayLxlrc,
}) })
if (isLock != desktopLyric.isLock) { if (isLock != desktopLyric.isLock) {

View File

@ -28,6 +28,7 @@ mainHandle(ipcWinLyricNames.get_lyric_config, async() => {
config: global.appSetting.desktopLyric, config: global.appSetting.desktopLyric,
languageId: global.appSetting.langId, languageId: global.appSetting.langId,
isShowLyricTranslation: global.appSetting.player.isShowLyricTranslation, isShowLyricTranslation: global.appSetting.player.isShowLyricTranslation,
isShowLyricRoma: global.appSetting.player.isShowLyricRoma,
isPlayLxlrc: global.appSetting.player.isPlayLxlrc, isPlayLxlrc: global.appSetting.player.isPlayLxlrc,
} }
}) })

View File

@ -4,7 +4,7 @@
transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut") transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut")
.control-bar(v-show="!lrcConfig.isLock") .control-bar(v-show="!lrcConfig.isLock")
core-control-bar(:lrcConfig="lrcConfig" :themes="themeList") core-control-bar(:lrcConfig="lrcConfig" :themes="themeList")
core-lyric(:lrcConfig="lrcConfig" :isPlayLxlrc="isPlayLxlrc" :isShowLyricTranslation="isShowLyricTranslation") core-lyric(:lrcConfig="lrcConfig" :isPlayLxlrc="isPlayLxlrc" :isShowLyricTranslation="isShowLyricTranslation" :isShowLyricRoma="isShowLyricRoma")
div.resize-left(@mousedown.self="handleMouseDown('left', $event)" @touchstart.self="handleTouchDown('left', $event)") div.resize-left(@mousedown.self="handleMouseDown('left', $event)" @touchstart.self="handleTouchDown('left', $event)")
div.resize-top(@mousedown.self="handleMouseDown('top', $event)" @touchstart.self="handleTouchDown('top', $event)") div.resize-top(@mousedown.self="handleMouseDown('top', $event)" @touchstart.self="handleTouchDown('top', $event)")
div.resize-right(@mousedown.self="handleMouseDown('right', $event)" @touchstart.self="handleTouchDown('right', $event)") div.resize-right(@mousedown.self="handleMouseDown('right', $event)" @touchstart.self="handleTouchDown('right', $event)")
@ -46,7 +46,8 @@ export default {
isZoomActiveLrc: true, isZoomActiveLrc: true,
}, },
}, },
isShowLyricTranslation: true, isShowLyricTranslation: false,
isShowLyricRoma: false,
isPlayLxlrc: true, isPlayLxlrc: true,
themeList: [ themeList: [
{ {
@ -111,9 +112,10 @@ export default {
document.removeEventListener('mouseup', this.handleMouseUp) document.removeEventListener('mouseup', this.handleMouseUp)
}, },
methods: { methods: {
handleUpdateConfig({ config, languageId, isShowLyricTranslation, isPlayLxlrc }) { handleUpdateConfig({ config, languageId, isShowLyricTranslation, isShowLyricRoma, isPlayLxlrc }) {
this.lrcConfig = config this.lrcConfig = config
this.isShowLyricTranslation = isShowLyricTranslation this.isShowLyricTranslation = isShowLyricTranslation
this.isShowLyricRoma = isShowLyricRoma
this.isPlayLxlrc = isPlayLxlrc this.isPlayLxlrc = isPlayLxlrc
if (this.$i18n.locale !== languageId && languageId != null) this.$i18n.locale = languageId if (this.$i18n.locale !== languageId && languageId != null) this.$i18n.locale = languageId
}, },

View File

@ -40,6 +40,10 @@ export default {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
isShowLyricRoma: {
type: Boolean,
default: true,
},
}, },
data() { data() {
return { return {
@ -74,6 +78,7 @@ export default {
lyrics: { lyrics: {
lyric: '', lyric: '',
tlyric: '', tlyric: '',
rlyric: '',
lxlyric: '', lxlyric: '',
}, },
} }
@ -143,6 +148,10 @@ export default {
this.setLyric() this.setLyric()
rendererSend(NAMES.winLyric.get_lyric_info, 'status') rendererSend(NAMES.winLyric.get_lyric_info, 'status')
}, },
isShowLyricRoma() {
this.setLyric()
rendererSend(NAMES.winLyric.get_lyric_info, 'status')
},
isPlayLxlrc() { isPlayLxlrc() {
this.setLyric() this.setLyric()
rendererSend(NAMES.winLyric.get_lyric_info, 'status') rendererSend(NAMES.winLyric.get_lyric_info, 'status')
@ -195,6 +204,7 @@ export default {
case 'lyric': case 'lyric':
this.lyrics.lyric = data.lrc this.lyrics.lyric = data.lrc
this.lyrics.tlyric = data.tlrc this.lyrics.tlyric = data.tlrc
this.lyrics.rlyric = data.rlrc
this.lyrics.lxlyric = data.lxlrc this.lyrics.lxlyric = data.lxlrc
this.setLyric() this.setLyric()
break break
@ -211,12 +221,14 @@ export default {
this.lyrics.lyric = '' this.lyrics.lyric = ''
this.lyrics.tlyric = '' this.lyrics.tlyric = ''
this.lyrics.lxlyric = '' this.lyrics.lxlyric = ''
this.lyrics.rlyric = ''
this.setLyric() this.setLyric()
break break
case 'info': case 'info':
// console.log('info', data) // console.log('info', data)
this.lyrics.lyric = data.lrc this.lyrics.lyric = data.lrc
this.lyrics.tlyric = data.tlrc this.lyrics.tlyric = data.tlrc
this.lyrics.rlyric = data.rlrc
this.lyrics.lxlyric = data.lxlrc this.lyrics.lxlyric = data.lxlrc
this.setLyric() this.setLyric()
this.$nextTick(() => { this.$nextTick(() => {
@ -343,10 +355,12 @@ export default {
rendererSend(NAMES.winLyric.close) rendererSend(NAMES.winLyric.close)
}, },
setLyric() { setLyric() {
const extendedLyrics = []
if (this.isShowLyricTranslation && this.lyrics.tlyric) extendedLyrics.push(this.lyrics.tlyric)
if (this.isShowLyricRoma && this.lyrics.rlyric) extendedLyrics.push(this.lyrics.rlyric)
window.lrc.setLyric( window.lrc.setLyric(
this.isPlayLxlrc && this.lyrics.lxlyric ? this.lyrics.lxlyric : this.lyrics.lyric, this.isPlayLxlrc && this.lyrics.lxlyric ? this.lyrics.lxlyric : this.lyrics.lyric,
this.isShowLyricTranslation && this.lyrics.tlyric ? this.lyrics.tlyric : '', extendedLyrics,
// (this.isShowLyricTranslation && this.lyrics.tlyric ? (this.lyrics.tlyric + '\n') : '') + (this.lyrics.lyric || ''),
) )
}, },
}, },
@ -375,11 +389,11 @@ export default {
display: inline-block; display: inline-block;
} }
.font, .translation { .font, .extended {
cursor: grab; cursor: grab;
} }
.translation { .extended {
transition: @transition-theme !important; transition: @transition-theme !important;
transition-property: font-size, color; transition-property: font-size, color;
font-size: 0.8em; font-size: 0.8em;
@ -396,7 +410,7 @@ export default {
.line { .line {
color: @color-theme; color: @color-theme;
} }
.translation { .extended {
color: @color-theme; color: @color-theme;
} }
// span { // span {
@ -465,7 +479,7 @@ export default {
.draging { .draging {
:global { :global {
.lrc-content { .lrc-content {
.font, .translation { .font, .extended {
cursor: grabbing; cursor: grabbing;
} }
} }
@ -475,7 +489,7 @@ export default {
:global { :global {
.lrc-content { .lrc-content {
&.active { &.active {
.translation { .extended {
font-size: .94em; font-size: .94em;
} }
span { span {
@ -508,7 +522,7 @@ each(@themes, {
:global { :global {
.lrc-content { .lrc-content {
&.active { &.active {
.translation { .extended {
color: ~'@{color-@{value}-theme}'; color: ~'@{color-@{value}-theme}';
} }
.line { .line {

View File

@ -14,41 +14,13 @@
</template> </template>
<script> <script>
import { useRefGetter, watch, onMounted } from '@renderer/utils/vueTools' import { onMounted } from '@renderer/utils/vueTools'
import useApp from '@renderer/core/useApp' import useApp from '@renderer/core/useApp'
import { isFullscreen } from '@renderer/core/share'
import { getFontSizeWithScreen } from '@renderer/utils'
export default { export default {
setup() { setup() {
const theme = useRefGetter('theme')
const font = useRefGetter('font')
const windowSizeActive = useRefGetter('windowSizeActive')
const dom_root = document.getElementById('root') const dom_root = document.getElementById('root')
watch(theme, (val) => {
dom_root.className = val
})
watch(font, (val) => {
document.documentElement.style.fontFamily = val
}, {
immediate: true,
})
watch(isFullscreen, val => {
if (val) {
document.body.classList.remove(window.dt ? 'disableTransparent' : 'transparent')
document.body.classList.add('fullscreen')
document.documentElement.style.fontSize = getFontSizeWithScreen(window.screen.width) + 'px'
} else {
document.body.classList.remove('fullscreen')
document.body.classList.add(window.dt ? 'disableTransparent' : 'transparent')
document.documentElement.style.fontSize = windowSizeActive.value.fontSize
}
}, {
immediate: true,
})
useApp() useApp()
onMounted(() => { onMounted(() => {
@ -124,9 +96,9 @@ body {
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
} }
#view { // 5px // #view { // 5px
margin-right: 5Px; // margin-right: 5Px;
} // }
} }
.fullscreen { .fullscreen {
background-color: #fff; background-color: #fff;

View File

@ -55,6 +55,7 @@ export default {
}, },
getTypeName(type) { getTypeName(type) {
switch (type) { switch (type) {
case 'flac32bit':
case 'flac': case 'flac':
case 'ape': case 'ape':
case 'wav': case 'wav':

View File

@ -5,6 +5,7 @@
<base-btn :class="$style.btn" @click="handleClick('128k')">{{$t('download__normal')}} - 128K</base-btn> <base-btn :class="$style.btn" @click="handleClick('128k')">{{$t('download__normal')}} - 128K</base-btn>
<base-btn :class="$style.btn" @click="handleClick('320k')">{{$t('download__high_quality')}} - 320K</base-btn> <base-btn :class="$style.btn" @click="handleClick('320k')">{{$t('download__high_quality')}} - 320K</base-btn>
<base-btn :class="$style.btn" @click="handleClick('flac')">{{$t('download__lossless')}} - FLAC</base-btn> <base-btn :class="$style.btn" @click="handleClick('flac')">{{$t('download__lossless')}} - FLAC</base-btn>
<base-btn :class="$style.btn" @click="handleClick('flac32bit')">{{$t('download__lossless')}} - FLAC 24bit</base-btn>
</main> </main>
</material-modal> </material-modal>
</template> </template>

View File

@ -68,7 +68,7 @@ export default {
} }
return { return {
setting: setting, setting,
isShowAddMusicTo, isShowAddMusicTo,
nextTogglePlayName, nextTogglePlayName,
toggleNextPlayMode, toggleNextPlayMode,

View File

@ -27,8 +27,10 @@
<div :class="[$style.lyricSelectContent, 'select', 'scroll', 'lyricSelectContent']" v-if="isShowLrcSelectContent" @contextmenu="handleCopySelectText"> <div :class="[$style.lyricSelectContent, 'select', 'scroll', 'lyricSelectContent']" v-if="isShowLrcSelectContent" @contextmenu="handleCopySelectText">
<div v-for="(info, index) in lyric.lines" :key="index" :class="[$style.lyricSelectline, { [$style.lrcActive]: lyric.line == index }]"> <div v-for="(info, index) in lyric.lines" :key="index" :class="[$style.lyricSelectline, { [$style.lrcActive]: lyric.line == index }]">
<span>{{info.text}}</span> <span>{{info.text}}</span>
<br v-if="info.translation"/> <template v-for="(lrc, index) in info.extendedLyrics" :key="index">
<span :class="$style.lyricSelectlineTransition">{{info.translation}}</span> <br />
<span :class="$style.lyricSelectlineExtended">{{lrc}}</span>
</template>
</div> </div>
</div> </div>
</transition> </transition>
@ -77,13 +79,17 @@ export default {
const lyricInfo = reactive({ const lyricInfo = reactive({
lyric: '', lyric: '',
tlyric: '', tlyric: '',
rlyric: '',
lxlyric: '', lxlyric: '',
rawlyric: '',
musicInfo: null, musicInfo: null,
}) })
const updateMusicInfo = () => { const updateMusicInfo = () => {
lyricInfo.lyric = playerMusicInfo.lrc lyricInfo.lyric = playerMusicInfo.lrc
lyricInfo.tlyric = playerMusicInfo.tlrc lyricInfo.tlyric = playerMusicInfo.tlrc
lyricInfo.rlyric = playerMusicInfo.rlrc
lyricInfo.lxlyric = playerMusicInfo.lxlrc lyricInfo.lxlyric = playerMusicInfo.lxlrc
lyricInfo.rawlyric = playerMusicInfo.rawlrc
lyricInfo.musicInfo = musicInfoItem.value lyricInfo.musicInfo = musicInfoItem.value
} }
const handleShowLyricMenu = event => { const handleShowLyricMenu = event => {
@ -92,13 +98,14 @@ export default {
lyricMenuXY.y = event.pageY lyricMenuXY.y = event.pageY
lyricMenuVisible.value = true lyricMenuVisible.value = true
} }
const handleUpdateLyric = ({ lyric, tlyric, lxlyric, offset }) => { const handleUpdateLyric = ({ lyric, tlyric, rlyric, lxlyric, offset }) => {
setMusicInfo({ setMusicInfo({
lrc: lyric, lrc: lyric,
tlrc: tlyric, tlrc: tlyric,
rlrc: rlyric,
lxlrc: lxlyric, lxlrc: lxlyric,
}) })
console.log(offset) // console.log(offset)
window.eventHub.emit(eventPlayerNames.updateLyricOffset, offset) window.eventHub.emit(eventPlayerNames.updateLyricOffset, offset)
} }
@ -181,13 +188,13 @@ export default {
:global { :global {
.lrc-content { .lrc-content {
line-height: 1.2; line-height: 1.2;
padding: calc(var(--playDetail-lrc-font-size, 16px) / 2) 0; padding: calc(var(--playDetail-lrc-font-size, 16px) / 2) 1px;
overflow-wrap: break-word; overflow-wrap: break-word;
color: @color-player-detail-lyric; color: @color-player-detail-lyric;
transition: @transition-theme; transition: @transition-theme;
transition-property: padding; transition-property: padding;
.translation { .extended {
transition: @transition-theme !important; transition: @transition-theme !important;
transition-property: font-size, color; transition-property: font-size, color;
font-size: .9em; font-size: .9em;
@ -203,7 +210,7 @@ export default {
.line { .line {
color: @color-theme; color: @color-theme;
} }
.translation { .extended {
color: @color-theme; color: @color-theme;
} }
// span { // span {
@ -241,7 +248,7 @@ export default {
:global { :global {
.lrc-content { .lrc-content {
&.active { &.active {
.translation { .extended {
font-size: .94em; font-size: .94em;
} }
span { span {
@ -314,7 +321,7 @@ export default {
transition-property: color, font-size; transition-property: color, font-size;
line-height: 1.3; line-height: 1.3;
} }
.lyricSelectlineTransition { .lyricSelectlineExtended {
font-size: 14px; font-size: 14px;
} }
.lrc-active { .lrc-active {
@ -334,7 +341,7 @@ each(@themes, {
color: ~'@{color-@{value}-player-detail-lyric}'; color: ~'@{color-@{value}-player-detail-lyric}';
&.active { &.active {
.translation { .extended {
color: ~'@{color-@{value}-player-detail-lyric-active}'; color: ~'@{color-@{value}-player-detail-lyric-active}';
} }
.line { .line {

View File

@ -29,7 +29,7 @@
<div :class="$style.group"> <div :class="$style.group">
<div :class="$style.subGroup"> <div :class="$style.subGroup">
<div :class="$style.title">{{$t('lyric_menu__offset', { offset })}}</div> <div :class="$style.title">{{$t('lyric_menu__offset', { offset })}}</div>
<button :class="[$style.btn, $style.titleBtn]" :disabled="offsetDisabled || !offset" @click="offsetReset">{{$t('lyric_menu__offset_reset')}}</button> <button :class="[$style.btn, $style.titleBtn]" :disabled="offsetDisabled || offset == originOffset" @click="offsetReset">{{$t('lyric_menu__offset_reset')}}</button>
</div> </div>
<div :class="$style.subGroup"> <div :class="$style.subGroup">
<button :class="$style.btn" :disabled="offsetDisabled" @click="setOffset(10)" ignore-tip :aria-label="$t('lyric_menu__offset_add_10')">+ 10ms</button> <button :class="$style.btn" :disabled="offsetDisabled" @click="setOffset(10)" ignore-tip :aria-label="$t('lyric_menu__offset_add_10')">+ 10ms</button>
@ -59,6 +59,15 @@ const removeLyric = debounce(musicInfo => {
removeLyricEdited(musicInfo) removeLyricEdited(musicInfo)
}) })
const getOffset = lrc => {
let offset = offsetTagRxp.exec(lrc)
if (offset) {
offset = parseInt(offset[1])
if (Number.isNaN(offset)) offset = 0
} else offset = 0
return offset
}
export default { export default {
name: 'LyricMenu', name: 'LyricMenu',
props: { props: {
@ -75,6 +84,7 @@ export default {
const offset = ref(0) const offset = ref(0)
const offsetDisabled = ref(true) const offsetDisabled = ref(true)
const originOffset = ref(0)
const visible = computed(() => props.modelValue) const visible = computed(() => props.modelValue)
const musicInfo = computed(() => props.lyricInfo.musicInfo) const musicInfo = computed(() => props.lyricInfo.musicInfo)
@ -104,28 +114,35 @@ export default {
const updateLyric = offset => { const updateLyric = offset => {
let lyric = props.lyricInfo.lyric let lyric = props.lyricInfo.lyric
let tlyric = props.lyricInfo.tlyric let tlyric = props.lyricInfo.tlyric
let rlyric = props.lyricInfo.rlyric
let lxlyric = props.lyricInfo.lxlyric let lxlyric = props.lyricInfo.lxlyric
if (offsetTagRxp.test(lyric)) { if (offsetTagRxp.test(lyric)) {
lyric = lyric.replace(offsetTagAllRxp, `[offset:${offset}]`) lyric = lyric.replace(offsetTagAllRxp, `[offset:${offset}]`)
if (tlyric) tlyric = tlyric.replace(offsetTagAllRxp, `[offset:${offset}]`) if (tlyric) tlyric = tlyric.replace(offsetTagAllRxp, `[offset:${offset}]`)
if (lxlyric) lxlyric = lxlyric.replace(offsetTagAllRxp, `[offset:${offset}]`) if (lxlyric) lxlyric = lxlyric.replace(offsetTagAllRxp, `[offset:${offset}]`)
if (rlyric) rlyric = rlyric.replace(offsetTagAllRxp, `[offset:${offset}]`)
} else { } else {
lyric = `[offset:${offset}]\n` + lyric lyric = `[offset:${offset}]\n` + lyric
if (tlyric) tlyric = `[offset:${offset}]\n` + tlyric if (tlyric) tlyric = `[offset:${offset}]\n` + tlyric
if (lxlyric) lxlyric = `[offset:${offset}]\n` + lxlyric if (lxlyric) lxlyric = `[offset:${offset}]\n` + lxlyric
if (rlyric) rlyric = `[offset:${offset}]\n` + rlyric
} }
if (offset) { if (offset == originOffset.value) {
removeLyric(props.lyricInfo.musicInfo)
} else {
saveLyric(props.lyricInfo.musicInfo, { saveLyric(props.lyricInfo.musicInfo, {
lyric, lyric,
tlyric, tlyric,
rlyric,
lxlyric, lxlyric,
}) })
} else removeLyric(props.lyricInfo.musicInfo) }
emit('updateLyric', { emit('updateLyric', {
lyric, lyric,
tlyric, tlyric,
rlyric,
lxlyric, lxlyric,
offset, offset,
}) })
@ -135,25 +152,15 @@ export default {
updateLyric(offset.value) updateLyric(offset.value)
} }
const offsetReset = () => { const offsetReset = () => {
if (!offset.value) return if (offset.value == originOffset.value) return
offset.value = 0 offset.value = originOffset.value
updateLyric(0) updateLyric(originOffset.value)
} }
const parseLrcOffset = () => { const parseLrcOffset = () => {
let lrcOffset offset.value = getOffset(props.lyricInfo.lyric)
if (props.lyricInfo.lyric) { originOffset.value = getOffset(props.lyricInfo.rawlyric)
lrcOffset = offsetTagRxp.exec(props.lyricInfo.lyric) offsetDisabled.value = !props.lyricInfo.lyric
if (lrcOffset) {
lrcOffset = parseInt(lrcOffset[1])
if (Number.isNaN(lrcOffset)) lrcOffset = 0
} else lrcOffset = 0
offsetDisabled.value = false
} else {
offsetDisabled.value = true
lrcOffset = 0
}
offset.value = lrcOffset
} }
@ -177,6 +184,7 @@ export default {
menuStyles, menuStyles,
playDetailSetting, playDetailSetting,
offset, offset,
originOffset,
fontSizeUp, fontSizeUp,
fontSizeDown, fontSizeDown,
fontSizeReset, fontSizeReset,

View File

@ -119,6 +119,7 @@ export default {
p { p {
line-height: 1.5; line-height: 1.5;
word-break: break-all; word-break: break-all;
overflow-wrap: break-word;
} }
} }

View File

@ -20,9 +20,10 @@ div(:class="$style.songList")
div.list-item(@click="handleListItemClick($event, index)" @contextmenu="handleListItemRightClick($event, index)" div.list-item(@click="handleListItemClick($event, index)" @contextmenu="handleListItemRightClick($event, index)"
:class="[{ selected: rightClickSelectedIndex == index }, { active: selectedList.includes(item) }]") :class="[{ selected: rightClickSelectedIndex == index }, { active: selectedList.includes(item) }]")
div.list-item-cell.nobreak.center(:style="{ width: rowWidth.r1 }" style="padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{index + 1}} div.list-item-cell.nobreak.center(:style="{ width: rowWidth.r1 }" style="padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{index + 1}}
div.list-item-cell.auto(:style="{ width: rowWidth.r2 }" :aria-label="item.name + ((item._types.ape || item._types.flac || item._types.wav) ? ` - ${$t('tag__lossless')}` : item._types['320k'] ? ` - ${$t('tag__high_quality')}` : '')") div.list-item-cell.auto(:style="{ width: rowWidth.r2 }" :aria-label="item.name + (item._types.flac32bit ? ` - ${$t('tag__lossless_24bit')}` : (item._types.ape || item._types.flac || item._types.wav) ? ` - ${$t('tag__lossless')}` : item._types['320k'] ? ` - ${$t('tag__high_quality')}` : '')")
span.select {{item.name}} span.select {{item.name}}
span.badge.badge-theme-success(:class="[$style.labelQuality, $style.noSelect]" v-if="item._types.ape || item._types.flac || item._types.wav") {{$t('tag__lossless')}} span.badge.badge-theme-success(:class="[$style.labelQuality, $style.noSelect]" v-if="item._types.flac32bit") {{$t('tag__lossless_24bit')}}
span.badge.badge-theme-success(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item._types.ape || item._types.flac || item._types.wav") {{$t('tag__lossless')}}
span.badge.badge-theme-info(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item._types['320k']") {{$t('tag__high_quality')}} span.badge.badge-theme-info(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item._types['320k']") {{$t('tag__high_quality')}}
div.list-item-cell(:style="{ width: rowWidth.r3 }" :aria-label="item.singer") div.list-item-cell(:style="{ width: rowWidth.r3 }" :aria-label="item.singer")
span.select {{item.singer}} span.select {{item.singer}}

View File

@ -6,7 +6,9 @@ export const musicInfo = window.musicInfo = reactive({
img: null, img: null,
lrc: null, lrc: null,
tlrc: null, tlrc: null,
rlrc: null,
lxlrc: null, lxlrc: null,
rawlrc: null,
url: null, url: null,
name: '', name: '',
singer: '', singer: '',

View File

@ -1,4 +1,4 @@
import { openUrl } from '@renderer/utils' import { openUrl, getFontSizeWithScreen } from '@renderer/utils'
import { base as eventBaseName } from '@renderer/event/names' import { base as eventBaseName } from '@renderer/event/names'
import { onSetConfig, onSystemThemeChange } from '@renderer/utils/tools' import { onSetConfig, onSystemThemeChange } from '@renderer/utils/tools'
import { isFullscreen, themeShouldUseDarkColors } from '@renderer/core/share' import { isFullscreen, themeShouldUseDarkColors } from '@renderer/core/share'
@ -14,9 +14,18 @@ import {
const handle_key_esc_down = ({ event }) => { const handle_key_esc_down = ({ event }) => {
if (event.repeat) return if (event.repeat) return
if (event.target.tagName != 'INPUT' || event.target.classList.contains('ignore-esc')) return if (event.target.tagName != 'INPUT' || event.target.classList.contains('ignore-esc')) {
if (isFullscreen.value) {
event.lx_handled = true
rendererInvoke(NAMES.mainWindow.fullscreen, false).then(fullscreen => {
isFullscreen.value = fullscreen
})
}
return
}
event.target.value = '' event.target.value = ''
event.target.blur() event.target.blur()
event.lx_handled = true
} }
const handleBodyClick = event => { const handleBodyClick = event => {
if (event.target.tagName != 'A') return if (event.target.tagName != 'A') return
@ -44,10 +53,37 @@ export default ({
isProd, isProd,
isLinux, isLinux,
}) => { }) => {
const setSetting = useCommit('setSetting') const theme = useRefGetter('theme')
const font = useRefGetter('font')
const windowSizeActive = useRefGetter('windowSizeActive') const windowSizeActive = useRefGetter('windowSizeActive')
const setSetting = useCommit('setSetting')
const isShowAnimation = useRefGetter('isShowAnimation') const isShowAnimation = useRefGetter('isShowAnimation')
const dom_root = document.getElementById('root')
watch(theme, (val) => {
dom_root.className = val
})
watch(font, (val) => {
document.documentElement.style.fontFamily = val
}, {
immediate: true,
})
watch(isFullscreen, val => {
if (val) {
document.body.classList.remove(window.dt ? 'disableTransparent' : 'transparent')
document.body.classList.add('fullscreen')
document.documentElement.style.fontSize = getFontSizeWithScreen(window.screen.width) + 'px'
} else {
document.body.classList.remove('fullscreen')
document.body.classList.add(window.dt ? 'disableTransparent' : 'transparent')
document.documentElement.style.fontSize = windowSizeActive.value.fontSize
}
}, {
immediate: true,
})
watch(windowSizeActive, ({ fontSize }) => { watch(windowSizeActive, ({ fontSize }) => {
document.documentElement.style.fontSize = fontSize document.documentElement.style.fontSize = fontSize
}) })
@ -61,6 +97,8 @@ export default ({
document.body.classList.add('disableAnimation') document.body.classList.add('disableAnimation')
} }
} }
}, {
immediate: true,
}) })
const rSetConfig = onSetConfig((event, config) => { const rSetConfig = onSetConfig((event, config) => {

View File

@ -39,7 +39,7 @@ export default ({ setting }) => {
promise: userApiRequest({ promise: userApiRequest({
requestKey, requestKey,
data: { data: {
source: source, source,
action: 'musicUrl', action: 'musicUrl',
info: { info: {
type, type,

View File

@ -43,11 +43,14 @@ export default ({ setting }) => {
const setLyric = () => { const setLyric = () => {
if (!musicInfo.songmid) return if (!musicInfo.songmid) return
const extendedLyrics = []
if (setting.value.player.isShowLyricTranslation && musicInfo.tlrc) extendedLyrics.push(musicInfo.tlrc)
if (setting.value.player.isShowLyricRoma && musicInfo.rlrc) extendedLyrics.push(musicInfo.rlrc)
lrc.setLyric( lrc.setLyric(
setting.value.player.isPlayLxlrc && musicInfo.lxlrc ? musicInfo.lxlrc : musicInfo.lrc, setting.value.player.isPlayLxlrc && musicInfo.lxlrc ? musicInfo.lxlrc : musicInfo.lrc,
setting.value.player.isShowLyricTranslation && musicInfo.tlrc ? musicInfo.tlrc : '', extendedLyrics,
) )
setDesktopLyricInfo('lyric', { lrc: musicInfo.lrc, tlrc: musicInfo.tlrc, lxlrc: musicInfo.lxlrc }) setDesktopLyricInfo('lyric', { lrc: musicInfo.lrc, tlrc: musicInfo.tlrc, rlrc: musicInfo.rlrc, lxlrc: musicInfo.lxlrc })
if (isPlay.value && (musicInfo.url || playMusicInfo.listId == 'download')) { if (isPlay.value && (musicInfo.url || playMusicInfo.listId == 'download')) {
setTimeout(() => { setTimeout(() => {
@ -99,6 +102,7 @@ export default ({ setting }) => {
album: musicInfo.album, album: musicInfo.album,
lrc: musicInfo.lrc, lrc: musicInfo.lrc,
tlrc: musicInfo.tlrc, tlrc: musicInfo.tlrc,
rlrc: musicInfo.rlrc,
lxlrc: musicInfo.lxlrc, lxlrc: musicInfo.lxlrc,
isPlay: isPlay.value, isPlay: isPlay.value,
line: lyric.line, line: lyric.line,

View File

@ -91,8 +91,12 @@ export default ({
} }
if (setting.value.player.autoSkipOnError) { if (setting.value.player.autoSkipOnError) {
setAllStatus(t('player__error')) if (document.hidden) {
addDelayNextTimeout() playNext()
} else {
setAllStatus(t('player__error'))
setTimeout(addDelayNextTimeout)
}
} }
} }

View File

@ -135,21 +135,24 @@ export default ({ setting }) => {
} }
} }
const setLrc = (targetSong) => { const setLrc = (targetSong) => {
getLrc(targetSong).then(({ lyric, tlyric, lxlyric }) => { getLrc(targetSong).then(({ lyric, tlyric, rlyric, lxlyric, rawInfo }) => {
if (targetSong.songmid !== musicInfo.songmid) return if (targetSong.songmid !== musicInfo.songmid) return
return ( return (
setting.value.player.isS2t setting.value.player.isS2t
? Promise.all([ ? Promise.all([
lyric ? langS2T(lyric) : Promise.resolve(''), lyric ? langS2T(lyric) : Promise.resolve(''),
tlyric ? langS2T(tlyric) : Promise.resolve(''), tlyric ? langS2T(tlyric) : Promise.resolve(''),
rlyric ? langS2T(rlyric) : Promise.resolve(''),
lxlyric ? langS2T(lxlyric) : Promise.resolve(''), lxlyric ? langS2T(lxlyric) : Promise.resolve(''),
]) ])
: Promise.resolve([lyric, tlyric, lxlyric]) : Promise.resolve([lyric, tlyric, rlyric, lxlyric])
).then(([lyric, tlyric, lxlyric]) => { ).then(([lyric, tlyric, rlyric, lxlyric]) => {
setMusicInfo({ setMusicInfo({
lrc: lyric, lrc: lyric,
tlrc: tlyric, tlrc: tlyric,
rlrc: rlyric,
lxlrc: lxlyric, lxlrc: lxlyric,
rawlrc: rawInfo.lyric,
}) })
}) })
}).catch((err) => { }).catch((err) => {
@ -212,7 +215,9 @@ export default ({ setting }) => {
img: null, img: null,
lrc: null, lrc: null,
tlrc: null, tlrc: null,
rlrc: null,
lxlrc: null, lxlrc: null,
rawlrc: null,
url: null, url: null,
name: '', name: '',
singer: '', singer: '',
@ -223,6 +228,7 @@ export default ({ setting }) => {
// 播放音乐 // 播放音乐
const playMusic = async() => { const playMusic = async() => {
// console.log('playMusic') // console.log('playMusic')
isGettingUrl = false
setStopStatus() setStopStatus()
if (window.restorePlayInfo) { if (window.restorePlayInfo) {
handleRestorePlay(window.restorePlayInfo) handleRestorePlay(window.restorePlayInfo)

View File

@ -32,8 +32,9 @@ export default () => {
}) })
const rOnError = onError(() => { const rOnError = onError(() => {
// console.log('onError') // console.log('onError')
window.eventHub.emit(player.player_error, getErrorCode()) const errorCode = getErrorCode()
window.eventHub.emit(player.error) window.eventHub.emit(player.player_error, errorCode)
window.eventHub.emit(player.error, errorCode)
}) })
const rOnLoadeddata = onLoadeddata(() => { const rOnLoadeddata = onLoadeddata(() => {
// console.log('onLoadeddata') // console.log('onLoadeddata')

View File

@ -10,7 +10,7 @@ function route(path, view, name, meta, props) {
path, path,
meta, meta,
props, props,
component: (resovle) => import(`../views/${view}.vue`).then(resovle), component: require(`../views/${view}.vue`).default,
} }
} }

View File

@ -69,6 +69,7 @@ const getExt = type => {
case 'ape': case 'ape':
return 'ape' return 'ape'
case 'flac': case 'flac':
case 'flac32bit':
return 'flac' return 'flac'
case 'wav': case 'wav':
return 'wav' return 'wav'
@ -185,14 +186,14 @@ const getLyric = function(musicInfo, isUseOtherSource, isS2t) {
return getLyricFromStorage(musicInfo).then(lrcInfo => { return getLyricFromStorage(musicInfo).then(lrcInfo => {
return ( return (
existTimeExp.test(lrcInfo.lyric) existTimeExp.test(lrcInfo.lyric)
? Promise.resolve({ lyric: lrcInfo.lyric, tlyric: lrcInfo.tlyric || '' }) ? Promise.resolve({ lyric: lrcInfo.lyric, tlyric: lrcInfo.tlyric || '', rlyric: lrcInfo.rlyric || '', lxlyric: lrcInfo.lxlyric || '' })
: ( : (
isUseOtherSource isUseOtherSource
? handleGetLyric.call(this, musicInfo) ? handleGetLyric.call(this, musicInfo)
: music[musicInfo.source].getLyric(musicInfo).promise : music[musicInfo.source].getLyric(musicInfo).promise
).then(({ lyric, tlyric, lxlyric }) => { ).then(({ lyric, tlyric, rlyric, lxlyric }) => {
setLyric(musicInfo, { lyric, tlyric, lxlyric }) setLyric(musicInfo, { lyric, tlyric, rlyric, lxlyric })
return { lyric, tlyric, lxlyric } return { lyric, tlyric, rlyric, lxlyric }
}).catch(err => { }).catch(err => {
console.log(err) console.log(err)
return null return null
@ -519,7 +520,8 @@ const actions = {
delete dls[item.key] delete dls[item.key]
} }
commit('removeTask', item) commit('removeTask', item)
if (item.status != downloadStatus.COMPLETED) { // 没有未完成、已下载大于1k
if (item.status != downloadStatus.COMPLETED && item.progress.total && item.progress.downloaded > 1024) {
try { try {
await deleteFile(item.metadata.filePath) await deleteFile(item.metadata.filePath)
} catch (_) {} } catch (_) {}
@ -541,7 +543,8 @@ const actions = {
delete dls[item.key] delete dls[item.key]
} }
} }
if (item.status != downloadStatus.COMPLETED) { // 没有未完成、已下载大于1k
if (item.status != downloadStatus.COMPLETED && item.progress.total && item.progress.downloaded > 1024) {
deleteFile(item.metadata.filePath).catch(_ => _) deleteFile(item.metadata.filePath).catch(_ => _)
} }
} }

View File

@ -4,6 +4,7 @@ import {
getRandom, getRandom,
checkPath, checkPath,
getLyric as getStoreLyric, getLyric as getStoreLyric,
getLyricRaw as getStoreLyricRaw,
setLyric, setLyric,
setMusicUrl, setMusicUrl,
getMusicUrl as getStoreMusicUrl, getMusicUrl as getStoreMusicUrl,
@ -177,6 +178,14 @@ const getLyric = function(musicInfo, retryedSource = [], originMusic) {
}) })
} }
const buildLyricInfo = async(lyricInfo, musicInfo) => {
const lyricRawInfo = await getStoreLyricRaw(musicInfo)
return {
...lyricInfo,
rawInfo: lyricRawInfo,
}
}
// getters // getters
const getters = { const getters = {
@ -214,7 +223,7 @@ const actions = {
}, },
async getLrc({ commit, state }, musicInfo) { async getLrc({ commit, state }, musicInfo) {
const lrcInfo = await getStoreLyric(musicInfo) const lrcInfo = await getStoreLyric(musicInfo)
// lrcInfo = {} // let lrcInfo = {}
// if (lrcRequest && lrcRequest.cancelHttp) lrcRequest.cancelHttp() // if (lrcRequest && lrcRequest.cancelHttp) lrcRequest.cancelHttp()
if (existTimeExp.test(lrcInfo.lyric) && lrcInfo.tlyric != null) { if (existTimeExp.test(lrcInfo.lyric) && lrcInfo.tlyric != null) {
// if (musicInfo.lrc.startsWith('\ufeff[id:$00000000]')) { // if (musicInfo.lrc.startsWith('\ufeff[id:$00000000]')) {
@ -231,16 +240,18 @@ const actions = {
case 'kw': case 'kw':
break break
default: default:
return lrcInfo return buildLyricInfo(lrcInfo, musicInfo)
} }
} else return lrcInfo } else if (lrcInfo.rlyric == null) {
if (musicInfo.source != 'wy') return buildLyricInfo(lrcInfo, musicInfo)
} else return buildLyricInfo(lrcInfo, musicInfo)
} }
// lrcRequest = music[musicInfo.source].getLyric(musicInfo) // lrcRequest = music[musicInfo.source].getLyric(musicInfo)
return getLyric.call(this, musicInfo).then(({ lyric, tlyric, lxlyric }) => { return getLyric.call(this, musicInfo).then(({ lyric, tlyric, rlyric, lxlyric }) => {
// lrcRequest = null // lrcRequest = null
commit('setLrc', { musicInfo, lyric, tlyric, lxlyric }) commit('setLrc', { musicInfo, lyric, tlyric, rlyric, lxlyric })
return { lyric, tlyric, lxlyric } return buildLyricInfo({ lyric, tlyric, rlyric, lxlyric }, musicInfo)
}).catch(err => { }).catch(err => {
// lrcRequest = null // lrcRequest = null
return Promise.reject(err) return Promise.reject(err)
@ -434,6 +445,7 @@ const mutations = {
setLyric(datas.musicInfo, { setLyric(datas.musicInfo, {
lyric: datas.lyric, lyric: datas.lyric,
tlyric: datas.tlyric, tlyric: datas.tlyric,
rlyric: datas.rlyric,
lxlyric: datas.lxlyric, lxlyric: datas.lxlyric,
}) })
}, },

View File

@ -151,6 +151,7 @@ const mutations = {
state.text = text state.text = text
}, },
setList(state, datas) { setList(state, datas) {
if (!state.text) return
let source = state.sourceList[datas.source] let source = state.sourceList[datas.source]
datas.list = deduplicationList(datas.list) datas.list = deduplicationList(datas.list)
source.list = markRawList(datas.list) source.list = markRawList(datas.list)
@ -160,6 +161,7 @@ const mutations = {
source.limit = datas.limit source.limit = datas.limit
}, },
setLists(state, { results, page }) { setLists(state, { results, page }) {
if (!state.text) return
let pages = [] let pages = []
let total = 0 let total = 0
let limit = 0 let limit = 0

View File

@ -65,6 +65,8 @@ const getters = {
}, },
} }
let loadId = null
// actions // actions
const actions = { const actions = {
getTags({ state, rootState, commit }) { getTags({ state, rootState, commit }) {
@ -76,11 +78,14 @@ const actions = {
let tabId = rootState.setting.songList.tagInfo.id let tabId = rootState.setting.songList.tagInfo.id
let sortId = rootState.setting.songList.sortId let sortId = rootState.setting.songList.sortId
// console.log(sortId) // console.log(sortId)
let key = `slist__${source}__${sortId}__${tabId}__${page}` let key = loadId = `slist__${source}__${sortId}__${tabId}__${page}`
if (state.list.list.length && state.list.key == key) return if (state.list.list.length && state.list.key == key) return
if (cache.has(key)) return Promise.resolve(cache.get(key)).then(result => commit('setList', { result, key, page })) if (cache.has(key)) return Promise.resolve(cache.get(key)).then(result => commit('setList', { result, key, page }))
commit('clearList') commit('clearList')
return music[source]?.songList.getList(sortId, tabId, page).then(result => commit('setList', { result, key, page })) return music[source]?.songList.getList(sortId, tabId, page).then(result => {
if (loadId != key) return
commit('setList', { result, key, page })
})
}, },
getListDetail({ state, commit }, { id, source, page, isRefresh = false }) { getListDetail({ state, commit }, { id, source, page, isRefresh = false }) {
let key = `sdetail__${source}__${id}__${page}` let key = `sdetail__${source}__${id}__${page}`

View File

@ -513,13 +513,14 @@ export const parseUrlParams = str => {
} }
export const getLyric = musicInfo => rendererInvoke(NAMES.mainWindow.get_lyric, `${musicInfo.source}_${musicInfo.songmid}`) export const getLyric = musicInfo => rendererInvoke(NAMES.mainWindow.get_lyric, `${musicInfo.source}_${musicInfo.songmid}`)
export const setLyric = (musicInfo, { lyric, tlyric, lxlyric }) => rendererSend(NAMES.mainWindow.save_lyric_raw, { export const getLyricRaw = musicInfo => rendererInvoke(NAMES.mainWindow.get_lyric_raw, `${musicInfo.source}_${musicInfo.songmid}`)
export const setLyric = (musicInfo, { lyric, tlyric, rlyric, lxlyric }) => rendererSend(NAMES.mainWindow.save_lyric_raw, {
id: `${musicInfo.source}_${musicInfo.songmid}`, id: `${musicInfo.source}_${musicInfo.songmid}`,
lyrics: { lyric, tlyric, lxlyric }, lyrics: { lyric, tlyric, rlyric, lxlyric },
}) })
export const setLyricEdited = (musicInfo, { lyric, tlyric, lxlyric }) => rendererSend(NAMES.mainWindow.save_lyric_edited, { export const setLyricEdited = (musicInfo, { lyric, tlyric, rlyric, lxlyric }) => rendererSend(NAMES.mainWindow.save_lyric_edited, {
id: `${musicInfo.source}_${musicInfo.songmid}`, id: `${musicInfo.source}_${musicInfo.songmid}`,
lyrics: { lyric, tlyric, lxlyric }, lyrics: { lyric, tlyric, rlyric, lxlyric },
}) })
export const removeLyricEdited = musicInfo => rendererSend(NAMES.mainWindow.remove_lyric_edited, `${musicInfo.source}_${musicInfo.songmid}`) export const removeLyricEdited = musicInfo => rendererSend(NAMES.mainWindow.remove_lyric_edited, `${musicInfo.source}_${musicInfo.songmid}`)

View File

@ -20,14 +20,14 @@ const createAnimation = (dom, duration) => new window.Animation(new window.Keyfr
// https://jsfiddle.net/ceqpnbky/1/ // https://jsfiddle.net/ceqpnbky/1/
module.exports = class FontPlayer { module.exports = class FontPlayer {
constructor({ time = 0, lyric = '', translationLyric = '', lineClassName = '', fontClassName = '', translationClassName = '', lineModeClassName = '', shadowContent = false, shadowClassName = '' }) { constructor({ time = 0, lyric = '', extendedLyrics = '', lineClassName = '', fontClassName = '', extendedLrcClassName = '', lineModeClassName = '', shadowContent = false, shadowClassName = '' }) {
this.time = time this.time = time
this.lyric = lyric this.lyric = lyric
this.translationLyric = translationLyric this.extendedLyrics = extendedLyrics
this.lineClassName = lineClassName this.lineClassName = lineClassName
this.fontClassName = fontClassName this.fontClassName = fontClassName
this.translationClassName = translationClassName this.extendedLrcClassName = extendedLrcClassName
this.lineModeClassName = lineModeClassName this.lineModeClassName = lineModeClassName
this.shadowContent = shadowContent this.shadowContent = shadowContent
this.shadowClassName = shadowClassName this.shadowClassName = shadowClassName
@ -40,8 +40,8 @@ module.exports = class FontPlayer {
this.fontContent = null this.fontContent = null
this.timeoutTools = new TimeoutTools() this.timeoutTools = new TimeoutTools(80)
this.waitPlayTimeout = new TimeoutTools() this.waitPlayTimeout = new TimeoutTools(80)
this._init() this._init()
} }
@ -64,20 +64,20 @@ module.exports = class FontPlayer {
this.fontContent.appendChild(this.fontShadowContent) this.fontContent.appendChild(this.fontShadowContent)
} }
this.lineContent.appendChild(this.fontContent) this.lineContent.appendChild(this.fontContent)
if (this.translationLyric) { for (const lrc of this.extendedLyrics) {
this.translationContent = document.createElement('div') const extendedLrcContent = document.createElement('div')
this.translationContent.style = 'position:relative;display:inline-block;' extendedLrcContent.style = 'position:relative;display:inline-block;'
this.translationContent.className = this.translationClassName extendedLrcContent.className = this.extendedLrcClassName
this.translationContent.textContent = this.translationLyric extendedLrcContent.textContent = lrc
this.lineContent.appendChild(document.createElement('br')) this.lineContent.appendChild(document.createElement('br'))
this.lineContent.appendChild(this.translationContent) this.lineContent.appendChild(extendedLrcContent)
if (this.shadowContent) { if (this.shadowContent) {
this.translationShadowContent = document.createElement('div') const extendedLrcShadowContent = document.createElement('div')
this.translationShadowContent.style = 'position:absolute;top:0;left:0;width:100%;z-index:-1;' extendedLrcShadowContent.style = 'position:absolute;top:0;left:0;width:100%;z-index:-1;'
this.translationShadowContent.className = this.shadowClassName extendedLrcShadowContent.className = this.shadowClassName
this.translationShadowContent.textContent = this.translationLyric extendedLrcShadowContent.textContent = lrc
this.translationContent.appendChild(this.translationShadowContent) extendedLrcContent.appendChild(extendedLrcShadowContent)
} }
} }
this._parseLyric() this._parseLyric()

View File

@ -6,11 +6,11 @@ const fontTimeExp = /<(\d+),(\d+)>/g
module.exports = class Lyric { module.exports = class Lyric {
constructor({ constructor({
lyric = '', lyric = '',
translationLyric = '', extendedLyrics = [],
offset = 0, offset = 0,
lineClassName = '', lineClassName = '',
fontClassName = 'font', fontClassName = 'font',
translationClassName = 'translation', extendedLrcClassName = 'extended',
activeLineClassName = 'active', activeLineClassName = 'active',
lineModeClassName = 'line', lineModeClassName = 'line',
shadowClassName = '', shadowClassName = '',
@ -19,14 +19,14 @@ module.exports = class Lyric {
onSetLyric = function() { }, onSetLyric = function() { },
}) { }) {
this.lyric = lyric this.lyric = lyric
this.translationLyric = translationLyric this.extendedLyrics = extendedLyrics
this.offset = offset this.offset = offset
this.onPlay = onPlay this.onPlay = onPlay
this.onSetLyric = onSetLyric this.onSetLyric = onSetLyric
this.lineClassName = lineClassName this.lineClassName = lineClassName
this.fontClassName = fontClassName this.fontClassName = fontClassName
this.translationClassName = translationClassName this.extendedLrcClassName = extendedLrcClassName
this.activeLineClassName = activeLineClassName this.activeLineClassName = activeLineClassName
this.lineModeClassName = lineModeClassName this.lineModeClassName = lineModeClassName
this.shadowClassName = shadowClassName this.shadowClassName = shadowClassName
@ -46,7 +46,7 @@ module.exports = class Lyric {
this.playingLineNum = -1 this.playingLineNum = -1
this.isLineMode = false this.isLineMode = false
this.linePlayer.setLyric(this.lyric, this.translationLyric) this.linePlayer.setLyric(this.lyric, this.extendedLyrics)
} }
_handleLinePlayerOnPlay = (num, text, curTime) => { _handleLinePlayerOnPlay = (num, text, curTime) => {
@ -104,10 +104,10 @@ module.exports = class Lyric {
const fontPlayer = new FontPlayer({ const fontPlayer = new FontPlayer({
time: line.time, time: line.time,
lyric: line.text, lyric: line.text,
translationLyric: line.translation, extendedLyrics: line.extendedLyrics,
lineClassName: this.lineClassName, lineClassName: this.lineClassName,
fontClassName: this.fontClassName, fontClassName: this.fontClassName,
translationClassName: this.translationClassName, extendedLrcClassName: this.extendedLrcClassName,
lineModeClassName: this.lineModeClassName, lineModeClassName: this.lineModeClassName,
shadowClassName: this.shadowClassName, shadowClassName: this.shadowClassName,
shadowContent: this.shadowContent, shadowContent: this.shadowContent,
@ -117,7 +117,7 @@ module.exports = class Lyric {
return { return {
text: line.text, text: line.text,
time: line.time, time: line.time,
translation: line.translation, extendedLyrics: line.extendedLyrics,
dom_line: fontPlayer.lineContent, dom_line: fontPlayer.lineContent,
} }
}) })
@ -126,10 +126,10 @@ module.exports = class Lyric {
const fontPlayer = new FontPlayer({ const fontPlayer = new FontPlayer({
time: line.time, time: line.time,
lyric: line.text, lyric: line.text,
translationLyric: line.translation, extendedLyrics: line.extendedLyrics,
lineClassName: this.lineClassName, lineClassName: this.lineClassName,
fontClassName: this.fontClassName, fontClassName: this.fontClassName,
translationClassName: this.translationClassName, extendedLrcClassName: this.extendedLrcClassName,
shadowClassName: this.shadowClassName, shadowClassName: this.shadowClassName,
shadowContent: this.shadowContent, shadowContent: this.shadowContent,
}) })
@ -138,7 +138,7 @@ module.exports = class Lyric {
return { return {
text: line.text.replace(fontTimeExp, ''), text: line.text.replace(fontTimeExp, ''),
time: line.time, time: line.time,
translation: line.translation, extendedLyrics: line.extendedLyrics,
dom_line: fontPlayer.lineContent, dom_line: fontPlayer.lineContent,
} }
}) })
@ -162,9 +162,9 @@ module.exports = class Lyric {
if (this.playingLineNum > -1) this._lineFonts[this.playingLineNum].pause() if (this.playingLineNum > -1) this._lineFonts[this.playingLineNum].pause()
} }
setLyric(lyric, translationLyric) { setLyric(lyric, extendedLyrics) {
this.lyric = lyric this.lyric = lyric
this.translationLyric = translationLyric this.extendedLyrics = extendedLyrics
this._init() this._init()
} }
} }

View File

@ -11,11 +11,26 @@ const tagRegMap = {
const timeoutTools = new TimeoutTools() const timeoutTools = new TimeoutTools()
const parseExtendedLyric = (lrcLinesMap, extendedLyric) => {
const extendedLines = extendedLyric.split(/\r\n|\n|\r/)
for (let i = 0; i < extendedLines.length; i++) {
const line = extendedLines[i].trim()
let result = timeExp.exec(line)
if (result) {
const text = line.replace(timeExp, '').trim()
if (text) {
const timeStr = RegExp.$1.replace(/(\.\d\d)0$/, '$1')
const targetLine = lrcLinesMap[timeStr]
if (targetLine) targetLine.extendedLyrics.push(text)
}
}
}
}
module.exports = class LinePlayer { module.exports = class LinePlayer {
constructor({ offset = 0, onPlay = function() { }, onSetLyric = function() { } } = {}) { constructor({ offset = 0, onPlay = function() { }, onSetLyric = function() { } } = {}) {
this.tags = {} this.tags = {}
this.lines = null this.lines = null
this.translationLines = null
this.onPlay = onPlay this.onPlay = onPlay
this.onSetLyric = onSetLyric this.onSetLyric = onSetLyric
this.isPlay = false this.isPlay = false
@ -28,7 +43,7 @@ module.exports = class LinePlayer {
_init() { _init() {
if (this.lyric == null) this.lyric = '' if (this.lyric == null) this.lyric = ''
if (this.translationLyric == null) this.translationLyric = '' if (this.extendedLyrics == null) this.extendedLyrics = []
this._initTag() this._initTag()
this._initLines() this._initLines()
this.onSetLyric(this.lines, this.tags.offset + this.offset) this.onSetLyric(this.lines, this.tags.offset + this.offset)
@ -50,17 +65,15 @@ module.exports = class LinePlayer {
_initLines() { _initLines() {
this.lines = [] this.lines = []
this.translationLines = []
const lines = this.lyric.split(/\r\n|\r|\n/) const lines = this.lyric.split(/\r\n|\r|\n/)
const linesMap = {} const linesMap = {}
// const translationLines = this.translationLyric.split('\n')
for (let i = 0; i < lines.length; i++) { for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim() const line = lines[i].trim()
let result = timeExp.exec(line) let result = timeExp.exec(line)
if (result) { if (result) {
const text = line.replace(timeExp, '').trim() const text = line.replace(timeExp, '').trim()
if (text) { if (text) {
const timeStr = RegExp.$1 const timeStr = RegExp.$1.replace(/(\.\d\d)0$/, '$1')
const timeArr = timeStr.split(':') const timeArr = timeStr.split(':')
if (timeArr.length < 3) timeArr.unshift(0) if (timeArr.length < 3) timeArr.unshift(0)
if (timeArr[2].indexOf('.') > -1) { if (timeArr[2].indexOf('.') > -1) {
@ -70,24 +83,13 @@ module.exports = class LinePlayer {
linesMap[timeStr] = { linesMap[timeStr] = {
time: parseInt(timeArr[0]) * 60 * 60 * 1000 + parseInt(timeArr[1]) * 60 * 1000 + parseInt(timeArr[2]) * 1000 + parseInt(timeArr[3] || 0), time: parseInt(timeArr[0]) * 60 * 60 * 1000 + parseInt(timeArr[1]) * 60 * 1000 + parseInt(timeArr[2]) * 1000 + parseInt(timeArr[3] || 0),
text, text,
extendedLyrics: [],
} }
} }
} }
} }
const translationLines = this.translationLyric.split('\n') for (const lrc of this.extendedLyrics) parseExtendedLyric(linesMap, lrc)
for (let i = 0; i < translationLines.length; i++) {
const line = translationLines[i].trim()
let result = timeExp.exec(line)
if (result) {
const text = line.replace(timeExp, '').trim()
if (text) {
const timeStr = RegExp.$1
const targetLine = linesMap[timeStr]
if (targetLine) targetLine.translation = text
}
}
}
this.lines = Object.values(linesMap) this.lines = Object.values(linesMap)
this.lines.sort((a, b) => { this.lines.sort((a, b) => {
return a.time - b.time return a.time - b.time
@ -172,11 +174,11 @@ module.exports = class LinePlayer {
} }
} }
setLyric(lyric, translationLyric) { setLyric(lyric, extendedLyrics) {
// console.log(translationLyric) // console.log(extendedLyrics)
if (this.isPlay) this.pause() if (this.isPlay) this.pause()
this.lyric = lyric this.lyric = lyric
this.translationLyric = translationLyric this.extendedLyrics = extendedLyrics
this._init() this._init()
} }
} }

View File

@ -2,12 +2,12 @@
const getNow = exports.getNow = typeof performance == 'object' && window.performance.now ? window.performance.now.bind(window.performance) : Date.now.bind(Date) const getNow = exports.getNow = typeof performance == 'object' && window.performance.now ? window.performance.now.bind(window.performance) : Date.now.bind(Date)
exports.TimeoutTools = class TimeoutTools { exports.TimeoutTools = class TimeoutTools {
constructor() { constructor(thresholdTime = 200) {
this.invokeTime = 0 this.invokeTime = 0
this.animationFrameId = null this.animationFrameId = null
this.timeoutId = null this.timeoutId = null
this.callback = null this.callback = null
this.thresholdTime = 200 this.thresholdTime = thresholdTime
} }
run() { run() {

View File

@ -79,11 +79,9 @@ export default {
item: /data-song="({.+?})"/g, item: /data-song="({.+?})"/g,
info: /{total[\s:]+"(\d+)", size[\s:]+"(\d+)", page[\s:]+"(\d+)"}/, info: /{total[\s:]+"(\d+)", size[\s:]+"(\d+)", page[\s:]+"(\d+)"}/,
}, },
requestObj: null,
getData(url) { getData(url) {
if (this.requestObj) this.requestObj.cancelHttp() const requestObj = httpFetch(url)
this.requestObj = httpFetch(url) return requestObj.promise
return this.requestObj.promise
}, },
filterData(rawList) { filterData(rawList) {
// console.log(rawList) // console.log(rawList)

View File

@ -5,15 +5,13 @@ import { formatPlayTime } from '../../index'
// import { debug } from '../../utils/env' // import { debug } from '../../utils/env'
// import { formatSinger } from './util' // import { formatSinger } from './util'
let searchRequest
export default { export default {
limit: 30, limit: 30,
total: 0, total: 0,
page: 0, page: 0,
allPage: 1, allPage: 1,
musicSearch(str, page, limit) { musicSearch(str, page, limit) {
if (searchRequest && searchRequest.cancelHttp) searchRequest.cancelHttp() const searchRequest = httpFetch(`http://tingapi.ting.baidu.com/v1/restserver/ting?from=android&version=5.6.5.6&method=baidu.ting.search.merge&format=json&query=${encodeURIComponent(str)}&page_no=${page}&page_size=${limit}&type=0&data_source=0&use_cluster=1`)
searchRequest = httpFetch(`http://tingapi.ting.baidu.com/v1/restserver/ting?from=android&version=5.6.5.6&method=baidu.ting.search.merge&format=json&query=${encodeURIComponent(str)}&page_no=${page}&page_size=${limit}&type=0&data_source=0&use_cluster=1`)
return searchRequest.promise.then(({ body }) => body) return searchRequest.promise.then(({ body }) => body)
}, },
handleResult(rawData) { handleResult(rawData) {
@ -81,7 +79,7 @@ export default {
return Promise.resolve({ return Promise.resolve({
list, list,
allPage: this.allPage, allPage: this.allPage,
limit: limit, limit,
total: this.total, total: this.total,
source: 'bd', source: 'bd',
}) })

View File

@ -6,7 +6,6 @@ export default {
_requestObj_tags: null, _requestObj_tags: null,
_requestObj_list: null, _requestObj_list: null,
_requestObj_listRecommend: null, _requestObj_listRecommend: null,
_requestObj_listDetail: null,
limit_list: 30, limit_list: 30,
limit_song: 10000, limit_song: 10000,
successCode: 22000, successCode: 22000,
@ -66,7 +65,7 @@ export default {
}, },
} }
let encrypted = CryptoJS.AES.encrypt(strData, key, { let encrypted = CryptoJS.AES.encrypt(strData, key, {
iv: iv, iv,
blockSize: 16, blockSize: 16,
mode: CryptoJS.mode.CBC, mode: CryptoJS.mode.CBC,
format: JsonFormatter, format: JsonFormatter,
@ -74,9 +73,9 @@ export default {
let ciphertext = encrypted.toString().ct let ciphertext = encrypted.toString().ct
let sign = toMD5('baidu_taihe_music' + ciphertext + timestamp) let sign = toMD5('baidu_taihe_music' + ciphertext + timestamp)
let jsonRet = { let jsonRet = {
timestamp: timestamp, timestamp,
param: ciphertext, param: ciphertext,
sign: sign, sign,
} }
return jsonRet return jsonRet
}, },
@ -188,13 +187,12 @@ export default {
// 获取歌曲列表内的音乐 // 获取歌曲列表内的音乐
getListDetail(id, page, tryNum = 0) { getListDetail(id, page, tryNum = 0) {
if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num')) if (tryNum > 2) return Promise.reject(new Error('try max num'))
if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1') if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1')
this._requestObj_listDetail = httpFetch(this.getListDetailUrl(id, page)) const requestObj_listDetail = httpFetch(this.getListDetailUrl(id, page))
return this._requestObj_listDetail.promise.then(({ body }) => { return requestObj_listDetail.promise.then(({ body }) => {
if (body.error_code !== this.successCode) return this.getListDetail(id, page, ++tryNum) if (body.error_code !== this.successCode) return this.getListDetail(id, page, ++tryNum)
let listData = this.filterData(body.result.songlist) let listData = this.filterData(body.result.songlist)
return { return {

View File

@ -71,16 +71,14 @@ export default {
listData: /global\.features = (\[.+\]);/, listData: /global\.features = (\[.+\]);/,
}, },
_requestBoardsObj: null, _requestBoardsObj: null,
_requestDataObj: null,
getBoardsData() { getBoardsData() {
if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp() if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp()
this._requestBoardsObj = httpFetch('http://mobilecdnbj.kugou.com/api/v3/rank/list?version=9108&plat=0&showtype=2&parentid=0&apiver=6&area_code=1&withsong=1') this._requestBoardsObj = httpFetch('http://mobilecdnbj.kugou.com/api/v3/rank/list?version=9108&plat=0&showtype=2&parentid=0&apiver=6&area_code=1&withsong=1')
return this._requestBoardsObj.promise return this._requestBoardsObj.promise
}, },
getData(url) { getData(url) {
if (this._requestDataObj) this._requestDataObj.cancelHttp() const requestDataObj = httpFetch(url)
this._requestDataObj = httpFetch(url) return requestDataObj.promise
return this._requestDataObj.promise
}, },
filterData(rawList) { filterData(rawList) {
// console.log(rawList) // console.log(rawList)

View File

@ -5,15 +5,13 @@ import { decodeName, formatPlayTime, sizeFormate } from '../../index'
// import { debug } from '../../utils/env' // import { debug } from '../../utils/env'
// import { formatSinger } from './util' // import { formatSinger } from './util'
let searchRequest
export default { export default {
limit: 30, limit: 30,
total: 0, total: 0,
page: 0, page: 0,
allPage: 1, allPage: 1,
musicSearch(str, page, limit) { musicSearch(str, page, limit) {
if (searchRequest && searchRequest.cancelHttp) searchRequest.cancelHttp() const searchRequest = httpFetch(`http://ioscdn.kugou.com/api/v3/search/song?keyword=${encodeURIComponent(str)}&page=${page}&pagesize=${limit}&showtype=10&plat=2&version=7910&tag=1&correct=1&privilege=1&sver=5`)
searchRequest = httpFetch(`http://ioscdn.kugou.com/api/v3/search/song?keyword=${encodeURIComponent(str)}&page=${page}&pagesize=${limit}&showtype=10&plat=2&version=7910&tag=1&correct=1&privilege=1&sver=5`)
return searchRequest.promise.then(({ body }) => body) return searchRequest.promise.then(({ body }) => body)
}, },
filterData(rawData) { filterData(rawData) {

View File

@ -18,7 +18,6 @@ export default {
_requestObj_listInfo: null, _requestObj_listInfo: null,
_requestObj_list: null, _requestObj_list: null,
_requestObj_listRecommend: null, _requestObj_listRecommend: null,
_requestObj_listDetail: null,
listDetailLimit: 10000, listDetailLimit: 10000,
currentTagInfo: { currentTagInfo: {
id: undefined, id: undefined,
@ -477,15 +476,14 @@ export default {
} }
} else if (!link.includes('song.html')) return this.getUserListDetail3(link.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page) } else if (!link.includes('song.html')) return this.getUserListDetail3(link.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page)
} }
if (this._requestObj_listDetailLink) this._requestObj_listDetailLink.cancelHttp()
this._requestObj_listDetailLink = httpFetch(link, { const requestObj_listDetailLink = httpFetch(link, {
headers: { headers: {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1', 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
Referer: link, Referer: link,
}, },
}) })
const { headers: { location }, statusCode, body } = await this._requestObj_listDetailLink.promise const { headers: { location }, statusCode, body } = await requestObj_listDetailLink.promise
// console.log(body, location) // console.log(body, location)
if (statusCode > 400) return this.getUserListDetail(link, page, ++retryNum) if (statusCode > 400) return this.getUserListDetail(link, page, ++retryNum)
if (location) { if (location) {
@ -512,7 +510,6 @@ export default {
}, },
getListDetail(id, page, tryNum = 0) { // 获取歌曲列表内的音乐 getListDetail(id, page, tryNum = 0) { // 获取歌曲列表内的音乐
if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num')) if (tryNum > 2) return Promise.reject(new Error('try max num'))
id = id.toString() id = id.toString()
@ -528,8 +525,8 @@ export default {
// if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1') // if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1')
this._requestObj_listDetail = httpFetch(this.getSongListDetailUrl(id)) const requestObj_listDetail = httpFetch(this.getSongListDetailUrl(id))
return this._requestObj_listDetail.promise.then(({ body }) => { return requestObj_listDetail.promise.then(({ body }) => {
let listData = body.match(this.regExps.listData) let listData = body.match(this.regExps.listData)
let listInfo = body.match(this.regExps.listInfo) let listInfo = body.match(this.regExps.listInfo)
if (!listData) return this.getListDetail(id, page, ++tryNum) if (!listData) return this.getListDetail(id, page, ++tryNum)

View File

@ -3,7 +3,6 @@ import { decodeName } from '../../index'
import { formatSinger, objStr2JSON } from './util' import { formatSinger, objStr2JSON } from './util'
// let requestObj_list // let requestObj_list
let requestObj_listDetail
export default { export default {
limit_list: 36, limit_list: 36,
limit_song: 1000, limit_song: 1000,
@ -72,11 +71,8 @@ export default {
return num return num
}, },
getAlbumListDetail(id, page, retryNum = 0) { getAlbumListDetail(id, page, retryNum = 0) {
if (requestObj_listDetail) {
requestObj_listDetail.cancelHttp()
}
if (retryNum > 2) return Promise.reject(new Error('try max num')) if (retryNum > 2) return Promise.reject(new Error('try max num'))
requestObj_listDetail = httpFetch(`http://search.kuwo.cn/r.s?pn=${page - 1}&rn=${this.limit_song}&stype=albuminfo&albumid=${id}&show_copyright_off=0&encoding=utf&vipver=MUSIC_9.1.0`) const requestObj_listDetail = httpFetch(`http://search.kuwo.cn/r.s?pn=${page - 1}&rn=${this.limit_song}&stype=albuminfo&albumid=${id}&show_copyright_off=0&encoding=utf&vipver=MUSIC_9.1.0`)
return requestObj_listDetail.promise.then(({ statusCode, body }) => { return requestObj_listDetail.promise.then(({ statusCode, body }) => {
if (statusCode !== 200) return this.getAlbumListDetail(id, page, ++retryNum) if (statusCode !== 200) return this.getAlbumListDetail(id, page, ++retryNum)
body = objStr2JSON(body) body = objStr2JSON(body)

View File

@ -69,16 +69,14 @@ export default {
limit: 100, limit: 100,
_requestBoardsObj: null, _requestBoardsObj: null,
_requestDataObj: null,
getBoardsData() { getBoardsData() {
if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp() if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp()
this._requestBoardsObj = httpFetch('http://qukudata.kuwo.cn/q.k?op=query&cont=tree&node=2&pn=0&rn=1000&fmt=json&level=2') this._requestBoardsObj = httpFetch('http://qukudata.kuwo.cn/q.k?op=query&cont=tree&node=2&pn=0&rn=1000&fmt=json&level=2')
return this._requestBoardsObj.promise return this._requestBoardsObj.promise
}, },
getData(url) { getData(url) {
if (this._requestDataObj) this._requestDataObj.cancelHttp() const requestDataObj = httpFetch(url)
this._requestDataObj = httpFetch(url) return requestDataObj.promise
return this._requestDataObj.promise
}, },
filterData(rawList) { filterData(rawList) {
// console.log(rawList) // console.log(rawList)

View File

@ -93,6 +93,58 @@ const buildParams = (id, isGetLyricx) => {
const timeExp = /^\[([\d:.]*)\]{1}/g const timeExp = /^\[([\d:.]*)\]{1}/g
const existTimeExp = /\[\d{1,2}:.*\d{1,4}\]/ const existTimeExp = /\[\d{1,2}:.*\d{1,4}\]/
export default { export default {
/* sortLrcArr(arr) {
const lrcSet = new Set()
let lrc = []
let lrcT = []
let markIndex = []
for (const item of arr) {
if (lrcSet.has(item.time)) {
if (lrc.length < 2) continue
const index = lrc.findIndex(l => l.time == item.time)
markIndex.push(index)
if (index == lrc.length - 1) {
lrcT.push({ ...lrc[index], time: item.time })
lrc.push(item)
} else {
lrcT.push({ ...lrc[index], time: lrc[index + 1].time })
if (item.text) {
// const lastIndex = lrc.length - 1
// markIndex.push(lastIndex)
// lrcT.push({ ...lrc[lastIndex], time: lrc[lastIndex - 1].time })
lrc.push(item)
}
}
} else {
lrc.push(item)
lrcSet.add(item.time)
}
}
// console.log(markIndex)
markIndex = Array.from(new Set(markIndex))
for (let index = markIndex.length - 1; index >= 0; index--) {
lrc.splice(markIndex[index], 1)
}
// if (lrcT.length) {
// if (lrc.length * 0.4 < lrcT.length) { // 翻译数量需大于歌词数量的0.4倍,否则认为没有翻译
// const tItem = lrc.pop()
// tItem.time = lrc[lrc.length - 1].time
// lrcT.push(tItem)
// } else {
// lrc = arr
// lrcT = []
// }
// }
console.log(lrc, lrcT)
return {
lrc,
lrcT,
}
}, */
sortLrcArr(arr) { sortLrcArr(arr) {
const lrcSet = new Set() const lrcSet = new Set()
let lrc = [] let lrc = []
@ -111,15 +163,16 @@ export default {
} }
} }
if (lrcT.length) { if (lrcT.length > lrc.length * 0.3) {
if (lrc.length * 0.4 < lrcT.length) { // 翻译数量需大于歌词数量的0.4倍,否则认为没有翻译 throw new Error('failed')
const tItem = lrc.pop() // if (lrc.length * 0.4 < lrcT.length) { // 翻译数量需大于歌词数量的0.4倍,否则认为没有翻译
tItem.time = lrc[lrc.length - 1].time // const tItem = lrc.pop()
lrcT.push(tItem) // tItem.time = lrc[lrc.length - 1].time
} else { // lrcT.push(tItem)
lrc = arr // } else {
lrcT = [] // lrc = arr
} // lrcT = []
// }
} }
return { return {

View File

@ -9,16 +9,14 @@ export default {
regExps: { regExps: {
mInfo: /bitrate:(\d+),format:(\w+),size:([\w.]+)/, mInfo: /bitrate:(\d+),format:(\w+),size:([\w.]+)/,
}, },
_musicSearchRequestObj: null,
limit: 30, limit: 30,
total: 0, total: 0,
page: 0, page: 0,
allPage: 1, allPage: 1,
// cancelFn: null, // cancelFn: null,
musicSearch(str, page, limit) { musicSearch(str, page, limit) {
if (this._musicSearchRequestObj) this._musicSearchRequestObj.cancelHttp() const musicSearchRequestObj = httpFetch(`http://search.kuwo.cn/r.s?client=kt&all=${encodeURIComponent(str)}&pn=${page - 1}&rn=${limit}&uid=794762570&ver=kwplayer_ar_9.2.2.1&vipver=1&show_copyright_off=1&newver=1&ft=music&cluster=0&strategy=2012&encoding=utf8&rformat=json&vermerge=1&mobi=1&issubtitle=1`)
this._musicSearchRequestObj = httpFetch(`http://search.kuwo.cn/r.s?client=kt&all=${encodeURIComponent(str)}&pn=${page - 1}&rn=${limit}&uid=794762570&ver=kwplayer_ar_9.2.2.1&vipver=1&show_copyright_off=1&newver=1&ft=music&cluster=0&strategy=2012&encoding=utf8&rformat=json&vermerge=1&mobi=1&issubtitle=1`) return musicSearchRequestObj.promise
return this._musicSearchRequestObj.promise
}, },
// getImg(songId) { // getImg(songId) {
// return httpGet(`http://player.kuwo.cn/webmusic/sj/dtflagdate?flag=6&rid=MUSIC_${songId}`) // return httpGet(`http://player.kuwo.cn/webmusic/sj/dtflagdate?flag=6&rid=MUSIC_${songId}`)

View File

@ -7,7 +7,6 @@ export default {
_requestObj_tags: null, _requestObj_tags: null,
_requestObj_hotTags: null, _requestObj_hotTags: null,
_requestObj_list: null, _requestObj_list: null,
_requestObj_listDetail: null,
limit_list: 36, limit_list: 36,
limit_song: 10000, limit_song: 10000,
successCode: 200, successCode: 200,
@ -165,13 +164,10 @@ export default {
}, },
getListDetailDigest8(id, page, tryNum = 0) { getListDetailDigest8(id, page, tryNum = 0) {
if (this._requestObj_listDetail) {
this._requestObj_listDetail.cancelHttp()
}
if (tryNum > 2) return Promise.reject(new Error('try max num')) if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listDetail = httpFetch(this.getListDetailUrl(id, page)) const requestObj = httpFetch(this.getListDetailUrl(id, page))
return this._requestObj_listDetail.promise.then(({ body }) => { return requestObj.promise.then(({ body }) => {
if (body.result !== 'ok') return this.getListDetail(id, page, ++tryNum) if (body.result !== 'ok') return this.getListDetail(id, page, ++tryNum)
return { return {
list: this.filterListDetail(body.musiclist), list: this.filterListDetail(body.musiclist),
@ -190,24 +186,18 @@ export default {
}) })
}, },
getListDetailDigest5Info(id, tryNum = 0) { getListDetailDigest5Info(id, tryNum = 0) {
if (this._requestObj_listDetail) {
this._requestObj_listDetail.cancelHttp()
}
if (tryNum > 2) return Promise.reject(new Error('try max num')) if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listDetail = httpFetch(`http://qukudata.kuwo.cn/q.k?op=query&cont=ninfo&node=${id}&pn=0&rn=1&fmt=json&src=mbox&level=2`) const requestObj = httpFetch(`http://qukudata.kuwo.cn/q.k?op=query&cont=ninfo&node=${id}&pn=0&rn=1&fmt=json&src=mbox&level=2`)
return this._requestObj_listDetail.promise.then(({ statusCode, body }) => { return requestObj.promise.then(({ statusCode, body }) => {
if (statusCode != 200 || !body.child) return this.getListDetail(id, ++tryNum) if (statusCode != 200 || !body.child) return this.getListDetail(id, ++tryNum)
// console.log(body) // console.log(body)
return body.child.length ? body.child[0].sourceid : null return body.child.length ? body.child[0].sourceid : null
}) })
}, },
getListDetailDigest5Music(id, page, tryNum = 0) { getListDetailDigest5Music(id, page, tryNum = 0) {
if (this._requestObj_listDetail) {
this._requestObj_listDetail.cancelHttp()
}
if (tryNum > 2) return Promise.reject(new Error('try max num')) if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listDetail = httpFetch(`http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=${id}&pn=${page - 1}}&rn=${this.limit_song}&encode=utf-8&keyset=pl2012&identity=kuwo&pcmp4=1`) const requestObj = httpFetch(`http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=${id}&pn=${page - 1}}&rn=${this.limit_song}&encode=utf-8&keyset=pl2012&identity=kuwo&pcmp4=1`)
return this._requestObj_listDetail.promise.then(({ body }) => { return requestObj.promise.then(({ body }) => {
// console.log(body) // console.log(body)
if (body.result !== 'ok') return this.getListDetail(id, page, ++tryNum) if (body.result !== 'ok') return this.getListDetail(id, page, ++tryNum)
return { return {

View File

@ -65,7 +65,6 @@ export default {
}, },
successCode: '000000', successCode: '000000',
requestBoardsObj: null, requestBoardsObj: null,
requestObj: null,
getBoardsData() { getBoardsData() {
if (this.requestBoardsObj) this._requestBoardsObj.cancelHttp() if (this.requestBoardsObj) this._requestBoardsObj.cancelHttp()
this.requestBoardsObj = httpFetch('https://app.c.nf.migu.cn/MIGUM3.0/v1.0/template/rank-list/release', { this.requestBoardsObj = httpFetch('https://app.c.nf.migu.cn/MIGUM3.0/v1.0/template/rank-list/release', {
@ -79,9 +78,8 @@ export default {
return this.requestBoardsObj.promise return this.requestBoardsObj.promise
}, },
getData(url) { getData(url) {
if (this.requestObj) this.requestObj.cancelHttp() const requestObj = httpFetch(url)
this.requestObj = httpFetch(url) return requestObj.promise
return this.requestObj.promise
}, },
getSinger(singers) { getSinger(singers) {
let arr = [] let arr = []
@ -105,28 +103,28 @@ export default {
let size let size
switch (type.formatType) { switch (type.formatType) {
case 'PQ': case 'PQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '128k', size }) types.push({ type: '128k', size })
_types['128k'] = { _types['128k'] = {
size, size,
} }
break break
case 'HQ': case 'HQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '320k', size }) types.push({ type: '320k', size })
_types['320k'] = { _types['320k'] = {
size, size,
} }
break break
case 'SQ': case 'SQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac', size }) types.push({ type: 'flac', size })
_types.flac = { _types.flac = {
size, size,
} }
break break
case 'ZQ': case 'ZQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac32bit', size }) types.push({ type: 'flac32bit', size })
_types.flac32bit = { _types.flac32bit = {
size, size,

View File

@ -57,14 +57,12 @@ export default {
}, },
successCode: '000000', successCode: '000000',
requestBoardsObj: null, requestBoardsObj: null,
requestObj: null,
regExps: { regExps: {
listData: /var listData = (\{.+\})<\/script>/, listData: /var listData = (\{.+\})<\/script>/,
}, },
getData(url) { getData(url) {
if (this.requestObj) this.requestObj.cancelHttp() const requestObj = httpFetch(url)
this.requestObj = httpFetch(url) return requestObj.promise
return this.requestObj.promise
}, },
getSinger(singers) { getSinger(singers) {
let arr = [] let arr = []

View File

@ -5,15 +5,13 @@ import { sizeFormate } from '../../index'
// import { debug } from '../../utils/env' // import { debug } from '../../utils/env'
// import { formatSinger } from './util' // import { formatSinger } from './util'
let searchRequest
export default { export default {
limit: 20, limit: 20,
total: 0, total: 0,
page: 0, page: 0,
allPage: 1, allPage: 1,
musicSearch(str, page, limit) { musicSearch(str, page, limit) {
if (searchRequest && searchRequest.cancelHttp) searchRequest.cancelHttp() const searchRequest = httpFetch(`http://pd.musicapp.migu.cn/MIGUM2.0/v1.0/content/search_all.do?ua=Android_migu&version=5.0.1&text=${encodeURIComponent(str)}&pageNo=${page}&pageSize=${limit}&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A0%2C%22mvSong%22%3A0%2C%22songlist%22%3A0%2C%22bestShow%22%3A1%7D`, {
searchRequest = httpFetch(`http://pd.musicapp.migu.cn/MIGUM2.0/v1.0/content/search_all.do?ua=Android_migu&version=5.0.1&text=${encodeURIComponent(str)}&pageNo=${page}&pageSize=${limit}&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A0%2C%22mvSong%22%3A0%2C%22songlist%22%3A0%2C%22bestShow%22%3A1%7D`, {
// searchRequest = httpFetch(`http://jadeite.migu.cn:7090/music_search/v2/search/searchAll?sid=4f87090d01c84984a11976b828e2b02c18946be88a6b4c47bcdc92fbd40762db&isCorrect=1&isCopyright=1&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A1%2C%22mvSong%22%3A0%2C%22bestShow%22%3A1%2C%22songlist%22%3A0%2C%22lyricSong%22%3A0%7D&pageSize=${limit}&text=${encodeURIComponent(str)}&pageNo=${page}&sort=0`, { // searchRequest = httpFetch(`http://jadeite.migu.cn:7090/music_search/v2/search/searchAll?sid=4f87090d01c84984a11976b828e2b02c18946be88a6b4c47bcdc92fbd40762db&isCorrect=1&isCopyright=1&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A1%2C%22mvSong%22%3A0%2C%22bestShow%22%3A1%2C%22songlist%22%3A0%2C%22lyricSong%22%3A0%7D&pageSize=${limit}&text=${encodeURIComponent(str)}&pageNo=${page}&sort=0`, {
headers: { headers: {
// sign: 'c3b7ae985e2206e97f1b2de8f88691e2', // sign: 'c3b7ae985e2206e97f1b2de8f88691e2',
@ -49,28 +47,28 @@ export default {
let size let size
switch (type.formatType) { switch (type.formatType) {
case 'PQ': case 'PQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '128k', size }) types.push({ type: '128k', size })
_types['128k'] = { _types['128k'] = {
size, size,
} }
break break
case 'HQ': case 'HQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '320k', size }) types.push({ type: '320k', size })
_types['320k'] = { _types['320k'] = {
size, size,
} }
break break
case 'SQ': case 'SQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac', size }) types.push({ type: 'flac', size })
_types.flac = { _types.flac = {
size, size,
} }
break break
case 'ZQ': case 'ZQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac32bit', size }) types.push({ type: 'flac32bit', size })
_types.flac32bit = { _types.flac32bit = {
size, size,

View File

@ -6,9 +6,6 @@ import { sizeFormate } from '../../index'
export default { export default {
_requestObj_tags: null, _requestObj_tags: null,
_requestObj_list: null, _requestObj_list: null,
_requestObj_listDetail: null,
_requestObj_listDetailLink: null,
_requestObj_listDetailInfo: null,
limit_list: 10, limit_list: 10,
limit_song: 50, limit_song: 50,
successCode: '000000', successCode: '000000',
@ -74,7 +71,6 @@ export default {
}, },
getListDetailList(id, page, tryNum = 0) { getListDetailList(id, page, tryNum = 0) {
if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num')) if (tryNum > 2) return Promise.reject(new Error('try max num'))
// https://h5.nf.migu.cn/app/v4/p/share/playlist/index.html?id=184187437&channel=0146921 // https://h5.nf.migu.cn/app/v4/p/share/playlist/index.html?id=184187437&channel=0146921
@ -82,8 +78,8 @@ export default {
id = id.replace(/.*(?:\?|&)id=(\d+)(?:&.*|$)/, '$1') id = id.replace(/.*(?:\?|&)id=(\d+)(?:&.*|$)/, '$1')
} else if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1') } else if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1')
this._requestObj_listDetail = httpFetch(this.getSongListDetailUrl(id, page), { headers: this.defaultHeaders }) const requestObj_listDetail = httpFetch(this.getSongListDetailUrl(id, page), { headers: this.defaultHeaders })
return this._requestObj_listDetail.promise.then(({ body }) => { return requestObj_listDetail.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getListDetail(id, page, ++tryNum) if (body.code !== this.successCode) return this.getListDetail(id, page, ++tryNum)
// console.log(JSON.stringify(body)) // console.log(JSON.stringify(body))
// console.log(body) // console.log(body)
@ -98,14 +94,13 @@ export default {
}, },
getListDetailInfo(id, tryNum = 0) { getListDetailInfo(id, tryNum = 0) {
if (this._requestObj_listDetailInfo) this._requestObj_listDetailInfo.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num')) if (tryNum > 2) return Promise.reject(new Error('try max num'))
if (this.cachedDetailInfo[id]) return Promise.resolve(this.cachedDetailInfo[id]) if (this.cachedDetailInfo[id]) return Promise.resolve(this.cachedDetailInfo[id])
this._requestObj_listDetailInfo = httpFetch(`https://c.musicapp.migu.cn/MIGUM3.0/resource/playlist/v2.0?playlistId=${id}`, { const requestObj_listDetailInfo = httpFetch(`https://c.musicapp.migu.cn/MIGUM3.0/resource/playlist/v2.0?playlistId=${id}`, {
headers: this.defaultHeaders, headers: this.defaultHeaders,
}) })
return this._requestObj_listDetailInfo.promise.then(({ body }) => { return requestObj_listDetailInfo.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getListDetail(id, ++tryNum) if (body.code !== this.successCode) return this.getListDetail(id, ++tryNum)
// console.log(JSON.stringify(body)) // console.log(JSON.stringify(body))
// console.log(body) // console.log(body)
@ -123,15 +118,13 @@ export default {
async getDetailUrl(link, page, retryNum = 0) { async getDetailUrl(link, page, retryNum = 0) {
if (retryNum > 3) return Promise.reject(new Error('link try max num')) if (retryNum > 3) return Promise.reject(new Error('link try max num'))
if (this._requestObj_listDetailLink) this._requestObj_listDetailLink.cancelHttp() const requestObj_listDetailLink = httpFetch(link, {
this._requestObj_listDetailLink = httpFetch(link, {
headers: { headers: {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1', 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
Referer: link, Referer: link,
}, },
}) })
const { headers: { location }, statusCode } = await this._requestObj_listDetailLink.promise const { headers: { location }, statusCode } = await requestObj_listDetailLink.promise
// console.log(body, location) // console.log(body, location)
if (statusCode > 400) return this.getDetailUrl(link, page, ++retryNum) if (statusCode > 400) return this.getDetailUrl(link, page, ++retryNum)
if (location) { if (location) {
@ -174,28 +167,28 @@ export default {
let size let size
switch (type.formatType) { switch (type.formatType) {
case 'PQ': case 'PQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '128k', size }) types.push({ type: '128k', size })
_types['128k'] = { _types['128k'] = {
size, size,
} }
break break
case 'HQ': case 'HQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '320k', size }) types.push({ type: '320k', size })
_types['320k'] = { _types['320k'] = {
size, size,
} }
break break
case 'SQ': case 'SQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac', size }) types.push({ type: 'flac', size })
_types.flac = { _types.flac = {
size, size,
} }
break break
case 'ZQ': case 'ZQ':
size = sizeFormate(type.size) size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac32bit', size }) types.push({ type: 'flac32bit', size })
_types.flac32bit = { _types.flac32bit = {
size, size,

View File

@ -86,16 +86,14 @@ export default {
periods: {}, periods: {},
periodUrl: 'https://c.y.qq.com/node/pc/wk_v15/top.html', periodUrl: 'https://c.y.qq.com/node/pc/wk_v15/top.html',
_requestBoardsObj: null, _requestBoardsObj: null,
_requestDataObj: null,
getBoardsData() { getBoardsData() {
if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp() if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp()
this._requestBoardsObj = httpFetch('https://c.y.qq.com/v8/fcg-bin/fcg_myqq_toplist.fcg?g_tk=1928093487&inCharset=utf-8&outCharset=utf-8&notice=0&format=json&uin=0&needNewCode=1&platform=h5') this._requestBoardsObj = httpFetch('https://c.y.qq.com/v8/fcg-bin/fcg_myqq_toplist.fcg?g_tk=1928093487&inCharset=utf-8&outCharset=utf-8&notice=0&format=json&uin=0&needNewCode=1&platform=h5')
return this._requestBoardsObj.promise return this._requestBoardsObj.promise
}, },
getData(url) { getData(url) {
if (this._requestDataObj) this._requestDataObj.cancelHttp() const requestDataObj = httpFetch(url)
this._requestDataObj = httpFetch(url) return requestDataObj.promise
return this._requestDataObj.promise
}, },
getSinger(singers) { getSinger(singers) {
let arr = [] let arr = []

View File

@ -5,7 +5,6 @@ import { formatPlayTime, sizeFormate } from '../../index'
// import { debug } from '../../utils/env' // import { debug } from '../../utils/env'
// import { formatSinger } from './util' // import { formatSinger } from './util'
let searchRequest
export default { export default {
limit: 30, limit: 30,
total: 0, total: 0,
@ -13,10 +12,9 @@ export default {
allPage: 1, allPage: 1,
successCode: 0, successCode: 0,
musicSearch(str, page, limit, retryNum = 0) { musicSearch(str, page, limit, retryNum = 0) {
if (searchRequest && searchRequest.cancelHttp) searchRequest.cancelHttp()
if (retryNum > 5) return Promise.reject(new Error('搜索失败')) if (retryNum > 5) return Promise.reject(new Error('搜索失败'))
// searchRequest = httpFetch(`https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&new_json=1&remoteplace=sizer.yqq.song_next&searchid=49252838123499591&t=0&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=${page}&n=${limit}&w=${encodeURIComponent(str)}&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0`) // searchRequest = httpFetch(`https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&new_json=1&remoteplace=sizer.yqq.song_next&searchid=49252838123499591&t=0&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=${page}&n=${limit}&w=${encodeURIComponent(str)}&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0`)
searchRequest = httpFetch(`https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&remoteplace=txt.yqq.top&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=${page}&n=${limit}&w=${encodeURIComponent(str)}&cv=4747474&ct=24&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0&uin=0&hostUin=0&loginUin=0`) const searchRequest = httpFetch(`https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&remoteplace=txt.yqq.top&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=${page}&n=${limit}&w=${encodeURIComponent(str)}&cv=4747474&ct=24&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0&uin=0&hostUin=0&loginUin=0`)
// searchRequest = httpFetch(`http://ioscdn.kugou.com/api/v3/search/song?keyword=${encodeURIComponent(str)}&page=${page}&pagesize=${this.limit}&showtype=10&plat=2&version=7910&tag=1&correct=1&privilege=1&sver=5`) // searchRequest = httpFetch(`http://ioscdn.kugou.com/api/v3/search/song?keyword=${encodeURIComponent(str)}&page=${page}&pagesize=${this.limit}&showtype=10&plat=2&version=7910&tag=1&correct=1&privilege=1&sver=5`)
return searchRequest.promise.then(({ body }) => { return searchRequest.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.musicSearch(str, page, limit, ++retryNum) if (body.code !== this.successCode) return this.musicSearch(str, page, limit, ++retryNum)

View File

@ -5,8 +5,6 @@ export default {
_requestObj_tags: null, _requestObj_tags: null,
_requestObj_hotTags: null, _requestObj_hotTags: null,
_requestObj_list: null, _requestObj_list: null,
_requestObj_listDetail: null,
_requestObj_listDetailLink: null,
limit_list: 36, limit_list: 36,
limit_song: 100000, limit_song: 100000,
successCode: 0, successCode: 0,
@ -175,11 +173,10 @@ export default {
}, },
async handleParseId(link, retryNum = 0) { async handleParseId(link, retryNum = 0) {
if (this._requestObj_listDetailLink) this._requestObj_listDetailLink.cancelHttp()
if (retryNum > 2) return Promise.reject(new Error('link try max num')) if (retryNum > 2) return Promise.reject(new Error('link try max num'))
this._requestObj_listDetailLink = httpFetch(link) const requestObj_listDetailLink = httpFetch(link)
const { headers: { location }, statusCode } = await this._requestObj_listDetailLink.promise const { headers: { location }, statusCode } = await requestObj_listDetailLink.promise
// console.log(headers) // console.log(headers)
if (statusCode > 400) return this.handleParseId(link, ++retryNum) if (statusCode > 400) return this.handleParseId(link, ++retryNum)
return location == null ? link : location return location == null ? link : location
@ -200,18 +197,17 @@ export default {
}, },
// 获取歌曲列表内的音乐 // 获取歌曲列表内的音乐
async getListDetail(id, tryNum = 0) { async getListDetail(id, tryNum = 0) {
if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num')) if (tryNum > 2) return Promise.reject(new Error('try max num'))
id = await this.getListId(id) id = await this.getListId(id)
this._requestObj_listDetail = httpFetch(this.getListDetailUrl(id), { const requestObj_listDetail = httpFetch(this.getListDetailUrl(id), {
headers: { headers: {
Origin: 'https://y.qq.com', Origin: 'https://y.qq.com',
Referer: `https://y.qq.com/n/yqq/playsquare/${id}.html`, Referer: `https://y.qq.com/n/yqq/playsquare/${id}.html`,
}, },
}) })
const { body } = await this._requestObj_listDetail.promise const { body } = await requestObj_listDetail.promise
if (body.code !== this.successCode) return this.getListDetail(id, ++tryNum) if (body.code !== this.successCode) return this.getListDetail(id, ++tryNum)
const cdlist = body.cdlist[0] const cdlist = body.cdlist[0]

View File

@ -104,7 +104,6 @@ export default {
list: /<textarea id="song-list-pre-data" style="display:none;">(.+?)<\/textarea>/, list: /<textarea id="song-list-pre-data" style="display:none;">(.+?)<\/textarea>/,
}, },
_requestBoardsObj: null, _requestBoardsObj: null,
_requestBoardsDetailObj: null,
getBoardsData() { getBoardsData() {
if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp() if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp()
this._requestBoardsObj = httpFetch('https://music.163.com/weapi/toplist', { this._requestBoardsObj = httpFetch('https://music.163.com/weapi/toplist', {
@ -114,8 +113,7 @@ export default {
return this._requestBoardsObj.promise return this._requestBoardsObj.promise
}, },
getData(id) { getData(id) {
if (this._requestBoardsDetailObj) this._requestBoardsDetailObj.cancelHttp() const requestBoardsDetailObj = httpFetch('https://music.163.com/weapi/v3/playlist/detail', {
this._requestBoardsDetailObj = httpFetch('https://music.163.com/weapi/v3/playlist/detail', {
method: 'post', method: 'post',
form: weapi({ form: weapi({
id, id,
@ -123,7 +121,7 @@ export default {
p: 1, p: 1,
}), }),
}) })
return this._requestBoardsDetailObj.promise return requestBoardsDetailObj.promise
}, },
filterBoardsData(rawList) { filterBoardsData(rawList) {

View File

@ -34,26 +34,30 @@ import { linuxapi } from './utils/crypto'
// return lxlyric.trim() // return lxlyric.trim()
// } // }
// https://github.com/Binaryify/NeteaseCloudMusicApi/pull/1523/files
export default songmid => { export default songmid => {
const requestObj = httpFetch('https://music.163.com/api/linux/forward', { const requestObj = httpFetch('https://music.163.com/api/linux/forward', {
method: 'post', method: 'post',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36', 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
form: linuxapi({ form: linuxapi({
method: 'POST', method: 'POST',
url: 'https://music.163.com/api/song/lyric', url: 'https://music.163.com/api/song/lyric?_nmclfl=1',
params: { params: {
id: songmid, id: songmid,
lv: -1,
kv: -1,
tv: -1, tv: -1,
lv: -1,
rv: -1,
kv: -1,
}, },
}), }),
}) })
requestObj.promise = requestObj.promise.then(({ body }) => { requestObj.promise = requestObj.promise.then(({ body }) => {
if (body.code !== 200 || !body?.lrc?.lyric) return Promise.reject(new Error('Get lyric failed')) if (body.code !== 200 || !body?.lrc?.lyric) return Promise.reject(new Error('Get lyric failed'))
// console.log(body)
return { return {
lyric: body.lrc.lyric, lyric: body.lrc.lyric,
tlyric: body.tlyric?.lyric ?? '', tlyric: body.tlyric?.lyric ?? '',
rlyric: body.romalrc?.lyric ?? '',
// lxlyric: parseLyric(body.klyric.lyric), // lxlyric: parseLyric(body.klyric.lyric),
} }
}) })

View File

@ -4,7 +4,6 @@ import { formatPlayTime, sizeFormate } from '../..'
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/song_detail.js // https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/song_detail.js
export default { export default {
_requestObj: null,
getSinger(singers) { getSinger(singers) {
let arr = [] let arr = []
singers.forEach(singer => { singers.forEach(singer => {
@ -70,10 +69,9 @@ export default {
return list return list
}, },
async getList(ids = [], retryNum = 0) { async getList(ids = [], retryNum = 0) {
if (this._requestObj) this._requestObj.cancelHttp()
if (retryNum > 2) return Promise.reject(new Error('try max num')) if (retryNum > 2) return Promise.reject(new Error('try max num'))
const _requestObj = httpFetch('https://music.163.com/weapi/v3/song/detail', { const requestObj = httpFetch('https://music.163.com/weapi/v3/song/detail', {
method: 'post', method: 'post',
headers: { headers: {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36', 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
@ -84,7 +82,7 @@ export default {
ids: '[' + ids.join(',') + ']', ids: '[' + ids.join(',') + ']',
}), }),
}) })
const { body, statusCode } = await _requestObj.promise const { body, statusCode } = await requestObj.promise
if (statusCode != 200 || body.code !== 200) throw new Error('获取歌曲详情失败') if (statusCode != 200 || body.code !== 200) throw new Error('获取歌曲详情失败')
// console.log(body) // console.log(body)
return { source: 'wy', list: this.filterList(body) } return { source: 'wy', list: this.filterList(body) }

View File

@ -4,15 +4,13 @@ import { sizeFormate, formatPlayTime } from '../../index'
// import musicDetailApi from './musicDetail' // import musicDetailApi from './musicDetail'
import { eapiRequest } from './utils' import { eapiRequest } from './utils'
let searchRequest
export default { export default {
limit: 30, limit: 30,
total: 0, total: 0,
page: 0, page: 0,
allPage: 1, allPage: 1,
musicSearch(str, page, limit) { musicSearch(str, page, limit) {
if (searchRequest && searchRequest.cancelHttp) searchRequest.cancelHttp() const searchRequest = eapiRequest('/api/cloudsearch/pc', {
searchRequest = eapiRequest('/api/cloudsearch/pc', {
s: str, s: str,
type: 1, // 1: 单曲, 10: 专辑, 100: 歌手, 1000: 歌单, 1002: 用户, 1004: MV, 1006: 歌词, 1009: 电台, 1014: 视频 type: 1, // 1: 单曲, 10: 专辑, 100: 歌手, 1000: 歌单, 1002: 用户, 1004: MV, 1006: 歌词, 1009: 电台, 1014: 视频
limit, limit,

View File

@ -12,8 +12,6 @@ export default {
_requestObj_tags: null, _requestObj_tags: null,
_requestObj_hotTags: null, _requestObj_hotTags: null,
_requestObj_list: null, _requestObj_list: null,
_requestObj_listDetail: null,
_requestObj_listDetailLink: null,
limit_list: 30, limit_list: 30,
limit_song: 100000, limit_song: 100000,
successCode: 200, successCode: 200,
@ -50,11 +48,10 @@ export default {
}, },
async handleParseId(link, retryNum = 0) { async handleParseId(link, retryNum = 0) {
if (this._requestObj_listDetailLink) this._requestObj_listDetailLink.cancelHttp()
if (retryNum > 2) throw new Error('link try max num') if (retryNum > 2) throw new Error('link try max num')
this._requestObj_listDetailLink = httpFetch(link) const requestObj_listDetailLink = httpFetch(link)
const { headers: { location }, statusCode } = await this._requestObj_listDetailLink.promise const { headers: { location }, statusCode } = await requestObj_listDetailLink.promise
// console.log(headers) // console.log(headers)
if (statusCode > 400) return this.handleParseId(link, ++retryNum) if (statusCode > 400) return this.handleParseId(link, ++retryNum)
const url = location == null ? link : location const url = location == null ? link : location
@ -83,13 +80,12 @@ export default {
return { id, cookie } return { id, cookie }
}, },
async getListDetail(rawId, page, tryNum = 0) { // 获取歌曲列表内的音乐 async getListDetail(rawId, page, tryNum = 0) { // 获取歌曲列表内的音乐
if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num')) if (tryNum > 2) return Promise.reject(new Error('try max num'))
const { id, cookie } = await this.getListId(rawId) const { id, cookie } = await this.getListId(rawId)
if (cookie) this.cookie = cookie if (cookie) this.cookie = cookie
this._requestObj_listDetail = httpFetch('https://music.163.com/api/linux/forward', { const requestObj_listDetail = httpFetch('https://music.163.com/api/linux/forward', {
method: 'post', method: 'post',
headers: { headers: {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36', 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
@ -105,7 +101,7 @@ export default {
}, },
}), }),
}) })
const { statusCode, body } = await this._requestObj_listDetail.promise const { statusCode, body } = await requestObj_listDetail.promise
if (statusCode !== 200 || body.code !== this.successCode) return this.getListDetail(id, page, ++tryNum) if (statusCode !== 200 || body.code !== this.successCode) return this.getListDetail(id, page, ++tryNum)
let limit = 1000 let limit = 1000
let rangeStart = (page - 1) * limit let rangeStart = (page - 1) * limit

View File

@ -19,7 +19,7 @@ const aesDecrypt = function(cipherBuffer, mode, key, iv) {
const rsaEncrypt = (buffer, key) => { const rsaEncrypt = (buffer, key) => {
buffer = Buffer.concat([Buffer.alloc(128 - buffer.length), buffer]) buffer = Buffer.concat([Buffer.alloc(128 - buffer.length), buffer])
return publicEncrypt({ key: key, padding: constants.RSA_NO_PADDING }, buffer) return publicEncrypt({ key, padding: constants.RSA_NO_PADDING }, buffer)
} }
export const weapi = object => { export const weapi = object => {

View File

@ -27,7 +27,7 @@ export const setAllowShowUserApiUpdateAlert = (id, enable) => {
export const saveMyList = data => { export const saveMyList = data => {
rendererSend(NAMES.mainWindow.save_playlist, { rendererSend(NAMES.mainWindow.save_playlist, {
type: 'myList', type: 'myList',
data: data, data,
}) })
} }
export const saveDownloadList = list => { export const saveDownloadList = list => {

View File

@ -22,7 +22,8 @@ div(:class="$style.search")
td.nobreak.center(style="width: 5%; padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{index + 1}} td.nobreak.center(style="width: 5%; padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{index + 1}}
td.break td.break
span.select {{item.name}} span.select {{item.name}}
span.badge.badge-theme-success(:class="[$style.labelQuality, $style.noSelect]" v-if="item._types.ape || item._types.flac || item._types.wav") {{$t('tag__lossless')}} span.badge.badge-theme-success(:class="[$style.labelQuality, $style.noSelect]" v-if="item._types.flac32bit") {{$t('tag__lossless_24bit')}}
span.badge.badge-theme-success(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item._types.ape || item._types.flac || item._types.wav") {{$t('tag__lossless')}}
span.badge.badge-theme-info(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item._types['320k']") {{$t('tag__high_quality')}} span.badge.badge-theme-info(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item._types['320k']") {{$t('tag__high_quality')}}
span(:class="[$style.labelSource, $style.noSelect]" v-if="searchSourceId == 'all'") {{item.source}} span(:class="[$style.labelSource, $style.noSelect]" v-if="searchSourceId == 'all'") {{item.source}}
td.break(style="width: 22%;") td.break(style="width: 22%;")
@ -107,6 +108,7 @@ export default {
}, },
}, },
isLoading: false, isLoading: false,
searchId: null,
} }
}, },
beforeRouteUpdate(to, from) { beforeRouteUpdate(to, from) {
@ -259,14 +261,20 @@ export default {
this.handleSelectAllData() this.handleSelectAllData()
}, },
handleSearch(text, page) { handleSearch(text, page) {
if (text === '') return this.clearList() const searchId = this.searchId = `${this.searchSourceId}__${page}__${text}`
if (text === '') {
this.isLoading = false
return this.clearList()
}
this.isLoading = true this.isLoading = true
this.search({ text, page, limit: this.listInfo.limit }).then(data => { this.search({ text, page, limit: this.listInfo.limit }).then(data => {
if (this.searchId != searchId) return
this.page = page this.page = page
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.dom_scrollContent.scrollTo(0, 0) this.$refs.dom_scrollContent.scrollTo(0, 0)
}) })
}).finally(() => { }).finally(() => {
if (this.searchId != searchId) return
this.isLoading = false this.isLoading = false
}) })
}, },

View File

@ -1,10 +1,12 @@
import { useCommit, useRouter } from '@renderer/utils/vueTools' import { useCommit, useRouter, useI18n } from '@renderer/utils/vueTools'
import musicSdk from '@renderer/utils/music' import musicSdk from '@renderer/utils/music'
import { openUrl, clipboardWriteText } from '@renderer/utils' import { openUrl, clipboardWriteText } from '@renderer/utils'
import { dialog } from '@renderer/plugins/Dialog'
export default ({ props, list, setting, selectedList, removeAllSelect }) => { export default ({ props, list, setting, selectedList, removeAllSelect }) => {
const router = useRouter() const router = useRouter()
const { t } = useI18n()
const listRemove = useCommit('list', 'listRemove') const listRemove = useCommit('list', 'listRemove')
const listRemoveMultiple = useCommit('list', 'listRemoveMultiple') const listRemoveMultiple = useCommit('list', 'listRemoveMultiple')
@ -31,8 +33,16 @@ export default ({ props, list, setting, selectedList, removeAllSelect }) => {
clipboardWriteText(setting.value.download.fileName.replace('歌名', minfo.name).replace('歌手', minfo.singer)) clipboardWriteText(setting.value.download.fileName.replace('歌名', minfo.name).replace('歌手', minfo.singer))
} }
const handleRemoveMusic = (index, single) => { const handleRemoveMusic = async(index, single) => {
if (selectedList.value.length && !single) { if (selectedList.value.length && !single) {
const confirm = await (selectedList.value.length > 1
? dialog.confirm({
message: t('lists__remove music_tip', { len: selectedList.value.length }),
confirmButtonText: t('lists__remove_tip_button'),
})
: Promise.resolve(true)
)
if (!confirm) return
listRemoveMultiple({ listId: props.listId, ids: selectedList.value.map(m => m.songmid) }) listRemoveMultiple({ listId: props.listId, ids: selectedList.value.map(m => m.songmid) })
removeAllSelect() removeAllSelect()
} else { } else {

View File

@ -5,6 +5,8 @@ dd
base-checkbox(id="setting_player_save_play_time" v-model="currentStting.player.isSavePlayTime" :label="$t('setting__play_save_play_time')") base-checkbox(id="setting_player_save_play_time" v-model="currentStting.player.isSavePlayTime" :label="$t('setting__play_save_play_time')")
.gap-top .gap-top
base-checkbox(id="setting_player_lyric_transition" v-model="currentStting.player.isShowLyricTranslation" :label="$t('setting__play_lyric_transition')") base-checkbox(id="setting_player_lyric_transition" v-model="currentStting.player.isShowLyricTranslation" :label="$t('setting__play_lyric_transition')")
.gap-top
base-checkbox(id="setting_player_lyric_roma" v-model="currentStting.player.isShowLyricRoma" :label="$t('setting__play_lyric_roma')")
.gap-top .gap-top
base-checkbox(id="setting_player_auto_skip_on_error" v-model="currentStting.player.autoSkipOnError" :label="$t('setting__play_auto_skip_on_error')") base-checkbox(id="setting_player_auto_skip_on_error" v-model="currentStting.player.autoSkipOnError" :label="$t('setting__play_auto_skip_on_error')")
.gap-top .gap-top

View File

@ -10,6 +10,7 @@ export const currentStting = ref({
mediaDeviceId: 'default', mediaDeviceId: 'default',
isMediaDeviceRemovedStopPlay: false, isMediaDeviceRemovedStopPlay: false,
isShowLyricTranslation: false, isShowLyricTranslation: false,
isShowLyricRoma: false,
isS2t: false, // 是否将歌词从简体转换为繁体 isS2t: false, // 是否将歌词从简体转换为繁体
isPlayLxlrc: true, isPlayLxlrc: true,
isSavePlayTime: false, isSavePlayTime: false,