diff --git a/.ncurc.js b/.ncurc.js index 8bcfcffa..f607d668 100644 --- a/.ncurc.js +++ b/.ncurc.js @@ -1,7 +1,6 @@ module.exports = { upgrade: true, reject: [ - 'vue-loader', 'webpack-dev-server', 'eslint', 'electron', diff --git a/.vscode/settings.json b/.vscode/settings.json index 0730dd7c..b7ce6379 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,13 +7,15 @@ "@common/*": "${workspaceFolder}/src/common/*", }, "i18n-ally.localesPaths": [ - "src/renderer/lang" + "src/lang" ], + // "i18n-ally.fullReloadOnChanged": true, + "i18n-ally.keystyle": "nested", "i18n-ally.displayLanguage": "zh-cn", "i18n-ally.sourceLanguage": "zh-cn", - "i18n-ally.namespace": true, - "i18n-ally.translate.engines": ["google-cn", "google"], - "i18n-ally.pathMatcher": "{locale}/{namespaces}.json", - "i18n-ally.keystyle": "flat", + "i18n-ally.translate.engines": [ + "google-cn", + "google" + ], "i18n-ally.sortKeys": true } diff --git a/FAQ.md b/FAQ.md index 711e3eb6..dbe0dfa8 100644 --- a/FAQ.md +++ b/FAQ.md @@ -250,6 +250,13 @@ Windows 7 未开启 Aero 效果时桌面歌词会有问题,详情看上面的 - 以管理员权限打开`cmd`,输入`sfc /scannow`回车等待检查完成重启电脑 - 若上面的方法**修复、重启**电脑后仍然不行,就自行百度弹出的**错误信息**看下别人是怎么解决的 +## MAC OS无法启动软件,提示 lx-music-desktop 已损坏 + +这是因为软件没有签名,被系统阻止运行, +在终端里输入 `sudo xattr -rd com.apple.quarantine /Applications/lx-music-desktop.app`,然后输入你的电脑密码即可 + +还可以参考: + ## 杀毒软件提示有病毒或恶意行为 本人只能保证我写的代码不包含任何**恶意代码**、**收集用户信息**的行为,并且软件代码已开源,请自行查阅,软件安装包也是由CI拉取源代码构建,构建日志:[GitHub Actions](https://github.com/lyswhut/lx-music-desktop/actions) diff --git a/README.md b/README.md index f32dd72c..d5bf470b 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,8 @@ 所用技术栈: -- Electron 15 -- Vue 2 +- Electron 13 +- Vue 3 已支持的平台: diff --git a/build-config/renderer-lyric/webpack.config.base.js b/build-config/renderer-lyric/webpack.config.base.js index 354bb3cf..523bb150 100644 --- a/build-config/renderer-lyric/webpack.config.base.js +++ b/build-config/renderer-lyric/webpack.config.base.js @@ -1,5 +1,5 @@ const path = require('path') -const VueLoaderPlugin = require('vue-loader/lib/plugin') +const { VueLoaderPlugin } = require('vue-loader') const HTMLPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const CleanCSSPlugin = require('less-plugin-clean-css') @@ -23,6 +23,7 @@ module.exports = { }, resolve: { alias: { + '@': path.join(__dirname, '../../src'), '@main': path.join(__dirname, '../../src/main'), '@renderer': path.join(__dirname, '../../src/renderer'), '@lyric': path.join(__dirname, '../../src/renderer-lyric'), @@ -126,7 +127,6 @@ module.exports = { template: path.join(__dirname, '../../src/renderer-lyric/index.pug'), isProd: process.env.NODE_ENV == 'production', browser: process.browser, - scriptLoading: 'blocking', __dirname, }), new VueLoaderPlugin(), diff --git a/build-config/renderer-lyric/webpack.config.prod.js b/build-config/renderer-lyric/webpack.config.prod.js index a2bc7ba2..5c582010 100644 --- a/build-config/renderer-lyric/webpack.config.prod.js +++ b/build-config/renderer-lyric/webpack.config.prod.js @@ -9,7 +9,8 @@ const baseConfig = require('./webpack.config.base') const { dependencies } = require('../../package.json') -let whiteListedModules = ['vue'] +// let whiteListedModules = ['vue'] +let whiteListedModules = ['vue', 'vue-router', 'vuex', 'vue-i18n'] module.exports = merge(baseConfig, { diff --git a/build-config/renderer/webpack.config.base.js b/build-config/renderer/webpack.config.base.js index 8705b907..a5855856 100644 --- a/build-config/renderer/webpack.config.base.js +++ b/build-config/renderer/webpack.config.base.js @@ -1,5 +1,5 @@ const path = require('path') -const VueLoaderPlugin = require('vue-loader/lib/plugin') +const { VueLoaderPlugin } = require('vue-loader') const HTMLPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const CleanCSSPlugin = require('less-plugin-clean-css') @@ -23,6 +23,7 @@ module.exports = { }, resolve: { alias: { + '@': path.join(__dirname, '../../src'), '@main': path.join(__dirname, '../../src/main'), '@renderer': path.join(__dirname, '../../src/renderer'), '@lyric': path.join(__dirname, '../../src/renderer-lyric'), @@ -123,10 +124,9 @@ module.exports = { plugins: [ new HTMLPlugin({ filename: 'index.html', - template: path.join(__dirname, '../../src/renderer/index.pug'), + template: path.join(__dirname, '../../src/renderer/index.html'), isProd: process.env.NODE_ENV == 'production', browser: process.browser, - scriptLoading: 'blocking', __dirname, }), new VueLoaderPlugin(), diff --git a/build-config/renderer/webpack.config.prod.js b/build-config/renderer/webpack.config.prod.js index a2bc7ba2..f908a5c1 100644 --- a/build-config/renderer/webpack.config.prod.js +++ b/build-config/renderer/webpack.config.prod.js @@ -9,7 +9,7 @@ const baseConfig = require('./webpack.config.base') const { dependencies } = require('../../package.json') -let whiteListedModules = ['vue'] +let whiteListedModules = ['vue', 'vue-router', 'vuex', 'vue-i18n'] module.exports = merge(baseConfig, { @@ -34,6 +34,7 @@ module.exports = merge(baseConfig, { }), ], optimization: { + minimize: false, minimizer: [ new TerserPlugin(), new CssMinimizerPlugin(), diff --git a/package-lock.json b/package-lock.json index 7a4bf349..24d2a206 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "lx-music-desktop", - "version": "1.15.3", + "version": "1.16.0-beta", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3001,6 +3001,55 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "@intlify/core-base": { + "version": "9.2.0-beta.22", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.2.0-beta.22.tgz", + "integrity": "sha512-SqgQx+npGJ8XgY920FoHnvfo7Th4bEFPxOUrMquqHCHH7J/HSLV6Xppzf8wE1ZzCWjn1wmUHkavTW9ASNCXj0Q==", + "requires": { + "@intlify/devtools-if": "9.2.0-beta.22", + "@intlify/message-compiler": "9.2.0-beta.22", + "@intlify/shared": "9.2.0-beta.22", + "@intlify/vue-devtools": "9.2.0-beta.22" + } + }, + "@intlify/devtools-if": { + "version": "9.2.0-beta.22", + "resolved": "https://registry.npmjs.org/@intlify/devtools-if/-/devtools-if-9.2.0-beta.22.tgz", + "integrity": "sha512-TRpig6P7hMZejDvmwTGygf+TbGUkeICnIMtNqKCMkAw78Yoc5FhgaKTCkK72Gjcmul7V9CdOF+ro/xtzY6MQGQ==", + "requires": { + "@intlify/shared": "9.2.0-beta.22" + } + }, + "@intlify/message-compiler": { + "version": "9.2.0-beta.22", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.2.0-beta.22.tgz", + "integrity": "sha512-+Nx8IpKcXBQ7L+f6UWLQf/iReD7rotBkV4oxUX1rKMQgaJToYB1ovrPYNoNDd6aK4rEQ9sJoM5nd2+ENmCs7dA==", + "requires": { + "@intlify/shared": "9.2.0-beta.22", + "source-map": "0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@intlify/shared": { + "version": "9.2.0-beta.22", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.2.0-beta.22.tgz", + "integrity": "sha512-IyU9Trx1d5EZgAh6/3y0+efgko5xRw1iTwpM19qublZsTSnDSkHB8NnSyHVZrMARj+LgZgL7pKktsSO5os9k2A==" + }, + "@intlify/vue-devtools": { + "version": "9.2.0-beta.22", + "resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.2.0-beta.22.tgz", + "integrity": "sha512-CZwrIFGiXl9q6Rrqyqtb3P+wwG7f0FcMvbQqJf2CnCsT7rLi4O+qbSzY5ZjN5J0bsLTfL2a4UIhxIlim4Ea+hQ==", + "requires": { + "@intlify/core-base": "9.2.0-beta.22", + "@intlify/shared": "9.2.0-beta.22" + } + }, "@malept/cross-spawn-promise": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", @@ -3280,89 +3329,141 @@ "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==", "dev": true }, - "@vue/component-compiler-utils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.2.2.tgz", - "integrity": "sha512-rAYMLmgMuqJFWAOb3Awjqqv5X3Q3hVr4jH/kgrFJpiU0j3a90tnNBplqbj+snzrgZhC9W128z+dtgMifOiMfJg==", - "dev": true, + "@vue/compiler-core": { + "version": "3.2.23", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.23.tgz", + "integrity": "sha512-4ZhiI/orx+7EJ1B+0zjgvXMV2uRN+XBfG06UN2sJfND9rH5gtEQT3QmO4erum1o6Irl7y754W8/KSaDJh4EUQg==", "requires": { - "consolidate": "^0.15.1", - "hash-sum": "^1.0.2", - "lru-cache": "^4.1.2", - "merge-source-map": "^1.1.0", - "postcss": "^7.0.36", - "postcss-selector-parser": "^6.0.2", - "prettier": "^1.18.2", - "source-map": "~0.6.1", - "vue-template-es2015-compiler": "^1.9.0" + "@babel/parser": "^7.15.0", + "@vue/shared": "3.2.23", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" }, "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "postcss": { - "version": "7.0.36", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", - "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } + "@babel/parser": { + "version": "7.16.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.4.tgz", + "integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==" }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, + "@vue/compiler-dom": { + "version": "3.2.23", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.23.tgz", + "integrity": "sha512-X2Nw8QFc5lgoK3kio5ktM95nqmLUH+q+N/PbV4kCHzF1avqv/EGLnAhaaF0Iu4bewNvHJAAhhwPZFeoV/22nbw==", + "requires": { + "@vue/compiler-core": "3.2.23", + "@vue/shared": "3.2.23" + } + }, + "@vue/compiler-sfc": { + "version": "3.2.23", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.23.tgz", + "integrity": "sha512-Aw+pb50Q5zTjyvWod8mNKmYZDRGHJBptmNNWE+84ZxrzEztPgMz8cNYIzWGbwcFVkmJlhvioAMvKnB+LM/sjSA==", + "requires": { + "@babel/parser": "^7.15.0", + "@vue/compiler-core": "3.2.23", + "@vue/compiler-dom": "3.2.23", + "@vue/compiler-ssr": "3.2.23", + "@vue/ref-transform": "3.2.23", + "@vue/shared": "3.2.23", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7", + "postcss": "^8.1.10", + "source-map": "^0.6.1" + }, + "dependencies": { + "@babel/parser": { + "version": "7.16.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.4.tgz", + "integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@vue/compiler-ssr": { + "version": "3.2.23", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.23.tgz", + "integrity": "sha512-Bqzn4jFyXPK1Ehqiq7e/czS8n62gtYF1Zfeu0DrR5uv+SBllh7LIvZjZU6+c8qbocAd3/T3I3gn2cZGmnDb6zg==", + "requires": { + "@vue/compiler-dom": "3.2.23", + "@vue/shared": "3.2.23" + } + }, + "@vue/devtools-api": { + "version": "6.0.0-beta.20.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.0.0-beta.20.1.tgz", + "integrity": "sha512-R2rfiRY+kZugzWh9ZyITaovx+jpU4vgivAEAiz80kvh3yviiTU3CBuGuyWpSwGz9/C7TkSWVM/FtQRGlZ16n8Q==" + }, + "@vue/reactivity": { + "version": "3.2.23", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.23.tgz", + "integrity": "sha512-8RGVr/5Kpgb/EkCjgHXqttgA5IMc6n0lIXFY4TVbMkzdXrvaIhzBd7Te44oIDsTSYVKZLpfHd6/wEnuDqE8vFw==", + "requires": { + "@vue/shared": "3.2.23" + } + }, + "@vue/ref-transform": { + "version": "3.2.23", + "resolved": "https://registry.npmjs.org/@vue/ref-transform/-/ref-transform-3.2.23.tgz", + "integrity": "sha512-gW0GD2PSAs/th7mC7tPB/UwpIQxclbApVtsDtscDmOJXb2+cdu60ny+SuHNgfrlUT/JqWKQHq7jFKO4woxLNaA==", + "requires": { + "@babel/parser": "^7.15.0", + "@vue/compiler-core": "3.2.23", + "@vue/shared": "3.2.23", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7" + }, + "dependencies": { + "@babel/parser": { + "version": "7.16.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.4.tgz", + "integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==" + } + } + }, + "@vue/runtime-core": { + "version": "3.2.23", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.23.tgz", + "integrity": "sha512-wSI5lmY2kCGLf89iiygqxVh6/5bsawz78Me9n1x4U2bHnN0yf3PWyuhN0WgIE8VfEaF7e75E333uboNEIFjgkg==", + "requires": { + "@vue/reactivity": "3.2.23", + "@vue/shared": "3.2.23" + } + }, + "@vue/runtime-dom": { + "version": "3.2.23", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.23.tgz", + "integrity": "sha512-z6lp0888NkLmxD9j2sGoll8Kb7J743s8s6w7GbiyUc4WZwm0KJ35B4qTFDMoIU0G7CatS6Z+yRTpPHc6srtByg==", + "requires": { + "@vue/runtime-core": "3.2.23", + "@vue/shared": "3.2.23", + "csstype": "^2.6.8" + } + }, + "@vue/server-renderer": { + "version": "3.2.23", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.23.tgz", + "integrity": "sha512-mgQ2VAE5WjeZELJKNbwE69uiBNpN+3LyL0ZDki1bJWVwHD2fhPfx7pwyYuiucE81xz2LxVsyGxhKKUL997g8vw==", + "requires": { + "@vue/compiler-ssr": "3.2.23", + "@vue/shared": "3.2.23" + } + }, + "@vue/shared": { + "version": "3.2.23", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.23.tgz", + "integrity": "sha512-U+/Jefa0QfXUF2qVy9Dqlrb6HKJSr9/wJcM66wXmWcTOoqg7hOWzF4qruDle51pyF4x3wMn6TSH54UdjKjCKMA==" + }, "@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -4631,12 +4732,61 @@ "wrap-ansi": "^7.0.0" }, "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, "camelcase": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -4742,6 +4892,15 @@ "temp-file": "^3.4.0" }, "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, "builder-util-runtime": { "version": "8.7.7", "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.7.7.tgz", @@ -4752,6 +4911,31 @@ "sax": "^1.2.4" } }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "debug": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", @@ -4772,6 +4956,12 @@ "universalify": "^2.0.0" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -4782,6 +4972,15 @@ "universalify": "^2.0.0" } }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -4800,9 +4999,9 @@ }, "dependencies": { "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "requires": { "ms": "2.1.2" } @@ -4924,9 +5123,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001282", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001282.tgz", - "integrity": "sha512-YhF/hG6nqBEllymSIjLtR2iWDDnChvhnVJqp+vloyt2tEHFG1yBR+ac2B/rOw0qOK0m0lEXU2dv4E/sMk5P9Kg==", + "version": "1.0.30001283", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001283.tgz", + "integrity": "sha512-9RoKo841j1GQFSJz/nCXOj0sD7tHBtlowjYlrqIUS812x9/emfBLBt6IyMz1zIaYc/eRL8Cs6HPUVi2Hzq4sIg==", "dev": true }, "caseless": { @@ -4942,6 +5141,57 @@ "requires": { "chalk": "^4.1.2", "window-size": "^1.1.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "chalk": { @@ -5559,15 +5809,6 @@ "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", "dev": true }, - "consolidate": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", - "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", - "dev": true, - "requires": { - "bluebird": "^3.1.1" - } - }, "constantinople": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", @@ -5679,9 +5920,9 @@ "dev": true }, "ajv": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.1.tgz", - "integrity": "sha512-6CiMNDrzv0ZR916u2T+iRunnD60uWmNn8SkdB44/6stVORUg0aAkWO7PkOhpCmjmW8f2I/G/xnowD66fxGyQJg==", + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", + "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -5691,9 +5932,9 @@ } }, "ajv-keywords": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.0.0.tgz", - "integrity": "sha512-ULd1QMjRoH6JDNUQIfDLrlE+OgZlFaxyYCjzt58uNuUQtKXt8/U+vK/8Ql0gyn/C5mqZzUWtKMqr/4YquvTrWA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.3" @@ -5822,12 +6063,12 @@ "dev": true }, "core-js-compat": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.19.1.tgz", - "integrity": "sha512-Q/VJ7jAF/y68+aUsQJ/afPOewdsGkDtcMb40J8MbuWKlK3Y+wtHq8bTHKPj2WKWLIqmS5JhHs4CzHtz6pT2W6g==", + "version": "3.19.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.19.2.tgz", + "integrity": "sha512-ObBY1W5vx/LFFMaL1P5Udo4Npib6fu+cMokeziWkA8Tns4FcDemKF5j9JvaI5JhdkW8EQJQGJN1EcrzmEwuAqQ==", "dev": true, "requires": { - "browserslist": "^4.17.6", + "browserslist": "^4.18.1", "semver": "7.0.0" }, "dependencies": { @@ -6126,6 +6367,11 @@ "css-tree": "^1.1.2" } }, + "csstype": { + "version": "2.6.19", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.19.tgz", + "integrity": "sha512-ZVxXaNy28/k3kJg0Fou5MiYpp88j7H9hLZp8PDC3jV0WFjfH5E9xHb56L0W59cPbKbcHXeP4qyT8PrHp8t6LcQ==" + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -6608,6 +6854,15 @@ "yargs": "^17.0.1" }, "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, "builder-util-runtime": { "version": "8.7.7", "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.7.7.tgz", @@ -6618,6 +6873,31 @@ "sax": "^1.2.4" } }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "debug": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", @@ -6638,6 +6918,12 @@ "universalify": "^2.0.0" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -6654,6 +6940,15 @@ "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", "dev": true }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -6745,6 +7040,15 @@ "mime": "^2.5.2" }, "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, "builder-util-runtime": { "version": "8.7.7", "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.7.7.tgz", @@ -6755,6 +7059,31 @@ "sax": "^1.2.4" } }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "debug": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", @@ -6775,6 +7104,12 @@ "universalify": "^2.0.0" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -6797,6 +7132,15 @@ "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", "dev": true }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -6923,9 +7267,9 @@ "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" }, "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "requires": { "ms": "2.1.2" } @@ -7228,6 +7572,51 @@ "sprintf-js": "~1.0.2" } }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -7255,6 +7644,12 @@ "type-fest": "^0.20.2" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -7794,6 +8189,11 @@ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", "dev": true }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -8944,9 +9344,9 @@ "dev": true }, "hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", + "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", "dev": true }, "he": { @@ -10463,6 +10863,14 @@ "yallist": "^4.0.0" } }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, "make-dir": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", @@ -10566,23 +10974,6 @@ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -10657,9 +11048,9 @@ "dev": true }, "ajv": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.1.tgz", - "integrity": "sha512-6CiMNDrzv0ZR916u2T+iRunnD60uWmNn8SkdB44/6stVORUg0aAkWO7PkOhpCmjmW8f2I/G/xnowD66fxGyQJg==", + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", + "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -10669,9 +11060,9 @@ } }, "ajv-keywords": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.0.0.tgz", - "integrity": "sha512-ULd1QMjRoH6JDNUQIfDLrlE+OgZlFaxyYCjzt58uNuUQtKXt8/U+vK/8Ql0gyn/C5mqZzUWtKMqr/4YquvTrWA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.3" @@ -10726,6 +11117,11 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "mitt": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", + "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==" + }, "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -10787,8 +11183,7 @@ "nanoid": { "version": "3.1.30", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", - "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==", - "dev": true + "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==" }, "nanomatch": { "version": "1.2.13", @@ -11413,8 +11808,7 @@ "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "picomatch": { "version": "2.2.2", @@ -11558,7 +11952,6 @@ "version": "8.4.4", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.4.tgz", "integrity": "sha512-joU6fBsN6EIer28Lj6GDFoC/5yOZzLCfn0zHAn/MYXI7aPt4m4hK5KC5ovEZXy+lnCjmYIbQWngvju2ddyEr8Q==", - "dev": true, "requires": { "nanoid": "^3.1.30", "picocolors": "^1.0.0", @@ -11964,13 +12357,6 @@ "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", "dev": true }, - "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", - "dev": true, - "optional": true - }, "pretty-error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", @@ -12025,12 +12411,6 @@ "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", "dev": true }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -13352,9 +13732,9 @@ }, "dependencies": { "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "requires": { "ms": "2.1.2" } @@ -13377,9 +13757,9 @@ }, "dependencies": { "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "requires": { "ms": "2.1.2" } @@ -13431,8 +13811,7 @@ "source-map-js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz", - "integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==", - "dev": true + "integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==" }, "source-map-resolve": { "version": "0.5.3", @@ -13471,6 +13850,11 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, "spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", @@ -14471,12 +14855,52 @@ "xdg-basedir": "^4.0.0" }, "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "dev": true }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "is-ci": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", @@ -14494,6 +14918,15 @@ "requires": { "lru-cache": "^6.0.0" } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } } } }, @@ -14687,47 +15120,107 @@ "dev": true }, "vue": { - "version": "2.6.14", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz", - "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==" - }, - "vue-hot-reload-api": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", - "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", - "dev": true + "version": "3.2.23", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.23.tgz", + "integrity": "sha512-MGp9JZC37lzGhwSu6c1tQxrQbXbw7XKFqtYh7SFwNrNK899FPxGAHwSHMZijMChTSC3uZrD2BGO/3EHOgMJ0cw==", + "requires": { + "@vue/compiler-dom": "3.2.23", + "@vue/compiler-sfc": "3.2.23", + "@vue/runtime-dom": "3.2.23", + "@vue/server-renderer": "3.2.23", + "@vue/shared": "3.2.23" + } }, "vue-i18n": { - "version": "8.26.7", - "resolved": "https://registry.npmmirror.com/vue-i18n/download/vue-i18n-8.26.7.tgz?cache=0&sync_timestamp=1635872432438&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fvue-i18n%2Fdownload%2Fvue-i18n-8.26.7.tgz", - "integrity": "sha1-rf1INzRJ/jFDjEjjs71DBE3DpoE=" + "version": "9.2.0-beta.22", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.2.0-beta.22.tgz", + "integrity": "sha512-XnkGLhC1ESZYiCpWnTkAsc3/z8p2ntu5dmQjp9M0hhOZi5w8JZ8VQieJiiq5MFlqjVeIxNkGMn6HM0hvC4xtFA==", + "requires": { + "@intlify/core-base": "9.2.0-beta.22", + "@intlify/shared": "9.2.0-beta.22", + "@intlify/vue-devtools": "9.2.0-beta.22", + "@vue/devtools-api": "^6.0.0-beta.13" + } }, "vue-loader": { - "version": "15.9.8", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.8.tgz", - "integrity": "sha512-GwSkxPrihfLR69/dSV3+5CdMQ0D+jXg8Ma1S4nQXKJAznYFX14vHdc/NetQc34Dw+rBbIJyP7JOuVb9Fhprvog==", + "version": "16.8.3", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz", + "integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==", "dev": true, "requires": { - "@vue/component-compiler-utils": "^3.1.0", - "hash-sum": "^1.0.2", - "loader-utils": "^1.1.0", - "vue-hot-reload-api": "^2.3.0", - "vue-style-loader": "^4.1.0" + "chalk": "^4.1.0", + "hash-sum": "^2.0.0", + "loader-utils": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "vue-router": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.3.tgz", - "integrity": "sha512-FUlILrW3DGitS2h+Xaw8aRNvGTwtuaxrRkNSHWTizOfLUie7wuYwezeZ50iflRn8YPV5kxmU2LQuu3nM/b3Zsg==" - }, - "vue-style-loader": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", - "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", - "dev": true, + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.12.tgz", + "integrity": "sha512-CPXvfqe+mZLB1kBWssssTiWg4EQERyqJZes7USiqfW9B5N2x+nHlnsM1D3b5CaJ6qgCvMmYJnz+G0iWjNCvXrg==", "requires": { - "hash-sum": "^1.0.2", - "loader-utils": "^1.0.2" + "@vue/devtools-api": "^6.0.0-beta.18" } }, "vue-template-compiler": { @@ -14740,16 +15233,13 @@ "he": "^1.1.0" } }, - "vue-template-es2015-compiler": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", - "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", - "dev": true - }, "vuex": { - "version": "3.6.2", - "resolved": "https://registry.npm.taobao.org/vuex/download/vuex-3.6.2.tgz", - "integrity": "sha1-I2vAhqhww655lG8QfxbeWdWJXnE=" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz", + "integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==", + "requires": { + "@vue/devtools-api": "^6.0.0-beta.11" + } }, "watchpack": { "version": "2.3.0", diff --git a/package.json b/package.json index 3c2f082b..6e2adf34 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lx-music-desktop", - "version": "1.15.3", + "version": "1.16.0-beta", "description": "一个免费的音乐查找助手", "main": "./dist/electron/main.js", "productName": "lx-music-desktop", @@ -70,7 +70,7 @@ "up": "cross-env ELECTRON_GET_USE_PROXY=true GLOBAL_AGENT_HTTPS_PROXY=http://localhost:1081 npm i" }, "browserslist": [ - "Electron 15.2.0" + "Electron 13.4.0" ], "engines": { "node": ">= 14" @@ -221,7 +221,7 @@ "spinnies": "^0.5.1", "terser-webpack-plugin": "^5.2.5", "url-loader": "^4.1.1", - "vue-loader": "^15.9.8", + "vue-loader": "^16.8.3", "vue-template-compiler": "^2.6.14", "webpack": "^5.64.4", "webpack-cli": "^4.9.1", @@ -241,14 +241,15 @@ "image-size": "^1.0.0", "koa": "^2.13.4", "long": "^5.2.0", + "mitt": "^3.0.0", "needle": "^3.0.0", "node-id3": "^0.2.3", "request": "^2.88.2", "socket.io": "^4.4.0", "utf-8-validate": "^5.0.7", - "vue": "^2.6.14", - "vue-i18n": "^8.26.7", - "vue-router": "^3.5.3", - "vuex": "^3.6.2" + "vue": "^3.2.23", + "vue-i18n": "^9.2.0-beta.22", + "vue-router": "^4.0.12", + "vuex": "^4.0.2" } } diff --git a/publish/changeLog.md b/publish/changeLog.md index f58f7d9f..40737581 100644 --- a/publish/changeLog.md +++ b/publish/changeLog.md @@ -1,3 +1,12 @@ +### 新增 + +- 播放详情页新增音量控制条 +- 播放详情页新增桌面歌词切换按钮 + ### 优化 -- 优化歌词自动换源机制 +- 优化列表性能,软件整体性能 + +### 其他 + +- 升级vue到 3.x diff --git a/src/common/config.js b/src/common/config.js index c6568f1d..ad870979 100644 --- a/src/common/config.js +++ b/src/common/config.js @@ -53,4 +53,76 @@ module.exports = { navigationUrlWhiteList: [ ], + themes: [ + { + id: 0, + name: '绿意盎然', + className: 'green', + }, + { + id: 1, + name: '蓝田生玉', + className: 'blue', + }, + { + id: 2, + name: '信口雌黄', + className: 'yellow', + }, + { + id: 3, + name: '橙黄橘绿', + className: 'orange', + }, + { + id: 4, + name: '热情似火', + className: 'red', + }, + { + id: 10, + name: '粉装玉琢', + className: 'pink', + }, + { + id: 5, + name: '重斤球紫', + className: 'purple', + }, + { + id: 6, + name: '灰常美丽', + className: 'grey', + }, + { + id: 11, + name: '青出于黑', + className: 'ming', + }, + { + id: 12, + name: '青出于黑', + className: 'blue2', + }, + { + id: 13, + name: '黑纸白字', + className: 'black', + }, + { + id: 7, + name: '月里嫦娥', + className: 'mid_autumn', + }, + { + id: 8, + name: '木叶之村', + className: 'naruto', + }, + { + id: 9, + name: '新年快乐', + className: 'happy_new_year', + }, + ], } diff --git a/src/common/ipcNames.js b/src/common/ipcNames.js index e2674b51..113c290c 100644 --- a/src/common/ipcNames.js +++ b/src/common/ipcNames.js @@ -8,6 +8,8 @@ const names = { clear_cache: 'clear_cache', get_cache_size: 'get_cache_size', get_env_params: 'get_env_params', + wait: 'wait', + wait_cancel: 'wait_cancel', set_music_meta: 'set_music_meta', progress: 'progress', @@ -35,7 +37,7 @@ const names = { lang_s2t: 'lang_s2t', - // handle_kw_decode_lyric: 'handle_kw_decode_lyric', + handle_kw_decode_lyric: 'handle_kw_decode_lyric', get_lyric_info: 'get_lyric_info', set_lyric_info: 'set_lyric_info', set_config: 'set_config', diff --git a/src/renderer/lang/Readme.md b/src/lang/Readme.md similarity index 100% rename from src/renderer/lang/Readme.md rename to src/lang/Readme.md diff --git a/src/lang/en-us.json b/src/lang/en-us.json new file mode 100644 index 00000000..a8524339 --- /dev/null +++ b/src/lang/en-us.json @@ -0,0 +1,389 @@ +{ + "action": "Manage", + "agree": "Accept", + "download": "Downloads", + "leaderboard": "Charts", + "my_list": "Your Library", + "search": "Search", + "setting": "Settings", + "song_list": "Playlists", + "close": "Close", + "comment__hot_load_error": "Hot comments failed to load, click to try to reload", + "comment__hot_loading": "Hot comments are loading", + "comment__hot_title": "Hot Comment", + "comment__new_load_error": "The latest comment failed to load, click to try to reload", + "comment__new_loading": "Latest comments are loading", + "comment__new_title": "Latest comment", + "comment__no_content": "No comments yet", + "comment__refresh": "Refresh comments", + "comment__show": "Song comments", + "comment__title": "{name} comment", + "copy_tip": " (Click to copy)", + "default_list": "Recently Played", + "desktop_lyric__back": "Back", + "desktop_lyric__close": "Close", + "desktop_lyric__font_decrease": "Reduce font size (Right click to fine-tune)", + "desktop_lyric__font_increase": "Increase font size (Right click to fine-tune)", + "desktop_lyric__lock": "Lock Lyrics", + "desktop_lyric__lrc_active_zoom_off": "Unzoom the currently playing lyrics", + "desktop_lyric__lrc_active_zoom_on": "Zoom the currently playing lyrics", + "desktop_lyric__opactiy_decrease": "Increase Transparency (Right click to fine-tune)", + "desktop_lyric__opactiy_increase": "Decrease Transparency (Right click to fine-tune)", + "desktop_lyric__theme": "Theme Color", + "desktop_lyric__unlock": "Unlock Lyrics", + "desktop_lyric__win_top_off": "Cancel the top lyrics interface", + "desktop_lyric__win_top_on": "Top lyrics interface", + "download__high_quality": "High Quality", + "download__lossless": "Lossless", + "download__multiple_tip": "{len} song selected", + "download__multiple_tip2": "Select preferred download quality", + "download__normal": "Normal", + "download__not_available_tip": "The audio quality is not available", + "list__add_to": "Add to ...", + "list__collect": "Collect", + "list__copy_name": "Copy name", + "list__download": "Download", + "list__file": "Locate the file", + "list__load_failed": "Ah, the loading failed 😭", + "list__loading": "List loading...⏳", + "list__move_to": "Move to ...", + "list__pause": "Pause Task", + "list__play": "Play", + "list__play_later": "Play later", + "list__remove": "Remove", + "list__search": "Search", + "list__sort": "Adjust position", + "list__source_detail": "Song Page", + "list__start": "Start Task", + "list_add__btn_title": "Add the song(s) to {name}", + "list_add__multiple_btn_title": "Add these song(s) to {name}", + "list_add__multiple_title_add": "Add the selected {num} song(s) to ...", + "list_add__multiple_title_move": "Move the selected {num} song(s) to ...", + "list_add__title_first_add": "Add", + "list_add__title_first_move": "Move", + "list_add__title_last": "to...", + "lists__duplicate": "Duplicate song", + "lists__export": "Export", + "lists__export_part_desc": "Choose where to save the list file", + "lists__import": "Import", + "lists__import_part_button_cancel": "No", + "lists__import_part_button_confirm": "Overwrite", + "lists__import_part_confirm": "The imported list ({importName}) has the same ID as the local list ({localName}). Do you overwrite the local list?", + "lists__import_part_desc": "Select list file", + "lists__movedown": "Move Down", + "lists__moveup": "Move Up", + "lists__new_list_btn": "Create list", + "lists__new_list_input": "New list...", + "lists__remove": "Remove", + "lists__remove_tip": "Do you really want to remove {name}?", + "lists__remove_tip_button": "Yes, that's right", + "lists__rename": "Rename", + "lists__sync": "Update", + "load_list_file_error_detail": "We have helped you back up the old list file to {path}\nIt is stored in JSON format, you can try to repair and restore it manually\n\nError details: {detail}", + "load_list_file_error_title": "Error loading playlist data", + "loding_list": "Loading...", + "love_list": "Favorites", + "lyric__load_error": "Failed to get lyrics", + "lyric__select": "Lyric text selection", + "min": "Minimize", + "music_album": "Album", + "music_name": "Name", + "music_singer": "Artist", + "music_time": "Length", + "no_item": "Nothing's here...", + "not_agree": "Not accept", + "pagination__next": "Next page", + "pagination__page": "Page {num}", + "pagination__prev": "Previous page", + "player__add_music_to": "Add the current song to...", + "player__buffering": "Buffering...", + "player__desktop_lyric_lock": "Right click to lock lyrics", + "player__desktop_lyric_off": "Close Desktop Lyrics", + "player__desktop_lyric_on": "Open Desktop Lyrics", + "player__desktop_lyric_unlock": "Right click to unlock lyrics", + "player__end": "Stopped", + "player__error": "Error loading music. Switch to next song after 5 seconds", + "player__geting_url": "Getting music link...", + "player__hide_detail_tip": "Hide detail page (Right-click in the view to quickly hide the details page)", + "player__loading": "Music loading...", + "player__music_album": "Album: ", + "player__music_name": "Name: ", + "player__music_singer": "Artist: ", + "player__next": "Next", + "player__pause": "Pause", + "player__pic_tip": "Right click to locate the currently playing song in \"My List\"", + "player__play": "Play", + "player__play_toggle_mode_list": "Play in order", + "player__play_toggle_mode_list_loop": "List Loop", + "player__play_toggle_mode_off": "Disable", + "player__play_toggle_mode_random": "List Random", + "player__play_toggle_mode_single_loop": "Single Loop", + "player__playing": "Now playing...", + "player__prev": "Prev", + "player__refresh_url": "Music URL expired, refreshing...", + "player__stop": "Paused", + "player__volume": "Volume: ", + "source_alias_all": "Aggregated", + "source_alias_bd": "BD Music", + "source_alias_kg": "KG Music", + "source_alias_kw": "KW Music", + "source_alias_mg": "MG Music", + "source_alias_tx": "TX Music", + "source_alias_wy": "WY Music", + "source_alias_xm": "XM Music", + "source_all": "Aggregated", + "source_bd": "Baidu", + "source_kg": "Kugou", + "source_kw": "Kuwo", + "source_mg": "Migu", + "source_tx": "Tencent", + "source_wy": "Netease", + "source_xm": "Xiami", + "sync__merge_btn_local_remote": "Local list merge remote list", + "sync__merge_btn_remote_local": "Remote list merge local list", + "sync__merge_label": "Merge", + "sync__merge_tip": "Merge:", + "sync__merge_tip_desc": "Merge the two lists together, the same song will be removed (the song of the merged person is removed), and different songs will be added.", + "sync__other_label": "Other", + "sync__other_tip": "Other: ", + "sync__other_tip_desc": "\"Only use real-time synchronization function\" will not modify the lists of both parties, only real-time synchronization operations; \"Cancel synchronization\" will directly disconnect the two parties.", + "sync__overwrite": "Full coverage", + "sync__overwrite_btn_cancel": "Cancel sync", + "sync__overwrite_btn_local_remote": "Local list Overwrite remote list", + "sync__overwrite_btn_none": "Only use real-time synchronization", + "sync__overwrite_btn_remote_local": "Remote list Overwrite local list", + "sync__overwrite_label": "Cover", + "sync__overwrite_tip": "Cover: ", + "sync__overwrite_tip_desc": "The list with the same ID of the covered person and the covered list will be deleted and replaced with the list of the covered person (lists with different list IDs will be merged together). If you check Complete coverage, all lists of the covered person will be moved. \nDivide, and then replace with a list of overriders.", + "sync__title": "Choose how to synchronize the list with {name}", + "tag__high_quality": "HQ", + "tag__lossless": "SQ", + "theme_black": "Black", + "theme_blue": "Blue", + "theme_blue2": "Purple Blue", + "theme_green": "Green", + "theme_grey": "Grey", + "theme_happy_new_year": "New Year", + "theme_mid_autumn": "Mid-Autumn", + "theme_ming": "Ming", + "theme_naruto": "Naruto", + "theme_orange": "Orange", + "theme_pink": "Pink", + "theme_purple": "Purple", + "theme_red": "Red", + "theme_yellow": "Yellow", + "search__welcome": "Search what I want to 😉", + "search__hot_search": "Top Searches", + "history_search": "History Searches", + "history_clear": "Clear History", + "history_remove": "Right click to remove this entry", + "download__progress": "Progress", + "download__status": "Status", + "download__quality": "Quality", + "download__all": "All Tasks", + "download__runing": "Downloading", + "download__paused": "Paused", + "download__error": "Error", + "download__finished": "Download complete", + "back": "Back", + + "songlist__open_list": "open {name} playlist", + "songlist__import_input_tip": "Enter songlist link or songlist ID", + "songlist__import_input_tip_1": "Cross-source playlists are not supported, please confirm whether the playlist to be opened corresponds to the current playlist source", + "songlist__import_input_tip_2": "If you encounter a link to a playlist that cannot be opened, welcome feedback", + "songlist__import_input_tip_3": "Kugou source does not support opening with playlist ID, but supports Kugou code opening", + "default": "Default", + "music_duplicate": "Duplicate song", + "export": "Export", + "list__export_part_desc": "Choose where to save the list file", + "import": "Import", + "list__import_part_button_cancel": "Don't", + "list__import_part_button_confirm": "Overwrite", + "list__import_part_confirm": "The imported list ({importName}) has the same ID as the local list ({localName}). Do you want to overwrite the local list?", + "list__import_part_desc": "Select List File", + "list__movedown": "Movedown", + "list__moveup": "Move up", + "list__new_list_btn": "New List", + "list__new_list_input": "New list...", + "list__remove_tip": "Do you really want to remove {name}?", + "list__remove_tip_button": "Yes, that's right", + "list__rename": "Rename", + "list__sync": "Update", + + "music_sort__title": "Adjust the position of {name} to:", + "music_sort__title_multiple": "Adjust the position of the selected {num} songs to:", + "music_sort__input_tip": "Please input which position you want to adjust to", + "music_sort__btn_confirm": "Confirm", + + "cancel_button_text": "Cancel", + "confirm_button_text": "OK", + "date_format_hour": "{num} hours ago", + "date_format_minute": "{num} minutes ago", + "date_format_second": "{num} seconds ago", + + + "setting__about": "About lx-music-desktop", + "setting__backup": "Backup and restore", + "setting__backup_all": "All data (list data and setting data)", + "setting__backup_all_export": "Export", + "setting__backup_all_export_desc": "Select the backup to...", + "setting__backup_all_import": "Import", + "setting__backup_all_import_desc": "Select a backup file", + "setting__backup_part": "Partial data (list data includes audition list, favorite list, user-defined list, setting data does not include shortcut key settings)", + "setting__backup_part_export_list": "Export lists", + "setting__backup_part_export_list_desc": "Save the list to...", + "setting__backup_part_export_setting": "Export settings", + "setting__backup_part_export_setting_desc": "Save the list to...", + "setting__backup_part_import_list": "Import lists", + "setting__backup_part_import_list_desc": "Select a list backup", + "setting__backup_part_import_setting": "Import settings", + "setting__backup_part_import_setting_desc": "Select the Settings file", + "setting__basic": "General", + "setting__basic_animation": "Random pop-up animation", + "setting__basic_control_btn_position": "Control Button Position", + "setting__basic_control_btn_position_left": "Left", + "setting__basic_control_btn_position_right": "Right", + "setting__basic_lang": "Language", + "setting__basic_lang_title": "The language displayed in the software", + "setting__basic_show_animation": "Show switching animation", + "setting__basic_source": "Music source", + "setting__basic_source_status_failed": "Initialization failed", + "setting__basic_source_status_initing": "Initializing", + "setting__basic_source_status_success": "Initialization successful", + "setting__basic_source_temp": "Temporary API (some features not available; workaround if Test API unavailable)", + "setting__basic_source_test": "Test API (Available for most software features)", + "setting__basic_source_title": "Choose a music source", + "setting__basic_source_user_api_btn": "Custom Source Management", + "setting__basic_sourcename": "Source name", + "setting__basic_sourcename_alias": "Aliases", + "setting__basic_sourcename_real": "Original", + "setting__basic_sourcename_title": "Select the name of music source", + "setting__basic_theme": "Theme", + "setting__basic_to_tray": "Do not exit the software when closing the software and minimize it to the system tray", + "setting__basic_window_size": "Window size", + "setting__basic_window_size_big": "Large", + "setting__basic_window_size_huge": "Huge", + "setting__basic_window_size_larger": "Larger", + "setting__basic_window_size_medium": "Medium", + "setting__basic_window_size_oversized": "Oversized", + "setting__basic_window_size_small": "Small", + "setting__basic_window_size_smaller": "Smaller", + "setting__basic_window_size_title": "Set the window size", + "setting__click_copy": "Click to copy", + "setting__click_open": "Click to open", + "setting__desktop_lyric": "Desktop Lyric Settings", + "setting__desktop_lyric_always_on_top": "Make the lyrics always above other windows", + "setting__desktop_lyric_enable": "Display lyrics", + "setting__desktop_lyric_font": "Lyric font", + "setting__desktop_lyric_font_default": "Default", + "setting__desktop_lyric_lock": "Lock lyrics", + "setting__desktop_lyric_lock_screen": "It is not allowed to drag the lyrics window out of the main screen", + "setting__download": "Download", + "setting__download_data_embed": "Whether to embed the following content in the audio file", + "setting__download_embed_lyric": "Embedding lyric", + "setting__download_embed_pic": "Embedding cover", + "setting__download_enable": "Whether to enable download function", + "setting__download_lyric": "Lyrics download", + "setting__download_lyric_format": "Downloaded lyrics file encoding format", + "setting__download_lyric_format_gbk": "GBK (Try to select this format when Chinese garbled characters appear on some devices)", + "setting__download_lyric_format_utf8": "UTF-8", + "setting__download_lyric_title": "Select whether to download the lyrics file", + "setting__download_name": "Music file naming", + "setting__download_name1": "Title - Artist", + "setting__download_name2": "Artist - Title", + "setting__download_name3": "Title only", + "setting__download_name_title": "Select the music naming method for downloading", + "setting__download_path": "Download path", + "setting__download_path_change_btn": "Change", + "setting__download_path_label": "Current: ", + "setting__download_path_open_label": "Click to open this path", + "setting__download_path_title": "Define the path to downloading", + "setting__download_select_save_path": "Select the save path", + "setting__download_use_other_source": "Automatically change the source to download (when the song cannot be downloaded from the original source, try to switch to another source to download. Note: this function does not 100% guarantee that the version of the song after the source is changed is the same as the original version)", + "setting__hot_key": "Shortcut Key Settings", + "setting__hot_key_common_focus_search_input": "Focus Search Box", + "setting__hot_key_common_min": "Minimize the program", + "setting__hot_key_common_toggle_close": "Quit Program", + "setting__hot_key_common_toggle_hide": "Show/Hide Program", + "setting__hot_key_common_toggle_min": "Minimize/Restore Program", + "setting__hot_key_desktop_lyric_toggle_always_top": "Top Desktop Lyrics Switch", + "setting__hot_key_desktop_lyric_toggle_lock": "Desktop Lyric Lock Switch", + "setting__hot_key_desktop_lyric_toggle_visible": "Turn on/off desktop lyrics", + "setting__hot_key_global_title": "Global Shortcut Key", + "setting__hot_key_local_title": "Shortcut Keys in Software", + "setting__hot_key_player_next": "Next Song", + "setting__hot_key_player_prev": "Previous Song", + "setting__hot_key_player_toggle_play": "Play/Pause Control", + "setting__hot_key_player_volume_down": "Reduce Volume", + "setting__hot_key_player_volume_mute": "Mute Switch", + "setting__hot_key_player_volume_up": "Increase Volume", + "setting__hot_key_tip_input": "Please enter a new key", + "setting__hot_key_unset_input": "Not Set", + "setting__is_enable": "Enabled", + "setting__is_show": "Showed", + "setting__list": "List", + "setting__list_add_music_location_type": "Position when adding a song to the list", + "setting__list_add_music_location_type_bottom": "Bottom", + "setting__list_add_music_location_type_top": "Top", + "setting__list_scroll": "Remember the position of the scroll bar of the playlist (only valid for my music classification)", + "setting__list_source": "Show song source (only valid for my music category)", + "setting__network": "Network", + "setting__network_proxy_host": "Host", + "setting__network_proxy_password": "Password", + "setting__network_proxy_port": "Port", + "setting__network_proxy_title": "HTTP Proxy (False setting would block Internet connections)", + "setting__network_proxy_username": "Username", + "setting__odc": "Auto clear", + "setting__odc_clear_search_input": "Clear the search box when you are not searching", + "setting__odc_clear_search_list": "Clear the search list when you are not searching", + "setting__other": "Extras", + "setting__other_play_list_cache": "List cache management (links to songs that have been cached in my list, alternative sources for playback, after cleaning up, you need to re-acquire them when you play and download songs, and do not clean up if there are no issues related to song playback)", + "setting__other_play_list_cache_clear_btn": "Clear list cache information", + "setting__other_resource_cache": "Resource cache management (pictures, audios and other caches, pictures and other resources will need to be downloaded again after cleaning up, it is not recommended to clean up, the software will dynamically manage the cache size according to the disk space)", + "setting__other_resource_cache_clear_btn": "Clear resource cache", + "setting__other_resource_cache_label": "The software has used cache size: ", + "setting__other_tray_theme": "Tray Icon Style", + "setting__other_tray_theme_black": "Black Color", + "setting__other_tray_theme_native": "Solid Color", + "setting__other_tray_theme_origin": "Primary Color", + "setting__play": "Play", + "setting__play_lyric_lxlrc": "Use Karaoke-style lyrics playback (if supported)", + "setting__play_lyric_transition": "Show lyrics translation", + "setting__play_mediaDevice": "Audio output", + "setting__play_mediaDevice_remove_stop_play": "Pause the song when the current sound output device is changed", + "setting__play_mediaDevice_title": "Select a media device for audio output", + "setting__play_quality": "Play 320K quality songs first (if supported)", + "setting__play_save_play_time": "Remember playback progress", + "setting__play_task_bar": "Show playing progress on the taskbar", + "setting__search": "Search", + "setting__search_focus_search_box": "Automatically focus the search box on startup", + "setting__search_history": "Search history", + "setting__search_hot": "Top Searches", + "setting__sync": "Data synchronization [This is a test function, it is recommended to back up the playlist before using it for the first time]", + "setting__sync_address": "Synchronization service address: {address}", + "setting__sync_auth_code": "Connection code: {code}", + "setting__sync_device": "Connected devices: {devices}", + "setting__sync_enable": "Enable the synchronization function (because the data is transmitted in clear text, please use it under a trusted network)", + "setting__sync_port": "Sync port settings", + "setting__sync_port_tip": "Please enter the synchronization service port number", + "setting__sync_refresh_code": "Refresh the connection code", + "setting__update": "Update", + "setting__update_checking": "Checking for updates...", + "setting__update_current_label": "Current version: ", + "setting__update_downloading": "Update is found and being downloaded...⏳", + "setting__update_init": "Processing update...", + "setting__update_latest": "The software is up-to-date, enjoy yourself!🥂", + "setting__update_latest_label": "Latest version: ", + "setting__update_open_version_modal_btn": "Open the update window🚀", + "setting__update_progress": "Download progress: ", + "setting__update_unknown": "Unknown", + + "user_api__title": "Custom Source Management", + "user_api__readme": "Source writing instructions: ", + "user_api__note": "Tip: Although we have isolated the script's running environment as much as possible, importing scripts containing malicious behaviors may still affect your system. Please import them carefully.", + "user_api__btn_remove": "Remove", + "user_api__btn_import": "Import", + "user_api__btn_export": "Export", + "user_api__import_file": "Select music API script file", + "user_api__noitem": "There is nothing here...😲" +} diff --git a/src/lang/index.js b/src/lang/index.js new file mode 100644 index 00000000..4083ed5c --- /dev/null +++ b/src/lang/index.js @@ -0,0 +1,42 @@ +import zh_cn from './zh-cn.json' +import zh_tw from './zh-tw.json' +import en_us from './en-us.json' + +const langs = [ + { + name: '简体中文', + locale: 'zh-cn', + alternate: 'zh-hans', + country: 'cn', + fallback: true, + message: zh_cn, + }, + { + name: '繁体中文', + locale: 'zh-tw', + alternate: 'zh-hk', + country: 'cn', + message: zh_tw, + }, + { + name: 'English', + locale: 'en-us', + country: 'us', + message: en_us, + }, +] + +const langList = [] +const messages = {} +langs.forEach(item => { + langList.push({ + name: item.name, + locale: item.locale, + }) + messages[item.locale] = item.message +}) + +export { + langList, + messages, +} diff --git a/src/renderer/lang/languages.json b/src/lang/languages.json similarity index 100% rename from src/renderer/lang/languages.json rename to src/lang/languages.json diff --git a/src/lang/zh-cn.json b/src/lang/zh-cn.json new file mode 100644 index 00000000..f3103cd8 --- /dev/null +++ b/src/lang/zh-cn.json @@ -0,0 +1,388 @@ +{ + "search": "搜索", + "song_list": "歌单", + "leaderboard": "排行榜", + "my_list": "我的列表", + "download": "下载", + "min": "最小化", + "close": "关闭", + "setting": "设置", + "download__not_available_tip": "该音质不可用", + "download__lossless": "无损音质", + "download__high_quality": "高品音质", + "download__normal": "普通音质", + "download__multiple_tip": "已选择 {len} 首歌曲", + "download__multiple_tip2": "请选择要优先下载的音质", + "list_add__title_first_add": "添加", + "list_add__title_first_move": "移动", + "list_add__title_last": "到...", + "list_add__btn_title": "把该歌曲添加到 {name}", + "list_add__multiple_title_add": "添加已选的 {num} 首歌曲到...", + "list_add__multiple_title_move": "移动已选的 {num} 首歌曲到...", + "list_add__multiple_btn_title": "把这些歌曲添加到 {name}", + "action": "操作", + "music_album": "专辑", + "default_list": "试听列表", + "list__add_to": "添加到...", + "list__copy_name": "复制歌曲名", + "list__download": "下载", + "list__move_to": "移动到...", + "list__play": "播放", + "list__collect": "收藏", + "list__play_later": "稍后播放", + "list__remove": "删除", + "list__search": "搜索", + "list__sort": "调整位置", + "list__source_detail": "歌曲详情页", + "list__start": "开始任务", + "list__pause": "暂停任务", + "list__file": "定位文件", + "list__load_failed": "啊,加载失败了😭", + "list__loading": "列表加载中...⏳", + "tag__lossless": "SQ", + "tag__high_quality": "HQ", + "lists__duplicate": "重复歌曲", + "lists__export": "导出", + "lists__export_part_desc": "选择列表文件保存位置", + "lists__import": "导入", + "lists__import_part_button_cancel": "不要啊", + "lists__import_part_button_confirm": "覆盖掉", + "lists__import_part_confirm": "导入的列表({importName})与本地列表({localName})的ID相同,是否覆盖本地列表?", + "lists__import_part_desc": "选择列表文件", + "lists__movedown": "下移", + "lists__moveup": "上移", + "lists__new_list_btn": "新建列表", + "lists__new_list_input": "新列表...", + "lists__remove": "删除", + "lists__remove_tip": "你真的想要移除 {name} 吗?", + "lists__remove_tip_button": "是的 没错", + "lists__rename": "重命名", + "lists__sync": "更新", + "loding": "加载中...", + "love_list": "收藏", + "music_name": "歌曲名", + "no_item": "列表竟然是空的...", + "music_singer": "歌手", + "music_time": "时长", + "sync__merge_btn_local_remote": "本机列表 合并 远程列表", + "sync__merge_btn_remote_local": "远程列表 合并 本机列表", + "sync__merge_label": "合并", + "sync__merge_tip": "合并:", + "sync__merge_tip_desc": "将两边的列表合并到一起,相同的歌曲将被去掉(去掉的是被合并者的歌曲),不同的歌曲将被添加。", + "sync__other_label": "其他", + "sync__other_tip": "其他:", + "sync__other_tip_desc": "“仅使用实时同步功能”将不修改双方的列表,仅实时同步操作;“取消同步”将直接断开双方的连接。", + "sync__overwrite": "完全覆盖", + "sync__overwrite_btn_cancel": "取消同步", + "sync__overwrite_btn_local_remote": "本机列表 覆盖 远程列表", + "sync__overwrite_btn_none": "仅使用实时同步功能", + "sync__overwrite_btn_remote_local": "远程列表 覆盖 本机列表", + "sync__overwrite_label": "覆盖", + "sync__overwrite_tip": "覆盖:", + "sync__overwrite_tip_desc": "被覆盖者与覆盖者列表ID相同的列表将被删除后替换成覆盖者的列表(列表ID不同的列表将被合并到一起),若勾选完全覆盖,则被覆盖者的所有列表将被移除,然后替换成覆盖者的列表。", + "sync__title": "选择与 {name} 的列表同步方式", + "agree": "接受", + "not_agree": "不接受", + "player__add_music_to": "添加当前歌曲到...", + "player__music_album": "专辑名:", + "player__buffering": "缓冲中...", + "comment__hot_load_error": "热门评论加载失败,点击尝试重新加载", + "comment__hot_loading": "热门评论加载中", + "comment__hot_title": "热门评论", + "comment__new_load_error": "最新评论加载失败,点击尝试重新加载", + "comment__new_loading": "最新评论加载中", + "comment__new_title": "最新评论", + "comment__no_content": "暂无评论", + "comment__refresh": "刷新评论", + "comment__show": "歌曲评论", + "comment__title": "{name} 的评论", + "copy_tip": "(点击复制)", + "player__desktop_lyric_lock": "右击锁定歌词", + "player__desktop_lyric_off": "关闭桌面歌词", + "player__desktop_lyric_on": "开启桌面歌词", + "player__desktop_lyric_unlock": "右击解锁歌词", + "player__end": "播放完毕", + "player__error": "音频加载出错,5 秒后切换下一首", + "player__geting_url": "歌曲链接获取中...", + "player__hide_detail_tip": "隐藏详情页(界面内右键双击可快速隐藏详情页)", + "player__loading": "音乐加载中...", + "lyric__load_error": "歌词获取失败", + "lyric__select": "歌词文本选择", + "player__music_name": "歌曲名:", + "player__next": "下一首", + "player__pause": "暂停", + "player__pic_tip": "右击在“我的列表”定位当前播放的歌曲", + "player__play": "播放", + "player__play_toggle_mode_list": "顺序播放", + "player__play_toggle_mode_list_loop": "列表循环", + "player__play_toggle_mode_off": "禁用", + "player__play_toggle_mode_random": "列表随机", + "player__play_toggle_mode_single_loop": "单曲循环", + "player__playing": "播放中...", + "player__prev": "上一首", + "player__refresh_url": "URL过期,正在刷新URL...", + "player__music_singer": "艺术家:", + "player__stop": "暂停播放", + "player__volume": "当前音量:", + "pagination__prev": "上一页", + "pagination__next": "下一页", + "pagination__page": "第 {num} 页", + "desktop_lyric__close": "关闭", + "desktop_lyric__back": "返回", + "desktop_lyric__lock": "锁定歌词", + "desktop_lyric__unlock": "解锁歌词", + "desktop_lyric__theme": "主题配色", + "desktop_lyric__font_increase": "增加字体大小(右击可微调)", + "desktop_lyric__font_decrease": "减小字体大小(右击可微调)", + "desktop_lyric__opactiy_increase": "减小透明度(右击可微调)", + "desktop_lyric__opactiy_decrease": "增加透明度(右击可微调)", + "desktop_lyric__lrc_active_zoom_on": "缩放当前播放的歌词", + "desktop_lyric__lrc_active_zoom_off": "取消缩放当前播放的歌词", + "desktop_lyric__win_top_on": "置顶歌词界面", + "desktop_lyric__win_top_off": "取消置顶歌词界面", + "theme_green": "绿意盎然", + "theme_blue": "蓝田生玉", + "theme_yellow": "信口雌黄", + "theme_orange": "橙黄橘绿", + "theme_red": "热情似火", + "theme_pink": "粉装玉琢", + "theme_purple": "重斤球紫", + "theme_grey": "灰常美丽", + "theme_ming": "青出于黑", + "theme_blue2": "清热版蓝", + "theme_black": "黑灯瞎火", + "theme_mid_autumn": "月里嫦娥", + "theme_naruto": "木叶之村", + "theme_happy_new_year": "新年快乐", + "source_kw": "酷我音乐", + "source_kg": "酷狗音乐", + "source_tx": "企鹅音乐", + "source_wy": "网易音乐", + "source_mg": "咪咕音乐", + "source_xm": "虾米音乐", + "source_bd": "百度音乐", + "source_all": "聚合搜索", + "source_alias_kw": "小蜗音乐", + "source_alias_kg": "小枸音乐", + "source_alias_tx": "小秋音乐", + "source_alias_wy": "小芸音乐", + "source_alias_mg": "小蜜音乐", + "source_alias_xm": "小霞音乐", + "source_alias_bd": "小杜音乐", + "source_alias_all": "聚合大会", + "load_list_file_error_title": "播放列表数据加载错误(建议到GitHub或加群反馈)", + "load_list_file_error_detail": "我们已经帮你把旧的列表文件备份到{path}\n它以 JSON 格式存储,你可以尝试手动修复并恢复它\n\n错误详情:{detail}", + "search__welcome": "搜我所想~~😉", + "search__hot_search": "热门搜索", + "history_search": "历史搜索", + "history_clear": "清空搜索历史", + "history_remove": "右击移除该历史", + + "download__progress": "进度", + "download__status": "状态", + "download__quality": "品质", + "download__all": "全部任务", + "download__runing": "正在下载", + "download__paused": "已暂停", + "download__error": "出错", + "download__finished": "下载完成", + "back": "返回", + + "songlist__open_list": "打开{name}歌单", + "songlist__import_input_tip": "输入歌单链接或歌单ID", + "songlist__import_input_tip_1": "不支持跨源打开歌单,请确认要打开的歌单与当前歌单源是否对应", + "songlist__import_input_tip_2": "若遇到无法打开的歌单链接,欢迎反馈", + "songlist__import_input_tip_3": "酷狗源不支持用歌单ID打开,但支持酷狗码打开", + "default": "默认", + + "music_duplicate": "重复歌曲", + "export": "导出", + "list__export_part_desc": "选择列表文件保存位置", + "import": "导入", + "list__import_part_button_cancel": "不要啊", + "list__import_part_button_confirm": "覆盖掉", + "list__import_part_confirm": "导入的列表({importName})与本地列表({localName})的ID相同,是否覆盖本地列表?", + "list__import_part_desc": "选择列表文件", + "list__movedown": "下移", + "list__moveup": "上移", + "list__new_list_btn": "新建列表", + "list__new_list_input": "新列表...", + "list__remove_tip": "你真的想要移除 {name} 吗?", + "list__remove_tip_button": "是的 没错", + "list__rename": "重命名", + "list__sync": "更新", + + "music_sort__title": "将 {name} 的位置调整到:", + "music_sort__title_multiple": "将已选的 {num} 首歌曲的位置调整到:", + "music_sort__input_tip": "请输入要调整到第几个位置", + "music_sort__btn_confirm": "确定", + "cancel_button_text": "我不", + "confirm_button_text": "好的", + "date_format_hour": "{num}小时前", + "date_format_minute": "{num}分钟前", + "date_format_second": "{num}秒前", + + "setting__about": "关于洛雪音乐", + "setting__backup": "备份与恢复", + "setting__backup_all": "所有数据(列表数据与设置数据)", + "setting__backup_all_export": "导出", + "setting__backup_all_export_desc": "选择备份保存位置", + "setting__backup_all_import": "导入", + "setting__backup_all_import_desc": "选择备份文件", + "setting__backup_part": "部分数据(列表数据包括试听列表、收藏列表、用户自定义列表,设置数据不包括快捷键设置)", + "setting__backup_part_export_list": "导出列表", + "setting__backup_part_export_list_desc": "选择歌单保存位置", + "setting__backup_part_export_setting": "导出设置", + "setting__backup_part_export_setting_desc": "选择设置保存位置", + "setting__backup_part_import_list": "导入列表", + "setting__backup_part_import_list_desc": "选择列表文件", + "setting__backup_part_import_setting": "导入设置", + "setting__backup_part_import_setting_desc": "选择配置文件", + "setting__basic": "基本设置", + "setting__basic_animation": "弹出层随机动画", + "setting__basic_control_btn_position": "控制按钮位置", + "setting__basic_control_btn_position_left": "左边", + "setting__basic_control_btn_position_right": "右边", + "setting__basic_lang": "语言", + "setting__basic_lang_title": "软件显示的语言", + "setting__basic_show_animation": "显示切换动画", + "setting__basic_source": "音乐来源", + "setting__basic_source_status_failed": "初始化失败", + "setting__basic_source_status_initing": "初始化中", + "setting__basic_source_status_success": "初始化成功", + "setting__basic_source_temp": "临时接口(软件的某些功能不可用,建议测试接口不可用再使用本接口)", + "setting__basic_source_test": "测试接口(几乎软件的所有功能都可用)", + "setting__basic_source_title": "选择音乐来源", + "setting__basic_source_user_api_btn": "自定义源管理", + "setting__basic_sourcename": "音源名字", + "setting__basic_sourcename_alias": "别名", + "setting__basic_sourcename_real": "原名", + "setting__basic_sourcename_title": "选择音源名字类型", + "setting__basic_theme": "主题颜色", + "setting__basic_to_tray": "关闭软件时不退出软件将其最小化到系统托盘", + "setting__basic_window_size": "窗口尺寸", + "setting__basic_window_size_big": "大", + "setting__basic_window_size_huge": "巨大", + "setting__basic_window_size_larger": "较大", + "setting__basic_window_size_medium": "中", + "setting__basic_window_size_oversized": "超大", + "setting__basic_window_size_small": "小", + "setting__basic_window_size_smaller": "较小", + "setting__basic_window_size_title": "设置软件窗口尺寸", + "setting__click_copy": "点击复制", + "setting__click_open": "点击打开", + "setting__desktop_lyric": "桌面歌词设置", + "setting__desktop_lyric_always_on_top": "使歌词总是在其他窗口之上", + "setting__desktop_lyric_enable": "显示歌词", + "setting__desktop_lyric_font": "歌词字体", + "setting__desktop_lyric_font_default": "默认", + "setting__desktop_lyric_lock": "锁定歌词", + "setting__desktop_lyric_lock_screen": "不允许歌词窗口拖出主屏幕之外", + "setting__download": "下载设置", + "setting__download_data_embed": "是否将以下内容嵌入到音频文件中", + "setting__download_embed_lyric": "歌词嵌入", + "setting__download_embed_pic": "封面嵌入", + "setting__download_enable": "是否启用下载功能", + "setting__download_lyric": "歌词下载", + "setting__download_lyric_format": "下载的歌词文件编码格式", + "setting__download_lyric_format_gbk": "GBK(在某些设备上出现中文乱码时可尝试选择此格式)", + "setting__download_lyric_format_utf8": "UTF-8", + "setting__download_lyric_title": "是否同时下载歌词文件", + "setting__download_name": "文件命名方式", + "setting__download_name1": "歌名 - 歌手", + "setting__download_name2": "歌手 - 歌名", + "setting__download_name3": "歌名", + "setting__download_name_title": "下载歌曲时的命名方式", + "setting__download_path": "下载路径", + "setting__download_path_change_btn": "更改", + "setting__download_path_label": "当前下载路径:", + "setting__download_path_open_label": "点击打开当前路径", + "setting__download_path_title": "下载歌曲保存的路径", + "setting__download_select_save_path": "选择歌曲保存路径", + "setting__download_use_other_source": "自动换源下载(当无法从歌曲的原始源下载时,尝试切换到其他源下载,注:此功能不100%保证换源后的歌曲版本与原版一致)", + "setting__hot_key": "快捷键设置", + "setting__hot_key_common_focus_search_input": "聚焦搜索框", + "setting__hot_key_common_min": "最小化程序", + "setting__hot_key_common_toggle_close": "退出程序", + "setting__hot_key_common_toggle_hide": "显示/隐藏程序", + "setting__hot_key_common_toggle_min": "最小化/还原程序", + "setting__hot_key_desktop_lyric_toggle_always_top": "桌面歌词置顶切换", + "setting__hot_key_desktop_lyric_toggle_lock": "桌面歌词锁定切换", + "setting__hot_key_desktop_lyric_toggle_visible": "开/关桌面歌词", + "setting__hot_key_global_title": "全局快捷键", + "setting__hot_key_local_title": "软件内快捷键", + "setting__hot_key_player_next": "下一首歌曲", + "setting__hot_key_player_prev": "上一首歌曲", + "setting__hot_key_player_toggle_play": "播放/暂停控制", + "setting__hot_key_player_volume_down": "减少音量", + "setting__hot_key_player_volume_mute": "静音切换", + "setting__hot_key_player_volume_up": "增加音量", + "setting__hot_key_tip_input": "请输入新的按键", + "setting__hot_key_unset_input": "未设置", + "setting__is_enable": "是否启用", + "setting__is_show": "是否显示", + "setting__list": "列表设置", + "setting__list_add_music_location_type": "添加歌曲到列表时的位置", + "setting__list_add_music_location_type_bottom": "底部", + "setting__list_add_music_location_type_top": "顶部", + "setting__list_scroll": "记住播放列表滚动条位置(仅对我的音乐分类有效)", + "setting__list_source": "显示歌曲源(仅对我的音乐分类有效)", + "setting__network": "网络设置", + "setting__network_proxy_host": "主机", + "setting__network_proxy_password": "密码", + "setting__network_proxy_port": "端口", + "setting__network_proxy_title": "HTTP代理设置(乱设置软件将无法联网)", + "setting__network_proxy_username": "用户名", + "setting__odc": "强迫症设置", + "setting__odc_clear_search_input": "离开搜索界面时清空搜索框", + "setting__odc_clear_search_list": "离开搜索界面时清空搜索列表", + "setting__other": "其他", + "setting__other_play_list_cache": "列表缓存管理(我的列表中已缓存的歌曲链接、播放代替源,清理后播放、下载歌曲时需要重新获取,没有歌曲播放相关的问题不要清理)", + "setting__other_play_list_cache_clear_btn": "清理列表缓存信息", + "setting__other_resource_cache": "资源缓存管理(图片、音频等缓存,清理后图片等资源将需要重新下载,不建议清理,软件会根据磁盘空间动态管理缓存大小)", + "setting__other_resource_cache_clear_btn": "清理资源缓存", + "setting__other_resource_cache_label": "软件已使用缓存大小:", + "setting__other_tray_theme": "托盘图标样式", + "setting__other_tray_theme_black": "黑色", + "setting__other_tray_theme_native": "纯色", + "setting__other_tray_theme_origin": "原色", + "setting__play": "播放设置", + "setting__play_lyric_lxlrc": "使用卡拉OK式歌词播放(如果支持)", + "setting__play_lyric_transition": "显示歌词翻译", + "setting__play_mediaDevice": "音频输出", + "setting__play_mediaDevice_remove_stop_play": "当前的声音输出设备被改变时暂停播放歌曲", + "setting__play_mediaDevice_title": "选择声音输出的媒体设备", + "setting__play_quality": "优先播放320K品质的歌曲(如果支持)", + "setting__play_save_play_time": "记住播放进度", + "setting__play_task_bar": "在任务栏上显示当前歌曲播放进度", + "setting__search": "搜索设置", + "setting__search_focus_search_box": "启动时自动聚焦搜索框", + "setting__search_history": "显示历史搜索记录", + "setting__search_hot": "显示热门搜索", + "setting__sync": "数据同步 [此为测试功能,首次使用前建议先备份一次歌单]", + "setting__sync_address": "同步服务地址:{address}", + "setting__sync_auth_code": "连接码:{code}", + "setting__sync_device": "已连接的设备:{devices}", + "setting__sync_enable": "启用同步功能(由于数据是明文传输,请在受信任的网络下使用)", + "setting__sync_port": "同步端口设置", + "setting__sync_port_tip": "请输入同步服务端口号", + "setting__sync_refresh_code": "刷新连接码", + "setting__update": "软件更新", + "setting__update_checking": "检查更新中...", + "setting__update_current_label": "当前版本:", + "setting__update_downloading": "发现新版本并在努力下载中,请稍后...⏳", + "setting__update_init": "处理更新中...", + "setting__update_latest": "软件已是最新,尽情地体验吧~🥂", + "setting__update_latest_label": "最新版本:", + "setting__update_open_version_modal_btn": "打开更新窗口 🚀", + "setting__update_progress": "下载进度:", + "setting__update_unknown": "未知", + "user_api__title": "自定义源管理", + "user_api__readme": "源编写说明:", + "user_api__note": "提示:虽然我们已经尽可能地隔离了脚本的运行环境,但导入包含恶意行为的脚本仍可能会影响你的系统,请谨慎导入。", + "user_api__btn_remove": "移除", + "user_api__btn_import": "导入", + "user_api__btn_export": "导出", + "user_api__import_file": "选择音乐API脚本文件", + "user_api__noitem": "这里竟然是空的 😲" +} diff --git a/src/lang/zh-tw.json b/src/lang/zh-tw.json new file mode 100644 index 00000000..78a17ed8 --- /dev/null +++ b/src/lang/zh-tw.json @@ -0,0 +1,385 @@ +{ + "action": "操作", + "agree": "接受", + "download": "下載管理", + "leaderboard": "排行榜", + "my_list": "我的列表", + "search": "搜索", + "setting": "設置", + "song_list": "歌單", + "close": "關閉", + "comment__hot_load_error": "熱門評論加載失敗,點擊嘗試重新加載", + "comment__hot_loading": "熱門評論加載中", + "comment__hot_title": "熱門評論", + "comment__new_load_error": "最新評論加載失敗,點擊嘗試重新加載", + "comment__new_loading": "最新評論加載中", + "comment__new_title": "最新評論", + "comment__no_content": "暫無評論", + "comment__refresh": "刷新評論", + "comment__show": "歌曲評論", + "comment__title": "{name} 的評論", + "copy_tip": "(點擊複製)", + "default_list": "試聽列表", + "desktop_lyric__back": "返回", + "desktop_lyric__close": "關閉", + "desktop_lyric__font_decrease": "減小字體大小(右擊可微調)", + "desktop_lyric__font_increase": "增加字體大小(右擊可微調)", + "desktop_lyric__lock": "鎖定歌詞", + "desktop_lyric__lrc_active_zoom_off": "取消縮放當前播放的歌詞", + "desktop_lyric__lrc_active_zoom_on": "縮放當前播放的歌詞", + "desktop_lyric__opactiy_decrease": "增加透明度(右擊可微調)", + "desktop_lyric__opactiy_increase": "減小透明度(右擊可微調)", + "desktop_lyric__theme": "主題配色", + "desktop_lyric__unlock": "解鎖歌詞", + "desktop_lyric__win_top_off": "取消置頂歌詞界面", + "desktop_lyric__win_top_on": "置頂歌詞界面", + "download__high_quality": "高品音質", + "download__lossless": "無損音質", + "download__multiple_tip": "已選擇 {len} 首歌曲", + "download__multiple_tip2": "請選擇要優先下載的音質", + "download__normal": "普通音質", + "download__not_available_tip": "該音質不可用", + "list__add_to": "添加到...", + "list__collect": "收藏", + "list__copy_name": "複製歌曲名", + "list__download": "下載", + "list__file": "定位文件", + "list__load_failed": "啊,加載失敗了😭", + "list__loading": "列表加載中...⏳", + "list__move_to": "移動到...", + "list__pause": "暫停任務", + "list__play": "播放", + "list__play_later": "稍後播放", + "list__remove": "刪除", + "list__search": "搜索", + "list__sort": "調整位置", + "list__source_detail": "歌曲詳情頁", + "list__start": "開始任務", + "list_add__btn_title": "把該歌曲添加到 {name}", + "list_add__multiple_btn_title": "把這些歌曲添加到 {name}", + "list_add__multiple_title_add": "添加已選的 {num} 首歌曲到...", + "list_add__multiple_title_move": "添加移動已選的 {num} 首歌曲到...", + "list_add__title_first_add": "添加", + "list_add__title_first_move": "移動", + "list_add__title_last": "到...", + "lists__duplicate": "重複歌曲", + "lists__export": "導出", + "lists__export_part_desc": "選擇列表文件保存位置", + "lists__import": "導入", + "lists__import_part_button_cancel": "不要啊", + "lists__import_part_button_confirm": "覆蓋掉", + "lists__import_part_confirm": "導入的列表({importName})與本地列表({localName})的ID相同,是否覆蓋本地列表?", + "lists__import_part_desc": "選擇列表文件", + "lists__movedown": "下移", + "lists__moveup": "上移", + "lists__new_list_btn": "新建列表", + "lists__new_list_input": "新列表...", + "lists__remove": "刪除", + "lists__remove_tip": "你真的想要移除 {name} 嗎?", + "lists__remove_tip_button": "是的 沒錯", + "lists__rename": "重命名", + "lists__sync": "更新", + "load_list_file_error_detail": "我們已經幫你把舊的列表文件備份到{path}\n它以 JSON 格式存儲,你可以嘗試手動修復並恢復它\n\n錯誤詳情:{detail}", + "load_list_file_error_title": "播放列表數據加載錯誤", + "loding": "加載中...", + "love_list": "收藏列表", + "lyric__load_error": "歌詞獲取失敗", + "lyric__select": "歌詞文本選擇", + "min": "最小化", + "music_album": "專輯", + "music_name": "歌曲名", + "music_singer": "歌手", + "music_time": "時長", + "no_item": "列表竟然是空的...", + "not_agree": "不接受", + "pagination__next": "下一頁", + "pagination__page": "第 {num} 頁", + "pagination__prev": "上一頁", + "player__add_music_to": "添加當前歌曲到...", + "player__album": "專輯名:", + "player__buffering": "緩衝中...", + "player__desktop_lyric_lock": "右擊鎖定歌詞", + "player__desktop_lyric_off": "關閉桌面歌詞", + "player__desktop_lyric_on": "開啟桌面歌詞", + "player__desktop_lyric_unlock": "右擊解鎖歌詞", + "player__end": "播放完畢", + "player__error": "音頻加載出錯,5 秒後切換下一首", + "player__geting_url": "歌曲鏈接獲取中...", + "player__hide_detail_tip": "隱藏詳情頁(界面內右鍵雙擊可快速隱藏詳情頁)", + "player__loading": "音樂加載中...", + "player__music_name": "歌曲名:", + "player__music_singer": "藝術家:", + "player__next": "下一首", + "player__pause": "暫停", + "player__pic_tip": "右擊在“我的列表”定位當前播放的歌曲", + "player__play": "播放", + "player__play_toggle_mode_list": "順序播放", + "player__play_toggle_mode_list_loop": "列表循環", + "player__play_toggle_mode_off": "禁用", + "player__play_toggle_mode_random": "列表隨機", + "player__play_toggle_mode_single_loop": "單曲循環", + "player__playing": "播放中...", + "player__prev": "上一首", + "player__refresh_url": "URL過期,正在刷新URL...", + "player__stop": "暫停播放", + "player__volume": "當前音量:", + "source_alias_all": "聚合大會", + "source_alias_bd": "小杜音樂", + "source_alias_kg": "小枸音樂", + "source_alias_kw": "小蝸音樂", + "source_alias_mg": "小蜜音樂", + "source_alias_tx": "小秋音樂", + "source_alias_wy": "小芸音樂", + "source_alias_xm": "小霞音樂", + "source_all": "聚合搜索", + "source_bd": "百度音樂", + "source_kg": "酷狗音樂", + "source_kw": "酷我音樂", + "source_mg": "咪咕音樂", + "source_tx": "企鵝音樂", + "source_wy": "網易音樂", + "source_xm": "蝦米音樂", + "sync__merge_btn_local_remote": "本機列表 合併 遠程列表", + "sync__merge_btn_remote_local": "遠程列表 合併 本機列表", + "sync__merge_label": "合併", + "sync__merge_tip": "合併:", + "sync__merge_tip_desc": "將兩邊的列表合併到一起,相同的歌曲將被去掉(去掉的是被合併者的歌曲),不同的歌曲將被添加。", + "sync__other_label": "其他", + "sync__other_tip": "其他:", + "sync__other_tip_desc": "“僅使用實時同步功能”將不修改雙方的列表,僅實時同步操作;“取消同步”將直接斷開雙方的連接。", + "sync__overwrite": "完全覆蓋", + "sync__overwrite_btn_cancel": "取消同步", + "sync__overwrite_btn_local_remote": "本機列表 覆蓋 遠程列表", + "sync__overwrite_btn_none": "僅使用實時同步功能", + "sync__overwrite_btn_remote_local": "遠程列表 覆蓋 本機列表", + "sync__overwrite_label": "覆蓋", + "sync__overwrite_tip": "覆蓋:", + "sync__overwrite_tip_desc": "被覆蓋者與覆蓋者列表ID相同的列表將被刪除後替換成覆蓋者的列表(列表ID不同的列表將被合併到一起),若勾選完全覆蓋,則被覆蓋者的所有列表將被移除,然後替換成覆蓋者的列表。", + "sync__title": "選擇與 {name} 的列表同步方式", + "tag__high_quality": "HQ", + "tag__lossless": "SQ", + "theme_black": "黑燈瞎火", + "theme_blue": "藍田生玉", + "theme_blue2": "清熱版藍", + "theme_green": "綠意盎然", + "theme_grey": "灰常美麗", + "theme_happy_new_year": "新年快樂", + "theme_mid_autumn": "月裡嫦娥", + "theme_ming": "青出於黑", + "theme_naruto": "木葉之村", + "theme_orange": "橙黃橘綠", + "theme_pink": "粉裝玉琢", + "theme_purple": "重斤球紫", + "theme_red": "熱情似火", + "theme_yellow": "信口雌黃", + "search__welcome": "搜我所想~~😉", + "search__hot_search": "熱門搜索", + "history_search": "歷史搜索", + "history_clear": "清空搜索歷史", + "history_remove": "右擊移除該歷史", + "download__progress": "進度", + "download__status": "狀態", + "download__quality": "品質", + "download__all": "全部任務", + "download__runing": "正在下載", + "download__paused": "已暫停", + "download__error": "出錯", + "download__finished": "下載完成", + "back": "返回", + "songlist__open_list": "打開{name}歌單", + "songlist__import_input_tip": "輸入歌單鏈接或歌單ID", + "songlist__import_input_tip_1": "不支持跨源打開歌單,請確認要打開的歌單與當前歌單源是否對應", + "songlist__import_input_tip_2": "若遇到無法打開的歌單鏈接,歡迎反饋", + "songlist__import_input_tip_3": "酷狗源不支持用歌單ID打開,但支持酷狗碼打開", + "default": "默認", + "music_duplicate": "重複歌曲", + "export": "導出", + "list__export_part_desc": "選擇列表文件保存位置", + "import": "導入", + "list__import_part_button_cancel": "不要啊", + "list__import_part_button_confirm": "覆蓋掉", + "list__import_part_confirm": "導入的列表({importName})與本地列表({localName})的ID相同,是否覆蓋本地列表?", + "list__import_part_desc": "選擇列表文件", + "list__movedown": "下移", + "list__moveup": "上移", + "list__new_list_btn": "新建列表", + "list__new_list_input": "新列表...", + "list__remove_tip": "你真的想要移除 {name} 嗎?", + "list__remove_tip_button": "是的 沒錯", + "list__rename": "重命名", + "list__sync": "更新", + "music_sort__title": "將 {name} 的位置調整到:", + "music_sort__title_multiple": "將已選的 {num} 首歌曲的位置調整到:", + "music_sort__input_tip": "請輸入要調整到第幾個位置", + "music_sort__btn_confirm": "確定", + "cancel_button_text": "取消", + "confirm_button_text": "好的", + "date_format_hour": "{num}小時前", + "date_format_minute": "{num}分鐘前", + "date_format_second": "{num}秒前", + + "setting__about": "關於洛雪音樂", + "setting__backup": "備份與恢復", + "setting__backup_all": "所有數據(列表數據與設置數據)", + "setting__backup_all_export": "導出", + "setting__backup_all_export_desc": "選擇備份保存位置", + "setting__backup_all_import": "導入", + "setting__backup_all_import_desc": "選擇備份文件", + "setting__backup_part": "部分數據(列表數據包括試聽列表、收藏列表、用戶自定義列表,設置數據不包括快捷鍵設置)", + "setting__backup_part_export_list": "導出列表", + "setting__backup_part_export_list_desc": "選擇歌單保存位置", + "setting__backup_part_export_setting": "導出設置", + "setting__backup_part_export_setting_desc": "選擇設置保存位置", + "setting__backup_part_import_list": "導入列表", + "setting__backup_part_import_list_desc": "選擇列表文件", + "setting__backup_part_import_setting": "導入設置", + "setting__backup_part_import_setting_desc": "選擇配置文件", + "setting__basic": "基本設置", + "setting__basic_animation": "彈出層隨機動畫", + "setting__basic_control_btn_position": "控制按鈕位置", + "setting__basic_control_btn_position_left": "左邊", + "setting__basic_control_btn_position_right": "右邊", + "setting__basic_lang": "語言", + "setting__basic_lang_title": "軟件顯示的語言", + "setting__basic_show_animation": "顯示切換動畫", + "setting__basic_source": "音樂來源", + "setting__basic_source_status_failed": "初始化失敗", + "setting__basic_source_status_initing": "初始化中", + "setting__basic_source_status_success": "初始化成功", + "setting__basic_source_temp": "臨時接口(軟件的某些功能不可用,建議測試接口不可用再使用本接口)", + "setting__basic_source_test": "測試接口(幾乎軟件的所有功能都可用)", + "setting__basic_source_title": "選擇音樂來源", + "setting__basic_source_user_api_btn": "自定義源管理", + "setting__basic_sourcename": "音源名字", + "setting__basic_sourcename_alias": "別名", + "setting__basic_sourcename_real": "原名", + "setting__basic_sourcename_title": "選擇音源名字類型", + "setting__basic_theme": "主題顏色", + "setting__basic_to_tray": "關閉軟件時不退出軟件將其最小化到系統托盤", + "setting__basic_window_size": "窗口尺寸", + "setting__basic_window_size_big": "大", + "setting__basic_window_size_huge": "巨大", + "setting__basic_window_size_larger": "較大", + "setting__basic_window_size_medium": "中", + "setting__basic_window_size_oversized": "超大", + "setting__basic_window_size_small": "小", + "setting__basic_window_size_smaller": "較小", + "setting__basic_window_size_title": "設置軟件窗口尺寸", + "setting__click_copy": "點擊複製", + "setting__click_open": "點擊打開", + "setting__desktop_lyric": "桌面歌詞設置", + "setting__desktop_lyric_always_on_top": "使歌詞總是在其他窗口之上", + "setting__desktop_lyric_enable": "顯示歌詞", + "setting__desktop_lyric_font": "歌詞字體", + "setting__desktop_lyric_font_default": "默認", + "setting__desktop_lyric_lock": "鎖定歌詞", + "setting__desktop_lyric_lock_screen": "不允許歌詞窗口拖出主屏幕之外", + "setting__download": "下載設置", + "setting__download_data_embed": "是否將以下內容嵌入到音頻文件中", + "setting__download_embed_lyric": "歌詞嵌入", + "setting__download_embed_pic": "封面嵌入", + "setting__download_enable": "是否啟用下載功能", + "setting__download_lyric": "歌詞下載", + "setting__download_lyric_format": "下載的歌詞文件編碼格式", + "setting__download_lyric_format_gbk": "GBK(在某些設備上出現中文亂碼時可嘗試選擇此格式)", + "setting__download_lyric_format_utf8": "UTF-8", + "setting__download_lyric_title": "是否同時下載歌詞文件", + "setting__download_name": "文件命名方式", + "setting__download_name1": "歌名 - 歌手", + "setting__download_name2": "歌手 - 歌名", + "setting__download_name3": "歌名", + "setting__download_name_title": "下載歌曲時的命名方式", + "setting__download_path": "下載路徑", + "setting__download_path_change_btn": "更改", + "setting__download_path_label": "當前下載路徑:", + "setting__download_path_open_label": "點擊打開當前路徑", + "setting__download_path_title": "下載歌曲保存的路徑", + "setting__download_select_save_path": "選擇歌曲保存路徑", + "setting__download_use_other_source": "自動換源下載(當無法從歌曲的原始源下載時,嘗試切換到其他源下載,注:此功能不100%保證換源後的歌曲版本與原版一致)", + "setting__hot_key": "快捷鍵設置", + "setting__hot_key_common_focus_search_input": "聚焦搜索框", + "setting__hot_key_common_min": "最小化程序", + "setting__hot_key_common_toggle_close": "退出程序", + "setting__hot_key_common_toggle_hide": "顯示/隱藏程序", + "setting__hot_key_common_toggle_min": "最小化/還原程序", + "setting__hot_key_desktop_lyric_toggle_always_top": "桌面歌詞置頂切換", + "setting__hot_key_desktop_lyric_toggle_lock": "桌面歌詞鎖定切換", + "setting__hot_key_desktop_lyric_toggle_visible": "開/關桌面歌詞", + "setting__hot_key_global_title": "全局快捷鍵", + "setting__hot_key_local_title": "軟件內快捷鍵", + "setting__hot_key_player_next": "下一首歌曲", + "setting__hot_key_player_prev": "上一首歌曲", + "setting__hot_key_player_toggle_play": "播放/暫停控制", + "setting__hot_key_player_volume_down": "減少音量", + "setting__hot_key_player_volume_mute": "靜音切換", + "setting__hot_key_player_volume_up": "增加音量", + "setting__hot_key_tip_input": "請輸入新的按鍵", + "setting__hot_key_unset_input": "未設置", + "setting__is_enable": "是否啟用", + "setting__is_show": "是否顯示", + "setting__list": "列表設置", + "setting__list_add_music_location_type": "添加歌曲到列表時的位置", + "setting__list_add_music_location_type_bottom": "底部", + "setting__list_add_music_location_type_top": "頂部", + "setting__list_scroll": "記住播放列表滾動條位置(僅對我的音樂分類有效)", + "setting__list_source": "顯示歌曲源(僅對我的音樂分類有效)", + "setting__network": "網絡設置", + "setting__network_proxy_host": "主機", + "setting__network_proxy_password": "密碼", + "setting__network_proxy_port": "端口", + "setting__network_proxy_title": "HTTP代理設置(亂設置軟件將無法聯網)", + "setting__network_proxy_username": "用戶名", + "setting__odc": "強迫症設置", + "setting__odc_clear_search_input": "離開搜索界面時清空搜索框", + "setting__odc_clear_search_list": "離開搜索界面時清空搜索列表", + "setting__other": "其他", + "setting__other_play_list_cache": "列表緩存管理(我的列表中已緩存的歌曲鏈接、播放代替源,清理後播放、下載歌曲時需要重新獲取,沒有歌曲播放相關的問題不要清理)", + "setting__other_play_list_cache_clear_btn": "清理列表緩存信息", + "setting__other_resource_cache": "資源緩存管理(圖片、音頻等緩存,清理後圖片等資源將需要重新下載,不建議清理,軟件會根據磁盤空間動態管理緩存大小)", + "setting__other_resource_cache_clear_btn": "清理資源緩存", + "setting__other_resource_cache_label": "軟件已使用緩存大小:", + "setting__other_tray_theme": "托盤圖標樣式", + "setting__other_tray_theme_black": "黑色", + "setting__other_tray_theme_native": "純色", + "setting__other_tray_theme_origin": "原色", + "setting__play": "播放設置", + "setting__play_lyric_lxlrc": "使用卡拉OK式歌詞播放(如果支持)", + "setting__play_lyric_transition": "顯示歌詞翻譯", + "setting__play_mediaDevice": "音頻輸出", + "setting__play_mediaDevice_remove_stop_play": "當前的聲音輸出設備被改變時暫停播放歌曲", + "setting__play_mediaDevice_title": "選擇聲音輸出的媒體設備", + "setting__play_quality": "優先播放320K品質的歌曲(如果支持)", + "setting__play_save_play_time": "記住播放進度", + "setting__play_task_bar": "在任務欄上顯示當前歌曲播放進度", + "setting__search": "搜索設置", + "setting__search_focus_search_box": "啟動時自動聚焦搜索框", + "setting__search_history": "顯示歷史搜索記錄", + "setting__search_hot": "顯示熱門搜索", + "setting__sync": "數據同步 [此為測試功能,首次使用前建議先備份一次歌單]", + "setting__sync_address": "同步服務地址:{address}", + "setting__sync_auth_code": "連接碼:{code}", + "setting__sync_device": "已連接的設備:{devices}", + "setting__sync_enable": "啟用同步功能(由於數據是明文傳輸,請在受信任的網絡下使用)", + "setting__sync_port": "同步端口設置", + "setting__sync_port_tip": "請輸入同步服務端口號", + "setting__sync_refresh_code": "刷新連接碼", + "setting__update": "軟件更新", + "setting__update_checking": "檢查更新中...", + "setting__update_current_label": "當前版本:", + "setting__update_downloading": "發現新版本並在努力下載中,請稍後...⏳", + "setting__update_init": "處理更新中...", + "setting__update_latest": "軟件已是最新,盡情地體驗吧~🥂", + "setting__update_latest_label": "最新版本:", + "setting__update_open_version_modal_btn": "打開更新窗口 🚀", + "setting__update_progress": "下載進度:", + "setting__update_unknown": "未知", + + "user_api__title": "自定義源管理", + "user_api__readme": "源編寫說明:", + "user_api__note": "提示:雖然我們已經盡可能地隔離了腳本的運行環境,但導入包含惡意行為的腳本仍可能會影響你的系統,請謹慎導入。", + "user_api__btn_remove": "移除", + "user_api__btn_import": "導入", + "user_api__btn_export": "導出", + "user_api__import_file": "選擇音樂API腳本文件", + "user_api__noitem": "這裡竟然是空的 😲" +} diff --git a/src/main/index.js b/src/main/index.js index 9b84e671..950cfe81 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -38,7 +38,7 @@ if (process.platform == 'linux') app.commandLine.appendSwitch('use-gl', 'desktop app.commandLine.appendSwitch('wm-window-animations-disabled') -const { navigationUrlWhiteList } = require('../common/config') +const { navigationUrlWhiteList, themes } = require('../common/config') const { getWindowSizeInfo, initSetting, updateSetting } = require('./utils') const { isMac, isLinux, initHotKey } = require('../common/utils') @@ -121,7 +121,7 @@ function createWindow() { }, }) - global.modules.mainWindow.loadURL(winURL) + global.modules.mainWindow.loadURL(winURL + `?dt=${!!global.envParams.cmdParams.dt}&theme=${themes.find(t => t.id == global.appSetting.themeId)?.className ?? themes[0].className}`) winEvent(global.modules.mainWindow) // global.modules.mainWindow.webContents.openDevTools() diff --git a/src/main/rendererEvents/index.js b/src/main/rendererEvents/index.js index 4d61666f..59839321 100644 --- a/src/main/rendererEvents/index.js +++ b/src/main/rendererEvents/index.js @@ -20,6 +20,7 @@ require('./data') require('./lyric') require('./musicUrl') require('./systemFonts') +require('./wait') // require('./kw_decodeLyric') diff --git a/src/main/rendererEvents/playList.js b/src/main/rendererEvents/playList.js index d73a7e29..681ffc71 100644 --- a/src/main/rendererEvents/playList.js +++ b/src/main/rendererEvents/playList.js @@ -8,16 +8,18 @@ mainHandle(ipcMainWindowNames.get_playlist, async(event, isIgnoredError = false) return { defaultList: electronStore_list.get('defaultList'), loveList: electronStore_list.get('loveList'), + tempList: electronStore_list.get('tempList'), userList: electronStore_list.get('userList'), downloadList: getStore('downloadList').get('list'), } }) -const handleSaveList = ({ defaultList, loveList, userList }) => { +const handleSaveList = ({ defaultList, loveList, userList, tempList }) => { let data = {} if (defaultList != null) data.defaultList = defaultList if (loveList != null) data.loveList = loveList if (userList != null) data.userList = userList + if (tempList != null) data.tempList = tempList getStore('playList').set(data) } mainOn(ipcMainWindowNames.save_playlist, (event, { type, data }) => { diff --git a/src/main/rendererEvents/setLyricInfo.js b/src/main/rendererEvents/setLyricInfo.js index 5d978d46..642bd8aa 100644 --- a/src/main/rendererEvents/setLyricInfo.js +++ b/src/main/rendererEvents/setLyricInfo.js @@ -1,4 +1,4 @@ -const { mainOn, mainSend, NAMES: { mainWindow: ipcMainWindowNames } } = require('../../common/ipc') +const { mainOn, mainSend, NAMES: { mainWindow: ipcMainWindowNames } } = require('@common/ipc') mainOn(ipcMainWindowNames.set_lyric_info, (event, info) => { diff --git a/src/main/rendererEvents/wait.js b/src/main/rendererEvents/wait.js new file mode 100644 index 00000000..a6d99368 --- /dev/null +++ b/src/main/rendererEvents/wait.js @@ -0,0 +1,25 @@ +const { mainOn, mainHandle, NAMES: { mainWindow: ipcMainWindowNames } } = require('@common/ipc') + +const timeoutMap = new Map() + +mainHandle(ipcMainWindowNames.wait, (event, { time, id }) => { + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + if (timeoutMap.has(id)) timeoutMap.delete(id) + resolve() + }, time) + timeoutMap.set(id, { + timeout, + resolve, + reject, + }) + }) +}) + +mainOn(ipcMainWindowNames.wait_cancel, (event, id) => { + if (!timeoutMap.has(id)) return + const timeout = timeoutMap.get(id) + timeoutMap.delete(id) + clearTimeout(timeout.timeout) + timeout.reject('cancelled') +}) diff --git a/src/renderer-lyric/App.vue b/src/renderer-lyric/App.vue index 455e45f4..e55e417d 100644 --- a/src/renderer-lyric/App.vue +++ b/src/renderer-lyric/App.vue @@ -105,7 +105,7 @@ export default { document.addEventListener('mousemove', this.handleMouseMove) document.addEventListener('mouseup', this.handleMouseUp) }, - beforeDestroy() { + beforeUnmount() { document.removeEventListener('mousemove', this.handleMouseMove) document.removeEventListener('mouseup', this.handleMouseUp) }, @@ -224,6 +224,15 @@ body { opacity: .8; } +body { + user-select: none; + height: 100vh; + box-sizing: border-box; +} +#root { + height: 100%; +} + #container { padding: 8px; box-sizing: border-box; diff --git a/src/renderer-lyric/components/core/ControlBar.vue b/src/renderer-lyric/components/core/ControlBar.vue index 8fa100a1..4811d768 100644 --- a/src/renderer-lyric/components/core/ControlBar.vue +++ b/src/renderer-lyric/components/core/ControlBar.vue @@ -2,38 +2,38 @@ div(:class="$style.container") transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated fadeOut") div(:class="$style.btns" v-show="!isShowThemeList") - button(:class="$style.btn" @click="handleClose" :title="$t('desktopLyric.close')") + button(:class="$style.btn" @click="handleClose" :title="$t('desktop_lyric__close')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve') use(xlink:href='#icon-close') - button(:class="$style.btn" @click="handleLock" :title="$t('desktopLyric.' + (lrcConfig.isLock ? 'unlock' : 'lock'))") + button(:class="$style.btn" @click="handleLock" :title="$t('desktop_lyric__' + (lrcConfig.isLock ? 'unlock' : 'lock'))") svg(v-if="config.isLock" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve') use(xlink:href='#icon-unlock') svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve') use(xlink:href='#icon-lock') - button(:class="$style.btn" :title="$t('desktopLyric.theme')" @click="isShowThemeList = true") + button(:class="$style.btn" :title="$t('desktop_lyric__theme')" @click="isShowThemeList = true") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve') use(xlink:href='#icon-theme') - button(:class="$style.btn" @click="handleFontChange('increase', 10)" @contextmenu="handleFontChange('increase', 1)" :title="$t('desktopLyric.font_increase')") + button(:class="$style.btn" @click="handleFontChange('increase', 10)" @contextmenu="handleFontChange('increase', 1)" :title="$t('desktop_lyric__font_increase')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve') use(xlink:href='#icon-font-increase') - button(:class="$style.btn" @click="handleFontChange('decrease', 10)" @contextmenu="handleFontChange('decrease', 1)" :title="$t('desktopLyric.font_decrease')") + button(:class="$style.btn" @click="handleFontChange('decrease', 10)" @contextmenu="handleFontChange('decrease', 1)" :title="$t('desktop_lyric__font_decrease')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve') use(xlink:href='#icon-font-decrease') - button(:class="$style.btn" @click="handleOpactiyChange('increase', 10)" @contextmenu="handleOpactiyChange('increase', 2)" :title="$t('desktopLyric.opactiy_increase')") + button(:class="$style.btn" @click="handleOpactiyChange('increase', 10)" @contextmenu="handleOpactiyChange('increase', 2)" :title="$t('desktop_lyric__opactiy_increase')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve') use(xlink:href='#icon-opactiy-increase') - button(:class="$style.btn" @click="handleOpactiyChange('decrease', 10)" @contextmenu="handleOpactiyChange('decrease', 2)" :title="$t('desktopLyric.opactiy_decrease')") + button(:class="$style.btn" @click="handleOpactiyChange('decrease', 10)" @contextmenu="handleOpactiyChange('decrease', 2)" :title="$t('desktop_lyric__opactiy_decrease')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve') use(xlink:href='#icon-opactiy-decrease') //- button(:class="$style.btn" v-text="lrcConfig.style.isZoomActiveLrc ? '取消缩放' : '缩放歌词'" @click="handleZoomLrc") - button(:class="$style.btn" @click="handleZoomLrc" :title="$t('desktopLyric.' + (lrcConfig.style.isZoomActiveLrc ? 'lrc_active_zoom_off' : 'lrc_active_zoom_on'))") + button(:class="$style.btn" @click="handleZoomLrc" :title="$t('desktop_lyric__' + (lrcConfig.style.isZoomActiveLrc ? 'lrc_active_zoom_off' : 'lrc_active_zoom_on'))") svg(v-if="config.style.isZoomActiveLrc" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve') use(xlink:href='#icon-vibrate-off') svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve') use(xlink:href='#icon-vibrate') - button(:class="$style.btn" @click="handleAlwaysOnTop" :title="$t('desktopLyric.' + (lrcConfig.isAlwaysOnTop ? 'win_top_off' : 'win_top_on'))") + button(:class="$style.btn" @click="handleAlwaysOnTop" :title="$t('desktop_lyric__' + (lrcConfig.isAlwaysOnTop ? 'win_top_off' : 'win_top_on'))") svg(v-if="config.isAlwaysOnTop" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve') use(xlink:href='#icon-top-off') svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 24 24' space='preserve') @@ -44,7 +44,7 @@ div(:class="$style.container") div(:class="$style.themes" v-show="isShowThemeList") ul(:class="$style.themeList") li(:class="$style.btnBack") - button(:class="$style.btn" @click="isShowThemeList = false" :title="$t('desktopLyric.back')") + button(:class="$style.btn" @click="isShowThemeList = false" :title="$t('desktop_lyric__back')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='20px' viewBox='0 0 512 512' space='preserve') use(xlink:href='#icon-back') li(:class="[$style.themeItem, theme.className, lrcConfig.theme == theme.id ? $style.active : null]" v-for="theme in themes" @click="handleToggleTheme(theme)") @@ -52,6 +52,7 @@ div(:class="$style.container") diff --git a/src/renderer/components/common/DownloadMultipleModal.vue b/src/renderer/components/common/DownloadMultipleModal.vue new file mode 100644 index 00000000..0053c2ec --- /dev/null +++ b/src/renderer/components/common/DownloadMultipleModal.vue @@ -0,0 +1,85 @@ + + + + {{$t('download__multiple_tip', { len: list.length })}}{{$t('download__multiple_tip2')}} + {{$t('download__normal')}} - 128K + {{$t('download__high_quality')}} - 320K + {{$t('download__lossless')}} - FLAC + + + + + + + + diff --git a/src/renderer/components/material/ListAddModal.vue b/src/renderer/components/common/ListAddModal.vue similarity index 63% rename from src/renderer/components/material/ListAddModal.vue rename to src/renderer/components/common/ListAddModal.vue index dca5e789..ca314109 100644 --- a/src/renderer/components/material/ListAddModal.vue +++ b/src/renderer/components/common/ListAddModal.vue @@ -1,21 +1,27 @@ - -material-modal(:show="show" :bg-close="bgClose" @close="handleClose") - main(:class="$style.main") - h2 - | {{$t('material.list_add_modal.' + (isMove ? 'title_first_move' : 'title_first_add'))}} - span(:class="$style.name") {{this.musicInfo && `${musicInfo.name}`}} - | {{$t('material.list_add_modal.title_last')}} - div.scroll(:class="$style.btnContent") - material-btn(:class="$style.btn" :tips="$t('material.list_add_modal.btn_title', { name: item.name })" :key="item.id" :disabled="item.isExist" @click="handleClick(index)" v-for="(item, index) in lists") {{item.name}} - material-btn(:class="[$style.btn, $style.newList, isEditing ? $style.editing : null]" @click="handleEditing($event)" :tips="$t('view.list.lists_new_list_btn')") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 42 42' space='preserve') - use(xlink:href='#icon-addTo') - input.key-bind(:class="$style.newListInput" :value="newListName" type="text" :placeholder="$t('view.list.lists_new_list_input')" @keyup.enter="handleSaveList($event)" @blur="handleSaveList($event)") - span(:class="$style.btn" v-for="i in spaceNum") + + + + {{$t('list_add__' + (isMove ? 'title_first_move' : 'title_first_add'))}} {{this.musicInfo && `${musicInfo.name}`}} {{$t('list_add__title_last')}} + + {{item.name}} + + + + + + + + + + + + diff --git a/src/renderer/components/common/VolumeBar.vue b/src/renderer/components/common/VolumeBar.vue new file mode 100644 index 00000000..c013bb06 --- /dev/null +++ b/src/renderer/components/common/VolumeBar.vue @@ -0,0 +1,144 @@ + + + + + + + + + + + + diff --git a/src/renderer/components/core/Aside.vue b/src/renderer/components/core/Aside.vue deleted file mode 100644 index f3210d98..00000000 --- a/src/renderer/components/core/Aside.vue +++ /dev/null @@ -1,299 +0,0 @@ - -div(:class="$style.aside") - div(:class="$style.controlBtn" v-if="setting.controlBtnPosition == 'left'") - button(type="button" :class="$style.close" :tips="$t('core.toolbar.close')" @click="close") - svg(:class="$style.controlBtniIcon" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='100%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-window-close') - button(type="button" :class="$style.min" :tips="$t('core.toolbar.min')" @click="min") - svg(:class="$style.controlBtniIcon" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='100%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-window-minimize') - - div(:class="['animated', logoAnimate, $style.logo]" v-else) L X - //- svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='100%' height='100%' viewBox='0 0 127 61' space='preserve') - use(xlink:href='#icon-logo') - - div(:class="$style.menu") - dl - //- dt {{$t('core.aside.online_music')}} - dd - router-link(:active-class="$style.active" to="search" :tips="$t('core.aside.search')") - div(:class="$style.icon") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 425.2 425.2' space='preserve') - use(xlink:href='#icon-search-2') - dd - router-link(:active-class="$style.active" to="songList" :tips="$t('core.aside.song_list')") - div(:class="$style.icon") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 425.2 425.2' space='preserve') - use(xlink:href='#icon-album') - //- span {{$t('core.aside.song_list')}} - dd - router-link(:active-class="$style.active" to="leaderboard" :tips="$t('core.aside.leaderboard')") - div(:class="$style.icon") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 425.22 425.2' space='preserve') - use(xlink:href='#icon-leaderboard') - //- span {{$t('core.aside.leaderboard')}} - dl - //- dt {{$t('core.aside.my_music')}} - dd - router-link(:active-class="$style.active" to="list" :tips="$t('core.aside.my_list')") - div(:class="$style.icon") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 444.87 391.18' space='preserve') - use(xlink:href='#icon-love') - dl - //- dt {{$t('core.aside.other')}} - dd(v-if="setting.download.enable") - router-link(:active-class="$style.active" to="download" :tips="$t('core.aside.download')") - div(:class="$style.icon") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 425.2 425.2' space='preserve') - use(xlink:href='#icon-download-2') - //- span {{$t('core.aside.download')}} - dd - router-link(:active-class="$style.active" to="setting" :tips="$t('core.aside.setting')") - div(:class="$style.icon") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 493.23 436.47' space='preserve') - use(xlink:href='#icon-setting') - //- span {{$t('core.aside.setting')}} - - - - - - diff --git a/src/renderer/components/core/Aside/ControlBtns.vue b/src/renderer/components/core/Aside/ControlBtns.vue new file mode 100644 index 00000000..dac40c0c --- /dev/null +++ b/src/renderer/components/core/Aside/ControlBtns.vue @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/renderer/components/core/Aside/Nav.vue b/src/renderer/components/core/Aside/Nav.vue new file mode 100644 index 00000000..6c11d357 --- /dev/null +++ b/src/renderer/components/core/Aside/Nav.vue @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/renderer/components/core/Aside/index.vue b/src/renderer/components/core/Aside/index.vue new file mode 100644 index 00000000..89c1b896 --- /dev/null +++ b/src/renderer/components/core/Aside/index.vue @@ -0,0 +1,63 @@ + + + + L X + + + + + + + + diff --git a/src/renderer/components/material/CommentFloor.vue b/src/renderer/components/core/MusicComment/CommentFloor.vue similarity index 63% rename from src/renderer/components/material/CommentFloor.vue rename to src/renderer/components/core/MusicComment/CommentFloor.vue index 0a776505..11af8975 100644 --- a/src/renderer/components/material/CommentFloor.vue +++ b/src/renderer/components/core/MusicComment/CommentFloor.vue @@ -1,27 +1,28 @@ - div(:class="$style.container") - ul - li(v-for="(item, index) in comments" :key="item.id" :class="$style.listItem") - div(:class="$style.content") - div(:class="$style.left") - img( :class="$style.avatar" :src="item.avatar || commentDefImg" @error="handleUserImg") - div(:class="$style.right") - div(:class="$style.info") - div.select(:class="$style.name") {{item.userName}} - time(:class="$style.time" v-if="item.timeStr") {{timeFormat(item.timeStr)}} - div(:class="$style.likes" v-if="item.likedCount != null") - svg(:class="$style.likesIcon" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 512 512' space='preserve') - use(xlink:href='#icon-thumbs-up') - | {{item.likedCount}} - div.select(:class="$style.comment_text") - p(v-for="text in item.text") {{text}} - material-comment-floor(v-if="item.reply && item.reply.length" :class="$style.reply_floor" :comments="item.reply") +div(:class="$style.container") + ul + li(v-for="(item, index) in comments" :key="item.id" :class="$style.listItem") + div(:class="$style.content") + div(:class="$style.left") + img( :class="$style.avatar" :src="item.avatar || commentDefImg" @error="handleUserImg") + div(:class="$style.right") + div(:class="$style.info") + div.select(:class="$style.name") {{item.userName}} + time(:class="$style.time" v-if="item.timeStr") {{timeFormat(item.timeStr)}} + div(:class="$style.likes" v-if="item.likedCount != null") + svg(:class="$style.likesIcon" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 512 512' space='preserve') + use(xlink:href='#icon-thumbs-up') + | {{item.likedCount}} + div.select(:class="$style.comment_text") + p(v-for="text in item.text") {{text}} + comment-floor(v-if="item.reply && item.reply.length" :class="$style.reply_floor" :comments="item.reply") diff --git a/src/renderer/components/core/PlayBar/index.vue b/src/renderer/components/core/PlayBar/index.vue new file mode 100644 index 00000000..5cd188ba --- /dev/null +++ b/src/renderer/components/core/PlayBar/index.vue @@ -0,0 +1,361 @@ + +div(:class="$style.player") + div(:class="$style.left" @contextmenu="handleToMusicLocation" @click="showPlayerDetail" :tips="$t('player__pic_tip')") + img(v-if="musicInfo.img" :src="musicInfo.img" @error="imgError") + svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='102%' width='100%' viewBox='0 0 60 60' space='preserve') + use(:xlink:href='`#${$style.iconPic}`') + div(:class="$style.middle") + div(:class="$style.column1") + div(:class="$style.container") + div(:class="$style.title" @click="handleCopy(title)" :tips="title + $t('copy_tip')") {{title}} + control-btns + div(:class="$style.column2") + common-progress-bar(:progress="progress" :handleTransitionEnd="handleTransitionEnd" :isActiveTransition="isActiveTransition") + div(:class="$style.column3") + span(:class="$style.statusText") {{statusText}} + span {{nowPlayTimeStr}} + span(style="margin: 0 5px;") / + span {{maxPlayTimeStr}} + div(:class="$style.right") + div(:class="$style.playBtn" @click='playPrev' :tips="$t('player__prev')" style="transform: rotate(180deg);") + svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 220.847 220.847' space='preserve') + use(xlink:href='#icon-nextMusic') + div(:class="$style.playBtn" :tips="isPlay ? $t('player__pause') : $t('player__play')" @click='togglePlay') + svg(v-if="isPlay" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 277.338 277.338' space='preserve') + use(xlink:href='#icon-pause') + svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 170 170' space='preserve') + use(xlink:href='#icon-play') + div(:class="$style.playBtn" @click='playNext' :tips="$t('player__next')") + svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 220.847 220.847' space='preserve') + use(xlink:href='#icon-nextMusic') + + svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' style="display: none;") + g(:id="$style.iconPic") + path(d='M29,0C12.984,0,0,12.984,0,29c0,16.016,12.984,29,29,29s29-12.984,29-29C58,12.984,45.016,0,29,0zM29,36.08c-3.91,0-7.08-3.17-7.08-7.08c0-3.91,3.17-7.08,7.08-7.08s7.08,3.17,7.08,7.08C36.08,32.91,32.91,36.08,29,36.08z') + path(:class="$style.c1" d='M6.487,22.932c-0.077,0-0.156-0.009-0.234-0.027c-0.537-0.13-0.868-0.67-0.739-1.206c0.946-3.935,2.955-7.522,5.809-10.376s6.441-4.862,10.376-5.809c0.536-0.127,1.077,0.202,1.206,0.739c0.129,0.536-0.202,1.076-0.739,1.206c-3.575,0.859-6.836,2.685-9.429,5.277s-4.418,5.854-5.277,9.429C7.349,22.624,6.938,22.932,6.487,22.932z') + path(:class="$style.c1" d='M36.066,52.514c-0.451,0-0.861-0.308-0.972-0.767c-0.129-0.536,0.202-1.076,0.739-1.206c3.576-0.859,6.837-2.685,9.43-5.277s4.418-5.854,5.277-9.429c0.129-0.538,0.668-0.868,1.206-0.739c0.537,0.13,0.868,0.67,0.739,1.206c-0.946,3.935-2.955,7.522-5.809,10.376s-6.441,4.862-10.377,5.809C36.223,52.505,36.144,52.514,36.066,52.514z') + path(:class="$style.c1" d='M11.313,24.226c-0.075,0-0.151-0.008-0.228-0.026c-0.538-0.125-0.873-0.663-0.747-1.2c0.72-3.09,2.282-5.904,4.52-8.141c2.236-2.237,5.051-3.8,8.141-4.52c0.535-0.131,1.075,0.209,1.2,0.747c0.126,0.537-0.209,1.075-0.747,1.2c-2.725,0.635-5.207,2.014-7.18,3.986s-3.352,4.455-3.986,7.18C12.179,23.914,11.768,24.226,11.313,24.226z') + path(:class="$style.c1" d='M34.773,47.688c-0.454,0-0.865-0.312-0.973-0.773c-0.126-0.537,0.209-1.075,0.747-1.2c2.725-0.635,5.207-2.014,7.18-3.986s3.352-4.455,3.986-7.18c0.125-0.538,0.662-0.88,1.2-0.747c0.538,0.125,0.873,0.663,0.747,1.2c-0.72,3.09-2.282,5.904-4.52,8.141c-2.236,2.237-5.051,3.8-8.141,4.52C34.925,47.68,34.849,47.688,34.773,47.688z') + path(:class="$style.c1" d='M16.14,25.519c-0.071,0-0.143-0.008-0.215-0.023c-0.539-0.118-0.881-0.651-0.763-1.19c0.997-4.557,4.586-8.146,9.143-9.143c0.537-0.116,1.071,0.222,1.19,0.763c0.118,0.539-0.224,1.072-0.763,1.19c-3.796,0.831-6.786,3.821-7.617,7.617C17.013,25.2,16.6,25.519,16.14,25.519z') + path(:class="$style.c1" d='M33.48,42.861c-0.46,0-0.873-0.318-0.976-0.786c-0.118-0.539,0.224-1.072,0.763-1.19c3.796-0.831,6.786-3.821,7.617-7.617c0.118-0.541,0.65-0.881,1.19-0.763c0.539,0.118,0.881,0.651,0.763,1.19c-0.997,4.557-4.586,8.146-9.143,9.143C33.623,42.854,33.552,42.861,33.48,42.861z') + path(:class="$style.c2" d='M29,38.08c-5.007,0-9.08-4.073-9.08-9.08s4.073-9.08,9.08-9.08s9.08,4.073,9.08,9.08S34.007,38.08,29,38.08z M29,23.92c-2.801,0-5.08,2.279-5.08,5.08s2.279,5.08,5.08,5.08s5.08-2.279,5.08-5.08S31.801,23.92,29,23.92z') + + + + + + diff --git a/src/renderer/components/core/PlayDetail/Lyric.vue b/src/renderer/components/core/PlayDetail/Lyric.vue new file mode 100644 index 00000000..43d7cd9d --- /dev/null +++ b/src/renderer/components/core/PlayDetail/Lyric.vue @@ -0,0 +1,230 @@ + + + + + + + + + + + {{info.text}} + + {{info.translation}} + + + + + + + + + + diff --git a/src/renderer/components/core/PlayDetail/PlayBar.vue b/src/renderer/components/core/PlayDetail/PlayBar.vue new file mode 100644 index 00000000..2384efd3 --- /dev/null +++ b/src/renderer/components/core/PlayDetail/PlayBar.vue @@ -0,0 +1,160 @@ + +div(:class="$style.footer") + div(:class="$style.footerLeft") + control-btns + div(:class="$style.progressContainer") + div(:class="$style.progressContent") + common-progress-bar(:class-name="$style.progress" :progress="progress" :handleTransitionEnd="handleTransitionEnd" :isActiveTransition="isActiveTransition") + div(:class="$style.timeLabel") + span(style="margin-left: 15px") {{status}} + div + span {{nowPlayTimeStr}} + span(style="margin: 0 5px;") / + span {{maxPlayTimeStr}} + div(:class="$style.playControl") + div(:class="$style.playBtn" @click="playPrev" style="transform: rotate(180deg);" :tips="$t('player__prev')") + svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 220.847 220.847' space='preserve') + use(xlink:href='#icon-nextMusic') + div(:class="$style.playBtn" @click="togglePlay" :tips="isPlay ? $t('player__pause') : $t('player__play')") + svg(v-if="isPlay" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 277.338 277.338' space='preserve') + use(xlink:href='#icon-pause') + svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 170 170' space='preserve') + use(xlink:href='#icon-play') + div(:class="$style.playBtn" @click="playNext" :tips="$t('player__next')") + svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 220.847 220.847' space='preserve') + use(xlink:href='#icon-nextMusic') + + + + + diff --git a/src/renderer/components/core/PlayDetail/components/ControlBtns.vue b/src/renderer/components/core/PlayDetail/components/ControlBtns.vue new file mode 100644 index 00000000..ce1cb2bb --- /dev/null +++ b/src/renderer/components/core/PlayDetail/components/ControlBtns.vue @@ -0,0 +1,142 @@ + +div(:class="$style.footerLeftControlBtns") + common-volume-bar(:setting="setting") + div(:class="[$style.footerLeftControlBtn, $style.lrcBtn]" @click="toggleDesktopLyric" @contextmenu="toggleLockDesktopLyric" :tips="toggleDesktopLyricBtnTitle") + svg(v-if="setting.desktopLyric.enable" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='125%' viewBox='0 0 512 512' space='preserve') + use(xlink:href='#icon-desktop-lyric-on') + svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='125%' viewBox='0 0 512 512' space='preserve') + use(xlink:href='#icon-desktop-lyric-off') + div(:class="[$style.footerLeftControlBtn, { [$style.active]: isShowLrcSelectContent }]" @click="toggleVisibleLrc" :tips="$t('lyric__select')") + svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='95%' viewBox='0 0 24 24' space='preserve') + use(xlink:href='#icon-text') + div(:class="[$style.footerLeftControlBtn, {[$style.active]: isShowPlayComment}]" @click="toggleVisibleComment" :tips="$t('comment__show')") + svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='95%' viewBox='0 0 24 24' space='preserve') + use(xlink:href='#icon-comment') + div(:class="$style.footerLeftControlBtn" @click="toggleNextPlayMode" :tips="nextTogglePlayName") + svg(v-if="setting.player.togglePlayMethod == 'listLoop'" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 24 24' space='preserve') + use(xlink:href='#icon-list-loop') + svg(v-else-if="setting.player.togglePlayMethod == 'random'" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 24 24' space='preserve') + use(xlink:href='#icon-list-random') + svg(v-else-if="setting.player.togglePlayMethod == 'list'" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='120%' viewBox='0 0 24 24' space='preserve') + use(xlink:href='#icon-list-order') + svg(v-else-if="setting.player.togglePlayMethod == 'singleLoop'" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='110%' viewBox='0 0 24 24' space='preserve') + use(xlink:href='#icon-single-loop') + svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='120%' viewBox='0 0 24 24' space='preserve') + use(xlink:href='#icon-single') + div(:class="$style.footerLeftControlBtn" @click="isShowAddMusicTo = true" :tips="$t('player__add_music_to')") + svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 512 512' space='preserve') + use(xlink:href='#icon-add-2') + teleport(to="#root") + common-list-add-modal(:show="isShowAddMusicTo" :musicInfo="musicInfoItem" @close="isShowAddMusicTo = false") + + + + + + diff --git a/src/renderer/components/core/PlayDetail/index.vue b/src/renderer/components/core/PlayDetail/index.vue new file mode 100644 index 00000000..fd17f161 --- /dev/null +++ b/src/renderer/components/core/PlayDetail/index.vue @@ -0,0 +1,538 @@ + +transition(enter-active-class="animated lightSpeedIn" leave-active-class="animated slideOutDown" @after-enter="handleAfterEnter" @after-leave="handleAfterLeave") + div(:class="[$style.container, setting.controlBtnPosition == 'left' ? $style.controlBtnLeft : $style.controlBtnRight]" @contextmenu="handleContextMenu" v-if="isShowPlayerDetail") + //- div(:class="$style.bg" :style="bgStyle") + //- div(:class="$style.bg2") + div(:class="$style.header") + div(:class="$style.controBtn") + button(type="button" :class="$style.hide" :tips="$t('player__hide_detail_tip')" @click="hide") + svg(:class="$style.controBtnIcon" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='80%' viewBox='0 0 30.727 30.727' space='preserve') + use(xlink:href='#icon-window-hide') + button(type="button" :class="$style.min" :tips="$t('min')" @click="min") + svg(:class="$style.controBtnIcon" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='100%' viewBox='0 0 24 24' space='preserve') + use(xlink:href='#icon-window-minimize') + + //- button(type="button" :class="$style.max" @click="max") + button(type="button" :class="$style.close" :tips="$t('close')" @click="close") + svg(:class="$style.controBtnIcon" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='100%' viewBox='0 0 24 24' space='preserve') + use(xlink:href='#icon-window-close') + + div(:class="[$style.main, {[$style.showComment]: isShowPlayComment}]") + div.left(:class="$style.left") + //- div(:class="$style.info") + div(:class="$style.info") + img(:class="$style.img" :src="musicInfo.img" v-if="musicInfo.img") + div.description(:class="$style.description") + p {{$t('player__music_name')}}{{musicInfo.name}} + p {{$t('player__music_singer')}}{{musicInfo.singer}} + p(v-if="musicInfo.album") {{$t('player__music_album')}}{{musicInfo.album}} + + transition(enter-active-class="animated fadeIn" leave-active-class="animated fadeOut") + Lyric(v-if="visibleLrc") + core-music-comment(:class="$style.comment" :musicInfo="musicInfoItem" :show="isShowPlayComment" @close="hideComment") + play-bar + + + + + + + diff --git a/src/renderer/components/core/Player.vue b/src/renderer/components/core/Player.vue deleted file mode 100644 index ec1049f8..00000000 --- a/src/renderer/components/core/Player.vue +++ /dev/null @@ -1,1379 +0,0 @@ - -div(:class="$style.player") - div(:class="$style.left" @contextmenu="handleToMusicLocation" @click="showPlayerDetail" :tips="$t('core.player.pic_tip')") - img(v-if="musicInfo.img" :src="musicInfo.img" @error="imgError") - svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='102%' width='100%' viewBox='0 0 60 60' space='preserve') - use(:xlink:href='`#${$style.iconPic}`') - div(:class="$style.middle") - div(:class="$style.middleContainer") - div(:class="$style.column1") - div(:class="$style.container") - div(:class="$style.title" @click="handleCopy(title)" :tips="title + $t('core.player.copy_title')") {{title}} - div(:class="$style.controlBtn") - - div(:class="$style.volumeContent") - div(:class="[$style.volume, setting.player.isMute ? $style.muted : null]") - div(:class="$style.volumeBar" ref="com_volumeBar" :style="{ transform: `scaleX(${volume || 0})` }") - div(:class="$style.volumeMask" @mousedown="handleVolumeMsDown" :tips="`${$t('core.player.volume')}${parseInt(volume * 100)}%`") - div(:class="$style.titleBtn" @click='toggleDesktopLyric' @contextmenu="handleToggleLockDesktopLyric" :tips="toggleDesktopLyricBtnTitle") - svg(v-if="setting.desktopLyric.enable" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 512 512' space='preserve') - use(xlink:href='#icon-desktop-lyric-on') - svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 512 512' space='preserve') - use(xlink:href='#icon-desktop-lyric-off') - div(:class="$style.titleBtn" @click='toggleNextPlayMode' :tips="nextTogglePlayName") - svg(v-if="setting.player.togglePlayMethod == 'listLoop'" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='80%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-list-loop') - svg(v-else-if="setting.player.togglePlayMethod == 'random'" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='100%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-list-random') - svg(v-else-if="setting.player.togglePlayMethod == 'list'" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='120%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-list-order') - svg(v-else-if="setting.player.togglePlayMethod == 'singleLoop'" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='100%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-single-loop') - svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='120%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-single') - div(:class="$style.titleBtn" @click='addMusicTo' :tips="$t('core.player.add_music_to')") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='80%' viewBox='0 0 512 512' space='preserve') - use(xlink:href='#icon-add-2') - //- div(:class="$style.playBtn" @click='playNext' tips="音量") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 291.063 291.064' space='preserve') - use(xlink:href='#icon-sound') - - div(:class="$style.column2") - div(:class="$style.progress" v-if="!isShowPlayerDetail") - //- div(:class="[$style.progressBar, $style.progressBar1]" :style="{ transform: `scaleX(${progress || 0})` }") - div(:class="[$style.progressBar, $style.progressBar2, isActiveTransition ? $style.barTransition : '']" - @transitionend="handleTransitionEnd" :style="{ transform: `scaleX(${progress || 0})` }") - div(:class="$style.progressMask" @click='handleSetProgress' ref="dom_progress") - div(:class="$style.column3") - span(:class="$style.statusText") {{statusText}} - span {{nowPlayTimeStr}} - span(style="margin: 0 5px;") / - span {{maxPlayTimeStr}} - div(:class="$style.right") - div(:class="$style.playBtn" @click='playPrev' :tips="$t('core.player.prev')" style="transform: rotate(180deg);") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 220.847 220.847' space='preserve') - use(xlink:href='#icon-nextMusic') - div(:class="$style.playBtn" :tips="isPlay ? $t('core.player.pause') : $t('core.player.play')" @click='togglePlay') - svg(v-if="isPlay" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 277.338 277.338' space='preserve') - use(xlink:href='#icon-pause') - svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 170 170' space='preserve') - use(xlink:href='#icon-play') - div(:class="$style.playBtn" @click='playNext' :tips="$t('core.player.next')") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 220.847 220.847' space='preserve') - use(xlink:href='#icon-nextMusic') - //- transition(enter-active-class="animated lightSpeedIn" - transition(enter-active-class="animated lightSpeedIn" - leave-active-class="animated slideOutDown") - core-player-detail(v-if="isShowPlayerDetail" :visible.sync="isShowPlayerDetail" :musicInfo="currentMusicInfo" - :lyric="lyric" :list="list" :listId="listId" - :playInfo="{ nowPlayTimeStr, maxPlayTimeStr, progress, nowPlayTime, status }" - :isPlay="isPlay" @action="handlePlayDetailAction" - :nextTogglePlayName="nextTogglePlayName" - @toggle-next-play-mode="toggleNextPlayMode" @add-music-to="addMusicTo") - - material-list-add-modal(:show="isShowAddMusicTo" :musicInfo="currentMusicInfo" @close="isShowAddMusicTo = false") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' style="display: none;") - defs - g(:id="$style.iconPic") - path(d='M29,0C12.984,0,0,12.984,0,29c0,16.016,12.984,29,29,29s29-12.984,29-29C58,12.984,45.016,0,29,0zM29,36.08c-3.91,0-7.08-3.17-7.08-7.08c0-3.91,3.17-7.08,7.08-7.08s7.08,3.17,7.08,7.08C36.08,32.91,32.91,36.08,29,36.08z') - path(:class="$style.c1" d='M6.487,22.932c-0.077,0-0.156-0.009-0.234-0.027c-0.537-0.13-0.868-0.67-0.739-1.206c0.946-3.935,2.955-7.522,5.809-10.376s6.441-4.862,10.376-5.809c0.536-0.127,1.077,0.202,1.206,0.739c0.129,0.536-0.202,1.076-0.739,1.206c-3.575,0.859-6.836,2.685-9.429,5.277s-4.418,5.854-5.277,9.429C7.349,22.624,6.938,22.932,6.487,22.932z') - path(:class="$style.c1" d='M36.066,52.514c-0.451,0-0.861-0.308-0.972-0.767c-0.129-0.536,0.202-1.076,0.739-1.206c3.576-0.859,6.837-2.685,9.43-5.277s4.418-5.854,5.277-9.429c0.129-0.538,0.668-0.868,1.206-0.739c0.537,0.13,0.868,0.67,0.739,1.206c-0.946,3.935-2.955,7.522-5.809,10.376s-6.441,4.862-10.377,5.809C36.223,52.505,36.144,52.514,36.066,52.514z') - path(:class="$style.c1" d='M11.313,24.226c-0.075,0-0.151-0.008-0.228-0.026c-0.538-0.125-0.873-0.663-0.747-1.2c0.72-3.09,2.282-5.904,4.52-8.141c2.236-2.237,5.051-3.8,8.141-4.52c0.535-0.131,1.075,0.209,1.2,0.747c0.126,0.537-0.209,1.075-0.747,1.2c-2.725,0.635-5.207,2.014-7.18,3.986s-3.352,4.455-3.986,7.18C12.179,23.914,11.768,24.226,11.313,24.226z') - path(:class="$style.c1" d='M34.773,47.688c-0.454,0-0.865-0.312-0.973-0.773c-0.126-0.537,0.209-1.075,0.747-1.2c2.725-0.635,5.207-2.014,7.18-3.986s3.352-4.455,3.986-7.18c0.125-0.538,0.662-0.88,1.2-0.747c0.538,0.125,0.873,0.663,0.747,1.2c-0.72,3.09-2.282,5.904-4.52,8.141c-2.236,2.237-5.051,3.8-8.141,4.52C34.925,47.68,34.849,47.688,34.773,47.688z') - path(:class="$style.c1" d='M16.14,25.519c-0.071,0-0.143-0.008-0.215-0.023c-0.539-0.118-0.881-0.651-0.763-1.19c0.997-4.557,4.586-8.146,9.143-9.143c0.537-0.116,1.071,0.222,1.19,0.763c0.118,0.539-0.224,1.072-0.763,1.19c-3.796,0.831-6.786,3.821-7.617,7.617C17.013,25.2,16.6,25.519,16.14,25.519z') - path(:class="$style.c1" d='M33.48,42.861c-0.46,0-0.873-0.318-0.976-0.786c-0.118-0.539,0.224-1.072,0.763-1.19c3.796-0.831,6.786-3.821,7.617-7.617c0.118-0.541,0.65-0.881,1.19-0.763c0.539,0.118,0.881,0.651,0.763,1.19c-0.997,4.557-4.586,8.146-9.143,9.143C33.623,42.854,33.552,42.861,33.48,42.861z') - path(:class="$style.c2" d='M29,38.08c-5.007,0-9.08-4.073-9.08-9.08s4.073-9.08,9.08-9.08s9.08,4.073,9.08,9.08S34.007,38.08,29,38.08z M29,23.92c-2.801,0-5.08,2.279-5.08,5.08s2.279,5.08,5.08,5.08s5.08-2.279,5.08-5.08S31.801,23.92,29,23.92z') - - - - - - diff --git a/src/renderer/components/core/PlayerDetail.vue b/src/renderer/components/core/PlayerDetail.vue deleted file mode 100644 index 04158663..00000000 --- a/src/renderer/components/core/PlayerDetail.vue +++ /dev/null @@ -1,943 +0,0 @@ - - div(:class="[$style.container, setting.controlBtnPosition == 'left' ? $style.controlBtnLeft : $style.controlBtnRight]" @contextmenu="handleContextMenu") - //- div(:class="$style.bg" :style="bgStyle") - //- div(:class="$style.bg2") - div(:class="$style.header") - div(:class="$style.controBtn") - button(type="button" :class="$style.hide" :tips="$t('core.player.hide_detail')" @click="hide") - svg(:class="$style.controBtnIcon" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='80%' viewBox='0 0 30.727 30.727' space='preserve') - use(xlink:href='#icon-window-hide') - button(type="button" :class="$style.min" :tips="$t('core.toolbar.min')" @click="min") - svg(:class="$style.controBtnIcon" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='100%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-window-minimize') - - //- button(type="button" :class="$style.max" @click="max") - button(type="button" :class="$style.close" :tips="$t('core.toolbar.close')" @click="close") - svg(:class="$style.controBtnIcon" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='100%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-window-close') - - div(:class="[$style.main, isShowComment ? $style.showComment : null]") - div(:class="$style.left") - //- div(:class="$style.info") - div(:class="$style.info") - img(:class="$style.img" :src="musicInfo.img" v-if="musicInfo.img") - div(:class="$style.description") - p {{$t('core.player.name')}}{{musicInfo.name}} - p {{$t('core.player.singer')}}{{musicInfo.singer}} - p(v-if="musicInfo.album") {{$t('core.player.album')}}{{musicInfo.album}} - - div(:class="$style.right") - div(:class="[$style.lyric, { [$style.draging]: lyricEvent.isMsDown }]" @wheel="handleWheel" @mousedown="handleLyricMouseDown" ref="dom_lyric") - div(:class="$style.lyricSpace") - div(:class="[$style.lyricText]" ref="dom_lyric_text") - //- p(v-for="(info, index) in lyricLines" :key="index" :class="lyric.line == index ? $style.lrcActive : null") {{info.text}} - div(:class="$style.lyricSpace") - transition(enter-active-class="animated fadeIn" leave-active-class="animated fadeOut") - div(:class="[$style.lyricSelectContent, 'select', 'scroll']" v-if="isShowLrcSelectContent" @contextmenu="handleCopySelectText") - //- div(:class="$style.lyricSpace") - div(v-for="(info, index) in lyricLines" :key="index" :class="[$style.lyricSelectline, { [$style.lrcActive]: lyric.line == index }]") - span {{info.text}} - br(v-if="info.translation") - span(:class="$style.lyricSelectlineTransition") {{info.translation}} - //- div(:class="$style.lyricSpace") - - material-music-comment(:class="$style.comment" :titleFormat="this.setting.download.fileName" :musicInfo="musicInfo" v-model="isShowComment") - - div(:class="$style.footer") - div(:class="$style.footerLeft") - div(:class="$style.footerLeftControlBtns") - div(:class="[$style.footerLeftControlBtn, { [$style.active]: isShowLrcSelectContent }]" @click="isShowLrcSelectContent = !isShowLrcSelectContent" :tips="$t('core.player.lyric_select')") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='95%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-text') - div(:class="[$style.footerLeftControlBtn, isShowComment ? $style.active : null]" @click="isShowComment = !isShowComment" :tips="$t('core.player.comment_show')") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='95%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-comment') - div(:class="$style.footerLeftControlBtn" @click="$emit('toggle-next-play-mode')" :tips="nextTogglePlayName") - svg(v-if="setting.player.togglePlayMethod == 'listLoop'" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-list-loop') - svg(v-else-if="setting.player.togglePlayMethod == 'random'" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-list-random') - svg(v-else-if="setting.player.togglePlayMethod == 'list'" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='120%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-list-order') - svg(v-else-if="setting.player.togglePlayMethod == 'singleLoop'" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='110%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-single-loop') - svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='120%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-single') - div(:class="$style.footerLeftControlBtn" @click="$emit('add-music-to', musicInfo)" :tips="$t('core.player.add_music_to')") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 512 512' space='preserve') - use(xlink:href='#icon-add-2') - div(:class="$style.progressContainer") - div(:class="$style.progressContent") - div(:class="$style.progress") - //- div(:class="[$style.progressBar, $style.progressBar1]" :style="{ transform: `scaleX(${progress || 0})` }") - div(:class="[$style.progressBar, $style.progressBar2, isActiveTransition ? $style.barTransition : '']" @transitionend="handleTransitionEnd" :style="{ transform: `scaleX(${playInfo.progress || 0})` }") - div(:class="$style.progressMask" @click='setProgress' ref="dom_progress") - div(:class="$style.timeLabel") - span(style="margin-left: 15px") {{playInfo.status}} - div - span {{playInfo.nowPlayTimeStr}} - span(style="margin: 0 5px;") / - span {{playInfo.maxPlayTimeStr}} - div(:class="$style.playControl") - //- div(:class="$style.playBtn") - //- div(:class="$style.playBtn") - div(:class="$style.playBtn" @click="$emit('action', { type: 'prev' })" style="transform: rotate(180deg);" :tips="$t('core.player.prev')") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 220.847 220.847' space='preserve') - use(xlink:href='#icon-nextMusic') - div(:class="$style.playBtn" :tips="isPlay ? $t('core.player.pause') : $t('core.player.play')" @click="$emit('action', { type: 'togglePlay' })") - svg(v-if="isPlay" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 277.338 277.338' space='preserve') - use(xlink:href='#icon-pause') - svg(v-else version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 170 170' space='preserve') - use(xlink:href='#icon-play') - div(:class="$style.playBtn" @click="$emit('action', { type: 'next' })" :tips="$t('core.player.next')") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 220.847 220.847' space='preserve') - use(xlink:href='#icon-nextMusic') - - - - - - - diff --git a/src/renderer/components/core/SyncModeModal.vue b/src/renderer/components/core/SyncModeModal.vue new file mode 100644 index 00000000..fb79e80a --- /dev/null +++ b/src/renderer/components/core/SyncModeModal.vue @@ -0,0 +1,159 @@ + +material-modal(:show="sync.isShowSyncMode" @close="handleClose(false)" :bgClose="false" :close-btn="false") + main(:class="$style.main") + h2 {{$t('sync__title', { name: sync.deviceName })}} + div.scroll(:class="$style.content") + dl(:class="$style.btnGroup") + dt(:class="$style.label") {{$t('sync__merge_label')}} + dd(:class="$style.btns") + base-btn(:class="$style.btn" @click="handleSelectMode('merge_local_remote')") {{$t('sync__merge_btn_local_remote')}} + base-btn(:class="$style.btn" @click="handleSelectMode('merge_remote_local')") {{$t('sync__merge_btn_remote_local')}} + dl(:class="$style.btnGroup") + dt(:class="$style.label") {{$t('sync__overwrite_label')}} + dd(:class="$style.btns") + base-btn(:class="$style.btn" @click="handleSelectMode('overwrite_local_remote')") {{$t('sync__overwrite_btn_local_remote')}} + base-btn(:class="$style.btn" @click="handleSelectMode('overwrite_remote_local')") {{$t('sync__overwrite_btn_remote_local')}} + dd(style="font-size: 14px; margin-top: 5px;") + base-checkbox(id="sync_mode_modal_isOverwrite" v-model="isOverwrite" :label="$t('sync__overwrite')") + + dl(:class="$style.btnGroup") + dt(:class="$style.label") {{$t('sync__other_label')}} + dd(:class="$style.btns") + base-btn(:class="$style.btn" @click="handleSelectMode('none')") {{$t('sync__overwrite_btn_none')}} + base-btn(:class="$style.btn" @click="handleSelectMode('cancel')") {{$t('sync__overwrite_btn_cancel')}} + dl(:class="$style.btnGroup") + dd + section(:class="$style.tipGroup") + h3(:class="$style.title") {{$t('sync__merge_tip')}} + p(:class="$style.tip") {{$t('sync__merge_tip_desc')}} + section(:class="$style.tipGroup") + h3(:class="$style.title") {{$t('sync__overwrite_tip')}} + p(:class="$style.tip") {{$t('sync__overwrite_tip_desc')}} + section(:class="$style.tipGroup") + h3(:class="$style.title") {{$t('sync__other_tip')}} + p(:class="$style.tip") {{$t('sync__other_tip_desc')}} + + + + + + diff --git a/src/renderer/components/core/Toolbar.vue b/src/renderer/components/core/Toolbar.vue deleted file mode 100644 index 355d6d77..00000000 --- a/src/renderer/components/core/Toolbar.vue +++ /dev/null @@ -1,242 +0,0 @@ - - div(:class="[$style.toolbar, setting.controlBtnPosition == 'left' ? $style.controlBtnLeft : $style.controlBtnRight]") - //- img(v-if="icon") - //- h1 {{title}} - material-search-input(:class="$style.input" - @event="handleEvent" :list="tipList" :visibleList="visibleList" - v-model="searchText") - - div(:class="$style.logo" v-if="setting.controlBtnPosition == 'left'") L X - div(:class="$style.control" v-else) - button(type="button" :class="$style.min" :tips="$t('core.toolbar.min')" @click="min") - svg(:class="$style.icon" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='100%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-window-minimize') - - //- button(type="button" :class="$style.max" @click="max") - button(type="button" :class="$style.close" :tips="$t('core.toolbar.close')" @click="close") - svg(:class="$style.icon" version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='100%' viewBox='0 0 24 24' space='preserve') - use(xlink:href='#icon-window-close') - - - - - - diff --git a/src/renderer/components/core/Toolbar/ControlBtns.vue b/src/renderer/components/core/Toolbar/ControlBtns.vue new file mode 100644 index 00000000..3143271d --- /dev/null +++ b/src/renderer/components/core/Toolbar/ControlBtns.vue @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/renderer/components/core/Toolbar/SearchInput.vue b/src/renderer/components/core/Toolbar/SearchInput.vue new file mode 100644 index 00000000..1f001472 --- /dev/null +++ b/src/renderer/components/core/Toolbar/SearchInput.vue @@ -0,0 +1,109 @@ + + + + + diff --git a/src/renderer/components/core/Toolbar/index.vue b/src/renderer/components/core/Toolbar/index.vue new file mode 100644 index 00000000..431d068d --- /dev/null +++ b/src/renderer/components/core/Toolbar/index.vue @@ -0,0 +1,65 @@ + + + + L X + + + + + + + + diff --git a/src/renderer/components/material/VersionModal.vue b/src/renderer/components/core/VersionModal.vue similarity index 71% rename from src/renderer/components/material/VersionModal.vue rename to src/renderer/components/core/VersionModal.vue index 45d5e421..afd9d3c4 100644 --- a/src/renderer/components/material/VersionModal.vue +++ b/src/renderer/components/core/VersionModal.vue @@ -1,14 +1,14 @@ -material-modal(:show="version.showModal" @close="handleClose" v-if="version.newVersion") - main(:class="$style.main" v-if="version.isDownloaded") +material-modal(:show="versionInfo.showModal" @close="handleClose") + main(:class="$style.main" v-if="versionInfo.isDownloaded") h2 🚀程序更新🚀 div.scroll.select(:class="$style.info") div(:class="$style.current") - h3 最新版本:{{version.newVersion.version}} - h3 当前版本:{{version.version}} + h3 最新版本:{{versionInfo.newVersion?.version}} + h3 当前版本:{{versionInfo.version}} h3 版本变化: - pre(:class="$style.desc" v-text="version.newVersion.desc") + pre(:class="$style.desc" v-text="versionInfo.newVersion?.desc") div(:class="[$style.history, $style.desc]" v-if="history.length") h3 历史版本: div(:class="$style.item" v-for="ver in history") @@ -23,16 +23,16 @@ material-modal(:show="version.showModal" @close="handleClose" v-if="version.newV | 或稍后 strong 关闭程序时 | 自动更新~ - material-btn(:class="$style.btn" @click.onec="handleRestartClick") 立即重启更新 - main(:class="$style.main" v-else-if="version.isError && !version.isUnknow && version.newVersion.version != version.version") + base-btn(:class="$style.btn" @click.onec="handleRestartClick") 立即重启更新 + main(:class="$style.main" v-else-if="versionInfo.isError && !versionInfo.isUnknow && versionInfo.newVersion?.version != versionInfo.version") h2 ❌ 版本更新出错 ❌ div.scroll.select(:class="$style.info") div(:class="$style.current") - h3 最新版本:{{version.newVersion.version}} - h3 当前版本:{{version.version}} + h3 最新版本:{{versionInfo.newVersion?.version}} + h3 当前版本:{{versionInfo.version}} h3 版本变化: - pre(:class="$style.desc" v-text="version.newVersion.desc") + pre(:class="$style.desc" v-text="versionInfo.newVersion?.desc") div(:class="[$style.history, $style.desc]" v-if="history.length") h3 历史版本: div(:class="$style.item" v-for="ver in history") @@ -54,16 +54,16 @@ material-modal(:show="version.showModal" @close="handleClose" v-if="version.newV | 国内Windows/MAC用户推荐到 strong 网盘 | 下载。 - material-btn(:class="$style.btn" @click.onec="handleIgnoreClick") {{ isIgnored ? '恢复当前版本的更新失败提醒' : '忽略当前版本的更新失败提醒'}} - main(:class="$style.main" v-else-if="version.isDownloading && version.isTimeOut && !version.isUnknow") + base-btn(:class="$style.btn" @click.onec="handleIgnoreClick") {{ isIgnored ? '恢复当前版本的更新失败提醒' : '忽略当前版本的更新失败提醒'}} + main(:class="$style.main" v-else-if="versionInfo.isDownloading && versionInfo.isTimeOut && !versionInfo.isUnknow") h2 ❗️ 新版本下载超时 ❗️ div(:class="$style.desc") p 你当前所在网络访问GitHub较慢,导致新版本下载超时(已经下了半个钟了😳),你仍可选择继续等,但墙裂建议手动更新版本! p | 你可以去 - material-btn(min @click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/releases')" tips="点击打开") 软件发布页 + base-btn(min @click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/releases')" tips="点击打开") 软件发布页 | 或 - material-btn(min @click="handleOpenUrl('https://www.lanzoui.com/b0bf2cfa/')" tips="点击打开") 网盘 + base-btn(min @click="handleOpenUrl('https://www.lanzoui.com/b0bf2cfa/')" tips="点击打开") 网盘 | (密码: strong.hover(@click="handleCopy('glqw')" tips="点击复制") glqw | )下载新版本, @@ -72,34 +72,34 @@ material-modal(:show="version.showModal" @close="handleClose" v-if="version.newV strong 网盘 | 下载。 p 当前下载进度:{{progress}} - main(:class="$style.main" v-else-if="version.isUnknow") + main(:class="$style.main" v-else-if="versionInfo.isUnknow") h2 ❓ 获取最新版本信息失败 ❓ div.scroll.select(:class="$style.info") div(:class="$style.current") - h3 当前版本:{{version.version}} + h3 当前版本:{{versionInfo.version}} div(:class="$style.desc") p 更新信息获取失败,可能是无法访问Github导致的,请手动检查更新! p | 检查方法:打开 - material-btn(min @click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/releases')" tips="点击打开") 软件发布页 + base-btn(min @click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/releases')" tips="点击打开") 软件发布页 | 或 - material-btn(min @click="handleOpenUrl('https://www.lanzoui.com/b0bf2cfa/')" tips="点击打开") 网盘 + base-btn(min @click="handleOpenUrl('https://www.lanzoui.com/b0bf2cfa/')" tips="点击打开") 网盘 | (密码: strong.hover(@click="handleCopy('glqw')" tips="点击复制") glqw | )查看它们的 strong 版本号 - | 与当前版本({{version.version}})对比是否一样, + | 与当前版本({{versionInfo.version}})对比是否一样, p 若一样则不必理会该弹窗,直接关闭即可,否则请手动下载新版本更新。 main(:class="$style.main" v-else) h2 🌟发现新版本🌟 div.scroll.select(:class="$style.info") div(:class="$style.current") - h3 最新版本:{{version.newVersion.version}} - h3 当前版本:{{version.version}} + h3 最新版本:{{versionInfo.newVersion?.version}} + h3 当前版本:{{versionInfo.version}} h3 版本变化: - pre(:class="$style.desc" v-text="version.newVersion.desc") + pre(:class="$style.desc" v-text="versionInfo.newVersion?.desc") div(:class="[$style.history, $style.desc]" v-if="history.length") h3 历史版本: div(:class="$style.item" v-for="ver in history") @@ -127,37 +127,41 @@ material-modal(:show="version.showModal" @close="handleClose" v-if="version.newV diff --git a/src/renderer/components/material/DownloadMultipleModal.vue b/src/renderer/components/material/DownloadMultipleModal.vue deleted file mode 100644 index 9fc731ef..00000000 --- a/src/renderer/components/material/DownloadMultipleModal.vue +++ /dev/null @@ -1,82 +0,0 @@ - -material-modal(:show="show" :bg-close="bgClose" @close="handleClose") - main(:class="$style.main") - h2 - | {{$t('material.download_multiple_modal.tip', { len: list.length })}} - br - | {{$t('material.download_multiple_modal.tip2')}} - material-btn(:class="$style.btn" @click="handleClick('128k')") {{$t('material.download_multiple_modal.normal')}} - 128K - material-btn(:class="$style.btn" @click="handleClick('320k')") {{$t('material.download_multiple_modal.high_quality')}} - 320K - //- material-btn(:class="$style.btn" @click="handleClick('ape')") 无损音质 - APE - material-btn(:class="$style.btn" @click="handleClick('flac')") {{$t('material.download_multiple_modal.lossless')}} - FLAC/WAV - - - - - - diff --git a/src/renderer/components/material/ListButtons.vue b/src/renderer/components/material/ListButtons.vue index 2189b1b3..746f9737 100644 --- a/src/renderer/components/material/ListButtons.vue +++ b/src/renderer/components/material/ListButtons.vue @@ -1,30 +1,30 @@ div(:class="$style.btns") - button(type="button" v-if="playBtn" @contextmenu.capture.stop :tips="$t('material.list_buttons.play')" @click.stop="handleClick('play')") + button(type="button" v-if="playBtn" @contextmenu.capture.stop :tips="$t('list__play')" @click.stop="handleClick('play')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 287.386 287.386' space='preserve' v-once) use(xlink:href='#icon-testPlay') - button(type="button" v-if="listAddBtn" @contextmenu.capture.stop :tips="$t('material.list_buttons.add_to')" @click.stop="handleClick('listAdd')") + button(type="button" v-if="listAddBtn" @contextmenu.capture.stop :tips="$t('list__add_to')" @click.stop="handleClick('listAdd')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 42 42' space='preserve' v-once) use(xlink:href='#icon-addTo') - button(type="button" v-if="downloadBtn && setting.download.enable" @contextmenu.capture.stop :tips="$t('material.list_buttons.download')" @click.stop="handleClick('download')") + button(type="button" v-if="downloadBtn && setting.download.enable" @contextmenu.capture.stop :tips="$t('list__download')" @click.stop="handleClick('download')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 475.078 475.077' space='preserve' v-once) use(xlink:href='#icon-download') - //- button(type="button" :tips="$t('material.list_buttons.add')" v-if="userInfo" @click.stop="handleClick('add')") + //- button(type="button" :tips="$t('list__add')" v-if="userInfo" @click.stop="handleClick('add')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 42 42' space='preserve') use(xlink:href='#icon-addTo') - button(type="button" v-if="startBtn" @contextmenu.capture.stop :tips="$t('material.list_buttons.start')" @click.stop="handleClick('start')") + button(type="button" v-if="startBtn" @contextmenu.capture.stop :tips="$t('list__start')" @click.stop="handleClick('start')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 170 170' space='preserve' v-once) use(xlink:href='#icon-play') - button(type="button" v-if="pauseBtn" @contextmenu.capture.stop :tips="$t('material.list_buttons.pause')" @click.stop="handleClick('pause')") + button(type="button" v-if="pauseBtn" @contextmenu.capture.stop :tips="$t('list__pause')" @click.stop="handleClick('pause')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 277.338 277.338' space='preserve' v-once) use(xlink:href='#icon-pause') - button(type="button" v-if="fileBtn" @contextmenu.capture.stop :tips="$t('material.list_buttons.file')" @click.stop="handleClick('file')") + button(type="button" v-if="fileBtn" @contextmenu.capture.stop :tips="$t('list__file')" @click.stop="handleClick('file')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='-61 0 512 512' space='preserve' v-once) use(xlink:href='#icon-musicFile') - button(type="button" v-if="searchBtn" @contextmenu.capture.stop :tips="$t('material.list_buttons.search')" @click.stop="handleClick('search')") + button(type="button" v-if="searchBtn" @contextmenu.capture.stop :tips="$t('list__search')" @click.stop="handleClick('search')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 30.239 30.239' space='preserve' v-once) use(xlink:href='#icon-search') - button(type="button" v-if="removeBtn" :tips="$t('material.list_buttons.remove')" @click.stop="handleClick('remove')") + button(type="button" v-if="removeBtn" :tips="$t('list__remove')" @click.stop="handleClick('remove')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 212.982 212.982' space='preserve' v-once) use(xlink:href='#icon-delete') @@ -85,7 +85,7 @@ export default { diff --git a/src/renderer/components/material/OnlineList/useList.js b/src/renderer/components/material/OnlineList/useList.js new file mode 100644 index 00000000..5e579091 --- /dev/null +++ b/src/renderer/components/material/OnlineList/useList.js @@ -0,0 +1,105 @@ +import { computed, useRefGetter, watch, ref, onBeforeUnmount } from '@renderer/utils/vueTools' +import { windowSizeList } from '@common/config' + +const useKeyEvent = ({ handleSelectAllData }) => { + const keyEvent = { + isShiftDown: false, + isModDown: false, + } + + const handle_key_shift_down = () => { + if (!keyEvent.isShiftDown) keyEvent.isShiftDown = true + } + const handle_key_shift_up = () => { + if (keyEvent.isShiftDown) keyEvent.isShiftDown = false + } + const handle_key_mod_down = () => { + if (!keyEvent.isModDown) keyEvent.isModDown = true + } + const handle_key_mod_up = () => { + if (keyEvent.isModDown) keyEvent.isModDown = false + } + const handle_key_mod_a_down = ({ event }) => { + if (event.target.tagName == 'INPUT') return + event.preventDefault() + if (event.repeat) return + keyEvent.isModDown = false + handleSelectAllData() + } + + onBeforeUnmount(() => { + window.eventHub.off('key_shift_down', handle_key_shift_down) + window.eventHub.off('key_shift_up', handle_key_shift_up) + window.eventHub.off('key_mod_down', handle_key_mod_down) + window.eventHub.off('key_mod_up', handle_key_mod_up) + window.eventHub.off('key_mod+a_down', handle_key_mod_a_down) + }) + window.eventHub.on('key_shift_down', handle_key_shift_down) + window.eventHub.on('key_shift_up', handle_key_shift_up) + window.eventHub.on('key_mod_down', handle_key_mod_down) + window.eventHub.on('key_mod_up', handle_key_mod_up) + window.eventHub.on('key_mod+a_down', handle_key_mod_a_down) + + return keyEvent +} + + +export default ({ props }) => { + const selectedList = ref([]) + const setting = useRefGetter('setting') + let lastSelectIndex = -1 + const listItemHeight = computed(() => { + return parseInt(windowSizeList.find(item => item.id == setting.value.windowSizeId).fontSize) / 16 * 37 + }) + + const removeAllSelect = () => { + selectedList.value = [] + } + const handleSelectAllData = () => { + removeAllSelect() + selectedList.value = [...props.list] + } + const keyEvent = useKeyEvent({ handleSelectAllData }) + + const handleSelectData = clickIndex => { + if (keyEvent.isShiftDown) { + if (selectedList.value.length) { + removeAllSelect() + if (lastSelectIndex != clickIndex) { + let isNeedReverse = false + if (clickIndex < lastSelectIndex) { + let temp = lastSelectIndex + lastSelectIndex = clickIndex + clickIndex = temp + isNeedReverse = true + } + selectedList.value = props.list.slice(lastSelectIndex, clickIndex + 1) + if (isNeedReverse) selectedList.value.reverse() + } + } else { + selectedList.value.push(props.list[clickIndex]) + lastSelectIndex = clickIndex + } + } else if (keyEvent.isModDown) { + lastSelectIndex = clickIndex + let item = props.list[clickIndex] + let index = selectedList.value.indexOf(item) + if (index < 0) { + selectedList.value.push(item) + } else { + selectedList.value.splice(index, 1) + } + } else if (selectedList.value.length) { + removeAllSelect() + } + } + + watch(() => props.list, removeAllSelect) + + return { + selectedList, + listItemHeight, + removeAllSelect, + handleSelectData, + } +} diff --git a/src/renderer/components/material/OnlineList/useMenu.js b/src/renderer/components/material/OnlineList/useMenu.js new file mode 100644 index 00000000..61ead38a --- /dev/null +++ b/src/renderer/components/material/OnlineList/useMenu.js @@ -0,0 +1,124 @@ +import { computed, ref, useI18n, useCssModule, nextTick } from '@renderer/utils/vueTools' +import musicSdk from '@renderer/utils/music' + +export default ({ + listRef, + assertApiSupport, + emit, + + handleShowDownloadModal, + handlePlayMusic, + handlePlayMusicLater, + handleSearch, + handleShowMusicAddModal, + handleOpenMusicDetail, +}) => { + const itemMenuControl = ref({ + play: true, + addTo: true, + playLater: true, + download: true, + search: true, + sourceDetail: true, + }) + const { t } = useI18n() + const styles = useCssModule() + const menuLocation = ref({ x: 0, y: 0 }) + const isShowItemMenu = ref(false) + + const menus = computed(() => { + return [ + { + name: t('list__play'), + action: 'play', + disabled: !itemMenuControl.value.play, + }, + { + name: t('list__download'), + action: 'download', + disabled: !itemMenuControl.value.download, + }, + { + name: t('list__play_later'), + action: 'playLater', + disabled: !itemMenuControl.value.playLater, + }, + { + name: t('list__search'), + action: 'search', + disabled: !itemMenuControl.value.search, + }, + { + name: t('list__add_to'), + action: 'addTo', + disabled: !itemMenuControl.value.addTo, + }, + { + name: t('list__source_detail'), + action: 'sourceDetail', + disabled: !itemMenuControl.value.sourceDetail, + }, + ] + }) + + const showMenu = (event, musicInfo) => { + itemMenuControl.value.sourceDetail = !!musicSdk[musicInfo.source].getMusicDetailPageUrl + // this.listMenu.itemMenuControl.play = + // this.listMenu.itemMenuControl.playLater = + itemMenuControl.download = assertApiSupport(musicInfo.source) + let dom_container = event.target.closest('.' + styles.songList) + const getOffsetValue = (target, x = 0, y = 0) => { + if (target === dom_container) return { x, y } + if (!target) return { x: 0, y: 0 } + x += target.offsetLeft + y += target.offsetTop + return getOffsetValue(target.offsetParent, x, y) + } + let { x, y } = getOffsetValue(event.target) + menuLocation.value.x = x + event.offsetX + menuLocation.value.y = y + event.offsetY - listRef.value.getScrollTop() + emit('show-menu') + nextTick(() => { + isShowItemMenu.value = true + }) + } + + const hideMenu = () => { + isShowItemMenu.value = false + } + + const menuClick = (action, index) => { + // console.log(action) + hideMenu() + if (!action) return + + switch (action.action) { + case 'download': + handleShowDownloadModal(index) + break + case 'play': + handlePlayMusic(index) + break + case 'playLater': + handlePlayMusicLater(index) + break + case 'search': + handleSearch(index) + break + case 'addTo': + handleShowMusicAddModal(index) + break + case 'sourceDetail': + handleOpenMusicDetail(index) + } + } + + return { + menus, + menuLocation, + isShowItemMenu, + showMenu, + menuClick, + hideMenu, + } +} diff --git a/src/renderer/components/material/OnlineList/useMusicActions.js b/src/renderer/components/material/OnlineList/useMusicActions.js new file mode 100644 index 00000000..3f439cf0 --- /dev/null +++ b/src/renderer/components/material/OnlineList/useMusicActions.js @@ -0,0 +1,30 @@ +import { useRouter } from '@renderer/utils/vueTools' +import musicSdk from '@renderer/utils/music' +import { openUrl } from '@renderer/utils' + + +export default ({ props }) => { + const router = useRouter() + + const handleSearch = index => { + const info = props.list[index] + router.push({ + path: 'search', + query: { + text: `${info.name} ${info.singer}`, + }, + }) + } + + const handleOpenMusicDetail = index => { + const minfo = props.list[index] + const url = musicSdk[minfo.source].getMusicDetailPageUrl(minfo) + if (!url) return + openUrl(url) + } + + return { + handleSearch, + handleOpenMusicDetail, + } +} diff --git a/src/renderer/components/material/OnlineList/useMusicAdd.js b/src/renderer/components/material/OnlineList/useMusicAdd.js new file mode 100644 index 00000000..73ea5b8a --- /dev/null +++ b/src/renderer/components/material/OnlineList/useMusicAdd.js @@ -0,0 +1,25 @@ +import { ref, nextTick } from '@renderer/utils/vueTools' + +export default ({ selectedList, props }) => { + const isShowListAdd = ref(false) + const isShowListAddMultiple = ref(false) + const selectedAddMusicInfo = ref(null) + + const handleShowMusicAddModal = (index, single) => { + if (selectedList.value.length && !single) { + isShowListAddMultiple.value = true + } else { + selectedAddMusicInfo.value = props.list[index] + nextTick(() => { + isShowListAdd.value = true + }) + } + } + + return { + isShowListAdd, + isShowListAddMultiple, + selectedAddMusicInfo, + handleShowMusicAddModal, + } +} diff --git a/src/renderer/components/material/OnlineList/useMusicDownload.js b/src/renderer/components/material/OnlineList/useMusicDownload.js new file mode 100644 index 00000000..0abc6f60 --- /dev/null +++ b/src/renderer/components/material/OnlineList/useMusicDownload.js @@ -0,0 +1,25 @@ +import { ref, nextTick } from '@renderer/utils/vueTools' + +export default ({ selectedList, props }) => { + const isShowDownload = ref(false) + const isShowDownloadMultiple = ref(false) + const musicInfo = ref(null) + + const handleShowDownloadModal = (index, single) => { + if (selectedList.value.length && !single) { + isShowDownloadMultiple.value = true + } else { + musicInfo.value = props.list[index] + nextTick(() => { + isShowDownload.value = true + }) + } + } + + return { + isShowDownload, + isShowDownloadMultiple, + selectedDownloadMusicInfo: musicInfo, + handleShowDownloadModal, + } +} diff --git a/src/renderer/components/material/OnlineList/usePlay.js b/src/renderer/components/material/OnlineList/usePlay.js new file mode 100644 index 00000000..2364fb91 --- /dev/null +++ b/src/renderer/components/material/OnlineList/usePlay.js @@ -0,0 +1,60 @@ +import { useCommit } from '@renderer/utils/vueTools' +import { defaultList } from '@renderer/core/share/list' +import { getList } from '@renderer/core/share/utils' + +export default ({ selectedList, props, removeAllSelect }) => { + let clickTime = 0 + let clickIndex = -1 + + const listAddMultiple = useCommit('list', 'listAddMultiple') + const listAdd = useCommit('list', 'listAdd') + const setList = useCommit('player', 'setList') + const setTempPlayList = useCommit('player', 'setTempPlayList') + + const handlePlayMusic = (index, single) => { + let targetSong = props.list[index] + const defaultListMusics = getList(defaultList.id) + if (selectedList.value.length && !single) { + listAddMultiple({ id: defaultList.id, list: [...selectedList.value] }) + removeAllSelect() + } else { + listAdd({ id: defaultList.id, musicInfo: targetSong }) + } + let targetIndex = defaultListMusics.findIndex(s => s.songmid === targetSong.songmid) + if (targetIndex > -1) { + setList({ + listId: defaultList.id, + index: targetIndex, + }) + } + } + + const handlePlayMusicLater = (index, single) => { + if (selectedList.length && !single) { + setTempPlayList(selectedList.value.map(s => ({ listId: '__temp__', musicInfo: s }))) + removeAllSelect() + } else { + setTempPlayList([{ listId: '__temp__', musicInfo: props.list[index] }]) + } + } + + const doubleClickPlay = index => { + if ( + window.performance.now() - clickTime > 400 || + clickIndex !== index + ) { + clickTime = window.performance.now() + clickIndex = index + return + } + handlePlayMusic(index, true) + clickTime = 0 + clickIndex = -1 + } + + return { + handlePlayMusic, + handlePlayMusicLater, + doubleClickPlay, + } +} diff --git a/src/renderer/components/material/Pagination.vue b/src/renderer/components/material/Pagination.vue index 0b7401af..7ed1fcda 100644 --- a/src/renderer/components/material/Pagination.vue +++ b/src/renderer/components/material/Pagination.vue @@ -1,37 +1,47 @@ - -div(:class="$style.pagination" v-if="allPage > 1") - ul - li(v-if="page===1" :class="$style.disabled") - span - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.846 451.847' space='preserve') - use(xlink:href='#icon-left') - li(v-else) - button(type="button" @click="handleClick(page - 1)" :tips="$t('material.pagination.prev')") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.846 451.847' space='preserve') - use(xlink:href='#icon-left') - li(v-if="allPage > btnLength && page > pageEvg+1" :class="$style.first") - button(type="button" @click="handleClick(1)" :tips="$t('material.pagination.page', { num: 1 })") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.846 451.847' space='preserve') - use(xlink:href='#icon-first') - - li(v-for="(p, index) in pages" :key="index" :class="{[$style.active] : p == page}") - span(v-if="p === page" v-text="page") - button(v-else type="button" @click="handleClick(p)" v-text="p" :tips="$t('material.pagination.page', { num: p })") - - li(v-if="allPage > btnLength && allPage - page > pageEvg" :class="$style.last") - button(type="button" @click="handleClick(allPage)" :tips="$t('material.pagination.page', { num: allPage })") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.846 451.847' space='preserve') - use(xlink:href='#icon-last') - - - li(v-if="page===allPage" :class="$style.disabled") - span - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.846 451.847' space='preserve') - use(xlink:href='#icon-right') - li(v-else) - button(type="button" @click="handleClick(page + 1)" :tips="$t('material.pagination.next')") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 451.846 451.847' space='preserve') - use(xlink:href='#icon-right') + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -116,7 +126,7 @@ export default { diff --git a/src/renderer/components/material/SyncModeModal.vue b/src/renderer/components/material/SyncModeModal.vue deleted file mode 100644 index bded1cc3..00000000 --- a/src/renderer/components/material/SyncModeModal.vue +++ /dev/null @@ -1,164 +0,0 @@ - -material-modal(:show="globalObj.sync.isShowSyncMode" @close="handleClose(false)" :bgClose="false" :close-btn="false") - main(:class="$style.main") - h2 {{$t('material.sync_mode_modal.title', { name: globalObj.sync.deviceName })}} - div.scroll(:class="$style.content") - dl(:class="$style.btnGroup") - dt(:class="$style.label") {{$t('material.sync_mode_modal.merge_label')}} - dd(:class="$style.btns") - material-btn(:class="$style.btn" @click="handleSelectMode('merge_local_remote')") {{$t('material.sync_mode_modal.merge_btn_local_remote')}} - material-btn(:class="$style.btn" @click="handleSelectMode('merge_remote_local')") {{$t('material.sync_mode_modal.merge_btn_remote_local')}} - dl(:class="$style.btnGroup") - dt(:class="$style.label") {{$t('material.sync_mode_modal.overwrite_label')}} - dd(:class="$style.btns") - material-btn(:class="$style.btn" @click="handleSelectMode('overwrite_local_remote')") {{$t('material.sync_mode_modal.overwrite_btn_local_remote')}} - material-btn(:class="$style.btn" @click="handleSelectMode('overwrite_remote_local')") {{$t('material.sync_mode_modal.overwrite_btn_remote_local')}} - dd(style="font-size: 14px; margin-top: 5px;") - material-checkbox(id="sync_mode_modal_isOverwrite" v-model="isOverwrite" :label="$t('material.sync_mode_modal.overwrite')") - - dl(:class="$style.btnGroup") - dt(:class="$style.label") {{$t('material.sync_mode_modal.other_label')}} - dd(:class="$style.btns") - material-btn(:class="$style.btn" @click="handleSelectMode('none')") {{$t('material.sync_mode_modal.overwrite_btn_none')}} - material-btn(:class="$style.btn" @click="handleSelectMode('cancel')") {{$t('material.sync_mode_modal.overwrite_btn_cancel')}} - dl(:class="$style.btnGroup") - dd - section(:class="$style.tipGroup") - h3(:class="$style.title") {{$t('material.sync_mode_modal.merge_tip')}} - p(:class="$style.tip") {{$t('material.sync_mode_modal.merge_tip_desc')}} - section(:class="$style.tipGroup") - h3(:class="$style.title") {{$t('material.sync_mode_modal.overwrite_tip')}} - p(:class="$style.tip") {{$t('material.sync_mode_modal.overwrite_tip_desc')}} - section(:class="$style.tipGroup") - h3(:class="$style.title") {{$t('material.sync_mode_modal.other_tip')}} - p(:class="$style.tip") {{$t('material.sync_mode_modal.other_tip_desc')}} - - - - - - diff --git a/src/renderer/components/material/VirtualizedList.vue b/src/renderer/components/material/VirtualizedList.vue deleted file mode 100644 index f53bd152..00000000 --- a/src/renderer/components/material/VirtualizedList.vue +++ /dev/null @@ -1,240 +0,0 @@ - - - - - - - - - - - - diff --git a/src/renderer/core/share/download.js b/src/renderer/core/share/download.js new file mode 100644 index 00000000..4c517940 --- /dev/null +++ b/src/renderer/core/share/download.js @@ -0,0 +1,26 @@ +import { reactive, ref, markRaw } from '@renderer/utils/vueTools' + +export const isInitedList = ref(false) + +export const setInited = () => { + isInitedList.value = true +} + +export const downloadList = reactive([]) +export const downloadListMap = new Map() + +export const initDownloadList = list => { + downloadList.splice(0, downloadList.length, ...list) + downloadListMap.clear() + for (const task of list) { + downloadListMap.set(task.key, task) + } +} + +export const downloadStatus = markRaw({ + RUN: 'run', + WAITING: 'waiting', + PAUSE: 'pause', + ERROR: 'error', + COMPLETED: 'completed', +}) diff --git a/src/renderer/core/share/index.js b/src/renderer/core/share/index.js new file mode 100644 index 00000000..4ae20ffc --- /dev/null +++ b/src/renderer/core/share/index.js @@ -0,0 +1,46 @@ +import { ref, reactive, shallowRef, markRaw } from '@renderer/utils/vueTools' +import { windowSizeList as configWindowSizeList, themes as configThemes } from '@common/config' +import { version } from '../../../../package.json' +process.versions.app = version + +export const apiSource = ref(null) +export const isShowPact = window.isShowPact = ref(false) +export const proxy = reactive({}) +export const versionInfo = window.versionInfo = reactive({ + version, + newVersion: null, + showModal: false, + isError: false, + isTimeOut: false, + isUnknow: false, + isDownloaded: false, + isDownloading: false, + isLatestVer: false, + downloadProgress: null, +}) +export const userApi = reactive({ + list: [], + status: false, + message: 'initing', + apis: {}, +}) +export const sync = window.sync = reactive({ + enable: false, + isShowSyncMode: false, + deviceName: '', + status: { + status: false, + message: '', + address: [], + code: '', + devices: [], + }, +}) + +export const windowSizeList = markRaw(configWindowSizeList) +export const themes = markRaw(configThemes) + +export const qualityList = shallowRef({}) +export const setQualityList = _qualityList => { + qualityList.value = _qualityList +} diff --git a/src/renderer/core/share/list.js b/src/renderer/core/share/list.js new file mode 100644 index 00000000..d813b89b --- /dev/null +++ b/src/renderer/core/share/list.js @@ -0,0 +1,112 @@ +import { reactive, ref, markRaw } from '@renderer/utils/vueTools' + +// const TEMP_LIST = 'TEMP_LIST' + +export const isInitedList = ref(false) + +export const setInited = () => { + isInitedList.value = true +} + +export const allList = markRaw({}) + +export const allListInit = (defaultList, loveList, tempList, userList) => { + for (const id of Object.keys(allList)) { + delete allList[id] + } + allList[defaultList.id] = reactive(defaultList.list) + allList[loveList.id] = reactive(loveList.list) + allList[tempList.id] = reactive(tempList.list) + userLists.splice(0, userLists.length) + for (const { list, ...listInfo } of userList) { + allList[listInfo.id] = reactive(list) + userLists.push(listInfo) + } +} +export const allListUpdate = (id, list) => { + if (allList[id]) { + allList[id].splice(0, allList[id].length, ...list) + } else { + allList[id] = list + } +} +export const allListRemove = id => { + delete allList[id] +} + +export const defaultList = reactive({ + id: 'default', + name: '试听列表', +}) + +export const loveList = reactive({ + id: 'love', + name: '我的收藏', +}) + +export const tempList = reactive({ + id: 'temp', + name: '临时列表', +}) + +export const userLists = reactive([]) + +export const addUserList = ({ + name, + id, + list, + source, + sourceListId, + position, +}) => { + if (position == null) { + userLists.push({ + name, + id, + source, + sourceListId, + }) + } else { + userLists.splice(position + 1, 0, { + name, + id, + source, + sourceListId, + }) + } + allListUpdate(id, list) +} + +export const updateList = ({ + name, + id, + list, + source, + sourceListId, +}) => { + let targetList + switch (id) { + case defaultList.id: + case loveList.id: + case tempList.id: + break + default: + targetList = userLists.find(l => l.id == id) + targetList.name = name + targetList.source = source + targetList.sourceListId = sourceListId + break + } + allListUpdate(id, list) +} + +export const removeUserList = id => { + const index = userLists.findIndex(l => l.id == id) + if (index < 0) return + userLists.splice(index, 1) + allListRemove(id) +} + +export const getList = id => { + return allList[id] ?? [] +} diff --git a/src/renderer/core/share/lyric.js b/src/renderer/core/share/lyric.js new file mode 100644 index 00000000..3d9efd62 --- /dev/null +++ b/src/renderer/core/share/lyric.js @@ -0,0 +1,15 @@ +import { reactive } from '@renderer/utils/vueTools' + +export const lyric = reactive({ + lines: [], + text: '', + line: 0, +}) + +export const setLines = lines => { + lyric.lines = lines +} +export const setText = (text, line) => { + lyric.text = text + lyric.line = line +} diff --git a/src/renderer/core/share/playProgress.js b/src/renderer/core/share/playProgress.js new file mode 100644 index 00000000..2e1f21c5 --- /dev/null +++ b/src/renderer/core/share/playProgress.js @@ -0,0 +1,22 @@ +import { reactive } from '@renderer/utils/vueTools' +import { formatPlayTime2 } from '@renderer/utils' + +export const playProgress = reactive({ + nowPlayTime: 0, + maxPlayTime: 0, + progress: 0, + nowPlayTimeStr: '00:00', + maxPlayTimeStr: '00:00', +}) + +export const setNowPlayTime = time => { + playProgress.nowPlayTime = time + playProgress.nowPlayTimeStr = formatPlayTime2(time) + playProgress.progress = playProgress.maxPlayTime ? time / playProgress.maxPlayTime : 0 +} + +export const setMaxplayTime = time => { + playProgress.maxPlayTime = time + playProgress.maxPlayTimeStr = formatPlayTime2(time) + playProgress.progress = time ? playProgress.nowPlayTime / time : 0 +} diff --git a/src/renderer/core/share/player.js b/src/renderer/core/share/player.js new file mode 100644 index 00000000..005bf11f --- /dev/null +++ b/src/renderer/core/share/player.js @@ -0,0 +1,175 @@ +import { reactive, ref, shallowRef } from '@renderer/utils/vueTools' +import { getList } from '@renderer/core/share/utils' + +export const musicInfo = window.musicInfo = reactive({ + songmid: null, + img: null, + lrc: null, + tlrc: null, + lxlrc: null, + url: null, + name: '', + singer: '', + album: '', +}) +const musicInfoKeys = Object.keys(musicInfo) +export const setMusicInfo = _musicInfo => { + for (const key of musicInfoKeys) { + if (_musicInfo[key] !== undefined) { + musicInfo[key] = _musicInfo[key] + } + } +} + +export const musicInfoItem = shallowRef({}) +export const setMusicInfoItem = musicInfo => { + musicInfoItem.value = musicInfo.key ? musicInfo.metadata.musicInfo : musicInfo +} + + +export const isPlay = ref(false) +export const setPlay = val => { + isPlay.value = val +} + +export const status = ref('') +export const setStatus = val => { + status.value = val +} + + +export const statusText = ref('') +export const setStatusText = val => { + statusText.value = val +} + +export const setAllStatus = val => { + status.value = val + statusText.value = val +} + + +export const isShowPlayerDetail = ref(false) +export const setShowPlayerDetail = val => { + isShowPlayerDetail.value = val +} + +export const isShowPlayComment = ref(false) +export const setShowPlayComment = val => { + isShowPlayComment.value = val +} + +export const isShowLrcSelectContent = ref(false) +export const setShowPlayLrcSelectContentLrc = val => { + isShowLrcSelectContent.value = val +} + + +export const playMusicInfo = reactive({ + listId: null, // 当前播放歌曲的列表 id + musicInfo: null, // 当前播放歌曲的歌曲信息 + isTempPlay: false, // 是否属于 “稍后播放” +}) +export const playInfo = reactive({ + playIndex: -1, // 当前正在播放歌曲 index + playListId: null, // 播放器的播放列表 id + listPlayIndex: -1, // 播放器播放歌曲 index +}) + +// 设置播放器的播放列表 +export const setPlayList = listId => { + playInfo.playListId = listId +} + +// 更新播放位置 +export const updatePlayIndex = () => { + const indexInfo = getPlayIndex(playMusicInfo.listId, playMusicInfo.musicInfo, playMusicInfo.isTempPlay) + // console.log(indexInfo) + playInfo.playIndex = indexInfo.playIndex + playInfo.listPlayIndex = indexInfo.listPlayIndex + + return indexInfo +} + +export const getPlayIndex = (listId, musicInfo, isTempPlay) => { + const playerList = getList(playInfo.playListId) + + // if (listIndex < 0) throw new Error('music info not found') + // playInfo.playIndex = listIndex + + let playIndex = -1 + let listPlayIndex = -1 + if (playerList?.length) { + listPlayIndex = Math.min(playInfo.listPlayIndex, playerList.length - 1) + } + + const list = getList(listId) + if (list?.length) { + if (musicInfo.key) { // 已下载的歌曲 + const currentKey = musicInfo.key + playIndex = list.findIndex(m => m.key == currentKey) + } else { + const currentSongmid = musicInfo.songmid + playIndex = list.findIndex(m => m.songmid == currentSongmid) + } + if (!isTempPlay) { + if (playIndex < 0) { + listPlayIndex = listPlayIndex < 1 ? (list.length - 1) : (listPlayIndex - 1) + } else { + listPlayIndex = playIndex + } + } + } + + return { + playIndex, + listPlayIndex, + } +} + +// 设置当前播放歌曲的信息 +export const setPlayMusicInfo = (listId, musicInfo, isTempPlay = false) => { + playMusicInfo.listId = listId + playMusicInfo.musicInfo = musicInfo + playMusicInfo.isTempPlay = isTempPlay + if (musicInfo == null) { + playInfo.playIndex = -1 + playInfo.playListId = null + playInfo.listPlayIndex = -1 + return + } + + const { playIndex, listPlayIndex } = getPlayIndex(listId, musicInfo, isTempPlay) + + playInfo.playIndex = playIndex + playInfo.listPlayIndex = listPlayIndex + + setMusicInfoItem(musicInfo) + // console.log(playInfo) +} + +export const playedList = window.playedList = reactive([]) +export const addPlayedList = (item) => { + if (playedList.some(m => m.musicInfo === item.musicInfo)) return + playedList.push(item) +} +export const removePlayedList = (index) => { + playedList.splice(index, 1) +} +export const clearPlayedList = () => { + playedList.splice(0, playedList.length) +} + +export const tempPlayList = reactive([]) +export const addTempPlayList = (list) => { + tempPlayList.push(...list.map(({ musicInfo, listId }) => ({ musicInfo, listId, isTempPlay: true }))) +} +export const removeTempPlayList = (index) => { + tempPlayList.splice(index, 1) +} +export const clearTempPlayeList = () => { + tempPlayList.splice(0, tempPlayList.length) +} + +window.playInfo = playInfo +window.playMusicInfo = playMusicInfo diff --git a/src/renderer/core/share/utils.js b/src/renderer/core/share/utils.js new file mode 100644 index 00000000..d1d6cfad --- /dev/null +++ b/src/renderer/core/share/utils.js @@ -0,0 +1,7 @@ +import { getList as getMyList } from './list' +import { downloadList } from './download' + + +export const getList = listId => { + return listId == 'download' ? downloadList : getMyList(listId) +} diff --git a/src/renderer/core/share/volume.js b/src/renderer/core/share/volume.js new file mode 100644 index 00000000..51bf77df --- /dev/null +++ b/src/renderer/core/share/volume.js @@ -0,0 +1,13 @@ +import { ref } from '@renderer/utils/vueTools' + + +export const volume = ref(0) +export const isMute = ref(false) + +export const setVolume = num => { + volume.value = num +} + +export const setMute = flag => { + isMute.value = flag +} diff --git a/src/renderer/core/useApp/index.js b/src/renderer/core/useApp/index.js new file mode 100644 index 00000000..464d058c --- /dev/null +++ b/src/renderer/core/useApp/index.js @@ -0,0 +1,55 @@ +import { isLinux } from '@common/utils' +import { getEnvParams, setIgnoreMouseEvents } from '@renderer/utils/tools' + +import { useRefGetter } from '@renderer/utils/vueTools' +import { sync, apiSource, proxy } from '@renderer/core/share' + +import useSync from './useSync' +import useUpdate from './useUpdate' +import useDataInit from './useDataInit' +import useHandleEnvParams from './useHandleEnvParams' +import useEventListener from './useEventListener' +import usePlayer from './usePlayer' + + +export default () => { + const isProd = process.env.NODE_ENV === 'production' + + const setting = useRefGetter('setting') + + sync.enable = setting.value.sync.enable + apiSource.value = setting.value.apiSource + proxy.value = Object.assign({}, setting.value.network.proxy) + + const dieableIgnoreMouseEvents = () => { + if (window.dt) return + setIgnoreMouseEvents(false) + } + const enableIgnoreMouseEvents = () => { + if (window.dt) return + setIgnoreMouseEvents(true) + } + + useUpdate(setting) + useSync() + useEventListener({ + dieableIgnoreMouseEvents, + enableIgnoreMouseEvents, + setting, + isProd, + isLinux, + }) + usePlayer({ setting }) + const handleEnvParams = useHandleEnvParams() + const initData = useDataInit({ + setting, + }) + + + getEnvParams().then(envParams => { + // 初始化我的列表、下载列表等数据 + initData().then(() => { + handleEnvParams(envParams) // 处理传入的启动参数 + }) + }) +} diff --git a/src/renderer/core/useApp/useDataInit.js b/src/renderer/core/useApp/useDataInit.js new file mode 100644 index 00000000..8929d61a --- /dev/null +++ b/src/renderer/core/useApp/useDataInit.js @@ -0,0 +1,175 @@ +import { useCommit, useRefGetter } from '@renderer/utils/vueTools' +import { getPlayList } from '@renderer/utils' +import { getPlayInfo, getSearchHistoryList } from '@renderer/utils/tools' +import { initListPosition, initListPrevSelectId } from '@renderer/utils/data' +import music from '@renderer/utils/music' +import { log } from '@common/utils' +import { + defaultList as stateDefaultList, + loveList as stateloveList, + userLists as stateUserLists, + tempList as stateTempList, +} from '@renderer/core/share/list' +import { getList } from '@renderer/core/share/utils' +import { downloadStatus, downloadList } from '@renderer/core/share/download' + + +import useSaveData from './useSaveData' +import useInitUserApi from './useInitUserApi' + +const useListInit = ({ + saveMyListThrottle, +}) => { + const initList = useCommit('list', 'initList') + const updateDownloadList = useCommit('download', 'initDownloadList') + + const initMyList = ({ defaultList, loveList, userList, tempList }) => { + if (!defaultList) defaultList = { ...stateDefaultList } + if (!loveList) loveList = { ...stateloveList } + if (!tempList) tempList = { ...stateTempList } + if (userList) { + let needSave = false + const getListId = id => id.includes('.') ? getListId(id.substring(0, id.lastIndexOf('_'))) : id + userList.forEach(l => { + if (!l.id.includes('__') || l.source) return + let [source, id] = l.id.split('__') + id = getListId(id) + l.source = source + l.sourceListId = id + if (!needSave) needSave = true + }) + if (needSave) saveMyListThrottle({ userList }) + } else { + userList = [...stateUserLists] + } + + if (!defaultList.list) defaultList.list = [] + if (!loveList.list) loveList.list = [] + if (!tempList.list) tempList.list = [] + initList({ defaultList, loveList, userList, tempList }) + } + + const initDownloadList = list => { + if (list) { + list = list.filter(item => item?.key) + for (const item of list) { + if (item.status == downloadStatus.RUN || item.status == downloadStatus.WAITING) { + item.status = downloadStatus.PAUSE + item.statusText = '暂停下载' + } + + if (!item.metadata) { // 转换v1.15.3及以前的任务信息 + if (item.name == null) { + item.name = `${item.musicInfo.name} - ${item.musicInfo.singer}` + item.songmid = item.musicInfo.songmid + } + item.metadata = { + musicInfo: item.musicInfo, + url: item.url, + type: item.type, + ext: item.ext, + fileName: item.fileName, + filePath: item.filePath, + } + delete item.musicInfo + delete item.url + delete item.type + delete item.ext + delete item.fileName + delete item.filePath + } + } + updateDownloadList(list) + } + } + + return () => { + return getPlayList().then(({ defaultList, loveList, userList, tempList, downloadList }) => { + initMyList({ defaultList, loveList, userList, tempList }) + initDownloadList(downloadList) // 初始化下载列表 + }) + } +} + +const usePlayInfoInit = () => { + const setPlayList = useCommit('player', 'setList') + + return downloadList => { + return getPlayInfo().then(info => { + window.restorePlayInfo = null + if (!info) return + if (info.index < 0) return + if (info.listId) { + if (info.listId == 'download') { + const list = downloadList + // console.log(list) + if (!list || !list[info.index]) return + info.list = list + } else { + const list = getList(info.listId) + // console.log(list) + if (!list[info.index]) return + info.list = list + } + } + if (!info.list || !info.list[info.index]) return + window.restorePlayInfo = info + setPlayList({ + listId: info.listId, + index: info.index, + }) + }) + } +} + +const useSearchHistoryInit = () => { + const setSearchHistoryList = useCommit('search', 'setHistory') + return saveSearchHistoryListThrottle => { + return getSearchHistoryList().then(historyList => { + if (historyList == null) { + historyList = [] + saveSearchHistoryListThrottle(historyList) + } else { + setSearchHistoryList(historyList) + } + }) + } +} + +export default ({ + setting, +}) => { + const searchHistoryList = useRefGetter('search', 'historyList') + + // 数据保存初始化 + const { saveMyListThrottle, saveSearchHistoryListThrottle } = useSaveData({ + setting, + searchHistoryList, + }) + + const initUserApi = useInitUserApi({ setting }) + + // 列表初始化 + const initList = useListInit({ + saveMyListThrottle, + }) + + + // 播放信息初始化 + const initPlayInfo = usePlayInfoInit() + + // 搜索历史初始化 + const initSearchHistory = useSearchHistoryInit() + + return async() => { + await Promise.all([ + initListPosition(), // 列表位置记录 + initListPrevSelectId(), // 上次选中的列表记录 + initUserApi(), // 自定义API + music.init(), // 初始化音乐sdk + ]).catch(err => log.error(err)) + await initList().catch(err => log.error(err)) // 初始化列表 + await initPlayInfo(downloadList.value).catch(err => log.error(err)) // 初始化上次的歌曲播放信息 + await initSearchHistory(saveSearchHistoryListThrottle).catch(err => log.error(err)) // 初始化搜索历史记录 + } +} diff --git a/src/renderer/core/useApp/useEventListener.js b/src/renderer/core/useApp/useEventListener.js new file mode 100644 index 00000000..b8bdbfd1 --- /dev/null +++ b/src/renderer/core/useApp/useEventListener.js @@ -0,0 +1,82 @@ +import { openUrl } from '@renderer/utils' +import { base as eventBaseName } from '@renderer/event/names' +import { onSetConfig } from '@renderer/utils/tools' + +import { + toRaw, + useCommit, + onBeforeUnmount, + watchEffect, + useRefGetter, +} from '@renderer/utils/vueTools' + +const handle_key_esc_down = ({ event }) => { + if (event.repeat) return + if (event.target.tagName != 'INPUT' || event.target.classList.contains('ignore-esc')) return + event.target.value = '' + event.target.blur() +} +const handleBodyClick = event => { + if (event.target.tagName != 'A') return + if (event.target.host == window.location.host) return + event.preventDefault() + if (/^https?:\/\//.test(event.target.href)) openUrl(event.target.href) +} + +export default ({ + dieableIgnoreMouseEvents, + enableIgnoreMouseEvents, + setting, + isProd, + isLinux, +}) => { + const setSetting = useCommit('setSetting') + const windowSizeActive = useRefGetter('windowSizeActive') + + watchEffect(() => { + document.documentElement.style.fontSize = windowSizeActive.value.fontSize + }) + watchEffect(() => { + if (setting.value.isShowAnimation) { + if (document.body.classList.contains('disableAnimation')) { + document.body.classList.remove('disableAnimation') + } + } else { + if (!document.body.classList.contains('disableAnimation')) { + document.body.classList.add('disableAnimation') + } + } + }) + + const rSetConfig = onSetConfig((event, config) => { + setSetting(Object.assign({}, toRaw(setting.value), config)) + window.eventHub.emit(eventBaseName.set_config, config) + }) + + window.eventHub.emit(eventBaseName.bindKey) + window.eventHub.on('key_escape_down', handle_key_esc_down) + document.body.addEventListener('click', handleBodyClick, true) + + if (isProd && !window.dt && !isLinux) { + document.body.addEventListener('mouseenter', enableIgnoreMouseEvents) + document.body.addEventListener('mouseleave', dieableIgnoreMouseEvents) + const dom_root = document.getElementById('root') + dom_root.addEventListener('mouseenter', dieableIgnoreMouseEvents) + dom_root.addEventListener('mouseleave', enableIgnoreMouseEvents) + } + + onBeforeUnmount(() => { + window.eventHub.off('key_escape_down', handle_key_esc_down) + document.body.removeEventListener('click', handleBodyClick) + window.eventHub.emit(eventBaseName.unbindKey) + rSetConfig() + + if (isProd && !window.dt && !isLinux) { + document.body.removeEventListener('mouseenter', enableIgnoreMouseEvents) + document.body.removeEventListener('mouseleave', dieableIgnoreMouseEvents) + const dom_root = document.getElementById('root') + dom_root.removeEventListener('mouseenter', dieableIgnoreMouseEvents) + dom_root.removeEventListener('mouseleave', enableIgnoreMouseEvents) + } + }) +} diff --git a/src/renderer/core/useApp/useHandleEnvParams.js b/src/renderer/core/useApp/useHandleEnvParams.js new file mode 100644 index 00000000..ac0612d2 --- /dev/null +++ b/src/renderer/core/useApp/useHandleEnvParams.js @@ -0,0 +1,101 @@ +import { useAction, useCommit, useRouter } from '@renderer/utils/vueTools' +import { parseUrlParams } from '@renderer/utils' +import { defaultList, loveList, userLists } from '@renderer/core/share/list' +import { getList } from '@renderer/core/share/utils' + +const getListPlayIndex = (list, index) => { + if (index == null) { + index = 1 + } else { + index = parseInt(index) + if (Number.isNaN(index)) { + index = 1 + } else { + if (index < 1) index = 1 + else if (index > list.length) index = list.length + } + } + return index - 1 +} + +const useInitEnvParamSearch = () => { + const router = useRouter() + + return search => { + if (search == null) return + router.push({ + path: 'search', + query: { + text: search, + }, + }) + } +} +const useInitEnvParamPlay = () => { + const getListDetailAll = useAction('songList', 'getListDetailAll') + const setPlayList = useCommit('player', 'setList') + const setTempList = useCommit('player', 'setTempList') + + const playSongListDetail = async(source, link, playIndex) => { + if (link == null) return + let list + try { + list = await getListDetailAll({ source, id: decodeURIComponent(link) }) + } catch (err) { + console.log(err) + } + setTempList({ + list, + index: getListPlayIndex(list, playIndex), + }) + } + + return (playStr) => { + if (playStr == null || typeof playStr != 'string') return + // -play="source=kw&link=链接、ID" + // -play="source=myList&name=名字" + // -play="source=myList&name=名字&index=位置" + const params = parseUrlParams(playStr) + if (params.type != 'songList') return + switch (params.source) { + case 'myList': + if (params.name != null) { + let targetList + const lists = [defaultList, loveList, ...userLists] + for (const list of lists) { + if (list.name === params.name) { + targetList = list + break + } + } + if (!targetList) return + + setPlayList({ + listId: targetList.id, + index: getListPlayIndex(getList(targetList.id), params.index), + }) + } + break + case 'kw': + case 'kg': + case 'tx': + case 'mg': + case 'wy': + playSongListDetail(params.source, params.link, params.index) + break + } + } +} + +export default () => { + // 处理启动参数 search + const initEnvParamSearch = useInitEnvParamSearch() + + // 处理启动参数 play + const initEnvParamPlay = useInitEnvParamPlay() + + return envParams => { + initEnvParamSearch(envParams.search) + initEnvParamPlay(envParams.play) + } +} diff --git a/src/renderer/core/useApp/useInitUserApi.js b/src/renderer/core/useApp/useInitUserApi.js new file mode 100644 index 00000000..550c5f1b --- /dev/null +++ b/src/renderer/core/useApp/useInitUserApi.js @@ -0,0 +1,86 @@ +import { onBeforeUnmount } from '@renderer/utils/vueTools' +import { onUserApiStatus, setUserApi, getUserApiList, userApiRequest, userApiRequestCancel } from '@renderer/utils/tools' +import apiSourceInfo from '@renderer/utils/music/api-source-info' +import music from '@renderer/utils/music' +import { apiSource, qualityList, userApi } from '@renderer/core/share' + + +export default ({ setting }) => { + if (/^user_api/.test(setting.value.apiSource)) { + setUserApi(setting.value.apiSource) + } else { + qualityList.value = music.supportQuality[setting.value.apiSource] + } + + const rUserApiStatus = onUserApiStatus(({ status, message, apiInfo }) => { + userApi.status = status + userApi.message = message + + if (status) { + if (apiInfo.id === setting.value.apiSource) { + let apis = {} + let qualitys = {} + for (const [source, { actions, type, qualitys: sourceQualitys }] of Object.entries(apiInfo.sources)) { + if (type != 'music') continue + apis[source] = {} + for (const action of actions) { + switch (action) { + case 'musicUrl': + apis[source].getMusicUrl = (songInfo, type) => { + const requestKey = `request__${Math.random().toString().substring(2)}` + return { + canceleFn() { + userApiRequestCancel(requestKey) + }, + promise: userApiRequest({ + requestKey, + data: { + source: source, + action: 'musicUrl', + info: { + type, + musicInfo: songInfo, + }, + }, + }).then(res => { + // console.log(res) + if (!/^https?:/.test(res.data.url)) return Promise.reject(new Error('Get url failed')) + return { type, url: res.data.url } + }).catch(err => { + console.log(err.message) + return Promise.reject(err) + }), + } + } + break + + default: + break + } + } + qualitys[source] = sourceQualitys + } + qualityList.value = qualitys + userApi.apis = apis + } + } + }) + + onBeforeUnmount(() => { + rUserApiStatus() + }) + + return () => { + return getUserApiList().then(list => { + // console.log(list) + if (![...apiSourceInfo.map(s => s.id), ...list.map(s => s.id)].includes(setting.value.apiSource)) { + console.warn('reset api') + let api = apiSourceInfo.find(api => !api.disabled) + if (api) apiSource.value = api.id + } + userApi.list = list + }).catch(err => { + console.log(err) + }) + } +} diff --git a/src/renderer/core/useApp/usePlayer/index.js b/src/renderer/core/useApp/usePlayer/index.js new file mode 100644 index 00000000..72c59941 --- /dev/null +++ b/src/renderer/core/useApp/usePlayer/index.js @@ -0,0 +1,15 @@ +import { + createAudio, +} from '@renderer/plugins/player' +import useMediaDevice from './useMediaDevice' +import usePlayerEvent from './usePlayerEvent' +import usePlayer from './usePlayer' + +export default ({ setting }) => { + createAudio() + + usePlayerEvent() + useMediaDevice({ setting }) // 初始化音频驱动输出设置 + usePlayer({ setting }) +} + diff --git a/src/renderer/core/useApp/usePlayer/useLyric.js b/src/renderer/core/useApp/usePlayer/useLyric.js new file mode 100644 index 00000000..6bfe94ac --- /dev/null +++ b/src/renderer/core/useApp/usePlayer/useLyric.js @@ -0,0 +1,125 @@ +import { onBeforeUnmount, watch, markRawList } from '@renderer/utils/vueTools' +import Lyric from '@renderer/utils/lyric-font-player' +import { getCurrentTime } from '@renderer/plugins/player' +import { setDesktopLyricInfo, onGetDesktopLyricInfo } from '@renderer/utils/tools' +import { player } from '@renderer/event/names' +import { lyric, setText, setLines } from '@renderer/core/share/lyric' +import { musicInfo, setStatusText, isPlay, playMusicInfo } from '@renderer/core/share/player' + +export default ({ setting }) => { + const lrc = window.lrc = new Lyric({ + lineClassName: 'lrc-content', + fontClassName: 'font', + shadowContent: false, + activeLineClassName: 'active', + onPlay: (line, text) => { + setText(text, line) + setStatusText(text) + // console.log(line, text) + }, + onSetLyric: lines => { // listening lyrics seting event + // console.log(lines) // lines is array of all lyric text + setLines(markRawList([...lines])) + setText(lines[0] ?? '', 0) + }, + // offset: 80, + }) + + + const setPlayInfo = musicInfo => { + setDesktopLyricInfo('music_info', { + songmid: musicInfo.songmid, + singer: musicInfo.singer, + name: musicInfo.name, + album: musicInfo.album, + }) + } + + const setLyric = () => { + if (!musicInfo.songmid) return + lrc.setLyric( + setting.value.player.isPlayLxlrc && musicInfo.lxlrc ? musicInfo.lxlrc : musicInfo.lrc, + setting.value.player.isShowLyricTranslation && musicInfo.tlrc ? musicInfo.tlrc : '', + ) + setDesktopLyricInfo('lyric', { lrc: musicInfo.lrc, tlrc: musicInfo.tlrc, lxlrc: musicInfo.lxlrc }) + + if (isPlay.value && (musicInfo.url || playMusicInfo.listId == 'download')) { + const time = getCurrentTime() * 1000 + setDesktopLyricInfo('play', time) + lrc.play(time) + } + } + + const handleSetPlayInfo = ({ musicInfo }) => { + setPlayInfo(musicInfo) + } + + const handlePlay = () => { + if (!musicInfo.lrc) return + const currentTime = getCurrentTime() * 1000 + lrc.play(currentTime) + setDesktopLyricInfo('play', currentTime) + } + const handlePause = () => { + lrc.pause() + setDesktopLyricInfo('pause') + } + const handleStop = () => { + lrc.pause() + setDesktopLyricInfo('stop') + setLines([]) + setText('', 0) + } + + watch(() => setting.value.player.isShowLyricTranslation, setLyric) + watch(() => setting.value.player.isPlayLxlrc, setLyric) + + const rGetDesktopLyricInfo = onGetDesktopLyricInfo((event, info) => { + switch (info.action) { + case 'info': + setDesktopLyricInfo('info', { + songmid: musicInfo.songmid, + singer: musicInfo.singer, + name: musicInfo.name, + album: musicInfo.album, + lrc: musicInfo.lrc, + tlrc: musicInfo.tlrc, + lxlrc: musicInfo.lxlrc, + isPlay: isPlay.value, + line: lyric.line, + played_time: getCurrentTime() * 1000, + }, info) + break + case 'status': + setDesktopLyricInfo('status', { + isPlay: isPlay.value, + line: lyric.line, + played_time: getCurrentTime() * 1000, + }, info) + break + + default: + break + } + }) + + + window.eventHub.on(player.setPlay, setPlayInfo) + window.eventHub.on(player.play, handlePlay) + window.eventHub.on(player.pause, handlePause) + window.eventHub.on(player.stop, handleStop) + window.eventHub.on(player.error, handlePause) + window.eventHub.on(player.setPlayInfo, handleSetPlayInfo) + window.eventHub.on(player.updateLyric, setLyric) + + onBeforeUnmount(() => { + rGetDesktopLyricInfo() + window.eventHub.off(player.setPlay, setPlayInfo) + window.eventHub.off(player.play, handlePlay) + window.eventHub.off(player.pause, handlePause) + window.eventHub.off(player.stop, handleStop) + window.eventHub.off(player.error, handlePause) + window.eventHub.off(player.setPlayInfo, handleSetPlayInfo) + window.eventHub.off(player.updateLyric, setLyric) + }) +} diff --git a/src/renderer/core/useApp/usePlayer/useMediaDevice.js b/src/renderer/core/useApp/usePlayer/useMediaDevice.js new file mode 100644 index 00000000..fe439eed --- /dev/null +++ b/src/renderer/core/useApp/usePlayer/useMediaDevice.js @@ -0,0 +1,72 @@ +import { + onBeforeUnmount, + watch, + useCommit, +} from '@renderer/utils/vueTools' +import { setMediaDeviceId } from '@renderer/plugins/player' +import { isPlay } from '@renderer/core/share/player' +import { player as eventPlayerNames } from '@renderer/event/names' + + +export default ({ setting }) => { + let prevDeviceLabel = null + + const saveMediaDeviceId = useCommit('setMediaDeviceId') + + const setMediaDevice = async(mediaDeviceId) => { + let label = prevDeviceLabel + const devices = await navigator.mediaDevices.enumerateDevices() + let device = devices.find(device => device.deviceId === mediaDeviceId) + if (device) { + mediaDeviceId = device.deviceId + label = device.label + } else { + mediaDeviceId = 'default' + device = devices.find(device => device.deviceId === mediaDeviceId) + if (device) label = device.label + } + + prevDeviceLabel = label + // console.log(device) + setMediaDeviceId(mediaDeviceId).catch(err => { + console.log(err) + saveMediaDeviceId('default') + }) + } + + const handleDeviceChangeStopPlay = (device, mediaDeviceId) => { + // console.log(device) + // console.log(this.setting.player.isMediaDeviceRemovedStopPlay, this.isPlay, device.label, this.prevDeviceLabel) + if ( + setting.value.player.isMediaDeviceRemovedStopPlay && + isPlay.value && + device.label != prevDeviceLabel + ) window.eventHub.emit(eventPlayerNames.setTogglePlay) + } + + const handleMediaListChange = async() => { + let mediaDeviceId = setting.value.player.mediaDeviceId + const devices = await navigator.mediaDevices.enumerateDevices() + let device = devices.find(device => device.deviceId === mediaDeviceId) + if (!device) device = devices.find(device => device.deviceId === 'default') + if (!device) device = { label: null, deviceId: null } + + handleDeviceChangeStopPlay(device, mediaDeviceId) + + setMediaDeviceId(device.deviceId).catch(err => { + console.log(err) + saveMediaDeviceId('default') + }) + } + + watch(() => setting.value.player.mediaDeviceId, setMediaDevice) + + + setMediaDevice(setting.value.player.mediaDeviceId) + + navigator.mediaDevices.addEventListener('devicechange', handleMediaListChange) + + onBeforeUnmount(() => { + navigator.mediaDevices.removeEventListener('devicechange', handleMediaListChange) + }) +} diff --git a/src/renderer/core/useApp/usePlayer/useMediaSessionInfo.js b/src/renderer/core/useApp/usePlayer/useMediaSessionInfo.js new file mode 100644 index 00000000..ae99d81e --- /dev/null +++ b/src/renderer/core/useApp/usePlayer/useMediaSessionInfo.js @@ -0,0 +1,110 @@ +import { onBeforeUnmount } from '@renderer/utils/vueTools' +import { getDuration, getPlaybackRate, getCurrentTime } from '@renderer/plugins/player' +import { player } from '@renderer/event/names' + +export default ({ playPrev, playNext }) => { + const updateMediaSessionInfo = ({ name, singer, albumName, img }) => { + const mediaMetadata = { + title: name, + artist: singer, + album: albumName, + } + if (img) mediaMetadata.artwork = [{ src: img }] + navigator.mediaSession.metadata = new window.MediaMetadata(mediaMetadata) + } + + const updatePositionState = (state = {}) => { + navigator.mediaSession.setPositionState({ + duration: state.duration ?? getDuration(), + playbackRate: state.playbackRate ?? getPlaybackRate(), + position: state.position ?? getCurrentTime(), + }) + } + + const setProgress = time => { + window.eventHub.emit(player.setProgress, time) + } + + const setStop = () => { + window.eventHub.emit(player.setStop) + } + const handlePlay = () => { + navigator.mediaSession.playbackState = 'playing' + } + const handlePause = () => { + navigator.mediaSession.playbackState = 'paused' + } + const handleStop = () => { + navigator.mediaSession.playbackState = 'none' + } + const handleSetPlayInfo = ({ musicInfo, progress }) => { + updateMediaSessionInfo(musicInfo) + updatePositionState({ + position: progress.time, + duration: progress.maxTime, + }) + handlePause() + } + + // const registerMediaSessionHandler = () => { + // navigator.mediaSession.setActionHandler('play', () => { + // if (this.isPlay || !this.playMusicInfo) return + // console.log('play') + // this.startPlay() + // }) + // navigator.mediaSession.setActionHandler('pause', () => { + // if (!this.isPlay || !this.playMusicInfo) return + // console.log('pause') + // this.stopPlay() + // }) + navigator.mediaSession.setActionHandler('stop', () => { + console.log('stop') + setStop() + }) + navigator.mediaSession.setActionHandler('seekbackward', details => { + console.log('seekbackward') + setProgress(Math.max(getCurrentTime() - details.seekOffset, 0)) + }) + navigator.mediaSession.setActionHandler('seekforward', details => { + console.log('seekforward') + setProgress(Math.min(getCurrentTime() + details.seekOffset, getDuration())) + }) + navigator.mediaSession.setActionHandler('seekto', details => { + console.log('seekto', details.seekTime) + let time = Math.min(details.seekTime, getDuration()) + time = Math.max(time, 0) + setProgress(time) + }) + navigator.mediaSession.setActionHandler('previoustrack', () => { + console.log('previoustrack') + playPrev() + }) + navigator.mediaSession.setActionHandler('nexttrack', () => { + console.log('nexttrack') + playNext() + }) + // navigator.mediaSession.setActionHandler('skipad', () => { + // console.log('') + // }) + // } + + window.eventHub.on(player.player_loadeddata, updatePositionState) + window.eventHub.on(player.player_canplay, updatePositionState) + window.eventHub.on(player.play, handlePlay) + window.eventHub.on(player.pause, handlePause) + window.eventHub.on(player.stop, handleStop) + window.eventHub.on(player.error, handlePause) + window.eventHub.on(player.setPlayInfo, handleSetPlayInfo) + window.eventHub.on(player.updatePic, updateMediaSessionInfo) + + onBeforeUnmount(() => { + window.eventHub.off(player.player_loadeddata, updatePositionState) + window.eventHub.off(player.player_canplay, updatePositionState) + window.eventHub.off(player.play, handlePlay) + window.eventHub.off(player.pause, handlePause) + window.eventHub.off(player.stop, handleStop) + window.eventHub.off(player.error, handlePause) + window.eventHub.off(player.setPlayInfo, handleSetPlayInfo) + window.eventHub.off(player.updatePic, updateMediaSessionInfo) + }) +} diff --git a/src/renderer/core/useApp/usePlayer/usePlayEvent.js b/src/renderer/core/useApp/usePlayer/usePlayEvent.js new file mode 100644 index 00000000..363d78a5 --- /dev/null +++ b/src/renderer/core/useApp/usePlayer/usePlayEvent.js @@ -0,0 +1,114 @@ +import { onBeforeUnmount, useI18n } from '@renderer/utils/vueTools' +import { player as eventPlayerNames } from '@renderer/event/names' +import { wait, waitCancel } from '@renderer/utils/tools' +import { musicInfo, musicInfoItem, playMusicInfo } from '@renderer/core/share/player' + +export default ({ + playNext, + setStatus, + setUrl, +}) => { + const { t } = useI18n() + let retryNum = 0 + + let loadingTimeout + let delayNextTimeout + const startLoadingTimeout = () => { + // console.log('start load timeout') + clearLoadingTimeout() + loadingTimeout = Math.random() + wait(30000, loadingTimeout).then(playNext).catch(_ => _) + } + const clearLoadingTimeout = () => { + if (!loadingTimeout) return + // console.log('clear load timeout') + waitCancel(loadingTimeout) + loadingTimeout = null + } + + const clearDelayNextTimeout = () => { + // console.log(this.delayNextTimeout) + if (!delayNextTimeout) return + waitCancel(delayNextTimeout) + delayNextTimeout = null + } + const addDelayNextTimeout = () => { + clearDelayNextTimeout() + delayNextTimeout = Math.random() + wait(5000, delayNextTimeout).then(playNext).catch(_ => _) + } + + const handleLoadstart = () => { + startLoadingTimeout() + const status = t('player__loading') + setStatus(status, status) + } + + const handleLoadeddata = () => { + const status = t('player__loading') + setStatus(status, status) + } + + const handleCanplay = () => { + const status = '' + setStatus(status, status) + } + + const handlePlaying = () => { + clearLoadingTimeout() + } + + const handleEmpied = () => { + clearDelayNextTimeout() + clearLoadingTimeout() + } + + const handleWating = () => { + const status = t('player__buffering') + setStatus(status, status) + } + + const handleError = errCode => { + if (!musicInfo.songmid) return + clearLoadingTimeout() + if (playMusicInfo.listId != 'download' && errCode !== 1 && retryNum < 2) { // 若音频URL无效则尝试刷新2次URL + // console.log(this.retryNum) + retryNum++ + setUrl(musicInfoItem.value, true) + const status = t('player__refresh_url') + setStatus(status, status) + return + } + + const status = t('player__error') + setStatus(status, status) + addDelayNextTimeout() + } + + const handleSetPlayInfo = () => { + retryNum = 0 + clearDelayNextTimeout() + clearLoadingTimeout() + } + + + window.eventHub.on(eventPlayerNames.player_loadstart, handleLoadstart) + window.eventHub.on(eventPlayerNames.player_loadeddata, handleLoadeddata) + window.eventHub.on(eventPlayerNames.player_canplay, handleCanplay) + window.eventHub.on(eventPlayerNames.player_playing, handlePlaying) + window.eventHub.on(eventPlayerNames.player_waiting, handleWating) + window.eventHub.on(eventPlayerNames.player_emptied, handleEmpied) + window.eventHub.on(eventPlayerNames.error, handleError) + window.eventHub.on(eventPlayerNames.setPlayInfo, handleSetPlayInfo) + + onBeforeUnmount(() => { + window.eventHub.off(eventPlayerNames.player_loadstart, handleLoadstart) + window.eventHub.off(eventPlayerNames.player_loadeddata, handleLoadeddata) + window.eventHub.off(eventPlayerNames.player_canplay, handleCanplay) + window.eventHub.off(eventPlayerNames.player_playing, handlePlaying) + window.eventHub.off(eventPlayerNames.player_waiting, handleWating) + window.eventHub.off(eventPlayerNames.player_emptied, handleEmpied) + window.eventHub.off(eventPlayerNames.error, handleError) + window.eventHub.off(eventPlayerNames.setPlayInfo, handleSetPlayInfo) + }) +} diff --git a/src/renderer/core/useApp/usePlayer/usePlayProgress.js b/src/renderer/core/useApp/usePlayer/usePlayProgress.js new file mode 100644 index 00000000..c52ea94d --- /dev/null +++ b/src/renderer/core/useApp/usePlayer/usePlayProgress.js @@ -0,0 +1,204 @@ +import { onBeforeUnmount, watch, useCommit } from '@renderer/utils/vueTools' +import { formatPlayTime2, getRandom, throttle } from '@renderer/utils' +import { setTaskBarProgress, wait, waitCancel, savePlayInfo } from '@renderer/utils/tools' +import { player as eventPlayerNames } from '@renderer/event/names' +import { onTimeupdate, getCurrentTime, getDuration, setCurrentTime, onVisibilityChange } from '@renderer/plugins/player' +import { playProgress, setNowPlayTime, setMaxplayTime } from '@renderer/core/share/playProgress' +import { musicInfo, musicInfoItem, playMusicInfo, playInfo } from '@renderer/core/share/player' +// import { getList } from '@renderer/core/share/utils' + +const delaySavePlayInfo = throttle(savePlayInfo, 2000) + +export default ({ setting, playNext }) => { + let restorePlayTime = 0 + let prevProgressStatus = 'none' + const mediaBuffer = { + timeout: null, + playTime: 0, + } + + const updateMusicInfo = useCommit('list', 'updateMusicInfo') + + const startBuffering = () => { + console.log('start t') + if (mediaBuffer.timeout) return + mediaBuffer.timeout = Math.random() + wait(3000, mediaBuffer.timeout).then(() => { + mediaBuffer.timeout = null + const currentTime = getCurrentTime() + if (!mediaBuffer.playTime) mediaBuffer.playTime = currentTime + let skipTime = currentTime + getRandom(3, 6) + if (skipTime > playProgress.maxPlayTime) skipTime = (playProgress.maxPlayTime - currentTime) / 2 + if (skipTime - mediaBuffer.playTime < 1 || playProgress.maxPlayTime - skipTime < 1) { + mediaBuffer.playTime = 0 + playNext() + return + } + startBuffering() + setCurrentTime(skipTime) + console.log(mediaBuffer.playTime) + console.log(currentTime) + }).catch(_ => _) + } + const clearBufferTimeout = () => { + console.log('clear t') + if (!mediaBuffer.timeout) return + waitCancel(mediaBuffer.timeout) + mediaBuffer.timeout = null + mediaBuffer.playTime = 0 + } + + const setProgress = (time) => { + if (!musicInfo.songmid) return + restorePlayTime = time + if (mediaBuffer.playTime) { + clearBufferTimeout() + mediaBuffer.playTime = time + startBuffering() + } + setNowPlayTime(time) + setCurrentTime(time) + + // if (!isPlay) audio.play() + } + + const handlePlay = () => { + prevProgressStatus = 'normal' + setTaskBarProgress(playProgress.progress, prevProgressStatus) + } + const handlePause = () => { + prevProgressStatus = 'paused' + setTaskBarProgress(playProgress.progress, prevProgressStatus) + clearBufferTimeout() + } + + const handleStop = () => { + setNowPlayTime(0) + setMaxplayTime(0) + prevProgressStatus = 'none' + setTaskBarProgress(playProgress.progress, prevProgressStatus) + } + + const handleError = () => { + if (!restorePlayTime) restorePlayTime = getCurrentTime() // 记录出错的播放时间 + prevProgressStatus = 'error' + setTaskBarProgress(playProgress.progress, prevProgressStatus) + } + + const handleLoadeddata = () => { + setMaxplayTime(getDuration()) + + if (restorePlayTime) { + setCurrentTime(restorePlayTime) + restorePlayTime = 0 + } + + if (!musicInfoItem.value.interval && playMusicInfo.listId != 'download') { + updateMusicInfo({ + listId: playMusicInfo.listId, + id: musicInfo.songmid, + musicInfo: musicInfoItem.value, + data: { interval: formatPlayTime2(playProgress.maxPlayTime) }, + }) + } + } + + const handleCanplay = () => { + if (mediaBuffer.playTime) { + let playTime = mediaBuffer.playTime + mediaBuffer.playTime = 0 + setCurrentTime(playTime) + } + } + const handleWating = () => { + startBuffering() + } + + const handleEmpied = () => { + mediaBuffer.playTime = 0 + clearBufferTimeout() + } + + const handleSetPlayInfo = ({ musicInfo, progress }) => { + setNowPlayTime(restorePlayTime = progress.time) + setMaxplayTime(progress.maxTime) + handlePause() + if (!playMusicInfo.isTempPlay) { + delaySavePlayInfo({ + time: playProgress.nowPlayTime, + maxTime: playProgress.maxPlayTime, + listId: playMusicInfo.listId, + index: playInfo.playIndex, + }) + } + } + + watch(() => playProgress.progress, (newValue, oldValue) => { + if (newValue.toFixed(2) === oldValue.toFixed(2)) return + setTaskBarProgress(newValue, prevProgressStatus) + }) + watch(() => playProgress.nowPlayTime, (newValue, oldValue) => { + if (Math.abs(newValue - oldValue) > 2) window.eventHub.emit(eventPlayerNames.activeTransition) + if (setting.value.player.isSavePlayTime && !playMusicInfo.isTempPlay) { + delaySavePlayInfo({ + time: newValue, + maxTime: playProgress.maxPlayTime, + listId: playMusicInfo.listId, + index: playInfo.playIndex, + }) + } + }) + watch(() => playProgress.maxPlayTime, maxPlayTime => { + if (!playMusicInfo.isTempPlay) { + delaySavePlayInfo({ + time: playProgress.nowPlayTime, + maxTime: maxPlayTime, + listId: playMusicInfo.listId, + index: playInfo.playIndex, + }) + } + }) + + window.eventHub.on(eventPlayerNames.play, handlePlay) + window.eventHub.on(eventPlayerNames.pause, handlePause) + window.eventHub.on(eventPlayerNames.stop, handleStop) + window.eventHub.on(eventPlayerNames.error, handleError) + window.eventHub.on(eventPlayerNames.setProgress, setProgress) + // window.eventHub.on(eventPlayerNames.restorePlay, handleRestorePlay) + window.eventHub.on(eventPlayerNames.player_loadeddata, handleLoadeddata) + window.eventHub.on(eventPlayerNames.player_canplay, handleCanplay) + window.eventHub.on(eventPlayerNames.player_waiting, handleWating) + window.eventHub.on(eventPlayerNames.player_emptied, handleEmpied) + window.eventHub.on(eventPlayerNames.setPlayInfo, handleSetPlayInfo) + + const rOnTimeupdate = onTimeupdate(() => { + setNowPlayTime(getCurrentTime()) + }) + + let currentPlayTime = 0 + const rVisibilityChange = onVisibilityChange(() => { + if (document.hidden) { + currentPlayTime = playProgress.nowPlayTime + } else { + if (Math.abs(playProgress.nowPlayTime - currentPlayTime) > 2) { + window.eventHub.emit(eventPlayerNames.activeTransition) + } + } + }) + + onBeforeUnmount(() => { + rOnTimeupdate() + rVisibilityChange() + window.eventHub.off(eventPlayerNames.play, handlePlay) + window.eventHub.off(eventPlayerNames.pause, handlePause) + window.eventHub.off(eventPlayerNames.stop, handleStop) + window.eventHub.off(eventPlayerNames.error, handleError) + window.eventHub.off(eventPlayerNames.setProgress, setProgress) + // window.eventHub.off(eventPlayerNames.restorePlay, handleRestorePlay) + window.eventHub.off(eventPlayerNames.player_loadeddata, handleLoadeddata) + window.eventHub.off(eventPlayerNames.player_canplay, handleCanplay) + window.eventHub.off(eventPlayerNames.player_waiting, handleWating) + window.eventHub.off(eventPlayerNames.player_emptied, handleEmpied) + window.eventHub.off(eventPlayerNames.setPlayInfo, handleSetPlayInfo) + }) +} diff --git a/src/renderer/core/useApp/usePlayer/usePlayer.js b/src/renderer/core/useApp/usePlayer/usePlayer.js new file mode 100644 index 00000000..e23bd0ce --- /dev/null +++ b/src/renderer/core/useApp/usePlayer/usePlayer.js @@ -0,0 +1,366 @@ +import { onBeforeUnmount, useI18n, watch, useCommit, useAction } from '@renderer/utils/vueTools' +import { setTitle, checkPath } from '@renderer/utils' +import { langS2T, wait, waitCancel } from '@renderer/utils/tools' + +import { + setStop as setPlayerStop, + setPause as setPlayerPause, + setPlay as setPlayerPlay, + setResource, + isEmpty as isPlayerEmpty, + setLoopPlay, +} from '@renderer/plugins/player' + +import { player as eventKeyPlayerNames } from '@common/hotKey' +import { player as eventPlayerNames } from '@renderer/event/names' + +import useMediaSessionInfo from './useMediaSessionInfo' +import usePlayProgress from './usePlayProgress' +import usePlayEvent from './usePlayEvent' +import { requestMsg } from '@renderer/utils/message' + +import { + isPlay, + setPlay, + setStatus, + setStatusText, + setAllStatus, + musicInfo, + setMusicInfo, + musicInfoItem, + playMusicInfo, + playInfo, + setPlayList, + setPlayMusicInfo, + playedList, +} from '@renderer/core/share/player' +import { downloadList } from '@renderer/core/share/download' +import { qualityList } from '@renderer/core/share' +import { getList } from '@renderer/core/share/utils' + +import path from 'path' +import useLyric from './useLyric' +import useVolume from './useVolume' +import useWatchList from './useWatchList' + + +const getPlayType = (highQuality, songInfo) => { + let type = '128k' + let list = qualityList.value[songInfo.source] + if (highQuality && songInfo._types['320k'] && list && list.includes('320k')) type = '320k' + return type +} + +const useDelayNextTimeout = ({ playNext }) => { + let delayNextTimeout + const clearDelayNextTimeout = () => { + // console.log(this.delayNextTimeout) + if (delayNextTimeout) { + waitCancel(delayNextTimeout) + delayNextTimeout = null + } + } + + const addDelayNextTimeout = () => { + clearDelayNextTimeout() + delayNextTimeout = Math.random() + wait(5000, delayNextTimeout).then(() => { + delayNextTimeout = null + playNext() + }).catch(_ => _) + } + + return { + clearDelayNextTimeout, + addDelayNextTimeout, + } +} + +export default ({ setting }) => { + const { t } = useI18n() + + const playNext = useAction('player', 'playNext') + const playPrev = useAction('player', 'playPrev') + const getUrl = useAction('player', 'getUrl') + const getPic = useAction('player', 'getPic') + const getLrc = useAction('player', 'getLrc') + + const clearPlayedList = useCommit('player', 'clearPlayedList') + const setPlayedList = useCommit('player', 'setPlayedList') + + const setUrl = (targetSong, isRefresh, isRetryed = false) => { + let type = getPlayType(setting.value.player.highQuality, targetSong) + // this.musicInfo.url = await getMusicUrl(targetSong, type) + setAllStatus(t('player__geting_url')) + + return getUrl({ + musicInfo: targetSong, + type, + isRefresh, + onToggleSource() { + setAllStatus('Try toggle source...') + }, + }).then(url => { + if (targetSong !== musicInfoItem.value || isPlay.value) return + setMusicInfo({ url }) + setResource(url) + }).catch(err => { + // console.log('err', err.message) + if (targetSong !== musicInfoItem.value || isPlay.value) return + if (err.message == requestMsg.cancelRequest) return + if (!isRetryed) return setUrl(targetSong, isRefresh, true) + setAllStatus(err.message) + addDelayNextTimeout() + return Promise.reject(err) + }) + } + const setImg = ({ listId, musicInfo: targetSong }) => { + if (targetSong.img) { + setMusicInfo({ img: targetSong.img }) + } else { + getPic({ musicInfo: targetSong, listId }).then(() => { + if (targetSong.songmid !== musicInfo.songmid) return + setMusicInfo({ img: targetSong.img }) + + window.eventHub.emit(eventPlayerNames.updatePic, musicInfo) + }) + } + } + const setLrc = (targetSong) => { + getLrc(targetSong).then(({ lyric, tlyric, lxlyric }) => { + if (targetSong.songmid !== musicInfo.songmid) return + return ( + global.i18n.locale == 'zh-tw' + ? Promise.all([ + lyric ? langS2T(lyric) : Promise.resolve(''), + tlyric ? langS2T(tlyric) : Promise.resolve(''), + lxlyric ? langS2T(lxlyric) : Promise.resolve(''), + ]) + : Promise.resolve([lyric, tlyric, lxlyric]) + ).then(([lyric, tlyric, lxlyric]) => { + setMusicInfo({ + lrc: lyric, + tlrc: tlyric, + lxlrc: lxlyric, + }) + }) + }).catch((err) => { + console.log(err) + if (targetSong.songmid !== musicInfo.songmid) return + setAllStatus(t('lyric__load_error')) + }).finally(() => { + if (targetSong.songmid !== musicInfo.songmid) return + window.eventHub.emit(eventPlayerNames.updateLyric, musicInfo) + }) + } + const { addDelayNextTimeout, clearDelayNextTimeout } = useDelayNextTimeout({ playNext }) + usePlayProgress({ setting, playNext }) + useMediaSessionInfo({ playPrev, playNext }) + usePlayEvent({ + playNext, + setStatus, + setUrl, + }) + useLyric({ + setting, + }) + useVolume({ setting }) + useWatchList({ playNext }) + + // 恢复上次播放的状态 + const handleRestorePlay = async(restorePlayInfo) => { + if (!restorePlayInfo.listId) return + const list = getList(restorePlayInfo.listId) + setPlayList(restorePlayInfo.listId) + setPlayMusicInfo(restorePlayInfo.listId, list[restorePlayInfo.index]) + + window.eventHub.emit(eventPlayerNames.setPlayInfo, { + musicInfo: musicInfoItem.value, + progress: { + time: restorePlayInfo.time || 0, + maxTime: restorePlayInfo.maxTime || 0, + }, + }) + + setImg(playMusicInfo) + setLrc(musicInfoItem.value) + if (setting.value.player.togglePlayMethod == 'random') setPlayedList({ ...playMusicInfo }) + } + + const setPlayStatus = () => { + setPlay(true) + setTitle(`${musicInfo.name} - ${musicInfo.singer}`) + } + const setPauseStatus = () => { + setPlay(false) + setTitle() + } + const setStopStatus = () => { + setPlay(false) + setTitle() + setStatus('^-^') + setStatusText('') + setMusicInfo({ + songmid: null, + img: null, + lrc: null, + tlrc: null, + lxlrc: null, + url: null, + name: '', + singer: '', + album: '', + }) + } + + // 播放音乐 + const playMusic = async() => { + // console.log('playMusic') + setStopStatus() + if (window.restorePlayInfo) { + handleRestorePlay(window.restorePlayInfo) + window.restorePlayInfo = null + return + } + + if (playInfo.musicInfo) { + setPlayerStop() + window.eventHub.emit(eventPlayerNames.pause) + setStopStatus() + } else { + window.eventHub.emit(eventPlayerNames.setStop) + } + + clearDelayNextTimeout() + + let targetSong = playMusicInfo.musicInfo + + if (setting.value.player.togglePlayMethod == 'random' && !playMusicInfo.isTempPlay) setPlayedList({ ...playMusicInfo }) + + if (targetSong.key) { // 如果是已下载的歌曲 + const filePath = path.join(setting.value.download.savePath, targetSong.metadata.fileName) + // console.log(filePath) + + if (!await checkPath(filePath) || !targetSong.isComplate || /\.ape$/.test(filePath)) { + return getList(playMusicInfo.listId).length == 1 ? null : playNext() + } + window.eventHub.emit(eventPlayerNames.setPlayInfo, { + musicInfo: musicInfoItem.value, + progress: { + time: 0, + maxTime: 0, + }, + }) + setResource(filePath) + // console.log(filePath) + } else { + // if (!this.assertApiSupport(targetSong.source)) return this.playNext() + window.eventHub.emit(eventPlayerNames.setPlayInfo, { + musicInfo: musicInfoItem.value, + progress: { + time: 0, + maxTime: 0, + }, + }) + setUrl(musicInfoItem.value) + } + + setImg({ musicInfo: musicInfoItem.value, listId: playMusicInfo.listId }) + setLrc(musicInfoItem.value) + } + + const handleSetPlayInfo = ({ musicInfo }) => { + setMusicInfo({ + songmid: musicInfo.songmid, + singer: musicInfo.singer, + name: musicInfo.name, + album: musicInfo.albumName, + }) + } + + const handelStop = () => { + setPlayerStop() + window.eventHub.emit(eventPlayerNames.stop) + } + + const handleEnded = () => { + setAllStatus(t('player__end')) + playNext() + } + + // 播放、暂停播放切换 + const handleTogglePlay = async() => { + if (isPlayerEmpty()) { + if (playMusicInfo.listId == 'download') { + const musicInfo = playMusicInfo.musicInfo + const filePath = path.join(setting.value.download.savePath, musicInfo.metadata.fileName) + if (!await checkPath(filePath) || !musicInfo.isComplate || /\.ape$/.test(filePath)) { + if (downloadList.length == 1) { + window.eventHub.emit(eventPlayerNames.setStop) + } else { + playNext() + } + return + } + setResource(filePath) + } else { + // if (!this.assertApiSupport(this.targetSong.source)) return this.playNext() + setUrl(musicInfoItem.value) + } + return + } + if (isPlay.value) { + setPlayerPause() + } else { + setPlayerPlay() + } + } + + watch(() => setting.value.player.togglePlayMethod, newValue => { + setLoopPlay(newValue === 'singleLoop') + if (playedList.length) clearPlayedList() + if (newValue == 'random' && playMusicInfo.musicInfo && !playMusicInfo.isTempPlay) setPlayedList({ ...playMusicInfo }) + }) + + setLoopPlay(setting.value.player.togglePlayMethod === 'singleLoop') + + + window.eventHub.on(eventKeyPlayerNames.next.action, playNext) + window.eventHub.on(eventKeyPlayerNames.prev.action, playPrev) + window.eventHub.on(eventKeyPlayerNames.toggle_play.action, handleTogglePlay) + + window.eventHub.on(eventPlayerNames.play, setPlayStatus) + window.eventHub.on(eventPlayerNames.pause, setPauseStatus) + window.eventHub.on(eventPlayerNames.error, setPauseStatus) + window.eventHub.on(eventPlayerNames.stop, setStopStatus) + + window.eventHub.on(eventPlayerNames.playMusic, playMusic) + window.eventHub.on(eventPlayerNames.setTogglePlay, handleTogglePlay) + window.eventHub.on(eventPlayerNames.setPlayPrev, playPrev) + window.eventHub.on(eventPlayerNames.setPlayNext, playNext) + window.eventHub.on(eventPlayerNames.setPlayInfo, handleSetPlayInfo) + window.eventHub.on(eventPlayerNames.setStop, handelStop) + + window.eventHub.on(eventPlayerNames.player_ended, handleEnded) + + + onBeforeUnmount(() => { + window.eventHub.off(eventKeyPlayerNames.next.action, playNext) + window.eventHub.off(eventKeyPlayerNames.prev.action, playPrev) + window.eventHub.off(eventKeyPlayerNames.toggle_play.action, handleTogglePlay) + + window.eventHub.off(eventPlayerNames.play, setPlayStatus) + window.eventHub.off(eventPlayerNames.pause, setPauseStatus) + window.eventHub.off(eventPlayerNames.error, setPauseStatus) + window.eventHub.off(eventPlayerNames.stop, setStopStatus) + + window.eventHub.off(eventPlayerNames.playMusic, playMusic) + window.eventHub.off(eventPlayerNames.setTogglePlay, handleTogglePlay) + window.eventHub.off(eventPlayerNames.setPlayPrev, playPrev) + window.eventHub.off(eventPlayerNames.setPlayNext, playNext) + window.eventHub.off(eventPlayerNames.setPlayInfo, handleSetPlayInfo) + window.eventHub.off(eventPlayerNames.setStop, handelStop) + + window.eventHub.off(eventPlayerNames.player_ended, handleEnded) + }) +} diff --git a/src/renderer/core/useApp/usePlayer/usePlayerEvent.js b/src/renderer/core/useApp/usePlayer/usePlayerEvent.js new file mode 100644 index 00000000..7bece9ef --- /dev/null +++ b/src/renderer/core/useApp/usePlayer/usePlayerEvent.js @@ -0,0 +1,73 @@ +import { onBeforeUnmount } from '@renderer/utils/vueTools' +import { + onPlaying, + onPause, + onEnded, + onError, + onLoadeddata, + onLoadstart, + onCanplay, + onEmptied, + onWaiting, + getErrorCode, +} from '@renderer/plugins/player' +import { player } from '@renderer/event/names' + + +export default () => { + const rOnPlaying = onPlaying(() => { + // console.log('onPlaying') + window.eventHub.emit(player.player_playing) + window.eventHub.emit(player.play) + }) + const rOnPause = onPause(() => { + // console.log('onPause') + window.eventHub.emit(player.player_pause) + window.eventHub.emit(player.pause) + }) + const rOnEnded = onEnded(() => { + // console.log('onEnded') + window.eventHub.emit(player.player_ended) + window.eventHub.emit(player.pause) + }) + const rOnError = onError(() => { + // console.log('onError') + window.eventHub.emit(player.player_error, getErrorCode()) + window.eventHub.emit(player.error) + }) + const rOnLoadeddata = onLoadeddata(() => { + // console.log('onLoadeddata') + window.eventHub.emit(player.player_loadeddata) + }) + const rOnLoadstart = onLoadstart(() => { + // console.log('onLoadstart') + window.eventHub.emit(player.player_loadstart) + }) + const rOnCanplay = onCanplay(() => { + // console.log('onCanplay') + window.eventHub.emit(player.player_canplay) + }) + const rOnEmptied = onEmptied(() => { + // console.log('onEmptied') + window.eventHub.emit(player.player_emptied) + // window.eventHub.emit(player.stop) + }) + const rOnWaiting = onWaiting(() => { + // console.log('onWaiting') + window.eventHub.emit(player.player_waiting) + window.eventHub.emit(player.pause) + }) + + + onBeforeUnmount(() => { + rOnPlaying() + rOnPause() + rOnEnded() + rOnError() + rOnLoadeddata() + rOnLoadstart() + rOnCanplay() + rOnEmptied() + rOnWaiting() + }) +} diff --git a/src/renderer/core/useApp/usePlayer/useVolume.js b/src/renderer/core/useApp/usePlayer/useVolume.js new file mode 100644 index 00000000..05310850 --- /dev/null +++ b/src/renderer/core/useApp/usePlayer/useVolume.js @@ -0,0 +1,63 @@ +import { onBeforeUnmount, watch, useCommit } from '@renderer/utils/vueTools' +import { setVolume as setPlayerVolume, setMute as setPlayerMute } from '@renderer/plugins/player' + +import { debounce } from '@renderer/utils' +import { player as eventKeyPlayerNames } from '@common/hotKey' +// import { player as eventPlayerNames } from '@renderer/event/names' +import { volume, isMute, setMute, setVolume } from '@renderer/core/share/volume' + +export default ({ setting }) => { + const saveVolume = useCommit('setVolume') + + const handleSaveVolume = debounce(saveVolume, 300) + + const handleToggleVolumeMute = (_isMute) => { + let muteStatus = _isMute ?? !isMute.value + saveVolume(muteStatus) + setMute(muteStatus) + } + + const handleSetVolume = num => { + const _volume = num < 0 ? 0 : num > 1 ? 1 : num + setVolume(_volume) + } + + const handleSetVolumeUp = (step = 0.02) => { + handleSetVolume(volume.value + step) + } + const handleSetVolumeDown = (step = 0.02) => { + handleSetVolume(volume.value - step) + } + + watch(volume, _volume => { + handleSaveVolume(_volume) + setPlayerVolume(_volume) + }) + watch(isMute, mute => { + handleSaveVolume(mute) + setPlayerMute(mute) + }) + watch(() => setting.value.player.volume, _volume => { + setVolume(_volume) + }) + watch(() => setting.value.player.isMute, muteStatus => { + setMute(muteStatus) + }) + + setVolume(setting.value.player.volume) + setMute(setting.value.player.isMute) + + window.eventHub.on(eventKeyPlayerNames.volume_up.action, handleSetVolumeUp) + window.eventHub.on(eventKeyPlayerNames.volume_down.action, handleSetVolumeDown) + window.eventHub.on(eventKeyPlayerNames.volume_mute.action, handleToggleVolumeMute) + // window.eventHub.on(eventPlayerNames.setVolume, handleSetVolume) + // window.eventHub.on(eventPlayerNames.toggleMute, handleToggleVolumeMute) + + onBeforeUnmount(() => { + window.eventHub.off(eventKeyPlayerNames.volume_up.action, handleSetVolumeUp) + window.eventHub.off(eventKeyPlayerNames.volume_down.action, handleSetVolumeDown) + window.eventHub.off(eventKeyPlayerNames.volume_mute.action, handleToggleVolumeMute) + // window.eventHub.off(eventPlayerNames.setVolume, handleSetVolume) + // window.eventHub.off(eventPlayerNames.toggleMute, handleToggleVolumeMute) + }) +} diff --git a/src/renderer/core/useApp/usePlayer/useWatchList.js b/src/renderer/core/useApp/usePlayer/useWatchList.js new file mode 100644 index 00000000..28491a24 --- /dev/null +++ b/src/renderer/core/useApp/usePlayer/useWatchList.js @@ -0,0 +1,25 @@ +import { onBeforeUnmount } from '@renderer/utils/vueTools' + +import { player as eventPlayerNames, list as eventListNames } from '@renderer/event/names' +import { playInfo, playMusicInfo, updatePlayIndex } from '@renderer/core/share/player' +import { getList } from '@renderer/core/share/utils' + +export default ({ playNext }) => { + const handleListChange = listIds => { + if (!listIds.includes(playInfo.playListId) && !listIds.includes(playMusicInfo.listId)) return + const { playIndex } = updatePlayIndex() + if (playIndex < 0 && !playMusicInfo.isTempPlay) { // 歌曲被移除 + if (getList(playMusicInfo.listId).length) { + playNext() + } else { + window.eventHub.emit(eventPlayerNames.setStop) + } + } + } + + window.eventHub.on(eventListNames.listChange, handleListChange) + + onBeforeUnmount(() => { + window.eventHub.off(eventListNames.listChange, handleListChange) + }) +} diff --git a/src/renderer/core/useApp/useSaveData.js b/src/renderer/core/useApp/useSaveData.js new file mode 100644 index 00000000..85751fb4 --- /dev/null +++ b/src/renderer/core/useApp/useSaveData.js @@ -0,0 +1,127 @@ +import { throttle, saveSetting } from '@renderer/utils' +import { saveMyList, saveDownloadList, saveSearchHistoryList, setUserApi } from '@renderer/utils/tools' +import music from '@renderer/utils/music' + +import { useCommit, watch, toRaw, onBeforeUnmount } from '@renderer/utils/vueTools' + +import { apiSource, qualityList, userApi, proxy } from '@renderer/core/share' +import { allList, defaultList, loveList, userLists, tempList } from '@renderer/core/share/list' +import { downloadList } from '@renderer/core/share/download' + +import { list as eventListNames, download as eventDownloadNames } from '@renderer/event/names' + +const listThrottle = (fn, delay = 100) => { + let timer = null + let _data = {} + return function(data) { + Object.assign(_data, data) + if (timer) return + timer = setTimeout(() => { + timer = null + fn.call(this, _data) + _data = {} + }, delay) + } +} +const saveMyListThrottle = listThrottle(data => saveMyList(data), 300) +const saveDownloadListThrottle = throttle(list => saveDownloadList(list), 1000) +const saveSearchHistoryListThrottle = throttle(list => saveSearchHistoryList(list), 500) + +export default ({ + setting, + searchHistoryList, +}) => { + watch(setting, (newValue, oldValue) => { + saveSetting(toRaw(newValue)) + global.appSetting = newValue + }, { deep: true }) + + // watch(defaultList, (newValue, oldValue) => { + // saveMyListThrottle({ defaultList: toRaw(newValue) }) + // }, { deep: true }) + // watch(loveList, (newValue, oldValue) => { + // saveMyListThrottle({ loveList: toRaw(newValue) }) + // }, { deep: true }) + // watch(userList, (newValue, oldValue) => { + // saveMyListThrottle({ userList: toRaw(newValue) }) + // }, { deep: true }) + + // watch(downloadList, (newValue, oldValue) => { + // saveDownloadListThrottle(window.downloadListFull) + // }, { deep: true }) + + watch(searchHistoryList, (newValue, oldValue) => { + saveSearchHistoryListThrottle(toRaw(newValue)) + }, { deep: true }) + + + const setApiSource = useCommit('setApiSource') + watch(apiSource, (apiSource, oldValue) => { + if (/^user_api/.test(apiSource)) { + qualityList.value = {} + userApi.status = false + userApi.message = 'initing' + } else { + qualityList.value = music.supportQuality[apiSource] + } + if (oldValue === null) return + + setUserApi(apiSource) + + if (apiSource != setting.value.apiSource) { + setApiSource(apiSource) + } + }) + + const setProxyEnable = useCommit('setProxyEnable') + watch(() => proxy.enable, (enable, oldValue) => { + if (enable != setting.value.network.proxy.enable) setProxyEnable(enable) + }) + + const handelListChange = ids => { + const lists = {} + let isUserList = false + for (const id of ids) { + switch (id) { + case defaultList.id: + lists.defaultList = { ...toRaw(defaultList), list: toRaw(allList[id]) } + break + case loveList.id: + lists.loveList = { ...toRaw(loveList), list: toRaw(allList[id]) } + break + case tempList.id: + lists.tempList = { ...toRaw(tempList), list: toRaw(allList[id]) } + break + default: + isUserList = true + } + } + if (isUserList) { + lists.userList = userLists.map(l => ({ ...toRaw(l), list: toRaw(allList[l.id]) })) + } + + saveMyListThrottle(lists) + } + const handelSaveList = ({ id }) => { + handelListChange([id]) + } + + const handelDownloadListChange = () => { + saveDownloadListThrottle(toRaw(downloadList)) + } + + window.eventHub.on(eventListNames.listChange, handelListChange) + window.eventHub.on(eventListNames.musicInfoChange, handelSaveList) + window.eventHub.on(eventDownloadNames.listChange, handelDownloadListChange) + + onBeforeUnmount(() => { + window.eventHub.off(eventListNames.listChange, handelListChange) + window.eventHub.off(eventListNames.musicInfoChange, handelSaveList) + window.eventHub.off(eventDownloadNames.listChange, handelDownloadListChange) + }) + + return { + saveMyListThrottle, + saveSearchHistoryListThrottle, + } +} diff --git a/src/renderer/core/useApp/useSync.js b/src/renderer/core/useApp/useSync.js new file mode 100644 index 00000000..b5076714 --- /dev/null +++ b/src/renderer/core/useApp/useSync.js @@ -0,0 +1,128 @@ +import { useCommit, useGetter, onBeforeUnmount } from '@renderer/utils/vueTools' +import { sync as eventSyncName } from '@renderer/event/names' +import { syncEnable, onSyncStatus } from '@renderer/utils/tools' +import { sync } from '@renderer/core/share' + +export default () => { + const setList = useCommit('setList') + const listAdd = useCommit('listAdd') + const listMove = useCommit('listMove') + const listAddMultiple = useCommit('listAddMultiple') + const listMoveMultiple = useCommit('listMoveMultiple') + const listRemove = useCommit('listRemove') + const listRemoveMultiple = useCommit('listRemoveMultiple') + const listClear = useCommit('listClear') + const updateMusicInfo = useCommit('updateMusicInfo') + const createUserList = useCommit('createUserList') + const removeUserList = useCommit('removeUserList') + const setUserListName = useCommit('setUserListName') + const moveupUserList = useCommit('moveupUserList') + const movedownUserList = useCommit('movedownUserList') + const setMusicPosition = useCommit('setMusicPosition') + const setSyncListData = useCommit('setSyncListData') + + const setting = useGetter('setting') + + const handleSyncAction = ({ action, data }) => { + if (typeof data == 'object') data.isSync = true + // console.log(action, data) + + switch (action) { + case 'set_list': + setList(data) + break + case 'list_add': + listAdd(data) + break + case 'list_move': + listMove(data) + break + case 'list_add_multiple': + listAddMultiple(data) + break + case 'list_move_multiple': + listMoveMultiple(data) + break + case 'list_remove': + listRemove(data) + break + case 'list_remove_multiple': + listRemoveMultiple(data) + break + case 'list_clear': + listClear(data) + break + case 'update_music_info': + updateMusicInfo(data) + break + case 'create_user_list': + createUserList(data) + break + case 'remove_user_list': + removeUserList(data) + break + case 'set_user_list_name': + setUserListName(data) + break + case 'moveup_user_list': + moveupUserList(data) + break + case 'movedown_user_list': + movedownUserList(data) + break + case 'set_music_position': + setMusicPosition(data) + break + default: + break + } + } + + const handleSyncList = ({ action, data }) => { + switch (action) { + case 'getData': + global.eventHub.emit(eventSyncName.send_sync_list, { + action: 'getData', + data: { + defaultList: this.defaultList, + loveList: this.loveList, + userList: this.userList, + }, + }) + break + case 'setData': + setSyncListData(data) + break + case 'selectMode': + sync.deviceName = data.deviceName + sync.isShowSyncMode = true + break + case 'closeSelectMode': + sync.isShowSyncMode = false + break + } + } + + window.eventHub.on(eventSyncName.handle_action_list, handleSyncAction) + window.eventHub.on(eventSyncName.handle_sync_list, handleSyncList) + + const rSyncStatus = onSyncStatus((event, status) => { + sync.status.status = status.status + sync.status.message = status.message + sync.status.address = status.address + sync.status.code = status.code + sync.status.devices = status.devices + }) + + if (setting.sync.enable && setting.sync.port) { + syncEnable({ + enable: setting.sync.enable, + port: setting.sync.port, + }) + } + + + onBeforeUnmount(() => { + rSyncStatus() + }) +} diff --git a/src/renderer/core/useApp/useUpdate.js b/src/renderer/core/useApp/useUpdate.js new file mode 100644 index 00000000..0ce37648 --- /dev/null +++ b/src/renderer/core/useApp/useUpdate.js @@ -0,0 +1,106 @@ +import { useAction, nextTick, onBeforeUnmount } from '@renderer/utils/vueTools' +import { onUpdateAvailable, onUpdateDownloaded, onUpdateError, onUpdateNotAvailable, onUpdateProgress } from '@renderer/utils/tools' +import { compareVer } from '@renderer/utils' +import { versionInfo } from '@renderer/core/share' + +export default (setting) => { + const getVersionInfo = useAction('getVersionInfo') + + // 更新超时定时器 + let updateTimeout = setTimeout(() => { + updateTimeout = null + versionInfo.isTimeOut = true + nextTick(() => { + showUpdateModal() + }) + }, 60 * 30 * 1000) + + const clearUpdateTimeout = () => { + if (!updateTimeout) return + clearTimeout(updateTimeout) + updateTimeout = null + } + + const showUpdateModal = () => { + (versionInfo.newVersion && versionInfo.newVersion.history + ? Promise.resolve(versionInfo.newVersion) + : getVersionInfo().then(body => { + versionInfo.newVersion = body + return body + }) + ).catch(() => { + if (versionInfo.newVersion) return versionInfo.newVersion + versionInfo.isUnknow = true + let result = { + version: '0.0.0', + desc: null, + } + versionInfo.newVersion = result + return result + }).then(result => { + if (result.version == '0.0.0') { + versionInfo.isUnknow = true + versionInfo.showModal = true + return + } + if (compareVer(versionInfo.version, result.version) != -1) { + versionInfo.isLatestVer = true + return + } + + if (result.version === setting.value.ignoreVersion) return + // console.log(this.version) + nextTick(() => { + versionInfo.showModal = true + }) + }) + } + + const rUpdateAvailable = onUpdateAvailable((event, info) => { + versionInfo.isDownloading = true + getVersionInfo().catch(() => ({ + version: info.version, + desc: info.releaseNotes, + })).then(body => { + // console.log(body) + versionInfo.newVersion = body + nextTick(() => { + versionInfo.showModal = true + }) + }) + }) + const rUpdateNotAvailable = onUpdateNotAvailable((event, info) => { + clearUpdateTimeout() + versionInfo.newVersion = { + version: info.version, + desc: info.releaseNotes, + } + versionInfo.isLatestVer = true + }) + const rUpdateError = onUpdateError((event, info) => { + clearUpdateTimeout() + versionInfo.isError = true + nextTick(() => { + showUpdateModal() + }) + }) + const rUpdateProgress = onUpdateProgress((event, progress) => { + versionInfo.downloadProgress = progress + }) + const rUpdateDownloaded = onUpdateDownloaded((event, info) => { + clearUpdateTimeout() + versionInfo.isDownloaded = true + nextTick(() => { + showUpdateModal() + }) + }) + + onBeforeUnmount(() => { + clearUpdateTimeout() + rUpdateAvailable() + rUpdateNotAvailable() + rUpdateError() + rUpdateProgress() + rUpdateDownloaded() + }) +} diff --git a/src/renderer/event/index.js b/src/renderer/event/index.js index efb3622a..865737e8 100644 --- a/src/renderer/event/index.js +++ b/src/renderer/event/index.js @@ -1,10 +1,11 @@ -import Vue from 'vue' +import mitt from 'mitt' import keyBind from '../utils/keyBind' import { rendererOn, rendererSend, NAMES, rendererInvoke } from '../../common/ipc' import { base as baseName, sync as syncName } from './names' import { common as hotKeyNamesCommon } from '../../common/hotKey' +import { sync } from '@renderer/core/share' -const eventHub = window.eventHub = new Vue() +const eventHub = window.eventHub = mitt() window.isEditingHotKey = false let appHotKeyConfig = { @@ -18,10 +19,10 @@ rendererInvoke(NAMES.mainWindow.get_hot_key).then(({ local, global }) => { } }) -eventHub.$on(baseName.bindKey, () => { +eventHub.on(baseName.bindKey, () => { keyBind.bindKey((key, type, event, keys) => { // console.log(`key_${key}_${type}`) - eventHub.$emit(baseName.key_down, { event, keys, key, type }) + eventHub.emit(baseName.key_down, { event, keys, key, type }) // console.log(event, key) if (!window.isEditingHotKey && appHotKeyConfig.local.enable && appHotKeyConfig.local.keys[key]) { if (type == 'up') return @@ -29,18 +30,18 @@ eventHub.$on(baseName.bindKey, () => { // 软件内快捷键的最小化触发时 // 如果已启用托盘,则隐藏程序,否则最小化程序 https://github.com/lyswhut/lx-music-desktop/issues/603 if (appHotKeyConfig.local.keys[key].action == hotKeyNamesCommon.min.action && global.appSetting.tray.isToTray) { - eventHub.$emit(hotKeyNamesCommon.hide_toggle.action) + eventHub.emit(hotKeyNamesCommon.hide_toggle.action) return } - eventHub.$emit(appHotKeyConfig.local.keys[key].action) + eventHub.emit(appHotKeyConfig.local.keys[key].action) return } - eventHub.$emit(`key_${key}_${type}`, { event, keys, key, type }) + eventHub.emit(`key_${key}_${type}`, { event, keys, key, type }) }) registerCommonEvents() }) -eventHub.$on(baseName.unbindKey, () => { +eventHub.on(baseName.unbindKey, () => { keyBind.unbindKey() unregisterCommonEvents() }) @@ -49,31 +50,31 @@ const registerMin = () => rendererSend(NAMES.mainWindow.min) const registerMinToggle = () => rendererSend(NAMES.mainWindow.min_toggle) const registerHideToggle = () => rendererSend(NAMES.mainWindow.hide_toggle) -eventHub.$on(baseName.min, registerMin) -eventHub.$on(baseName.max, () => rendererSend(NAMES.mainWindow.max)) -eventHub.$on(baseName.close, () => rendererSend(NAMES.mainWindow.close)) +eventHub.on(baseName.min, registerMin) +eventHub.on(baseName.max, () => rendererSend(NAMES.mainWindow.max)) +eventHub.on(baseName.close, () => rendererSend(NAMES.mainWindow.close)) const registerCommonEvents = () => { - eventHub.$on(hotKeyNamesCommon.close.action, registerQuit) - eventHub.$on(hotKeyNamesCommon.min.action, registerMin) - eventHub.$on(hotKeyNamesCommon.min_toggle.action, registerMinToggle) - eventHub.$on(hotKeyNamesCommon.hide_toggle.action, registerHideToggle) + eventHub.on(hotKeyNamesCommon.close.action, registerQuit) + eventHub.on(hotKeyNamesCommon.min.action, registerMin) + eventHub.on(hotKeyNamesCommon.min_toggle.action, registerMinToggle) + eventHub.on(hotKeyNamesCommon.hide_toggle.action, registerHideToggle) } const unregisterCommonEvents = () => { - eventHub.$off(hotKeyNamesCommon.close.action, registerQuit) - eventHub.$off(hotKeyNamesCommon.min.action, registerMin) - eventHub.$off(hotKeyNamesCommon.min_toggle.action, registerMinToggle) - eventHub.$off(hotKeyNamesCommon.hide_toggle.action, registerHideToggle) + eventHub.off(hotKeyNamesCommon.close.action, registerQuit) + eventHub.off(hotKeyNamesCommon.min.action, registerMin) + eventHub.off(hotKeyNamesCommon.min_toggle.action, registerMinToggle) + eventHub.off(hotKeyNamesCommon.hide_toggle.action, registerHideToggle) } rendererOn(NAMES.mainWindow.focus, () => { keyBind.clearDownKeys() - eventHub.$emit(baseName.focus) + eventHub.emit(baseName.focus) }) rendererOn(NAMES.mainWindow.key_down, (event, { type, key }) => { // console.log(appHotKeyConfig.global.keys[key]) if (appHotKeyConfig.global.keys[key]) { - window.eventHub.$emit(appHotKeyConfig.global.keys[key].action) + window.eventHub.emit(appHotKeyConfig.global.keys[key].action) } }) rendererOn(NAMES.mainWindow.set_hot_key_config, (event, config) => { @@ -83,20 +84,20 @@ rendererOn(NAMES.mainWindow.set_hot_key_config, (event, config) => { for (const type of Object.keys(config)) { window.appHotKeyConfig[type] = config[type] } - window.eventHub.$emit(baseName.set_hot_key_config, config) + window.eventHub.emit(baseName.set_hot_key_config, config) }) rendererOn(NAMES.mainWindow.sync_action_list, (event, { action, data }) => { - window.eventHub.$emit(syncName.handle_action_list, { action, data }) + window.eventHub.emit(syncName.handle_action_list, { action, data }) }) -eventHub.$on(syncName.send_action_list, ({ action, data }) => { - if (!window.globalObj.sync.enable) return +eventHub.on(syncName.send_action_list, ({ action, data }) => { + if (!sync.enable) return rendererSend(NAMES.mainWindow.sync_action_list, { action, data }) }) rendererOn(NAMES.mainWindow.sync_list, (event, { action, data }) => { - window.eventHub.$emit(syncName.handle_sync_list, { action, data }) + window.eventHub.emit(syncName.handle_sync_list, { action, data }) }) -eventHub.$on(syncName.send_sync_list, ({ action, data }) => { - if (!window.globalObj.sync.enable) return +eventHub.on(syncName.send_sync_list, ({ action, data }) => { + if (!sync.enable) return rendererSend(NAMES.mainWindow.sync_list, { action, data }) }) diff --git a/src/renderer/event/names.js b/src/renderer/event/names.js index 7ce8c475..909fca95 100644 --- a/src/renderer/event/names.js +++ b/src/renderer/event/names.js @@ -10,6 +10,49 @@ const names = { set_config: 'set_config', set_hot_key_config: 'set_hot_key_config', }, + player: { + playMusic: 'playMusic', + setTogglePlay: 'setTogglePlay', + setProgress: 'setProgress', + play: 'play', + setPlay: 'setPlay', + pause: 'pause', + setPause: 'setPause', + stop: 'stop', + setStop: 'setStop', + error: 'error', + setPlayPrev: 'setPlayPrev', + setPlayNext: 'setPlayNext', + + updatePic: 'updatePic', + updateLyric: 'updateLyric', + setPlayInfo: 'setPlayInfo', + + activeTransition: 'activeTransition', + + setVolume: 'setVolume', + toggleMute: 'toggleMute', + volume: 'volume', + + player_playing: 'player_playing', + player_pause: 'player_pause', + player_stop: 'player_stop', + player_ended: 'player_ended', + player_error: 'player_error', + player_loadeddata: 'player_loadeddata', + player_loadstart: 'player_loadstart', + player_canplay: 'player_canplay', + player_emptied: 'player_emptied', + player_waiting: 'player_waiting', + }, + list: { + saveMyList: 'saveMyList', + listChange: 'listChange', + musicInfoChange: 'musicInfoChange', + }, + download: { + listChange: 'listChange', + }, sync: { send_action_list: 'send_action_list', handle_action_list: 'handle_action_list', @@ -26,4 +69,7 @@ for (const item of Object.keys(names)) { } export const base = names.base +export const player = names.player +export const list = names.list +export const download = names.download export const sync = names.sync diff --git a/src/renderer/index.html b/src/renderer/index.html new file mode 100644 index 00000000..ab9d8531 --- /dev/null +++ b/src/renderer/index.html @@ -0,0 +1,24 @@ + + + + + + 洛雪音乐助手 + + + + + + + + diff --git a/src/renderer/index.pug b/src/renderer/index.pug deleted file mode 100644 index f06ec597..00000000 --- a/src/renderer/index.pug +++ /dev/null @@ -1,15 +0,0 @@ -html(lang="cn") - head - meta(charset="UTF-8") - meta(name="viewport" content="width=device-width, initial-scale=1.0") - meta(http-equiv="X-UA-Compatible" content="ie=edge") - title 洛雪音乐助手 - - body - #root - //- if htmlWebpackPlugin.options.isProd - //- script. - //- window.__static = '!{require('path').join(htmlWebpackPlugin.options.__dirname, '/static').replace(/\\/g, '\\\\')}' - //- if !htmlWebpackPlugin.options.browser && htmlWebpackPlugin.options.isProd - script. - window.__static = require('path').join(__dirname, '/resources').replace(/\\/g, '\\\\') diff --git a/src/renderer/lang/en-us/base.json b/src/renderer/lang/en-us/base.json deleted file mode 100644 index b40621d8..00000000 --- a/src/renderer/lang/en-us/base.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "cancel_button_text": "Cancel", - "confirm_button_text": "OK", - "date_format_hour": "{num} hours ago", - "date_format_minute": "{num} minutes ago", - "date_format_second": "{num} seconds ago" -} diff --git a/src/renderer/lang/en-us/core/aside.json b/src/renderer/lang/en-us/core/aside.json deleted file mode 100644 index 30d86e45..00000000 --- a/src/renderer/lang/en-us/core/aside.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "search": "Search", - "song_list": "Playlists", - "leaderboard": "Charts", - "my_list": "Your Library", - "download": "Downloads", - "setting": "Settings" -} diff --git a/src/renderer/lang/en-us/core/player.json b/src/renderer/lang/en-us/core/player.json deleted file mode 100644 index 5823f168..00000000 --- a/src/renderer/lang/en-us/core/player.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "add_music_to": "Add the current song to...", - "album": "Album: ", - "buffering": "Buffering...", - "comment_hot_load_error": "Hot comments failed to load, click to try to reload", - "comment_hot_loading": "Hot comments are loading", - "comment_hot_title": "Hot Comment", - "comment_new_load_error": "The latest comment failed to load, click to try to reload", - "comment_new_loading": "Latest comments are loading", - "comment_new_title": "Latest comment", - "comment_no_content": "No comments yet", - "comment_refresh": "Refresh comments", - "comment_show": "Song comments", - "comment_title": "{name} comment", - "copy_title": " (Click to copy)", - "desktop_lyric_lock": "Right click to lock lyrics", - "desktop_lyric_off": "Close Desktop Lyrics", - "desktop_lyric_on": "Open Desktop Lyrics", - "desktop_lyric_unlock": "Right click to unlock lyrics", - "end": "Stopped", - "error": "Error loading music. Switch to next song after 5 seconds", - "geting_url": "Getting music link...", - "hide_detail": "Hide detail page (Right-click in the view to quickly hide the details page)", - "loading": "Music loading...", - "lyric_error": "Failed to get lyrics", - "lyric_select": "Lyric text selection", - "name": "Name: ", - "next": "Next", - "pause": "Pause", - "pic_tip": "Right click to locate the currently playing song in \"My List\"", - "play": "Play", - "play_toggle_mode_list": "Play in order", - "play_toggle_mode_list_loop": "List Loop", - "play_toggle_mode_off": "Disable", - "play_toggle_mode_random": "List Random", - "play_toggle_mode_single_loop": "Single Loop", - "playing": "Now playing...", - "prev": "Prev", - "refresh_url": "Music URL expired, refreshing...", - "singer": "Artist: ", - "stop": "Paused", - "volume": "Volume: " -} diff --git a/src/renderer/lang/en-us/core/toolbar.json b/src/renderer/lang/en-us/core/toolbar.json deleted file mode 100644 index fdfd9acb..00000000 --- a/src/renderer/lang/en-us/core/toolbar.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "min": "Minimize", - "close": "Close" -} diff --git a/src/renderer/lang/en-us/desktopLyric.json b/src/renderer/lang/en-us/desktopLyric.json deleted file mode 100644 index 54dd8ebf..00000000 --- a/src/renderer/lang/en-us/desktopLyric.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "close": "Close", - "back": "Back", - "lock": "Lock Lyrics", - "unlock": "Unlock Lyrics", - "theme": "Theme Color", - "font_increase": "Increase font size (Right click to fine-tune)", - "font_decrease": "Reduce font size (Right click to fine-tune)", - "opactiy_increase": "Decrease Transparency (Right click to fine-tune)", - "opactiy_decrease": "Increase Transparency (Right click to fine-tune)", - "lrc_active_zoom_on": "Zoom the currently playing lyrics", - "lrc_active_zoom_off": "Unzoom the currently playing lyrics", - "win_top_on": "Top lyrics interface", - "win_top_off": "Cancel the top lyrics interface" -} diff --git a/src/renderer/lang/en-us/material/download_modal.json b/src/renderer/lang/en-us/material/download_modal.json deleted file mode 100644 index 141e4ff7..00000000 --- a/src/renderer/lang/en-us/material/download_modal.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "btn_tip": "The audio quality is not available", - "lossless": "Lossless", - "high_quality": "High Quality", - "normal": "Normal" -} diff --git a/src/renderer/lang/en-us/material/download_multiple_modal.json b/src/renderer/lang/en-us/material/download_multiple_modal.json deleted file mode 100644 index ade41005..00000000 --- a/src/renderer/lang/en-us/material/download_multiple_modal.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "tip": "{len} song selected", - "tip2": "Select preferred download quality", - "normal": "Normal", - "high_quality": "High Quality", - "lossless": "Lossless" -} diff --git a/src/renderer/lang/en-us/material/flow_btn.json b/src/renderer/lang/en-us/material/flow_btn.json deleted file mode 100644 index 2d7dadbd..00000000 --- a/src/renderer/lang/en-us/material/flow_btn.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "play": "Play", - "add": "Add", - "download": "Download", - "start": "Start", - "pause": "Pause", - "remove": "Remove" -} diff --git a/src/renderer/lang/en-us/material/list_add_modal.json b/src/renderer/lang/en-us/material/list_add_modal.json deleted file mode 100644 index 18bf5f0e..00000000 --- a/src/renderer/lang/en-us/material/list_add_modal.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "title_first_add": "Add", - "title_first_move": "Move", - "title_last": "to...", - "btn_title": "Add the song(s) to {name}" -} diff --git a/src/renderer/lang/en-us/material/list_add_multiple_modal.json b/src/renderer/lang/en-us/material/list_add_multiple_modal.json deleted file mode 100644 index 52b578b9..00000000 --- a/src/renderer/lang/en-us/material/list_add_multiple_modal.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "title_add": "Add the selected {num} song(s) to ...", - "title_move": "Move the selected {num} song(s) to ...", - "btn_title": "Add these song(s) to {name}" -} diff --git a/src/renderer/lang/en-us/material/list_buttons.json b/src/renderer/lang/en-us/material/list_buttons.json deleted file mode 100644 index 9ec42288..00000000 --- a/src/renderer/lang/en-us/material/list_buttons.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "play": "Play", - "add_to": "Add to...", - "download": "Download", - "add": "Add", - "start": "Start", - "pause": "Pause", - "file": "Locate the file", - "search": "Search", - "remove": "Remove" -} diff --git a/src/renderer/lang/en-us/material/list_sort_modal.json b/src/renderer/lang/en-us/material/list_sort_modal.json deleted file mode 100644 index c767e46e..00000000 --- a/src/renderer/lang/en-us/material/list_sort_modal.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "title": "Adjust the position of {name} to:", - "title_multiple": "Adjust the position of the selected {num} songs to:", - "input_tip": "Please input which position you want to adjust to", - "btn_confirm": "Confirm" -} diff --git a/src/renderer/lang/en-us/material/pagination.json b/src/renderer/lang/en-us/material/pagination.json deleted file mode 100644 index 25e9a07d..00000000 --- a/src/renderer/lang/en-us/material/pagination.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "prev": "Previous page", - "next": "Next page", - "page": "Page {num}" -} diff --git a/src/renderer/lang/en-us/material/song_list.json b/src/renderer/lang/en-us/material/song_list.json deleted file mode 100644 index d0b3ba23..00000000 --- a/src/renderer/lang/en-us/material/song_list.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "list_play": "Play", - "list_play_later": "Play later", - "list_add_to": "Add to ...", - "list_download": "Download", - "list_search": "Search", - "list_source_detail": "Song Page", - "name": "Name", - "singer": "Artist", - "album": "Album", - "action": "Manage", - "time": "Length", - "lossless": "Lossless", - "high_quality": "High Quality", - "loding_list": "Loading the list..." -} diff --git a/src/renderer/lang/en-us/material/sync_mode_modal.json b/src/renderer/lang/en-us/material/sync_mode_modal.json deleted file mode 100644 index 77d3b00d..00000000 --- a/src/renderer/lang/en-us/material/sync_mode_modal.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "merge_btn_local_remote": "Local list merge remote list", - "merge_btn_remote_local": "Remote list merge local list", - "merge_label": "Merge", - "merge_tip": "Merge:", - "merge_tip_desc": "Merge the two lists together, the same song will be removed (the song of the merged person is removed), and different songs will be added.", - "other_label": "Other", - "other_tip": "Other: ", - "other_tip_desc": "\"Only use real-time synchronization function\" will not modify the lists of both parties, only real-time synchronization operations; \"Cancel synchronization\" will directly disconnect the two parties.", - "overwrite": "Full coverage", - "overwrite_btn_cancel": "Cancel sync", - "overwrite_btn_local_remote": "Local list Overwrite remote list", - "overwrite_btn_none": "Only use real-time synchronization", - "overwrite_btn_remote_local": "Remote list Overwrite local list", - "overwrite_label": "Cover", - "overwrite_tip": "Cover: ", - "overwrite_tip_desc": "The list with the same ID of the covered person and the covered list will be deleted and replaced with the list of the covered person (lists with different list IDs will be merged together). If you check Complete coverage, all lists of the covered person will be moved. \nDivide, and then replace with a list of overriders.", - "title": "Choose how to synchronize the list with {name}" -} diff --git a/src/renderer/lang/en-us/material/tag_list.json b/src/renderer/lang/en-us/material/tag_list.json deleted file mode 100644 index 6116c814..00000000 --- a/src/renderer/lang/en-us/material/tag_list.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "default": "Default" -} diff --git a/src/renderer/lang/en-us/material/user_api_modal.json b/src/renderer/lang/en-us/material/user_api_modal.json deleted file mode 100644 index 79c983ec..00000000 --- a/src/renderer/lang/en-us/material/user_api_modal.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "title": "Custom Source Management", - "readme": "Source writing instructions: ", - "note": "Tip: Although we have isolated the script's running environment as much as possible, importing scripts containing malicious behaviors may still affect your system. Please import them carefully.", - "btn_remove": "Remove", - "btn_import": "Import", - "btn_export": "Export", - "import_file": "Select music API script file", - "noitem": "There is nothing here...😲" -} diff --git a/src/renderer/lang/en-us/material/xm_verify_modal.json b/src/renderer/lang/en-us/material/xm_verify_modal.json deleted file mode 100644 index d19f82aa..00000000 --- a/src/renderer/lang/en-us/material/xm_verify_modal.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "title": "Xiami music verify" -} diff --git a/src/renderer/lang/en-us/store/state.json b/src/renderer/lang/en-us/store/state.json deleted file mode 100644 index 44e7fc10..00000000 --- a/src/renderer/lang/en-us/store/state.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "theme_green": "Green", - "theme_blue": "Blue", - "theme_yellow": "Yellow", - "theme_orange": "Orange", - "theme_red": "Red", - "theme_pink": "Pink", - "theme_purple": "Purple", - "theme_grey": "Grey", - "theme_ming": "Ming", - "theme_blue2": "Purple Blue", - "theme_black": "Black", - "theme_mid_autumn": "Mid-Autumn", - "theme_naruto": "Naruto", - "theme_happy_new_year": "New Year", - - "source_kw": "Kuwo", - "source_kg": "Kugou", - "source_tx": "Tencent", - "source_wy": "Netease", - "source_mg": "Migu", - "source_xm": "Xiami", - "source_bd": "Baidu", - "source_all": "Aggregated", - - "source_alias_kw": "KW Music", - "source_alias_kg": "KG Music", - "source_alias_tx": "TX Music", - "source_alias_wy": "WY Music", - "source_alias_mg": "MG Music", - "source_alias_xm": "XM Music", - "source_alias_bd": "BD Music", - - "source_alias_all": "Aggregated", - - "load_list_file_error_title": "Error loading playlist data", - "load_list_file_error_detail": "We have helped you back up the old list file to {path}\nIt is stored in JSON format, you can try to repair and restore it manually\n\nError details: {detail}" -} diff --git a/src/renderer/lang/en-us/view/download.json b/src/renderer/lang/en-us/view/download.json deleted file mode 100644 index 1fd22e42..00000000 --- a/src/renderer/lang/en-us/view/download.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "menu_play": "Play", - "menu_play_later": "Play later", - "menu_start": "Start task", - "menu_pause": "Pause Task", - "menu_file": "Locate File", - "menu_search": "Search", - "menu_remove": "Remove", - "menu_source_detail": "Song Page", - "name": "Name", - "progress": "Progress", - "status": "State", - "quality": "Quality", - "action": "Manage", - "all": "All", - "runing": "Downloading", - "paused": "Paused", - "error": "Error", - "finished": "Completed" -} diff --git a/src/renderer/lang/en-us/view/list.json b/src/renderer/lang/en-us/view/list.json deleted file mode 100644 index 167cd50d..00000000 --- a/src/renderer/lang/en-us/view/list.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "action": "Manage", - "album": "Album", - "default_list": "Recently Played", - "list_add_to": "Add to ...", - "list_copy_name": "Copy name", - "list_download": "Download", - "list_move_to": "Move to ...", - "list_play": "Play", - "list_play_later": "Play later", - "list_remove": "Remove", - "list_search": "Search", - "list_sort": "Adjust position", - "list_source_detail": "Song Page", - "lists_duplicate": "Duplicate song", - "lists_export": "Export", - "lists_export_part_desc": "Choose where to save the list file", - "lists_import": "Import", - "lists_import_part_button_cancel": "No", - "lists_import_part_button_confirm": "Overwrite", - "lists_import_part_confirm": "The imported list ({importName}) has the same ID as the local list ({localName}). Do you overwrite the local list?", - "lists_import_part_desc": "Select list file", - "lists_movedown": "Move Down", - "lists_moveup": "Move Up", - "lists_new_list_btn": "Create list", - "lists_new_list_input": "New list...", - "lists_remove": "Remove", - "lists_remove_tip": "Do you really want to remove {name}?", - "lists_remove_tip_button": "Yes, that's right", - "lists_rename": "Rename", - "lists_sync": "Update", - "loding_list": "Loading...", - "love_list": "Favorites", - "name": "Name", - "no_item": "Nothing's here...", - "singer": "Artist", - "time": "Length" -} diff --git a/src/renderer/lang/en-us/view/search.json b/src/renderer/lang/en-us/view/search.json deleted file mode 100644 index 655d1d4c..00000000 --- a/src/renderer/lang/en-us/view/search.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "list_play": "Play", - "list_play_later": "Play later", - "list_add_to": "Add to ...", - "list_download": "Download", - "list_source_detail": "Song Page", - "name": "Name", - "singer": "Artist", - "album": "Album", - "action": "Manage", - "time": "Length", - "lossless": "SQ", - "high_quality": "HQ", - "loding_list": "Loading...", - "no_item": "Search what I want to 😉", - "hot_search": "Top Searches", - "history_search": "History Searches", - "history_clear": "Clear History", - "history_remove": "Right click to remove this entry" -} diff --git a/src/renderer/lang/en-us/view/setting.json b/src/renderer/lang/en-us/view/setting.json deleted file mode 100644 index b648ae86..00000000 --- a/src/renderer/lang/en-us/view/setting.json +++ /dev/null @@ -1,157 +0,0 @@ -{ - "about": "About lx-music-desktop", - "backup": "Backup and restore", - "backup_all": "All data (list data and setting data)", - "backup_all_export": "Export", - "backup_all_export_desc": "Select the backup to...", - "backup_all_import": "Import", - "backup_all_import_desc": "Select a backup file", - "backup_part": "Partial data (list data includes audition list, favorite list, user-defined list, setting data does not include shortcut key settings)", - "backup_part_export_list": "Export lists", - "backup_part_export_list_desc": "Save the list to...", - "backup_part_export_setting": "Export settings", - "backup_part_export_setting_desc": "Save the list to...", - "backup_part_import_list": "Import lists", - "backup_part_import_list_desc": "Select a list backup", - "backup_part_import_setting": "Import settings", - "backup_part_import_setting_desc": "Select the Settings file", - "basic": "General", - "basic_animation": "Random pop-up animation", - "basic_control_btn_position": "Control Button Position", - "basic_control_btn_position_left": "Left", - "basic_control_btn_position_right": "Right", - "basic_lang": "Language", - "basic_lang_title": "The language displayed in the software", - "basic_show_animation": "Show switching animation", - "basic_source": "Music source", - "basic_source_status_failed": "Initialization failed", - "basic_source_status_initing": "Initializing", - "basic_source_status_success": "Initialization successful", - "basic_source_temp": "Temporary API (some features not available; workaround if Test API unavailable)", - "basic_source_test": "Test API (Available for most software features)", - "basic_source_title": "Choose a music source", - "basic_source_user_api_btn": "Custom Source Management", - "basic_sourcename": "Source name", - "basic_sourcename_alias": "Aliases", - "basic_sourcename_real": "Original", - "basic_sourcename_title": "Select the name of music source", - "basic_theme": "Theme", - "basic_to_tray": "Do not exit the software when closing the software and minimize it to the system tray", - "basic_window_size": "Window size", - "basic_window_size_big": "Large", - "basic_window_size_huge": "Huge", - "basic_window_size_larger": "Larger", - "basic_window_size_medium": "Medium", - "basic_window_size_oversized": "Oversized", - "basic_window_size_small": "Small", - "basic_window_size_smaller": "Smaller", - "basic_window_size_title": "Set the window size", - "click_copy": "Click to copy", - "click_open": "Click to open", - "desktop_lyric": "Desktop Lyric Settings", - "desktop_lyric_always_on_top": "Make the lyrics always above other windows", - "desktop_lyric_enable": "Display lyrics", - "desktop_lyric_font": "Lyric font", - "desktop_lyric_font_default": "Default", - "desktop_lyric_lock": "Lock lyrics", - "desktop_lyric_lock_screen": "It is not allowed to drag the lyrics window out of the main screen", - "download": "Download", - "download_data_embed": "Whether to embed the following content in the audio file", - "download_embed_lyric": "Embedding lyric", - "download_embed_pic": "Embedding cover", - "download_enable": "Whether to enable download function", - "download_lyric": "Lyrics download", - "download_lyric_format": "Downloaded lyrics file encoding format", - "download_lyric_format_gbk": "GBK (Try to select this format when Chinese garbled characters appear on some devices)", - "download_lyric_format_utf8": "UTF-8", - "download_lyric_title": "Select whether to download the lyrics file", - "download_name": "Music file naming", - "download_name1": "Title - Artist", - "download_name2": "Artist - Title", - "download_name3": "Title only", - "download_name_title": "Select the music naming method for downloading", - "download_path": "Download path", - "download_path_change_btn": "Change", - "download_path_label": "Current: ", - "download_path_open_label": "Click to open this path", - "download_path_title": "Define the path to downloading", - "download_select_save_path": "Select the save path", - "download_use_other_source": "Automatically change the source to download (when the song cannot be downloaded from the original source, try to switch to another source to download. Note: this function does not 100% guarantee that the version of the song after the source is changed is the same as the original version)", - "hot_key": "Shortcut Key Settings", - "hot_key_common_focus_search_input": "Focus Search Box", - "hot_key_common_min": "Minimize the program", - "hot_key_common_toggle_close": "Quit Program", - "hot_key_common_toggle_hide": "Show/Hide Program", - "hot_key_common_toggle_min": "Minimize/Restore Program", - "hot_key_desktop_lyric_toggle_always_top": "Top Desktop Lyrics Switch", - "hot_key_desktop_lyric_toggle_lock": "Desktop Lyric Lock Switch", - "hot_key_desktop_lyric_toggle_visible": "Turn on/off desktop lyrics", - "hot_key_global_title": "Global Shortcut Key", - "hot_key_local_title": "Shortcut Keys in Software", - "hot_key_player_next": "Next Song", - "hot_key_player_prev": "Previous Song", - "hot_key_player_toggle_play": "Play/Pause Control", - "hot_key_player_volume_down": "Reduce Volume", - "hot_key_player_volume_mute": "Mute Switch", - "hot_key_player_volume_up": "Increase Volume", - "hot_key_tip_input": "Please enter a new key", - "hot_key_unset_input": "Not Set", - "is_enable": "Enabled", - "is_show": "Showed", - "list": "List", - "list_add_music_location_type": "Position when adding a song to the list", - "list_add_music_location_type_bottom": "Bottom", - "list_add_music_location_type_top": "Top", - "list_scroll": "Remember the position of the scroll bar of the playlist (only valid for my music classification)", - "list_source": "Show song source (only valid for my music category)", - "network": "Network", - "network_proxy_host": "Host", - "network_proxy_password": "Password", - "network_proxy_port": "Port", - "network_proxy_title": "HTTP Proxy (False setting would block Internet connections)", - "network_proxy_username": "Username", - "odc": "Auto clear", - "odc_clear_search_input": "Clear the search box when you are not searching", - "odc_clear_search_list": "Clear the search list when you are not searching", - "other": "Extras", - "other_play_list_cache": "List cache management (links to songs that have been cached in my list, alternative sources for playback, after cleaning up, you need to re-acquire them when you play and download songs, and do not clean up if there are no issues related to song playback)", - "other_play_list_cache_clear_btn": "Clear list cache information", - "other_resource_cache": "Resource cache management (pictures, audios and other caches, pictures and other resources will need to be downloaded again after cleaning up, it is not recommended to clean up, the software will dynamically manage the cache size according to the disk space)", - "other_resource_cache_clear_btn": "Clear resource cache", - "other_resource_cache_label": "The software has used cache size: ", - "other_tray_theme": "Tray Icon Style", - "other_tray_theme_black": "Black Color", - "other_tray_theme_native": "Solid Color", - "other_tray_theme_origin": "Primary Color", - "play": "Play", - "play_lyric_lxlrc": "Use Karaoke-style lyrics playback (if supported)", - "play_lyric_transition": "Show lyrics translation", - "play_mediaDevice": "Audio output", - "play_mediaDevice_remove_stop_play": "Pause the song when the current sound output device is changed", - "play_mediaDevice_title": "Select a media device for audio output", - "play_quality": "Play 320K quality songs first (if supported)", - "play_save_play_time": "Remember playback progress", - "play_task_bar": "Show playing progress on the taskbar", - "search": "Search", - "search_focus_search_box": "Automatically focus the search box on startup", - "search_history": "Search history", - "search_hot": "Top Searches", - "sync": "Data synchronization [This is a test function, it is recommended to back up the playlist before using it for the first time]", - "sync_address": "Synchronization service address: {address}", - "sync_auth_code": "Connection code: {code}", - "sync_device": "Connected devices: {devices}", - "sync_enable": "Enable the synchronization function (because the data is transmitted in clear text, please use it under a trusted network)", - "sync_port": "Sync port settings", - "sync_port_tip": "Please enter the synchronization service port number", - "sync_refresh_code": "Refresh the connection code", - "update": "Update", - "update_checking": "Checking for updates...", - "update_current_label": "Current version: ", - "update_downloading": "Update is found and being downloaded...⏳", - "update_init": "Processing update...", - "update_latest": "The software is up-to-date, enjoy yourself!🥂", - "update_latest_label": "Latest version: ", - "update_open_version_modal_btn": "Open the update window🚀", - "update_progress": "Download progress: ", - "update_unknown": "Unknown" -} diff --git a/src/renderer/lang/en-us/view/song_list.json b/src/renderer/lang/en-us/view/song_list.json deleted file mode 100644 index 55512767..00000000 --- a/src/renderer/lang/en-us/view/song_list.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "back": "Back", - "loding_list": "Loading the list...", - "loding_list_fail": "List loading failed", - "open_list": "open {name} playlist", - "input_text": "Enter songlist link or songlist ID", - "tip_1": "Cross-source playlists are not supported, please confirm whether the playlist to be opened corresponds to the current playlist source", - "tip_2": "If you encounter a link to a playlist that cannot be opened, welcome feedback", - "tip_3": "Kugou source does not support opening with playlist ID, but supports Kugou code opening", - "play_all": "Play", - "play_later": "Play later", - "add_all": "Collect" -} diff --git a/src/renderer/lang/index.js b/src/renderer/lang/index.js deleted file mode 100644 index 22204a90..00000000 --- a/src/renderer/lang/index.js +++ /dev/null @@ -1,32 +0,0 @@ -// http://kazupon.github.io/vue-i18n/en/messages.html - -const requireLang = require.context( - '@renderer/lang', - true, - /\.json$/, -) - -const messages = {} - -for (const file of requireLang.keys()) { - switch (file) { - case './index.js': - case './languages.json': - case './Readme.md': - continue - } - - const path = file.replace(/(\.\/|\.json$)/g, '').split('/') - - path.reduce((o, s, i) => { - if (o[s]) return o[s] - - o[s] = i + 1 === path.length - ? requireLang(file) - : {} - - return o[s] - }, messages) -} - -export default messages diff --git a/src/renderer/lang/zh-cn/base.json b/src/renderer/lang/zh-cn/base.json deleted file mode 100644 index 2df59ecb..00000000 --- a/src/renderer/lang/zh-cn/base.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "cancel_button_text": "我不", - "confirm_button_text": "好的", - "date_format_hour": "{num}小时前", - "date_format_minute": "{num}分钟前", - "date_format_second": "{num}秒前" -} diff --git a/src/renderer/lang/zh-cn/core/aside.json b/src/renderer/lang/zh-cn/core/aside.json deleted file mode 100644 index 32b709b7..00000000 --- a/src/renderer/lang/zh-cn/core/aside.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "search": "搜索", - "song_list": "歌单", - "leaderboard": "排行榜", - "my_list": "我的列表", - "download": "下载", - "setting": "设置" -} diff --git a/src/renderer/lang/zh-cn/core/player.json b/src/renderer/lang/zh-cn/core/player.json deleted file mode 100644 index 14d46fcc..00000000 --- a/src/renderer/lang/zh-cn/core/player.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "add_music_to": "添加当前歌曲到...", - "album": "专辑名:", - "buffering": "缓冲中...", - "comment_hot_load_error": "热门评论加载失败,点击尝试重新加载", - "comment_hot_loading": "热门评论加载中", - "comment_hot_title": "热门评论", - "comment_new_load_error": "最新评论加载失败,点击尝试重新加载", - "comment_new_loading": "最新评论加载中", - "comment_new_title": "最新评论", - "comment_no_content": "暂无评论", - "comment_refresh": "刷新评论", - "comment_show": "歌曲评论", - "comment_title": "{name} 的评论", - "copy_title": "(点击复制)", - "desktop_lyric_lock": "右击锁定歌词", - "desktop_lyric_off": "关闭桌面歌词", - "desktop_lyric_on": "开启桌面歌词", - "desktop_lyric_unlock": "右击解锁歌词", - "end": "播放完毕", - "error": "音频加载出错,5 秒后切换下一首", - "geting_url": "歌曲链接获取中...", - "hide_detail": "隐藏详情页(界面内右键双击可快速隐藏详情页)", - "loading": "音乐加载中...", - "lyric_error": "歌词获取失败", - "lyric_select": "歌词文本选择", - "name": "歌曲名:", - "next": "下一首", - "pause": "暂停", - "pic_tip": "右击在“我的列表”定位当前播放的歌曲", - "play": "播放", - "play_toggle_mode_list": "顺序播放", - "play_toggle_mode_list_loop": "列表循环", - "play_toggle_mode_off": "禁用", - "play_toggle_mode_random": "列表随机", - "play_toggle_mode_single_loop": "单曲循环", - "playing": "播放中...", - "prev": "上一首", - "refresh_url": "URL过期,正在刷新URL...", - "singer": "艺术家:", - "stop": "暂停播放", - "volume": "当前音量:" -} diff --git a/src/renderer/lang/zh-cn/core/toolbar.json b/src/renderer/lang/zh-cn/core/toolbar.json deleted file mode 100644 index 4b2ccef5..00000000 --- a/src/renderer/lang/zh-cn/core/toolbar.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "min": "最小化", - "close": "关闭" -} diff --git a/src/renderer/lang/zh-cn/desktopLyric.json b/src/renderer/lang/zh-cn/desktopLyric.json deleted file mode 100644 index baec633c..00000000 --- a/src/renderer/lang/zh-cn/desktopLyric.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "close": "关闭", - "back": "返回", - "lock": "锁定歌词", - "unlock": "解锁歌词", - "theme": "主题配色", - "font_increase": "增加字体大小(右击可微调)", - "font_decrease": "减小字体大小(右击可微调)", - "opactiy_increase": "减小透明度(右击可微调)", - "opactiy_decrease": "增加透明度(右击可微调)", - "lrc_active_zoom_on": "缩放当前播放的歌词", - "lrc_active_zoom_off": "取消缩放当前播放的歌词", - "win_top_on": "置顶歌词界面", - "win_top_off": "取消置顶歌词界面" -} diff --git a/src/renderer/lang/zh-cn/material/download_modal.json b/src/renderer/lang/zh-cn/material/download_modal.json deleted file mode 100644 index e101440a..00000000 --- a/src/renderer/lang/zh-cn/material/download_modal.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "btn_tip": "该音质不可用", - "lossless": "无损音质", - "high_quality": "高品音质", - "normal": "普通音质" -} diff --git a/src/renderer/lang/zh-cn/material/download_multiple_modal.json b/src/renderer/lang/zh-cn/material/download_multiple_modal.json deleted file mode 100644 index 6abdc9e6..00000000 --- a/src/renderer/lang/zh-cn/material/download_multiple_modal.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "tip": "已选择 {len} 首歌曲", - "tip2": "请选择要优先下载的音质", - "normal": "普通音质", - "high_quality": "高品音质", - "lossless": "无损音质" -} diff --git a/src/renderer/lang/zh-cn/material/flow_btn.json b/src/renderer/lang/zh-cn/material/flow_btn.json deleted file mode 100644 index d1914f4a..00000000 --- a/src/renderer/lang/zh-cn/material/flow_btn.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "play": "播放", - "add": "添加", - "download": "下载", - "start": "开始", - "pause": "暂停", - "remove": "移除" -} diff --git a/src/renderer/lang/zh-cn/material/list_add_modal.json b/src/renderer/lang/zh-cn/material/list_add_modal.json deleted file mode 100644 index 5c4d74ee..00000000 --- a/src/renderer/lang/zh-cn/material/list_add_modal.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "title_first_add": "添加", - "title_first_move": "移动", - "title_last": "到...", - "btn_title": "把该歌曲添加到 {name}" -} diff --git a/src/renderer/lang/zh-cn/material/list_add_multiple_modal.json b/src/renderer/lang/zh-cn/material/list_add_multiple_modal.json deleted file mode 100644 index afa07a8a..00000000 --- a/src/renderer/lang/zh-cn/material/list_add_multiple_modal.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "title_add": "添加已选的 {num} 首歌曲到...", - "title_move": "移动已选的 {num} 首歌曲到...", - "btn_title": "把这些歌曲添加到 {name}" -} diff --git a/src/renderer/lang/zh-cn/material/list_buttons.json b/src/renderer/lang/zh-cn/material/list_buttons.json deleted file mode 100644 index 5b44176b..00000000 --- a/src/renderer/lang/zh-cn/material/list_buttons.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "play": "播放", - "add_to": "添加到...", - "download": "下载", - "add": "添加", - "start": "开始", - "pause": "暂停", - "file": "定位文件", - "search": "搜索", - "remove": "移除" -} diff --git a/src/renderer/lang/zh-cn/material/list_sort_modal.json b/src/renderer/lang/zh-cn/material/list_sort_modal.json deleted file mode 100644 index 457585b2..00000000 --- a/src/renderer/lang/zh-cn/material/list_sort_modal.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "title": "将 {name} 的位置调整到:", - "title_multiple": "将已选的 {num} 首歌曲的位置调整到:", - "input_tip": "请输入要调整到第几个位置", - "btn_confirm": "确定" -} diff --git a/src/renderer/lang/zh-cn/material/pagination.json b/src/renderer/lang/zh-cn/material/pagination.json deleted file mode 100644 index 09991320..00000000 --- a/src/renderer/lang/zh-cn/material/pagination.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "prev": "上一页", - "next": "下一页", - "page": "第 {num} 页" -} diff --git a/src/renderer/lang/zh-cn/material/song_list.json b/src/renderer/lang/zh-cn/material/song_list.json deleted file mode 100644 index 8735c6d9..00000000 --- a/src/renderer/lang/zh-cn/material/song_list.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "list_play": "播放", - "list_play_later": "稍后播放", - "list_add_to": "添加到...", - "list_download": "下载", - "list_source_detail": "歌曲详情页", - "list_search": "搜索", - "name": "歌曲名", - "singer": "歌手", - "album": "专辑", - "action": "操作", - "time": "时长", - "lossless": "无损", - "high_quality": "高品质", - "loding_list": "列表加载中..." -} diff --git a/src/renderer/lang/zh-cn/material/sync_mode_modal.json b/src/renderer/lang/zh-cn/material/sync_mode_modal.json deleted file mode 100644 index e50ff220..00000000 --- a/src/renderer/lang/zh-cn/material/sync_mode_modal.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "merge_btn_local_remote": "本机列表 合并 远程列表", - "merge_btn_remote_local": "远程列表 合并 本机列表", - "merge_label": "合并", - "merge_tip": "合并:", - "merge_tip_desc": "将两边的列表合并到一起,相同的歌曲将被去掉(去掉的是被合并者的歌曲),不同的歌曲将被添加。", - "other_label": "其他", - "other_tip": "其他:", - "other_tip_desc": "“仅使用实时同步功能”将不修改双方的列表,仅实时同步操作;“取消同步”将直接断开双方的连接。", - "overwrite": "完全覆盖", - "overwrite_btn_cancel": "取消同步", - "overwrite_btn_local_remote": "本机列表 覆盖 远程列表", - "overwrite_btn_none": "仅使用实时同步功能", - "overwrite_btn_remote_local": "远程列表 覆盖 本机列表", - "overwrite_label": "覆盖", - "overwrite_tip": "覆盖:", - "overwrite_tip_desc": "被覆盖者与覆盖者列表ID相同的列表将被删除后替换成覆盖者的列表(列表ID不同的列表将被合并到一起),若勾选完全覆盖,则被覆盖者的所有列表将被移除,然后替换成覆盖者的列表。", - "title": "选择与 {name} 的列表同步方式" -} diff --git a/src/renderer/lang/zh-cn/material/tag_list.json b/src/renderer/lang/zh-cn/material/tag_list.json deleted file mode 100644 index 8abd2f62..00000000 --- a/src/renderer/lang/zh-cn/material/tag_list.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "default": "默认" -} diff --git a/src/renderer/lang/zh-cn/material/user_api_modal.json b/src/renderer/lang/zh-cn/material/user_api_modal.json deleted file mode 100644 index 894fb5b6..00000000 --- a/src/renderer/lang/zh-cn/material/user_api_modal.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "title": "自定义源管理", - "readme": "源编写说明:", - "note": "提示:虽然我们已经尽可能地隔离了脚本的运行环境,但导入包含恶意行为的脚本仍可能会影响你的系统,请谨慎导入。", - "btn_remove": "移除", - "btn_import": "导入", - "btn_export": "导出", - "import_file": "选择音乐API脚本文件", - "noitem": "这里竟然是空的 😲" -} diff --git a/src/renderer/lang/zh-cn/material/xm_verify_modal.json b/src/renderer/lang/zh-cn/material/xm_verify_modal.json deleted file mode 100644 index 82003b0e..00000000 --- a/src/renderer/lang/zh-cn/material/xm_verify_modal.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "title": "虾米音乐校验" -} diff --git a/src/renderer/lang/zh-cn/store/state.json b/src/renderer/lang/zh-cn/store/state.json deleted file mode 100644 index 9c338f2a..00000000 --- a/src/renderer/lang/zh-cn/store/state.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "theme_green": "绿意盎然", - "theme_blue": "蓝田生玉", - "theme_yellow": "信口雌黄", - "theme_orange": "橙黄橘绿", - "theme_red": "热情似火", - "theme_pink": "粉装玉琢", - "theme_purple": "重斤球紫", - "theme_grey": "灰常美丽", - "theme_ming": "青出于黑", - "theme_blue2": "清热版蓝", - "theme_black": "黑灯瞎火", - "theme_mid_autumn": "月里嫦娥", - "theme_naruto": "木叶之村", - "theme_happy_new_year": "新年快乐", - - "source_kw": "酷我音乐", - "source_kg": "酷狗音乐", - "source_tx": "企鹅音乐", - "source_wy": "网易音乐", - "source_mg": "咪咕音乐", - "source_xm": "虾米音乐", - "source_bd": "百度音乐", - - "source_all": "聚合搜索", - - "source_alias_kw": "小蜗音乐", - "source_alias_kg": "小枸音乐", - "source_alias_tx": "小秋音乐", - "source_alias_wy": "小芸音乐", - "source_alias_mg": "小蜜音乐", - "source_alias_xm": "小霞音乐", - "source_alias_bd": "小杜音乐", - - "source_alias_all": "聚合大会", - - "load_list_file_error_title": "播放列表数据加载错误(建议到GitHub或加群反馈)", - "load_list_file_error_detail": "我们已经帮你把旧的列表文件备份到{path}\n它以 JSON 格式存储,你可以尝试手动修复并恢复它\n\n错误详情:{detail}" -} diff --git a/src/renderer/lang/zh-cn/view/download.json b/src/renderer/lang/zh-cn/view/download.json deleted file mode 100644 index 846f3de1..00000000 --- a/src/renderer/lang/zh-cn/view/download.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "menu_play": "播放", - "menu_play_later": "稍后播放", - "menu_start": "开始任务", - "menu_pause": "暂停任务", - "menu_file": "定位文件", - "menu_search": "搜索", - "menu_remove": "删除", - "menu_source_detail": "歌曲详情页", - "name": "歌曲名", - "progress": "进度", - "status": "状态", - "quality": "品质", - "action": "操作", - "all": "全部任务", - "runing": "正在下载", - "paused": "已暂停", - "error": "出错", - "finished": "下载完成" -} diff --git a/src/renderer/lang/zh-cn/view/leaderboard.json b/src/renderer/lang/zh-cn/view/leaderboard.json deleted file mode 100644 index b14dd570..00000000 --- a/src/renderer/lang/zh-cn/view/leaderboard.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "play": "播放", - "collect": "收藏" -} diff --git a/src/renderer/lang/zh-cn/view/list.json b/src/renderer/lang/zh-cn/view/list.json deleted file mode 100644 index 18650f0c..00000000 --- a/src/renderer/lang/zh-cn/view/list.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "action": "操作", - "album": "专辑", - "default_list": "试听列表", - "list_add_to": "添加到...", - "list_copy_name": "复制歌曲名", - "list_download": "下载", - "list_move_to": "移动到...", - "list_play": "播放", - "list_play_later": "稍后播放", - "list_remove": "删除", - "list_search": "搜索", - "list_sort": "调整位置", - "list_source_detail": "歌曲详情页", - "lists_duplicate": "重复歌曲", - "lists_export": "导出", - "lists_export_part_desc": "选择列表文件保存位置", - "lists_import": "导入", - "lists_import_part_button_cancel": "不要啊", - "lists_import_part_button_confirm": "覆盖掉", - "lists_import_part_confirm": "导入的列表({importName})与本地列表({localName})的ID相同,是否覆盖本地列表?", - "lists_import_part_desc": "选择列表文件", - "lists_movedown": "下移", - "lists_moveup": "上移", - "lists_new_list_btn": "新建列表", - "lists_new_list_input": "新列表...", - "lists_remove": "删除", - "lists_remove_tip": "你真的想要移除 {name} 吗?", - "lists_remove_tip_button": "是的 没错", - "lists_rename": "重命名", - "lists_sync": "更新", - "loding_list": "加载中...", - "love_list": "收藏", - "name": "歌曲名", - "no_item": "列表竟然是空的...", - "singer": "歌手", - "time": "时长" -} diff --git a/src/renderer/lang/zh-cn/view/search.json b/src/renderer/lang/zh-cn/view/search.json deleted file mode 100644 index bc418cb6..00000000 --- a/src/renderer/lang/zh-cn/view/search.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "list_play": "播放", - "list_play_later": "稍后播放", - "list_add_to": "添加到...", - "list_download": "下载", - "list_source_detail": "歌曲详情页", - "name": "歌曲名", - "singer": "歌手", - "album": "专辑", - "action": "操作", - "time": "时长", - "lossless": "无损", - "high_quality": "高品质", - "loding_list": "加载中...", - "no_item": "搜我所想~~😉", - "hot_search": "热门搜索", - "history_search": "历史搜索", - "history_clear": "清空搜索历史", - "history_remove": "右击移除该历史" -} diff --git a/src/renderer/lang/zh-cn/view/setting.json b/src/renderer/lang/zh-cn/view/setting.json deleted file mode 100644 index 944cd47d..00000000 --- a/src/renderer/lang/zh-cn/view/setting.json +++ /dev/null @@ -1,157 +0,0 @@ -{ - "about": "关于洛雪音乐", - "backup": "备份与恢复", - "backup_all": "所有数据(列表数据与设置数据)", - "backup_all_export": "导出", - "backup_all_export_desc": "选择备份保存位置", - "backup_all_import": "导入", - "backup_all_import_desc": "选择备份文件", - "backup_part": "部分数据(列表数据包括试听列表、收藏列表、用户自定义列表,设置数据不包括快捷键设置)", - "backup_part_export_list": "导出列表", - "backup_part_export_list_desc": "选择歌单保存位置", - "backup_part_export_setting": "导出设置", - "backup_part_export_setting_desc": "选择设置保存位置", - "backup_part_import_list": "导入列表", - "backup_part_import_list_desc": "选择列表文件", - "backup_part_import_setting": "导入设置", - "backup_part_import_setting_desc": "选择配置文件", - "basic": "基本设置", - "basic_animation": "弹出层随机动画", - "basic_control_btn_position": "控制按钮位置", - "basic_control_btn_position_left": "左边", - "basic_control_btn_position_right": "右边", - "basic_lang": "语言", - "basic_lang_title": "软件显示的语言", - "basic_show_animation": "显示切换动画", - "basic_source": "音乐来源", - "basic_source_status_failed": "初始化失败", - "basic_source_status_initing": "初始化中", - "basic_source_status_success": "初始化成功", - "basic_source_temp": "临时接口(软件的某些功能不可用,建议测试接口不可用再使用本接口)", - "basic_source_test": "测试接口(几乎软件的所有功能都可用)", - "basic_source_title": "选择音乐来源", - "basic_source_user_api_btn": "自定义源管理", - "basic_sourcename": "音源名字", - "basic_sourcename_alias": "别名", - "basic_sourcename_real": "原名", - "basic_sourcename_title": "选择音源名字类型", - "basic_theme": "主题颜色", - "basic_to_tray": "关闭软件时不退出软件将其最小化到系统托盘", - "basic_window_size": "窗口尺寸", - "basic_window_size_big": "大", - "basic_window_size_huge": "巨大", - "basic_window_size_larger": "较大", - "basic_window_size_medium": "中", - "basic_window_size_oversized": "超大", - "basic_window_size_small": "小", - "basic_window_size_smaller": "较小", - "basic_window_size_title": "设置软件窗口尺寸", - "click_copy": "点击复制", - "click_open": "点击打开", - "desktop_lyric": "桌面歌词设置", - "desktop_lyric_always_on_top": "使歌词总是在其他窗口之上", - "desktop_lyric_enable": "显示歌词", - "desktop_lyric_font": "歌词字体", - "desktop_lyric_font_default": "默认", - "desktop_lyric_lock": "锁定歌词", - "desktop_lyric_lock_screen": "不允许歌词窗口拖出主屏幕之外", - "download": "下载设置", - "download_data_embed": "是否将以下内容嵌入到音频文件中", - "download_embed_lyric": "歌词嵌入", - "download_embed_pic": "封面嵌入", - "download_enable": "是否启用下载功能", - "download_lyric": "歌词下载", - "download_lyric_format": "下载的歌词文件编码格式", - "download_lyric_format_gbk": "GBK(在某些设备上出现中文乱码时可尝试选择此格式)", - "download_lyric_format_utf8": "UTF-8", - "download_lyric_title": "是否同时下载歌词文件", - "download_name": "文件命名方式", - "download_name1": "歌名 - 歌手", - "download_name2": "歌手 - 歌名", - "download_name3": "歌名", - "download_name_title": "下载歌曲时的命名方式", - "download_path": "下载路径", - "download_path_change_btn": "更改", - "download_path_label": "当前下载路径:", - "download_path_open_label": "点击打开当前路径", - "download_path_title": "下载歌曲保存的路径", - "download_select_save_path": "选择歌曲保存路径", - "download_use_other_source": "自动换源下载(当无法从歌曲的原始源下载时,尝试切换到其他源下载,注:此功能不100%保证换源后的歌曲版本与原版一致)", - "hot_key": "快捷键设置", - "hot_key_common_focus_search_input": "聚焦搜索框", - "hot_key_common_min": "最小化程序", - "hot_key_common_toggle_close": "退出程序", - "hot_key_common_toggle_hide": "显示/隐藏程序", - "hot_key_common_toggle_min": "最小化/还原程序", - "hot_key_desktop_lyric_toggle_always_top": "桌面歌词置顶切换", - "hot_key_desktop_lyric_toggle_lock": "桌面歌词锁定切换", - "hot_key_desktop_lyric_toggle_visible": "开/关桌面歌词", - "hot_key_global_title": "全局快捷键", - "hot_key_local_title": "软件内快捷键", - "hot_key_player_next": "下一首歌曲", - "hot_key_player_prev": "上一首歌曲", - "hot_key_player_toggle_play": "播放/暂停控制", - "hot_key_player_volume_down": "减少音量", - "hot_key_player_volume_mute": "静音切换", - "hot_key_player_volume_up": "增加音量", - "hot_key_tip_input": "请输入新的按键", - "hot_key_unset_input": "未设置", - "is_enable": "是否启用", - "is_show": "是否显示", - "list": "列表设置", - "list_add_music_location_type": "添加歌曲到列表时的位置", - "list_add_music_location_type_bottom": "底部", - "list_add_music_location_type_top": "顶部", - "list_scroll": "记住播放列表滚动条位置(仅对我的音乐分类有效)", - "list_source": "显示歌曲源(仅对我的音乐分类有效)", - "network": "网络设置", - "network_proxy_host": "主机", - "network_proxy_password": "密码", - "network_proxy_port": "端口", - "network_proxy_title": "HTTP代理设置(乱设置软件将无法联网)", - "network_proxy_username": "用户名", - "odc": "强迫症设置", - "odc_clear_search_input": "离开搜索界面时清空搜索框", - "odc_clear_search_list": "离开搜索界面时清空搜索列表", - "other": "其他", - "other_play_list_cache": "列表缓存管理(我的列表中已缓存的歌曲链接、播放代替源,清理后播放、下载歌曲时需要重新获取,没有歌曲播放相关的问题不要清理)", - "other_play_list_cache_clear_btn": "清理列表缓存信息", - "other_resource_cache": "资源缓存管理(图片、音频等缓存,清理后图片等资源将需要重新下载,不建议清理,软件会根据磁盘空间动态管理缓存大小)", - "other_resource_cache_clear_btn": "清理资源缓存", - "other_resource_cache_label": "软件已使用缓存大小:", - "other_tray_theme": "托盘图标样式", - "other_tray_theme_black": "黑色", - "other_tray_theme_native": "纯色", - "other_tray_theme_origin": "原色", - "play": "播放设置", - "play_lyric_lxlrc": "使用卡拉OK式歌词播放(如果支持)", - "play_lyric_transition": "显示歌词翻译", - "play_mediaDevice": "音频输出", - "play_mediaDevice_remove_stop_play": "当前的声音输出设备被改变时暂停播放歌曲", - "play_mediaDevice_title": "选择声音输出的媒体设备", - "play_quality": "优先播放320K品质的歌曲(如果支持)", - "play_save_play_time": "记住播放进度", - "play_task_bar": "在任务栏上显示当前歌曲播放进度", - "search": "搜索设置", - "search_focus_search_box": "启动时自动聚焦搜索框", - "search_history": "显示历史搜索记录", - "search_hot": "显示热门搜索", - "sync": "数据同步 [此为测试功能,首次使用前建议先备份一次歌单]", - "sync_address": "同步服务地址:{address}", - "sync_auth_code": "连接码:{code}", - "sync_device": "已连接的设备:{devices}", - "sync_enable": "启用同步功能(由于数据是明文传输,请在受信任的网络下使用)", - "sync_port": "同步端口设置", - "sync_port_tip": "请输入同步服务端口号", - "sync_refresh_code": "刷新连接码", - "update": "软件更新", - "update_checking": "检查更新中...", - "update_current_label": "当前版本:", - "update_downloading": "发现新版本并在努力下载中,请稍后...⏳", - "update_init": "处理更新中...", - "update_latest": "软件已是最新,尽情地体验吧~🥂", - "update_latest_label": "最新版本:", - "update_open_version_modal_btn": "打开更新窗口 🚀", - "update_progress": "下载进度:", - "update_unknown": "未知" -} diff --git a/src/renderer/lang/zh-cn/view/song_list.json b/src/renderer/lang/zh-cn/view/song_list.json deleted file mode 100644 index f0b6f1fb..00000000 --- a/src/renderer/lang/zh-cn/view/song_list.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "back": "返回", - "loding_list": "列表加载中...", - "loding_list_fail": "列表加载失败", - "open_list": "打开{name}歌单", - "input_text": "输入歌单链接或歌单ID", - "tip_1": "不支持跨源打开歌单,请确认要打开的歌单与当前歌单源是否对应", - "tip_2": "若遇到无法打开的歌单链接,欢迎反馈", - "tip_3": "酷狗源不支持用歌单ID打开,但支持酷狗码打开", - "play_all": "播放", - "play_later": "稍后播放", - "add_all": "收藏" -} diff --git a/src/renderer/lang/zh-tw/base.json b/src/renderer/lang/zh-tw/base.json deleted file mode 100644 index 60631394..00000000 --- a/src/renderer/lang/zh-tw/base.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "cancel_button_text": "取消", - "confirm_button_text": "好的", - "date_format_hour": "{num}小時前", - "date_format_minute": "{num}分鐘前", - "date_format_second": "{num}秒前" -} diff --git a/src/renderer/lang/zh-tw/core/aside.json b/src/renderer/lang/zh-tw/core/aside.json deleted file mode 100644 index 23f3fce6..00000000 --- a/src/renderer/lang/zh-tw/core/aside.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "search": "搜索", - "song_list": "歌單", - "leaderboard": "排行榜", - "my_list": "我的列表", - "download": "下載管理", - "setting": "設置" -} diff --git a/src/renderer/lang/zh-tw/core/player.json b/src/renderer/lang/zh-tw/core/player.json deleted file mode 100644 index f0395b37..00000000 --- a/src/renderer/lang/zh-tw/core/player.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "add_music_to": "添加當前歌曲到...", - "album": "專輯名:", - "buffering": "緩衝中...", - "comment_hot_load_error": "熱門評論加載失敗,點擊嘗試重新加載", - "comment_hot_loading": "熱門評論加載中", - "comment_hot_title": "熱門評論", - "comment_new_load_error": "最新評論加載失敗,點擊嘗試重新加載", - "comment_new_loading": "最新評論加載中", - "comment_new_title": "最新評論", - "comment_no_content": "暫無評論", - "comment_refresh": "刷新評論", - "comment_show": "歌曲評論", - "comment_title": "{name} 的評論", - "copy_title": "(點擊複製)", - "desktop_lyric_lock": "右擊鎖定歌詞", - "desktop_lyric_off": "關閉桌面歌詞", - "desktop_lyric_on": "開啟桌面歌詞", - "desktop_lyric_unlock": "右擊解鎖歌詞", - "end": "播放完畢", - "error": "音頻加載出錯,5 秒後切換下一首", - "geting_url": "歌曲鏈接獲取中...", - "hide_detail": "隱藏詳情頁(界面內右鍵雙擊可快速隱藏詳情頁)", - "loading": "音樂加載中...", - "lyric_error": "歌詞獲取失敗", - "lyric_select": "歌詞文本選擇", - "name": "歌曲名:", - "next": "下一首", - "pause": "暫停", - "pic_tip": "右擊在“我的列表”定位當前播放的歌曲", - "play": "播放", - "play_toggle_mode_list": "順序播放", - "play_toggle_mode_list_loop": "列表循環", - "play_toggle_mode_off": "禁用", - "play_toggle_mode_random": "列表隨機", - "play_toggle_mode_single_loop": "單曲循環", - "playing": "播放中...", - "prev": "上一首", - "refresh_url": "URL過期,正在刷新URL...", - "singer": "藝術家:", - "stop": "暫停播放", - "volume": "當前音量:" -} diff --git a/src/renderer/lang/zh-tw/core/toolbar.json b/src/renderer/lang/zh-tw/core/toolbar.json deleted file mode 100644 index ed8d5f37..00000000 --- a/src/renderer/lang/zh-tw/core/toolbar.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "min": "最小化", - "close": "關閉" -} diff --git a/src/renderer/lang/zh-tw/desktopLyric.json b/src/renderer/lang/zh-tw/desktopLyric.json deleted file mode 100644 index a34300a5..00000000 --- a/src/renderer/lang/zh-tw/desktopLyric.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "close": "關閉", - "back": "返回", - "lock": "鎖定歌詞", - "unlock": "解鎖歌詞", - "theme": "主題配色", - "font_increase": "增加字體大小(右擊可微調)", - "font_decrease": "減小字體大小(右擊可微調)", - "opactiy_increase": "減小透明度(右擊可微調)", - "opactiy_decrease": "增加透明度(右擊可微調)", - "lrc_active_zoom_on": "縮放當前播放的歌詞", - "lrc_active_zoom_off": "取消縮放當前播放的歌詞", - "win_top_on": "置頂歌詞界面", - "win_top_off": "取消置頂歌詞界面" -} diff --git a/src/renderer/lang/zh-tw/material/download_modal.json b/src/renderer/lang/zh-tw/material/download_modal.json deleted file mode 100644 index 81ad2913..00000000 --- a/src/renderer/lang/zh-tw/material/download_modal.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "btn_tip": "該音質不可用", - "lossless": "無損音質", - "high_quality": "高品音質", - "normal": "普通音質" -} diff --git a/src/renderer/lang/zh-tw/material/download_multiple_modal.json b/src/renderer/lang/zh-tw/material/download_multiple_modal.json deleted file mode 100644 index 8a075558..00000000 --- a/src/renderer/lang/zh-tw/material/download_multiple_modal.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "tip": "已選擇 {len} 首歌曲", - "tip2": "請選擇要優先下載的音質", - "normal": "普通音質", - "high_quality": "高品音質", - "lossless": "無損音質" -} diff --git a/src/renderer/lang/zh-tw/material/flow_btn.json b/src/renderer/lang/zh-tw/material/flow_btn.json deleted file mode 100644 index c8cb7bf2..00000000 --- a/src/renderer/lang/zh-tw/material/flow_btn.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "play": "播放", - "add": "添加", - "download": "下載", - "start": "開始", - "pause": "暫停", - "remove": "移除" -} diff --git a/src/renderer/lang/zh-tw/material/list_add_modal.json b/src/renderer/lang/zh-tw/material/list_add_modal.json deleted file mode 100644 index 809070b2..00000000 --- a/src/renderer/lang/zh-tw/material/list_add_modal.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "title_first_add": "添加", - "title_first_move": "移動", - "title_last": "到...", - "btn_title": "把該歌曲添加到 {name}" -} diff --git a/src/renderer/lang/zh-tw/material/list_add_multiple_modal.json b/src/renderer/lang/zh-tw/material/list_add_multiple_modal.json deleted file mode 100644 index 5ac6f2d7..00000000 --- a/src/renderer/lang/zh-tw/material/list_add_multiple_modal.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "title_add": "添加已選的 {num} 首歌曲到...", - "title_move": "添加移動已選的 {num} 首歌曲到...", - "btn_title": "把這些歌曲添加到 {name}" -} diff --git a/src/renderer/lang/zh-tw/material/list_buttons.json b/src/renderer/lang/zh-tw/material/list_buttons.json deleted file mode 100644 index 0fe28518..00000000 --- a/src/renderer/lang/zh-tw/material/list_buttons.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "play": "播放", - "add_to": "添加到...", - "download": "下載", - "add": "添加", - "start": "開始", - "pause": "暫停", - "file": "定位文件", - "search": "搜索", - "remove": "移除" -} diff --git a/src/renderer/lang/zh-tw/material/list_sort_modal.json b/src/renderer/lang/zh-tw/material/list_sort_modal.json deleted file mode 100644 index 7e3ddd66..00000000 --- a/src/renderer/lang/zh-tw/material/list_sort_modal.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "title": "將 {name} 的位置調整到:", - "title_multiple": "將已選的 {num} 首歌曲的位置調整到:", - "input_tip": "請輸入要調整到第幾個位置", - "btn_confirm": "確定" -} diff --git a/src/renderer/lang/zh-tw/material/pagination.json b/src/renderer/lang/zh-tw/material/pagination.json deleted file mode 100644 index 5895727b..00000000 --- a/src/renderer/lang/zh-tw/material/pagination.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "prev": "上一頁", - "next": "下一頁", - "page": "第 {num} 頁" -} diff --git a/src/renderer/lang/zh-tw/material/song_list.json b/src/renderer/lang/zh-tw/material/song_list.json deleted file mode 100644 index c91c8ca4..00000000 --- a/src/renderer/lang/zh-tw/material/song_list.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "list_play": "播放", - "list_play_later": "稍後播放", - "list_add_to": "添加到...", - "list_download": "下載", - "list_search": "搜索", - "list_source_detail": "歌曲詳情頁", - "name": "歌曲名", - "singer": "歌手", - "album": "專輯", - "action": "操作", - "time": "時長", - "lossless": "無損", - "high_quality": "高品質", - "loding_list": "列表加載中..." -} diff --git a/src/renderer/lang/zh-tw/material/sync_mode_modal.json b/src/renderer/lang/zh-tw/material/sync_mode_modal.json deleted file mode 100644 index bce8305b..00000000 --- a/src/renderer/lang/zh-tw/material/sync_mode_modal.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "merge_btn_local_remote": "本機列表 合併 遠程列表", - "merge_btn_remote_local": "遠程列表 合併 本機列表", - "merge_label": "合併", - "merge_tip": "合併:", - "merge_tip_desc": "將兩邊的列表合併到一起,相同的歌曲將被去掉(去掉的是被合併者的歌曲),不同的歌曲將被添加。", - "other_label": "其他", - "other_tip": "其他:", - "other_tip_desc": "“僅使用實時同步功能”將不修改雙方的列表,僅實時同步操作;“取消同步”將直接斷開雙方的連接。", - "overwrite": "完全覆蓋", - "overwrite_btn_cancel": "取消同步", - "overwrite_btn_local_remote": "本機列表 覆蓋 遠程列表", - "overwrite_btn_none": "僅使用實時同步功能", - "overwrite_btn_remote_local": "遠程列表 覆蓋 本機列表", - "overwrite_label": "覆蓋", - "overwrite_tip": "覆蓋:", - "overwrite_tip_desc": "被覆蓋者與覆蓋者列表ID相同的列表將被刪除後替換成覆蓋者的列表(列表ID不同的列表將被合併到一起),若勾選完全覆蓋,則被覆蓋者的所有列表將被移除,然後替換成覆蓋者的列表。", - "title": "選擇與 {name} 的列表同步方式" -} diff --git a/src/renderer/lang/zh-tw/material/tag_list.json b/src/renderer/lang/zh-tw/material/tag_list.json deleted file mode 100644 index ee2b3e66..00000000 --- a/src/renderer/lang/zh-tw/material/tag_list.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "default": "默認" -} diff --git a/src/renderer/lang/zh-tw/material/user_api_modal.json b/src/renderer/lang/zh-tw/material/user_api_modal.json deleted file mode 100644 index 0d1948ee..00000000 --- a/src/renderer/lang/zh-tw/material/user_api_modal.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "title": "自定義源管理", - "readme": "源編寫說明:", - "note": "提示:雖然我們已經盡可能地隔離了腳本的運行環境,但導入包含惡意行為的腳本仍可能會影響你的系統,請謹慎導入。", - "btn_remove": "移除", - "btn_import": "導入", - "btn_export": "導出", - "import_file": "選擇音樂API腳本文件", - "noitem": "這裡竟然是空的 😲" -} diff --git a/src/renderer/lang/zh-tw/material/xm_verify_modal.json b/src/renderer/lang/zh-tw/material/xm_verify_modal.json deleted file mode 100644 index 1bfd5ceb..00000000 --- a/src/renderer/lang/zh-tw/material/xm_verify_modal.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "title": "蝦米音樂校驗" -} diff --git a/src/renderer/lang/zh-tw/store/state.json b/src/renderer/lang/zh-tw/store/state.json deleted file mode 100644 index e7e5463d..00000000 --- a/src/renderer/lang/zh-tw/store/state.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "theme_green": "綠意盎然", - "theme_blue": "藍田生玉", - "theme_yellow": "信口雌黃", - "theme_orange": "橙黃橘綠", - "theme_red": "熱情似火", - "theme_pink": "粉裝玉琢", - "theme_purple": "重斤球紫", - "theme_grey": "灰常美麗", - "theme_ming": "青出於黑", - "theme_blue2": "清熱版藍", - "theme_black": "黑燈瞎火", - "theme_mid_autumn": "月裡嫦娥", - "theme_naruto": "木葉之村", - "theme_happy_new_year": "新年快樂", - "source_kw": "酷我音樂", - "source_kg": "酷狗音樂", - "source_tx": "企鵝音樂", - "source_wy": "網易音樂", - "source_mg": "咪咕音樂", - "source_xm": "蝦米音樂", - "source_bd": "百度音樂", - "source_all": "聚合搜索", - "source_alias_kw": "小蝸音樂", - "source_alias_kg": "小枸音樂", - "source_alias_tx": "小秋音樂", - "source_alias_wy": "小芸音樂", - "source_alias_mg": "小蜜音樂", - "source_alias_bd": "小杜音樂", - "source_alias_xm": "小霞音樂", - "source_alias_all": "聚合大會", - - "load_list_file_error_title": "播放列表數據加載錯誤", - "load_list_file_error_detail": "我們已經幫你把舊的列表文件備份到{path}\n它以 JSON 格式存儲,你可以嘗試手動修復並恢復它\n\n錯誤詳情:{detail}" -} diff --git a/src/renderer/lang/zh-tw/view/download.json b/src/renderer/lang/zh-tw/view/download.json deleted file mode 100644 index 6264abae..00000000 --- a/src/renderer/lang/zh-tw/view/download.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "menu_play": "播放", - "menu_play_later": "稍後播放", - "menu_start": "開始任務", - "menu_pause": "暫停任務", - "menu_file": "定位文件", - "menu_search": "搜索", - "menu_remove": "刪除", - "menu_source_detail": "歌曲詳情頁", - "name": "歌曲名", - "progress": "進度", - "status": "狀態", - "quality": "品質", - "action": "操作", - "all": "全部任務", - "runing": "正在下載", - "paused": "已暫停", - "error": "出錯", - "finished": "下載完成" -} diff --git a/src/renderer/lang/zh-tw/view/list.json b/src/renderer/lang/zh-tw/view/list.json deleted file mode 100644 index 07a2aee5..00000000 --- a/src/renderer/lang/zh-tw/view/list.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "action": "操作", - "album": "專輯", - "default_list": "試聽列表", - "list_add_to": "添加到...", - "list_copy_name": "複製歌曲名", - "list_download": "下載", - "list_move_to": "移動到...", - "list_play": "播放", - "list_play_later": "稍後播放", - "list_remove": "刪除", - "list_search": "搜索", - "list_sort": "調整位置", - "list_source_detail": "歌曲詳情頁", - "lists_duplicate": "重複歌曲", - "lists_export": "導出", - "lists_export_part_desc": "選擇列表文件保存位置", - "lists_import": "導入", - "lists_import_part_button_cancel": "不要啊", - "lists_import_part_button_confirm": "覆蓋掉", - "lists_import_part_confirm": "導入的列表({importName})與本地列表({localName})的ID相同,是否覆蓋本地列表?", - "lists_import_part_desc": "選擇列表文件", - "lists_movedown": "下移", - "lists_moveup": "上移", - "lists_new_list_btn": "新建列表", - "lists_new_list_input": "新列表...", - "lists_remove": "刪除", - "lists_remove_tip": "你真的想要移除 {name} 嗎?", - "lists_remove_tip_button": "是的 沒錯", - "lists_rename": "重命名", - "lists_sync": "更新", - "loding_list": "加載中...", - "love_list": "收藏列表", - "name": "歌曲名", - "no_item": "列表竟然是空的...", - "singer": "歌手", - "time": "時長" -} diff --git a/src/renderer/lang/zh-tw/view/search.json b/src/renderer/lang/zh-tw/view/search.json deleted file mode 100644 index 9a1c2168..00000000 --- a/src/renderer/lang/zh-tw/view/search.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "list_play": "播放", - "list_play_later": "稍後播放", - "list_add_to": "添加到...", - "list_download": "下載", - "list_source_detail": "歌曲詳情頁", - "name": "歌曲名", - "singer": "歌手", - "album": "專輯", - "action": "操作", - "time": "時長", - "lossless": "無損", - "high_quality": "高品質", - "loding_list": "加載中...", - "no_item": "搜我所想~~😉", - "hot_search": "熱門搜索", - "history_search": "歷史搜索", - "history_clear": "清空搜索歷史", - "history_remove": "右擊移除該歷史" -} diff --git a/src/renderer/lang/zh-tw/view/setting.json b/src/renderer/lang/zh-tw/view/setting.json deleted file mode 100644 index 9297c126..00000000 --- a/src/renderer/lang/zh-tw/view/setting.json +++ /dev/null @@ -1,157 +0,0 @@ -{ - "about": "關於洛雪音樂", - "backup": "備份與恢復", - "backup_all": "所有數據(列表數據與設置數據)", - "backup_all_export": "導出", - "backup_all_export_desc": "選擇備份保存位置", - "backup_all_import": "導入", - "backup_all_import_desc": "選擇備份文件", - "backup_part": "部分數據(列表數據包括試聽列表、收藏列表、用戶自定義列表,設置數據不包括快捷鍵設置)", - "backup_part_export_list": "導出列表", - "backup_part_export_list_desc": "選擇歌單保存位置", - "backup_part_export_setting": "導出設置", - "backup_part_export_setting_desc": "選擇設置保存位置", - "backup_part_import_list": "導入列表", - "backup_part_import_list_desc": "選擇列表文件", - "backup_part_import_setting": "導入設置", - "backup_part_import_setting_desc": "選擇配置文件", - "basic": "基本設置", - "basic_animation": "彈出層隨機動畫", - "basic_control_btn_position": "控制按鈕位置", - "basic_control_btn_position_left": "左邊", - "basic_control_btn_position_right": "右邊", - "basic_lang": "語言", - "basic_lang_title": "軟件顯示的語言", - "basic_show_animation": "顯示切換動畫", - "basic_source": "音樂來源", - "basic_source_status_failed": "初始化失敗", - "basic_source_status_initing": "初始化中", - "basic_source_status_success": "初始化成功", - "basic_source_temp": "臨時接口(軟件的某些功能不可用,建議測試接口不可用再使用本接口)", - "basic_source_test": "測試接口(幾乎軟件的所有功能都可用)", - "basic_source_title": "選擇音樂來源", - "basic_source_user_api_btn": "自定義源管理", - "basic_sourcename": "音源名字", - "basic_sourcename_alias": "別名", - "basic_sourcename_real": "原名", - "basic_sourcename_title": "選擇音源名字類型", - "basic_theme": "主題顏色", - "basic_to_tray": "關閉軟件時不退出軟件將其最小化到系統托盤", - "basic_window_size": "窗口尺寸", - "basic_window_size_big": "大", - "basic_window_size_huge": "巨大", - "basic_window_size_larger": "較大", - "basic_window_size_medium": "中", - "basic_window_size_oversized": "超大", - "basic_window_size_small": "小", - "basic_window_size_smaller": "較小", - "basic_window_size_title": "設置軟件窗口尺寸", - "click_copy": "點擊複製", - "click_open": "點擊打開", - "desktop_lyric": "桌面歌詞設置", - "desktop_lyric_always_on_top": "使歌詞總是在其他窗口之上", - "desktop_lyric_enable": "顯示歌詞", - "desktop_lyric_font": "歌詞字體", - "desktop_lyric_font_default": "默認", - "desktop_lyric_lock": "鎖定歌詞", - "desktop_lyric_lock_screen": "不允許歌詞窗口拖出主屏幕之外", - "download": "下載設置", - "download_data_embed": "是否將以下內容嵌入到音頻文件中", - "download_embed_lyric": "歌詞嵌入", - "download_embed_pic": "封面嵌入", - "download_enable": "是否啟用下載功能", - "download_lyric": "歌詞下載", - "download_lyric_format": "下載的歌詞文件編碼格式", - "download_lyric_format_gbk": "GBK(在某些設備上出現中文亂碼時可嘗試選擇此格式)", - "download_lyric_format_utf8": "UTF-8", - "download_lyric_title": "是否同時下載歌詞文件", - "download_name": "文件命名方式", - "download_name1": "歌名 - 歌手", - "download_name2": "歌手 - 歌名", - "download_name3": "歌名", - "download_name_title": "下載歌曲時的命名方式", - "download_path": "下載路徑", - "download_path_change_btn": "更改", - "download_path_label": "當前下載路徑:", - "download_path_open_label": "點擊打開當前路徑", - "download_path_title": "下載歌曲保存的路徑", - "download_select_save_path": "選擇歌曲保存路徑", - "download_use_other_source": "自動換源下載(當無法從歌曲的原始源下載時,嘗試切換到其他源下載,注:此功能不100%保證換源後的歌曲版本與原版一致)", - "hot_key": "快捷鍵設置", - "hot_key_common_focus_search_input": "聚焦搜索框", - "hot_key_common_min": "最小化程序", - "hot_key_common_toggle_close": "退出程序", - "hot_key_common_toggle_hide": "顯示/隱藏程序", - "hot_key_common_toggle_min": "最小化/還原程序", - "hot_key_desktop_lyric_toggle_always_top": "桌面歌詞置頂切換", - "hot_key_desktop_lyric_toggle_lock": "桌面歌詞鎖定切換", - "hot_key_desktop_lyric_toggle_visible": "開/關桌面歌詞", - "hot_key_global_title": "全局快捷鍵", - "hot_key_local_title": "軟件內快捷鍵", - "hot_key_player_next": "下一首歌曲", - "hot_key_player_prev": "上一首歌曲", - "hot_key_player_toggle_play": "播放/暫停控制", - "hot_key_player_volume_down": "減少音量", - "hot_key_player_volume_mute": "靜音切換", - "hot_key_player_volume_up": "增加音量", - "hot_key_tip_input": "請輸入新的按鍵", - "hot_key_unset_input": "未設置", - "is_enable": "是否啟用", - "is_show": "是否顯示", - "list": "列表設置", - "list_add_music_location_type": "添加歌曲到列表時的位置", - "list_add_music_location_type_bottom": "底部", - "list_add_music_location_type_top": "頂部", - "list_scroll": "記住播放列表滾動條位置(僅對我的音樂分類有效)", - "list_source": "顯示歌曲源(僅對我的音樂分類有效)", - "network": "網絡設置", - "network_proxy_host": "主機", - "network_proxy_password": "密碼", - "network_proxy_port": "端口", - "network_proxy_title": "HTTP代理設置(亂設置軟件將無法聯網)", - "network_proxy_username": "用戶名", - "odc": "強迫症設置", - "odc_clear_search_input": "離開搜索界面時清空搜索框", - "odc_clear_search_list": "離開搜索界面時清空搜索列表", - "other": "其他", - "other_play_list_cache": "列表緩存管理(我的列表中已緩存的歌曲鏈接、播放代替源,清理後播放、下載歌曲時需要重新獲取,沒有歌曲播放相關的問題不要清理)", - "other_play_list_cache_clear_btn": "清理列表緩存信息", - "other_resource_cache": "資源緩存管理(圖片、音頻等緩存,清理後圖片等資源將需要重新下載,不建議清理,軟件會根據磁盤空間動態管理緩存大小)", - "other_resource_cache_clear_btn": "清理資源緩存", - "other_resource_cache_label": "軟件已使用緩存大小:", - "other_tray_theme": "托盤圖標樣式", - "other_tray_theme_black": "黑色", - "other_tray_theme_native": "純色", - "other_tray_theme_origin": "原色", - "play": "播放設置", - "play_lyric_lxlrc": "使用卡拉OK式歌詞播放(如果支持)", - "play_lyric_transition": "顯示歌詞翻譯", - "play_mediaDevice": "音頻輸出", - "play_mediaDevice_remove_stop_play": "當前的聲音輸出設備被改變時暫停播放歌曲", - "play_mediaDevice_title": "選擇聲音輸出的媒體設備", - "play_quality": "優先播放320K品質的歌曲(如果支持)", - "play_save_play_time": "記住播放進度", - "play_task_bar": "在任務欄上顯示當前歌曲播放進度", - "search": "搜索設置", - "search_focus_search_box": "啟動時自動聚焦搜索框", - "search_history": "顯示歷史搜索記錄", - "search_hot": "顯示熱門搜索", - "sync": "數據同步 [此為測試功能,首次使用前建議先備份一次歌單]", - "sync_address": "同步服務地址:{address}", - "sync_auth_code": "連接碼:{code}", - "sync_device": "已連接的設備:{devices}", - "sync_enable": "啟用同步功能(由於數據是明文傳輸,請在受信任的網絡下使用)", - "sync_port": "同步端口設置", - "sync_port_tip": "請輸入同步服務端口號", - "sync_refresh_code": "刷新連接碼", - "update": "軟件更新", - "update_checking": "檢查更新中...", - "update_current_label": "當前版本:", - "update_downloading": "發現新版本並在努力下載中,請稍後...⏳", - "update_init": "處理更新中...", - "update_latest": "軟件已是最新,盡情地體驗吧~🥂", - "update_latest_label": "最新版本:", - "update_open_version_modal_btn": "打開更新窗口 🚀", - "update_progress": "下載進度:", - "update_unknown": "未知" -} diff --git a/src/renderer/lang/zh-tw/view/song_list.json b/src/renderer/lang/zh-tw/view/song_list.json deleted file mode 100644 index b4f484b5..00000000 --- a/src/renderer/lang/zh-tw/view/song_list.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "back": "返回", - "loding_list": "列表加载中...", - "loding_list_fail": "列表加載失敗", - "open_list": "打開{name}歌單", - "input_text": "輸入歌單鏈接或歌單ID", - "tip_1": "不支持跨源打開歌單,請確認要打開的歌單與當前歌單源是否對應", - "tip_2": "若遇到無法打開的歌單鏈接,歡迎反饋", - "tip_3": "酷狗源不支持用歌單ID打開,但支持酷狗碼打開", - "play_all": "播放", - "play_later": "稍後播放", - "add_all": "收藏" -} diff --git a/src/renderer/main.js b/src/renderer/main.js index 8ef83b07..ccbeacb1 100644 --- a/src/renderer/main.js +++ b/src/renderer/main.js @@ -1,29 +1,29 @@ -import Vue from 'vue' +import '../common/error' +import { createApp } from 'vue' // import { sync } from 'vuex-router-sync' import './event' // Components -import './components' +import mountComponents from './components' // Plugins -import './plugins' +import initPlugins from './plugins' import i18n from './plugins/i18n' import App from './App' import router from './route' import store from './store' -import '../common/error' import { getSetting } from './utils' -import languageList from '@renderer/lang/languages.json' +import { langList } from '@/lang' import { rendererSend, NAMES } from '../common/ipc' // sync(store, router) -Vue.config.productionTip = false -Vue.config.devtools = process.env.NODE_ENV === 'development' +window.ELECTRON_DISABLE_SECURITY_WARNINGS = process.env.ELECTRON_DISABLE_SECURITY_WARNINGS + getSetting().then(({ setting, version }) => { global.appSetting = setting @@ -34,7 +34,7 @@ getSetting().then(({ setting, version }) => { if (window.i18n.availableLocales.includes(locale)) { langId = locale } else { - for (const lang of languageList) { + for (const lang of langList) { if (lang.alternate == locale) { langId = lang.locale break @@ -50,14 +50,12 @@ getSetting().then(({ setting, version }) => { store.commit('setSetting', setting) store.commit('setSettingVersion', version) - new Vue({ - router, - store, - i18n, - el: '#root', - render: h => h(App), - }) - - // window.__VUE_DEVTOOLS_GLOBAL_HOOK__.Vue = app.constructor + const app = createApp(App) + app.use(router) + .use(store) + .use(i18n) + initPlugins(app) + mountComponents(app) + app.mount('#root') }) diff --git a/src/renderer/plugins/Dialog/Dialog.vue b/src/renderer/plugins/Dialog/Dialog.vue index e5628f4f..310fc03f 100644 --- a/src/renderer/plugins/Dialog/Dialog.vue +++ b/src/renderer/plugins/Dialog/Dialog.vue @@ -10,8 +10,14 @@ diff --git a/src/renderer/views/Search.vue b/src/renderer/views/Search.vue index e8aac721..2089fe54 100644 --- a/src/renderer/views/Search.vue +++ b/src/renderer/views/Search.vue @@ -1,73 +1,77 @@ - div(:class="$style.search") - //- transition - div(:class="$style.header") - material-tab(:class="$style.tab" :list="sources" align="left" item-key="id" item-name="name" v-model="searchSourceId") - div(:class="$style.main") - div(:class="$style.list" v-show="isLoading || listInfo.list.length") - div(:class="$style.thead") - table - thead - tr - th.nobreak.center(style="width: 5%;") # - th.nobreak {{$t('view.search.name')}} - th.nobreak(style="width: 22%;") {{$t('view.search.singer')}} - th.nobreak(style="width: 22%;") {{$t('view.search.album')}} - th.nobreak(style="width: 8%;") {{$t('view.search.time')}} - th.nobreak(style="width: 13%;") {{$t('view.search.action')}} - div.scroll(:class="$style.tbody" ref="dom_scrollContent") - table - tbody(@contextmenu.capture="handleContextMenu" ref="dom_tbody") - tr(v-for='(item, index) in listInfo.list' :key='item.songmid' @contextmenu="handleListItemRigthClick($event, index)" @click="handleDoubleClick($event, index)") - td.nobreak.center(style="width: 5%; padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{index + 1}} - td.break - 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('material.song_list.lossless')}} - span.badge.badge-theme-info(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item._types['320k']") {{$t('material.song_list.high_quality')}} - span(:class="[$style.labelSource, $style.noSelect]" v-if="searchSourceId == 'all'") {{item.source}} - td.break(style="width: 22%;") - span.select {{item.singer}} - td.break(style="width: 22%;") - span.select {{item.albumName}} - td(style="width: 8%;") - span(:class="[$style.time, $style.noSelect]") {{item.interval || '--/--'}} - td(style="width: 13%; padding-left: 0; padding-right: 0;") - material-list-buttons(:index="index" :remove-btn="false" :class="$style.listBtn" - :play-btn="assertApiSupport(item.source)" - :download-btn="assertApiSupport(item.source)" - @btn-click="handleListBtnClick") - div(:class="$style.pagination") - material-pagination(:max-page="listInfo.allPage" :limit="listInfo.limit" :page="page" @btn-click="handleTogglePage") - transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated fadeOut") - div(v-show="isLoading" :class="$style.loading") - p {{$t('view.search.loding_list')}} - transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut") - div(v-show="!isLoading && !listInfo.list.length" :class="$style.noitem") - div.scroll(:class="$style.noitemListContainer" v-if="setting.search.isShowHotSearch || setting.search.isShowHistorySearch") - dl(:class="[$style.noitemList, $style.noitemHotSearchList]" v-if="setting.search.isShowHotSearch") - dt(:class="$style.noitemListTitle") {{$t('view.search.hot_search')}} - dd(:class="$style.noitemListItem" @click="handleNoitemSearch(item)" v-for="item in hotSearchList") {{item}} - dl(:class="$style.noitemList" v-if="setting.search.isShowHistorySearch && historyList.length") - dt(:class="$style.noitemListTitle") - span {{$t('view.search.history_search')}} - span(:class="$style.historyClearBtn" @click="clearHistory" :tips="$t('view.search.history_clear')") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 512 512' space='preserve') - use(xlink:href='#icon-eraser') - dd(:class="$style.noitemListItem" v-for="(item, index) in historyList" @contextmenu="removeHistory(index)" :key="index + item" @click="handleNoitemSearch(item)" :tips="$t('view.search.history_remove')") {{item}} - div(v-else :class="$style.noitem_list") - p {{$t('view.search.no_item')}} - material-menu(:menus="listItemMenu" :location="listMenu.menuLocation" item-name="name" :isShow="listMenu.isShowItemMenu" @menu-click="handleListItemMenuClick") - material-download-modal(:show="isShowDownload" :musicInfo="musicInfo" @select="handleAddDownload" @close="isShowDownload = false") - material-download-multiple-modal(:show="isShowDownloadMultiple" :list="selectedData" @select="handleAddDownloadMultiple" @close="isShowDownloadMultiple = false") - //- material-flow-btn(:show="isShowEditBtn && (searchSourceId == 'all' || assertApiSupport(searchSourceId))" :remove-btn="false" @btn-click="handleFlowBtnClick") - material-list-add-modal(:show="isShowListAdd" :musicInfo="musicInfo" @close="handleListAddModalClose") - material-list-add-multiple-modal(:show="isShowListAddMultiple" :musicList="selectedData" @close="handleListAddMultipleModalClose") +div(:class="$style.search") + //- transition + div(:class="$style.header") + base-tab(:class="$style.tab" :list="sources" align="left" item-key="id" item-name="name" v-model="searchSourceId") + div(:class="$style.main") + div(:class="$style.list" v-show="isLoading || listInfo.list.length") + div.thead(:class="$style.thead") + table + thead + tr + th.nobreak.center(style="width: 5%;") # + th.nobreak {{$t('music_name')}} + th.nobreak(style="width: 22%;") {{$t('music_singer')}} + th.nobreak(style="width: 22%;") {{$t('music_album')}} + th.nobreak(style="width: 8%;") {{$t('music_time')}} + th.nobreak(style="width: 13%;") {{$t('action')}} + div.scroll(:class="$style.tbody" ref="dom_scrollContent") + table + tbody(@contextmenu.capture="handleContextMenu" ref="dom_tbody") + tr(v-for='(item, index) in listInfo.list' :key='item.songmid' @contextmenu="handleListItemRigthClick($event, index)" @click="handleDoubleClick($event, index)") + td.nobreak.center(style="width: 5%; padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{index + 1}} + td.break + 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-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}} + td.break(style="width: 22%;") + span.select {{item.singer}} + td.break(style="width: 22%;") + span.select {{item.albumName}} + td(style="width: 8%;") + span(:class="[$style.time, $style.noSelect]") {{item.interval || '--/--'}} + td(style="width: 13%; padding-left: 0; padding-right: 0;") + material-list-buttons(:index="index" :remove-btn="false" :class="$style.listBtn" + :play-btn="assertApiSupport(item.source)" + :download-btn="assertApiSupport(item.source)" + @btn-click="handleListBtnClick") + div(:class="$style.pagination") + material-pagination(:max-page="listInfo.allPage" :limit="listInfo.limit" :page="page" @btn-click="handleTogglePage") + transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated fadeOut") + div(v-show="isLoading" :class="$style.loading") + p {{$t('list__loading')}} + transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut") + div(v-show="!isLoading && !listInfo.list.length" :class="$style.noitem") + div.scroll(:class="$style.noitemListContainer" v-if="setting.search.isShowHotSearch || (setting.search.isShowHistorySearch && setting.search.isShowHistorySearch.length)") + dl(:class="[$style.noitemList, $style.noitemHotSearchList]" v-if="setting.search.isShowHotSearch") + dt(:class="$style.noitemListTitle") {{$t('search__hot_search')}} + dd(:class="$style.noitemListItem" @click="handleNoitemSearch(item)" v-for="item in hotSearchList") {{item}} + dl(:class="$style.noitemList" v-if="setting.search.isShowHistorySearch && historyList.length") + dt(:class="$style.noitemListTitle") + span {{$t('history_search')}} + span(:class="$style.historyClearBtn" @click="clearHistory" :tips="$t('history_clear')") + svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 512 512' space='preserve') + use(xlink:href='#icon-eraser') + dd(:class="$style.noitemListItem" v-for="(item, index) in historyList" @contextmenu="removeHistory(index)" :key="index + item" @click="handleNoitemSearch(item)" :tips="$t('history_remove')") {{item}} + div(v-else :class="$style.noitem_list") + p {{$t('search__welcome')}} + //- common-flow-btn(:show="isShowEditBtn && (searchSourceId == 'all' || assertApiSupport(searchSourceId))" :remove-btn="false" @btn-click="handleFlowBtnClick") + teleport(to="#view") + common-download-modal(v-model:show="isShowDownload" :musicInfo="musicInfo") + common-download-multiple-modal(v-model:show="isShowDownloadMultiple" :list="selectedData" @confirm="removeAllSelect") + common-list-add-modal(v-model:show="isShowListAdd" :musicInfo="musicInfo") + common-list-add-multiple-modal(v-model:show="isShowListAddMultiple" :musicList="selectedData" @confirm="removeAllSelect") + base-menu(:menus="listItemMenu" :location="listMenu.menuLocation" item-name="name" :isShow="listMenu.isShowItemMenu" @menu-click="handleListItemMenuClick") - - diff --git a/src/renderer/views/list/List.vue b/src/renderer/views/list/List.vue new file mode 100644 index 00000000..878dab28 --- /dev/null +++ b/src/renderer/views/list/List.vue @@ -0,0 +1,340 @@ + + + + + + + + + + + diff --git a/src/renderer/components/material/DuplicateMusicModal.vue b/src/renderer/views/list/components/DuplicateMusicModal.vue similarity index 70% rename from src/renderer/components/material/DuplicateMusicModal.vue rename to src/renderer/views/list/components/DuplicateMusicModal.vue index b3db0e7d..1e8aadd4 100644 --- a/src/renderer/components/material/DuplicateMusicModal.vue +++ b/src/renderer/views/list/components/DuplicateMusicModal.vue @@ -1,31 +1,41 @@ - -Modal(:show="visible" @close="$emit('update:visible', false)" bg-close) - div(:class="$style.header") - h2 {{listInfo.name}} - main.scroll(:class="$style.main") - ul(ref="dom_list" v-if="duplicateList.length" :class="$style.list") - li(v-for="(item, index) in duplicateList" :key="item.songmid" :class="$style.listItem") - div(:class="$style.num") {{item.index + 1}} - div(:class="$style.text") - h3(:class="$style.text") {{item.musicInfo.name}} - {{item.musicInfo.singer}} - h3(v-if="item.musicInfo.albumName" :class="[$style.text, $style.albumName]") {{item.musicInfo.albumName}} - div(:class="$style.label") {{item.musicInfo.source}} - div(:class="$style.label") {{item.musicInfo.interval}} - div(:class="$style.btns") - button(type="button" @click="handlePlay(index)" :class="$style.btn") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='50%' viewBox='0 0 287.386 287.386' space='preserve' v-once) - use(xlink:href='#icon-testPlay') - button(type="button" @click="handleRemove(index)" :class="$style.btn") - svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='50%' viewBox='0 0 212.982 212.982' space='preserve' v-once) - use(xlink:href='#icon-delete') - div(:class="$style.noItem" v-else) - p(v-text="$t('view.list.no_item')") + + + + {{listInfo.name}} + + + + + {{item.index + 1}} + + {{item.musicInfo.name}} - {{item.musicInfo.singer}} + {{item.musicInfo.albumName}} + + {{item.musicInfo.source}} + {{item.musicInfo.interval}} + + + + + + + + + + + + + + + + + + + diff --git a/src/renderer/views/list/components/MusicList/index.vue b/src/renderer/views/list/components/MusicList/index.vue new file mode 100644 index 00000000..a48a5885 --- /dev/null +++ b/src/renderer/views/list/components/MusicList/index.vue @@ -0,0 +1,370 @@ + +div(:class="$style.list") + //- transition + div.thead(:class="$style.thead") + table + thead + tr + th.nobreak.center(:class="$style.th" style="width: 5%;") # + th.nobreak(:class="$style.th") {{$t('music_name')}} + th.nobreak(:class="$style.th" style="width: 22%;") {{$t('music_singer')}} + th.nobreak(:class="$style.th" style="width: 22%;") {{$t('music_album')}} + th.nobreak(:class="$style.th" style="width: 9%;") {{$t('music_time')}} + th.nobreak(:class="$style.th" style="width: 15%;") {{$t('action')}} + div(v-show="list.length" :class="$style.content" ref="dom_listContent") + base-virtualized-list(:list="list" key-name="songmid" ref="listRef" #default="{ item, index }" :item-height="listItemHeight" + @scroll="saveListPosition" containerClass="scroll" contentClass="list" @contextmenu.capture="handleListRightClick") + div.list-item(@click="handleListItemClick($event, index)" + :class="[{ [$style.active]: playerInfo.isPlayList && playerInfo.playIndex === index }, { selected: selectedIndex == index || rightClickSelectedIndex == index }, { active: selectedList.includes(item) }, { [$style.disabled]: !assertApiSupport(item.source) }]" + @contextmenu="handleListItemRightClick($event, index)") + div.list-item-cell.nobreak.center(style="flex: 0 0 5%; padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{index + 1}} + div.list-item-cell.auto(:tips="item.name") + span.select {{item.name}} + span(:class="[$style.labelSource, $style.noSelect]" v-if="isShowSource") {{item.source}} + div.list-item-cell(style="flex: 0 0 22%;" :tips="item.singer") + span.select {{item.singer}} + div.list-item-cell(style="flex: 0 0 22%;" :tips="item.albumName") + span.select {{item.albumName}} + div.list-item-cell(style="flex: 0 0 9%;") + span(:class="[$style.time, $style.noSelect]") {{item.interval || '--/--'}} + div.list-item-cell(style="flex: 0 0 15%; padding-left: 0; padding-right: 0;") + material-list-buttons(:index="index" @btn-click="handleListBtnClick" :download-btn="assertApiSupport(item.source)") + div(:class="$style.noItem" v-show="!list.length") + p(v-text="$t('no_item')") + //- material-flow-btn(:show="isShowEditBtn && assertApiSupport(source)" :remove-btn="false" @btn-click="handleFlowBtnClick") + teleport(to="#view") + common-download-modal(v-model:show="isShowDownload" :musicInfo="selectedDownloadMusicInfo") + common-download-multiple-modal(v-model:show="isShowDownloadMultiple" :list="selectedList" @confirm="removeAllSelect") + common-list-add-modal(v-model:show="isShowListAdd" :is-move="isMove" :musicInfo="selectedAddMusicInfo" :exclude-list-id="excludeListIds") + common-list-add-multiple-modal(v-model:show="isShowListAddMultiple" :is-move="isMoveMultiple" :musicList="selectedList" @confirm="removeAllSelect" :exclude-list-id="excludeListIds") + search-list(:list="list" @action="handleMusicSearchAction" :visible="isShowSearchBar") + list-sort-modal(v-model:show="isShowListSortModal" :music-info="selectedSortMusicInfo" :selected-num="selectedNum" @confirm="sortMusic") + base-menu(:menus="menus" :location="menuLocation" item-name="name" :isShow="isShowItemMenu" @menu-click="handleMenuClick") + + + + + + diff --git a/src/renderer/views/list/components/MusicList/useList.js b/src/renderer/views/list/components/MusicList/useList.js new file mode 100644 index 00000000..7624ffd0 --- /dev/null +++ b/src/renderer/views/list/components/MusicList/useList.js @@ -0,0 +1,104 @@ +import { computed, watch, ref, onBeforeUnmount } from '@renderer/utils/vueTools' +import { windowSizeList } from '@renderer/core/share' + +const useKeyEvent = ({ handleSelectAllData }) => { + const keyEvent = { + isShiftDown: false, + isModDown: false, + } + + const handle_key_shift_down = () => { + if (!keyEvent.isShiftDown) keyEvent.isShiftDown = true + } + const handle_key_shift_up = () => { + if (keyEvent.isShiftDown) keyEvent.isShiftDown = false + } + const handle_key_mod_down = () => { + if (!keyEvent.isModDown) keyEvent.isModDown = true + } + const handle_key_mod_up = () => { + if (keyEvent.isModDown) keyEvent.isModDown = false + } + const handle_key_mod_a_down = ({ event }) => { + if (event.target.tagName == 'INPUT') return + event.preventDefault() + if (event.repeat) return + keyEvent.isModDown = false + handleSelectAllData() + } + + onBeforeUnmount(() => { + window.eventHub.off('key_shift_down', handle_key_shift_down) + window.eventHub.off('key_shift_up', handle_key_shift_up) + window.eventHub.off('key_mod_down', handle_key_mod_down) + window.eventHub.off('key_mod_up', handle_key_mod_up) + window.eventHub.off('key_mod+a_down', handle_key_mod_a_down) + }) + window.eventHub.on('key_shift_down', handle_key_shift_down) + window.eventHub.on('key_shift_up', handle_key_shift_up) + window.eventHub.on('key_mod_down', handle_key_mod_down) + window.eventHub.on('key_mod_up', handle_key_mod_up) + window.eventHub.on('key_mod+a_down', handle_key_mod_a_down) + + return keyEvent +} + +export default ({ list, setting }) => { + const selectedList = ref([]) + + let lastSelectIndex = -1 + const listItemHeight = computed(() => { + return parseInt(windowSizeList.find(item => item.id == setting.value.windowSizeId).fontSize) / 16 * 37 + }) + + const removeAllSelect = () => { + selectedList.value = [] + } + const handleSelectAllData = () => { + removeAllSelect() + selectedList.value = [...list.value] + } + const keyEvent = useKeyEvent({ handleSelectAllData }) + + const handleSelectData = clickIndex => { + if (keyEvent.isShiftDown) { + if (selectedList.value.length) { + removeAllSelect() + if (lastSelectIndex != clickIndex) { + let isNeedReverse = false + if (clickIndex < lastSelectIndex) { + let temp = lastSelectIndex + lastSelectIndex = clickIndex + clickIndex = temp + isNeedReverse = true + } + selectedList.value = list.value.slice(lastSelectIndex, clickIndex + 1) + if (isNeedReverse) selectedList.value.reverse() + } + } else { + selectedList.value.push(list.value[clickIndex]) + lastSelectIndex = clickIndex + } + } else if (keyEvent.isModDown) { + lastSelectIndex = clickIndex + let item = list.value[clickIndex] + let index = selectedList.value.indexOf(item) + if (index < 0) { + selectedList.value.push(item) + } else { + selectedList.value.splice(index, 1) + } + } else if (selectedList.value.length) { + removeAllSelect() + } + } + + watch(() => list, removeAllSelect) + + return { + selectedList, + listItemHeight, + removeAllSelect, + handleSelectData, + } +} diff --git a/src/renderer/views/list/components/MusicList/useListInfo.js b/src/renderer/views/list/components/MusicList/useListInfo.js new file mode 100644 index 00000000..b5f25b2d --- /dev/null +++ b/src/renderer/views/list/components/MusicList/useListInfo.js @@ -0,0 +1,45 @@ +import { ref, watch, useRefGetter, computed } from '@renderer/utils/vueTools' +import { playMusicInfo, playInfo } from '@renderer/core/share/player' +import { getList } from '@renderer/core/share/utils' + + +export default ({ props }) => { + const setting = useRefGetter('setting') + + const rightClickSelectedIndex = ref(-1) + const selectedIndex = ref(-1) + const dom_listContent = ref(null) + const listRef = ref(null) + + const excludeListIds = computed(() => ([props.listId])) + + + const list = ref(getList(props.listId)) + watch(() => props.listId, id => { + list.value = getList(id) + }) + + const playerInfo = computed(() => ({ + isPlayList: playMusicInfo.listId == props.listId, + playIndex: playInfo.playIndex, + })) + + const setSelectedIndex = index => { + selectedIndex.value = index + } + + const isShowSource = computed(() => setting.value.list.isShowSource) + + return { + rightClickSelectedIndex, + selectedIndex, + dom_listContent, + listRef, + list, + playerInfo, + setSelectedIndex, + setting, + isShowSource, + excludeListIds, + } +} diff --git a/src/renderer/views/list/components/MusicList/useListScroll.js b/src/renderer/views/list/components/MusicList/useListScroll.js new file mode 100644 index 00000000..14a562ba --- /dev/null +++ b/src/renderer/views/list/components/MusicList/useListScroll.js @@ -0,0 +1,64 @@ +import { useRoute, useRouter, onMounted, onBeforeUnmount, nextTick, watch } from '@renderer/utils/vueTools' +import { setListPosition, getListPosition } from '@renderer/utils/data' + +export default ({ props, listRef, list, setting }) => { + const route = useRoute() + const router = useRouter() + + const saveListPosition = () => { + setListPosition(props.listId, listRef.value?.getScrollTop() || 0) + } + + const handleScrollList = (index, isAnimation, callback = () => {}) => { + listRef.value.scrollToIndex(index, -150, isAnimation).then(callback) + } + + const restoreScroll = (index, isAnimation) => { + // console.log(index, isAnimation) + if (!list.value.length) return + if (index == null) { + let location = getListPosition(props.listId) || 0 + if (setting.value.list.isSaveScrollLocation && location != null) { + listRef.value.scrollTo(location) + } + return + } + + handleScrollList(index, isAnimation) + } + + watch(() => route.query.id, (id, oId) => { + if (!oId || route.path != '/list') return + const scrollIndex = route.query.scrollIndex + nextTick(() => { + restoreScroll(scrollIndex, false) + if (scrollIndex != null) { + router.replace({ + path: 'list', + query: { + id: props.listId, + }, + }) + } + }) + }) + onMounted(() => { + restoreScroll(route.query.scrollIndex, false) + if (route.query.scrollIndex != null) { + router.replace({ + path: 'list', + query: { + id: props.listId, + }, + }) + } + }) + onBeforeUnmount(() => { + saveListPosition() + }) + + return { + saveListPosition, + restoreScroll, + } +} diff --git a/src/renderer/views/list/components/MusicList/useMenu.js b/src/renderer/views/list/components/MusicList/useMenu.js new file mode 100644 index 00000000..0f4c715f --- /dev/null +++ b/src/renderer/views/list/components/MusicList/useMenu.js @@ -0,0 +1,163 @@ +import { computed, ref, reactive, useI18n, nextTick } from '@renderer/utils/vueTools' +import musicSdk from '@renderer/utils/music' + +export default ({ + listRef, + assertApiSupport, + emit, + + handleShowDownloadModal, + handlePlayMusic, + handlePlayMusicLater, + handleSearch, + handleShowMusicAddModal, + handleShowMusicMoveModal, + handleShowSortModal, + handleOpenMusicDetail, + handleCopyName, + handleRemoveMusic, +}) => { + const itemMenuControl = reactive({ + play: true, + playLater: true, + copyName: true, + addTo: true, + moveTo: true, + sort: true, + download: true, + search: true, + remove: true, + sourceDetail: true, + }) + const { t } = useI18n() + const menuLocation = ref({ x: 0, y: 0 }) + const isShowItemMenu = ref(false) + + const menus = computed(() => { + return [ + { + name: t('list__play'), + action: 'play', + disabled: !itemMenuControl.play, + }, + { + name: t('list__download'), + action: 'download', + disabled: !itemMenuControl.download, + }, + { + name: t('list__play_later'), + action: 'playLater', + disabled: !itemMenuControl.playLater, + }, + { + name: t('list__add_to'), + action: 'addTo', + disabled: !itemMenuControl.addTo, + }, + { + name: t('list__move_to'), + action: 'moveTo', + disabled: !itemMenuControl.moveTo, + }, + { + name: t('list__sort'), + action: 'sort', + disabled: !itemMenuControl.sort, + }, + { + name: t('list__copy_name'), + action: 'copyName', + disabled: !itemMenuControl.copyName, + }, + { + name: t('list__source_detail'), + action: 'sourceDetail', + disabled: !itemMenuControl.sourceDetail, + }, + { + name: t('list__search'), + action: 'search', + disabled: !itemMenuControl.search, + }, + { + name: t('list__remove'), + action: 'remove', + disabled: !itemMenuControl.remove, + }, + ] + }) + + const showMenu = (event, musicInfo) => { + itemMenuControl.sourceDetail = !!musicSdk[musicInfo.source].getMusicDetailPageUrl + // itemMenuControl.play = + // itemMenuControl.playLater = + itemMenuControl.download = assertApiSupport(musicInfo.source) + + let dom_container = event.target.closest('#my-list') + const getOffsetValue = (target, x = 0, y = 0) => { + if (target === dom_container) return { x, y } + if (!target) return { x: 0, y: 0 } + x += target.offsetLeft + y += target.offsetTop + return getOffsetValue(target.offsetParent, x, y) + } + let { x, y } = getOffsetValue(event.target) + menuLocation.value.x = x + event.offsetX + menuLocation.value.y = y + event.offsetY - listRef.value.getScrollTop() + emit('show-menu') + nextTick(() => { + isShowItemMenu.value = true + }) + } + + const hideMenu = () => { + isShowItemMenu.value = false + } + + const menuClick = (action, index) => { + // console.log(action) + hideMenu() + if (!action) return + switch (action.action) { + case 'play': + handlePlayMusic(index) + break + case 'playLater': + handlePlayMusicLater(index) + break + case 'copyName': + handleCopyName(index) + break + case 'addTo': + handleShowMusicAddModal(index) + break + case 'moveTo': + handleShowMusicMoveModal(index) + break + case 'sort': + handleShowSortModal(index) + break + case 'download': + handleShowDownloadModal(index) + break + case 'search': + handleSearch(index) + break + case 'remove': + handleRemoveMusic(index) + break + case 'sourceDetail': + handleOpenMusicDetail(index) + } + } + + return { + menus, + menuLocation, + isShowItemMenu, + showMenu, + menuClick, + hideMenu, + } +} diff --git a/src/renderer/views/list/components/MusicList/useMusicActions.js b/src/renderer/views/list/components/MusicList/useMusicActions.js new file mode 100644 index 00000000..2c0f39fe --- /dev/null +++ b/src/renderer/views/list/components/MusicList/useMusicActions.js @@ -0,0 +1,49 @@ +import { useCommit, useRouter } from '@renderer/utils/vueTools' +import musicSdk from '@renderer/utils/music' +import { openUrl, clipboardWriteText } from '@renderer/utils' + + +export default ({ props, list, setting, selectedList, removeAllSelect }) => { + const router = useRouter() + + const listRemove = useCommit('list', 'listRemove') + const listRemoveMultiple = useCommit('list', 'listRemoveMultiple') + + const handleSearch = index => { + const info = list.value[index] + router.push({ + path: 'search', + query: { + text: `${info.name} ${info.singer}`, + }, + }) + } + + const handleOpenMusicDetail = index => { + const minfo = list.value[index] + const url = musicSdk[minfo.source].getMusicDetailPageUrl(minfo) + if (!url) return + openUrl(url) + } + + const handleCopyName = index => { + const minfo = list.value[index] + clipboardWriteText(setting.value.download.fileName.replace('歌名', minfo.name).replace('歌手', minfo.singer)) + } + + const handleRemoveMusic = (index, single) => { + if (selectedList.value.length && !single) { + listRemoveMultiple({ listId: props.listId, ids: selectedList.value.map(m => m.songmid) }) + removeAllSelect() + } else { + listRemove({ listId: props.listId, id: list.value[index].songmid }) + } + } + + return { + handleSearch, + handleOpenMusicDetail, + handleCopyName, + handleRemoveMusic, + } +} diff --git a/src/renderer/views/list/components/MusicList/useMusicAdd.js b/src/renderer/views/list/components/MusicList/useMusicAdd.js new file mode 100644 index 00000000..571a98cd --- /dev/null +++ b/src/renderer/views/list/components/MusicList/useMusicAdd.js @@ -0,0 +1,45 @@ +import { ref, nextTick } from '@renderer/utils/vueTools' + +export default ({ selectedList, list }) => { + const isShowListAdd = ref(false) + const isMove = ref(false) + const isMoveMultiple = ref(false) + const isShowListAddMultiple = ref(false) + const selectedAddMusicInfo = ref(null) + + const handleShowMusicAddModal = (index, single) => { + if (selectedList.value.length && !single) { + isMoveMultiple.value = false + isShowListAddMultiple.value = true + } else { + isMove.value = false + selectedAddMusicInfo.value = list.value[index] + nextTick(() => { + isShowListAdd.value = true + }) + } + } + + const handleShowMusicMoveModal = (index, single) => { + if (selectedList.value.length && !single) { + isMoveMultiple.value = true + isShowListAddMultiple.value = true + } else { + isMove.value = true + selectedAddMusicInfo.value = list.value[index] + nextTick(() => { + isShowListAdd.value = true + }) + } + } + + return { + isShowListAdd, + isMove, + isMoveMultiple, + isShowListAddMultiple, + selectedAddMusicInfo, + handleShowMusicAddModal, + handleShowMusicMoveModal, + } +} diff --git a/src/renderer/views/list/components/MusicList/useMusicDownload.js b/src/renderer/views/list/components/MusicList/useMusicDownload.js new file mode 100644 index 00000000..621a2292 --- /dev/null +++ b/src/renderer/views/list/components/MusicList/useMusicDownload.js @@ -0,0 +1,25 @@ +import { ref, nextTick } from '@renderer/utils/vueTools' + +export default ({ selectedList, list }) => { + const isShowDownload = ref(false) + const isShowDownloadMultiple = ref(false) + const musicInfo = ref(null) + + const handleShowDownloadModal = (index, single) => { + if (selectedList.value.length && !single) { + isShowDownloadMultiple.value = true + } else { + musicInfo.value = list.value[index] + nextTick(() => { + isShowDownload.value = true + }) + } + } + + return { + isShowDownload, + isShowDownloadMultiple, + selectedDownloadMusicInfo: musicInfo, + handleShowDownloadModal, + } +} diff --git a/src/renderer/views/list/components/MusicList/usePlay.js b/src/renderer/views/list/components/MusicList/usePlay.js new file mode 100644 index 00000000..31b2cb4e --- /dev/null +++ b/src/renderer/views/list/components/MusicList/usePlay.js @@ -0,0 +1,45 @@ +import { useCommit } from '@renderer/utils/vueTools' + +export default ({ props, selectedList, list, removeAllSelect }) => { + let clickTime = 0 + let clickIndex = -1 + + const setList = useCommit('player', 'setList') + const setTempPlayList = useCommit('player', 'setTempPlayList') + + const handlePlayMusic = (index) => { + setList({ + listId: props.listId, + index, + }) + } + + const handlePlayMusicLater = (index, single) => { + if (selectedList.length && !single) { + setTempPlayList(selectedList.value.map(s => ({ listId: props.listId, musicInfo: s }))) + removeAllSelect() + } else { + setTempPlayList([{ listId: props.listId, musicInfo: list.value[index] }]) + } + } + + const doubleClickPlay = index => { + if ( + window.performance.now() - clickTime > 400 || + clickIndex !== index + ) { + clickTime = window.performance.now() + clickIndex = index + return + } + handlePlayMusic(index, true) + clickTime = 0 + clickIndex = -1 + } + + return { + handlePlayMusic, + handlePlayMusicLater, + doubleClickPlay, + } +} diff --git a/src/renderer/views/list/components/MusicList/useSearch.js b/src/renderer/views/list/components/MusicList/useSearch.js new file mode 100644 index 00000000..6e9c7230 --- /dev/null +++ b/src/renderer/views/list/components/MusicList/useSearch.js @@ -0,0 +1,38 @@ +import { ref, onBeforeUnmount } from '@renderer/utils/vueTools' + +export default ({ setSelectedIndex, handlePlayMusic, listRef }) => { + const isShowSearchBar = ref(false) + const searchList = ref([]) + + const handleShowSearchBar = () => { + isShowSearchBar.value = true + } + + const handleMusicSearchAction = ({ action, data: { index, isPlay } = {} }) => { + isShowSearchBar.value = false + switch (action) { + case 'listClick': + if (index < 0) return + listRef.value.scrollToIndex(index, -150, true).then(() => { + setSelectedIndex(index) + setTimeout(() => { + setSelectedIndex(-1) + if (isPlay) handlePlayMusic(index) + }, 600) + }) + break + } + } + + window.eventHub.on('key_mod+f_down', handleShowSearchBar) + + onBeforeUnmount(() => { + window.eventHub.off('key_mod+f_down', handleShowSearchBar) + }) + + return { + isShowSearchBar, + searchList, + handleMusicSearchAction, + } +} diff --git a/src/renderer/views/list/components/MusicList/useSort.js b/src/renderer/views/list/components/MusicList/useSort.js new file mode 100644 index 00000000..4c2ff31e --- /dev/null +++ b/src/renderer/views/list/components/MusicList/useSort.js @@ -0,0 +1,40 @@ +import { ref, nextTick, useCommit } from '@renderer/utils/vueTools' + +export default ({ props, list, selectedList, removeAllSelect }) => { + const isShowListSortModal = ref(false) + const selectedNum = ref(0) + const musicInfo = ref(null) + + const setMusicPosition = useCommit('list', 'setMusicPosition') + + const handleShowSortModal = (index, single) => { + if (selectedList.value.length && !single) { + selectedNum.value = selectedList.value.length + } else { + selectedNum.value = 0 + musicInfo.value = list.value[index] + nextTick(() => { + isShowListSortModal.value = true + }) + } + } + + const sortMusic = num => { + num = Math.min(num, list.value.length) + setMusicPosition({ + id: props.listId, + position: num, + list: selectedNum.value ? [...selectedList.value] : [musicInfo.value], + }) + removeAllSelect() + isShowListSortModal.value = false + } + + return { + isShowListSortModal, + selectedNum, + selectedSortMusicInfo: musicInfo, + handleShowSortModal, + sortMusic, + } +} diff --git a/src/renderer/components/material/SearchList.vue b/src/renderer/views/list/components/SearchList.vue similarity index 95% rename from src/renderer/components/material/SearchList.vue rename to src/renderer/views/list/components/SearchList.vue index 05bef818..b963a494 100644 --- a/src/renderer/components/material/SearchList.vue +++ b/src/renderer/views/list/components/SearchList.vue @@ -25,7 +25,7 @@ div(:class="$style.container" ref="dom_container" v-show="isShow") + + diff --git a/src/renderer/views/setting/components/About.vue b/src/renderer/views/setting/components/About.vue new file mode 100644 index 00000000..6c61391b --- /dev/null +++ b/src/renderer/views/setting/components/About.vue @@ -0,0 +1,75 @@ + +dt#about {{$t('setting__about')}} +dd + p.small + | 本软件完全免费,代码已开源,开源地址: + span.hover.underline(:tips="$t('setting__click_open')" @click="openUrl('https://github.com/lyswhut/lx-music-desktop#readme')") https://github.com/lyswhut/lx-music-desktop + p.small + | 最新版网盘下载地址(网盘内有Windows、MAC版): + span.hover.underline(:tips="$t('setting__click_open')" @click="openUrl('https://www.lanzoui.com/b0bf2cfa/')") 网盘地址 + | 密码: + span.hover(:tips="$t('setting__click_copy')" @click="clipboardWriteText('glqw')") glqw + p.small + | 软件的常见问题可转至: + span.hover.underline(:tips="$t('setting__click_open')" @click="openUrl('https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md')") 常见问题 + p.small + strong 本软件没有客服 + | ,但我们整理了一些常见的使用问题, + strong 仔细 仔细 仔细 + | 地阅读常见问题后, + p.small + | 仍有问题可加企鹅群 + span.hover(:tips="$t('setting__click_open')" @click="openUrl('https://jq.qq.com/?_wv=1027&k=51ECeq2')") 830125506 + | 反馈 + strong (为免满人,无事勿加,入群先看群公告) + | ,或到 GitHub 提交 + span.hover.underline(:tips="$t('setting__click_open')" @click="openUrl('https://github.com/lyswhut/lx-music-desktop/issues')") issue + + br + p.small + | 如果你喜欢并经常使用洛雪音乐,并想要第一时间尝鲜洛雪的新功能 + span(style="text-decoration: line-through;") (当小白鼠) + | , + p + | 可以加入测试企鹅群 + span.hover(:tips="$t('setting__click_open')" @click="openUrl('https://qm.qq.com/cgi-bin/qm/qr?k=zR6aYosQoKb07g4FGFZdO9n9zL1dhFpE&jump_from=webapi')") 768786588 + | ,注意:测试版的功可能会不稳定, + strong 打算潜水的勿加 + + br + p.small 由于软件开发的初衷仅是为了对新技术的学习与研究,因此软件直至停止维护都将会一直保持纯净。 + + p.small + | 你已签署本软件的 + base-btn(min @click="handleShowPact") 许可协议 + | ,协议的在线版本在 + strong.hover.underline(:tips="$t('setting__click_open')" @click="openUrl('https://github.com/lyswhut/lx-music-desktop#%E9%A1%B9%E7%9B%AE%E5%8D%8F%E8%AE%AE')") 这里 + | 。 + br + + p + small By: + | 落雪无痕 + + + diff --git a/src/renderer/views/setting/components/Backup.vue b/src/renderer/views/setting/components/Backup.vue new file mode 100644 index 00000000..82cb90b3 --- /dev/null +++ b/src/renderer/views/setting/components/Backup.vue @@ -0,0 +1,243 @@ + +dt#backup {{$t('setting__backup')}} +dd + h3#backup_part {{$t('setting__backup_part')}} + div + base-btn.btn.gap-left(min @click="handleImportPlayList") {{$t('setting__backup_part_import_list')}} + base-btn.btn.gap-left(min @click="handleExportPlayList") {{$t('setting__backup_part_export_list')}} + base-btn.btn.gap-left(min @click="handleImportSetting") {{$t('setting__backup_part_import_setting')}} + base-btn.btn.gap-left(min @click="handleExportSetting") {{$t('setting__backup_part_export_setting')}} +dd + h3#backup_all {{$t('setting__backup_all')}} + div + base-btn.btn.gap-left(min @click="handleImportAllData") {{$t('setting__backup_all_import')}} + base-btn.btn.gap-left(min @click="handleExportAllData") {{$t('setting__backup_all_export')}} + + + + + diff --git a/src/renderer/views/setting/components/Basic.vue b/src/renderer/views/setting/components/Basic.vue new file mode 100644 index 00000000..02587ce6 --- /dev/null +++ b/src/renderer/views/setting/components/Basic.vue @@ -0,0 +1,235 @@ + +dt#basic {{$t('setting__basic')}} +dd + h3#basic_theme {{$t('setting__basic_theme')}} + div + ul(:class="$style.theme") + li(v-for="theme in themes.list" :key="theme.id" :tips="$t('theme_' + theme.className)" @click="currentStting.themeId = theme.id" :class="[theme.className, {[$style.active]: themes.active == theme.id}]") + span + label {{$t('theme_' + theme.className)}} + +dd + .gap-top.top + base-checkbox(id="setting_show_animate" v-model="currentStting.isShowAnimation" :label="$t('setting__basic_show_animation')") + .gap-top + base-checkbox(id="setting_animate" v-model="currentStting.randomAnimate" :label="$t('setting__basic_animation')") + .gap-top + base-checkbox(id="setting_to_tray" v-model="currentStting.tray.isShow" :label="$t('setting__basic_to_tray')") + +dd(:tips="$t('setting__basic_source_title')") + h3#basic_source {{$t('setting__basic_source')}} + div + .gap-top(v-for="item in apiSources" :key="item.id") + base-checkbox(:id="`setting_api_source_${item.id}`" name="setting_api_source" + need v-model="currentStting.apiSource" :disabled="item.disabled" :value="item.id" :label="item.label") + p.gap-top + base-btn.btn(min @click="isShowUserApiModal = true") {{$t('setting__basic_source_user_api_btn')}} + +dd(:tips="$t('setting__basic_window_size_title')") + h3#basic_window_size {{$t('setting__basic_window_size')}} + div + base-checkbox.gap-left(v-for="(item, index) in windowSizeList" :id="`setting_window_size_${item.id}`" name="setting_window_size" + need v-model="currentStting.windowSizeId" :value="item.id" :label="$t('setting__basic_window_size_' + item.name)" :key="item.id") + +dd(:tips="$t('setting__basic_lang_title')") + h3#basic_lang {{$t('setting__basic_lang')}} + div + base-checkbox.gap-left(v-for="item in langList" :key="item.locale" :id="`setting_lang_${item.locale}`" name="setting_lang" + need v-model="currentStting.langId" :value="item.locale" :label="item.name") + +dd(:tips="$t('setting__basic_sourcename_title')") + h3#basic_sourcename {{$t('setting__basic_sourcename')}} + div + base-checkbox.gap-left(v-for="item in sourceNameTypes" :key="item.id" :id="`setting_abasic_sourcename_${item.id}`" + name="setting_basic_sourcename" need v-model="currentStting.sourceNameType" :value="item.id" :label="item.label") + +dd + h3#basic_control_btn_position {{$t('setting__basic_control_btn_position')}} + div + base-checkbox.gap-left(v-for="item in controlBtnPositionList" :key="item.id" :id="`setting_basic_control_btn_position_${item.id}`" + name="setting_basic_control_btn_position" need v-model="currentStting.controlBtnPosition" :value="item.id" :label="item.name") +teleport(to="#view") + user-api-modal(v-model="isShowUserApiModal") + + + + + diff --git a/src/renderer/views/setting/components/DesktopLyric.vue b/src/renderer/views/setting/components/DesktopLyric.vue new file mode 100644 index 00000000..a780fccb --- /dev/null +++ b/src/renderer/views/setting/components/DesktopLyric.vue @@ -0,0 +1,41 @@ + +dt#desktop_lyric {{$t('setting__desktop_lyric')}} +dd + .gap-top + base-checkbox(id="setting_desktop_lyric_enable" v-model="currentStting.desktopLyric.enable" :label="$t('setting__desktop_lyric_enable')") + .gap-top + base-checkbox(id="setting_desktop_lyric_lock" v-model="currentStting.desktopLyric.isLock" :label="$t('setting__desktop_lyric_lock')") + .gap-top + base-checkbox(id="setting_desktop_lyric_alwaysOnTop" v-model="currentStting.desktopLyric.isAlwaysOnTop" :label="$t('setting__desktop_lyric_always_on_top')") + .gap-top + base-checkbox(id="setting_desktop_lyric_lockScreen" v-model="currentStting.desktopLyric.isLockScreen" :label="$t('setting__desktop_lyric_lock_screen')") +dd + h3#desktop_lyric_font {{$t('setting__desktop_lyric_font')}} + div + base-selection.gap-teft(:list="fontList" v-model="currentStting.desktopLyric.style.font" item-key="id" item-name="label") + + + diff --git a/src/renderer/views/setting/components/Download.vue b/src/renderer/views/setting/components/Download.vue new file mode 100644 index 00000000..91a6871f --- /dev/null +++ b/src/renderer/views/setting/components/Download.vue @@ -0,0 +1,92 @@ + +dt#download {{$t('setting__download')}} +dd + base-checkbox(id="setting_download_enable" v-model="currentStting.download.enable" :label="$t('setting__download_enable')") +dd(:tips="$t('setting__download_path_title')") + h3#download_path {{$t('setting__download_path')}} + div + p + | {{$t('setting__download_path_label')}} + span.auto-hidden.hover(:class="$style.savePath" @click="openDirInExplorer(currentStting.download.savePath)" :tips="$t('setting__download_path_open_label')") {{currentStting.download.savePath}} + p + base-btn.btn(min @click="handleChangeSavePath") {{$t('setting__download_path_change_btn')}} +dd + h3#download_use_other_source {{$t('setting__download_use_other_source')}} + div + base-checkbox(id="setting_download_isUseOtherSource" v-model="currentStting.download.isUseOtherSource" :label="$t('setting__is_enable')") + div +dd(:tips="$t('setting__download_name_title')") + h3#download_name {{$t('setting__download_name')}} + div + base-checkbox.gap-left(:id="`setting_download_musicName_${item.value}`" name="setting_download_musicName" :value="item.value" :key="item.value" need + v-model="currentStting.download.fileName" v-for="item in musicNames" :label="item.name") +dd + h3#download_data_embed {{$t('setting__download_data_embed')}} + .gap-top + base-checkbox(id="setting_download_isEmbedPic" v-model="currentStting.download.isEmbedPic" :label="$t('setting__download_embed_pic')") + .gap-top + base-checkbox(id="setting_download_isEmbedLyric" v-model="currentStting.download.isEmbedLyric" :label="$t('setting__download_embed_lyric')") +dd(:tips="$t('setting__download_lyric_title')") + h3#download_lyric {{$t('setting__download_lyric')}} + div + base-checkbox(id="setting_download_isDownloadLrc" v-model="currentStting.download.isDownloadLrc" :label="$t('setting__is_enable')") +dd + h3#download_lyric {{$t('setting__download_lyric_format')}} + div + base-checkbox.gap-left(v-for="item in lrcFormatList" :key="item.id" :id="`setting_download_lrcFormat_${item.id}`" + name="setting_download_lrcFormat" need v-model="currentStting.download.lrcFormat" :value="item.id" :label="item.name") + + + + + diff --git a/src/renderer/views/setting/components/HotKey.vue b/src/renderer/views/setting/components/HotKey.vue new file mode 100644 index 00000000..059fe8fc --- /dev/null +++ b/src/renderer/views/setting/components/HotKey.vue @@ -0,0 +1,314 @@ + +dt#hot_key {{$t('setting__hot_key')}} +dd + h3#hot_key_local_title {{$t('setting__hot_key_local_title')}} + div + base-checkbox(id="setting_download_hotKeyLocal" v-model="current_hot_key.local.enable" :label="$t('setting__is_enable')" @change="handleHotKeySaveConfig") + div(:class="$style.hotKeyContainer" :style="{ opacity: current_hot_key.local.enable ? 1 : .6 }") + div(:class="$style.hotKeyItem" v-for="item in allHotKeys.local") + h4(:class="$style.hotKeyItemTitle") {{$t('setting__hot_key_' + item.name)}} + base-input.key-bind(:class="$style.hotKeyItemInput" readonly @keyup.prevent :placeholder="$t('setting__hot_key_unset_input')" + :value="hotKeyConfig.local[item.name] && formatHotKeyName(hotKeyConfig.local[item.name].key)" + @focus="handleHotKeyFocus($event, item, 'local')" + @blur="handleHotKeyBlur($event, item, 'local')") + + h3#hot_key_global_title {{$t('setting__hot_key_global_title')}} + div + base-checkbox(id="setting_download_hotKeyGlobal" v-model="current_hot_key.global.enable" :label="$t('setting__is_enable')" @change="handleEnableHotKey") + div(:class="$style.hotKeyContainer" :style="{ opacity: current_hot_key.global.enable ? 1 : .6 }") + div(:class="$style.hotKeyItem" v-for="item in allHotKeys.global") + h4(:class="$style.hotKeyItemTitle") {{$t('setting__hot_key_' + item.name)}} + base-input.key-bind(:class="[$style.hotKeyItemInput, hotKeyConfig.global[item.name] && hotKeyStatus[hotKeyConfig.global[item.name].key] && hotKeyStatus[hotKeyConfig.global[item.name].key].status === false ? $style.hotKeyFailed : null]" + :value="hotKeyConfig.global[item.name] && formatHotKeyName(hotKeyConfig.global[item.name].key)" @input.prevent readonly :placeholder="$t('setting__hot_key_unset_input')" + @focus="handleHotKeyFocus($event, item, 'global')" + @blur="handleHotKeyBlur($event, item, 'global')") + + + + + diff --git a/src/renderer/views/setting/components/List.vue b/src/renderer/views/setting/components/List.vue new file mode 100644 index 00000000..6fce39e8 --- /dev/null +++ b/src/renderer/views/setting/components/List.vue @@ -0,0 +1,30 @@ + +dt#list {{$t('setting__list')}} +dd + .gap-top + base-checkbox(id="setting_list_showSource_enable" v-model="currentStting.list.isShowSource" :label="$t('setting__list_source')") + .gap-top + base-checkbox(id="setting_list_scroll_enable" v-model="currentStting.list.isSaveScrollLocation" :label="$t('setting__list_scroll')") +dd(:tips="$t('setting__basic_sourcename_title')") + h3#list_addMusicLocationType {{$t('setting__list_add_music_location_type')}} + div + base-checkbox.gap-left(id="setting_list_add_music_location_type_top" + name="setting_list_add_music_location_type" need v-model="currentStting.list.addMusicLocationType" value="top" :label="$t('setting__list_add_music_location_type_top')") + base-checkbox.gap-left(id="setting_list_add_music_location_type_bottom" + name="setting_list_add_music_location_type" need v-model="currentStting.list.addMusicLocationType" value="bottom" :label="$t('setting__list_add_music_location_type_bottom')") + + + + diff --git a/src/renderer/views/setting/components/Network.vue b/src/renderer/views/setting/components/Network.vue new file mode 100644 index 00000000..7dc843ff --- /dev/null +++ b/src/renderer/views/setting/components/Network.vue @@ -0,0 +1,57 @@ + +dt#network {{$t('setting__network')}} +dd + h3#network_proxy_title {{$t('setting__network_proxy_title')}} + div + p + base-checkbox(id="setting_network_proxy_enable" v-model="currentStting.network.proxy.enable" :label="$t('setting__is_enable')") + p + base-input.gap-left(v-model.trim="currentStting.network.proxy.host" :placeholder="$t('setting__network_proxy_host')") + base-input.gap-left(v-model.trim="currentStting.network.proxy.port" :placeholder="$t('setting__network_proxy_port')") + p + base-input.gap-left(v-model.trim="currentStting.network.proxy.username" :placeholder="$t('setting__network_proxy_username')") + base-input.gap-left(v-model.trim="currentStting.network.proxy.password" type="password" :placeholder="$t('setting__network_proxy_password')") + + + + diff --git a/src/renderer/views/setting/components/Odc.vue b/src/renderer/views/setting/components/Odc.vue new file mode 100644 index 00000000..738c8580 --- /dev/null +++ b/src/renderer/views/setting/components/Odc.vue @@ -0,0 +1,22 @@ + +dt#odc {{$t('setting__odc')}} +dd + .gap-top + base-checkbox(id="setting_odc_isAutoClearSearchInput" v-model="currentStting.odc.isAutoClearSearchInput" :label="$t('setting__odc_clear_search_input')") + .gap-top + base-checkbox(id="setting_odc_isAutoClearSearchList" v-model="currentStting.odc.isAutoClearSearchList" :label="$t('setting__odc_clear_search_list')") + + + diff --git a/src/renderer/views/setting/components/Other.vue b/src/renderer/views/setting/components/Other.vue new file mode 100644 index 00000000..6ca78224 --- /dev/null +++ b/src/renderer/views/setting/components/Other.vue @@ -0,0 +1,76 @@ + +dt#other {{$t('setting__other')}} +dd + h3#other_tray_theme {{$t('setting__other_tray_theme')}} + div + base-checkbox.gap-left(:id="'setting_tray_theme_' + item.id" v-model="currentStting.tray.themeId" name="setting_tray_theme" need + :label="item.label" :key="item.id" :value="item.id" v-for="item in trayThemeList") +dd + h3#other_resource_cache {{$t('setting__other_resource_cache')}} + div + p + | {{$t('setting__other_resource_cache_label')}} + span.auto-hidden {{cacheSize}} + p + base-btn.btn(min :disabled="isDisabledResourceCacheClear" @click="clearResourceCache") {{$t('setting__other_resource_cache_clear_btn')}} +dd + h3#other_play_list_cache {{$t('setting__other_play_list_cache')}} + div + base-btn.btn(min :disabled="isDisabledListCacheClear" @click="clearListCache") {{$t('setting__other_play_list_cache_clear_btn')}} + + + diff --git a/src/renderer/views/setting/components/Play.vue b/src/renderer/views/setting/components/Play.vue new file mode 100644 index 00000000..b8fa1eb3 --- /dev/null +++ b/src/renderer/views/setting/components/Play.vue @@ -0,0 +1,62 @@ + +dt#play {{$t('setting__play')}} +dd + .gap-top + base-checkbox(id="setting_player_save_play_time" v-model="currentStting.player.isSavePlayTime" :label="$t('setting__play_save_play_time')") + .gap-top + 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_play_lxlrc" v-model="currentStting.player.isPlayLxlrc" :label="$t('setting__play_lyric_lxlrc')") + .gap-top + base-checkbox(id="setting_player_highQuality" v-model="currentStting.player.highQuality" :label="$t('setting__play_quality')") + .gap-top + base-checkbox(id="setting_player_showTaskProgess" v-model="currentStting.player.isShowTaskProgess" :label="$t('setting__play_task_bar')") + .gap-top + base-checkbox(id="setting_player_isMediaDeviceRemovedStopPlay" v-model="currentStting.player.isMediaDeviceRemovedStopPlay" :label="$t('setting__play_mediaDevice_remove_stop_play')") +dd(:tips="$t('setting__play_mediaDevice_title')") + h3#play_mediaDevice {{$t('setting__play_mediaDevice')}} + div + base-selection.gap-left(:list="mediaDevices" v-model="currentStting.player.mediaDeviceId" item-key="deviceId" item-name="label") + + + diff --git a/src/renderer/views/setting/components/Search.vue b/src/renderer/views/setting/components/Search.vue new file mode 100644 index 00000000..b12047a4 --- /dev/null +++ b/src/renderer/views/setting/components/Search.vue @@ -0,0 +1,25 @@ + +dt#search {{$t('setting__search')}} +dd + .gap-top + base-checkbox(id="setting_search_showHot_enable" v-model="currentStting.search.isShowHotSearch" :label="$t('setting__search_hot')") + .gap-top + base-checkbox(id="setting_search_showHistory_enable" v-model="currentStting.search.isShowHistorySearch" :label="$t('setting__search_history')") + .gap-top + base-checkbox(id="setting_search_focusSearchBox_enable" v-model="currentStting.search.isFocusSearchBox" :label="$t('setting__search_focus_search_box')") + + + + diff --git a/src/renderer/views/setting/components/Sync.vue b/src/renderer/views/setting/components/Sync.vue new file mode 100644 index 00000000..44177351 --- /dev/null +++ b/src/renderer/views/setting/components/Sync.vue @@ -0,0 +1,80 @@ + +dt#sync {{$t('setting__sync')}} +dd + base-checkbox(id="setting_sync_enable" v-model="currentStting.sync.enable" :label="syncEnableTitle") + div + p.small {{$t('setting__sync_auth_code', { code: sync.status.code || '' })}} + p.small {{$t('setting__sync_address', { address: sync.status.address.join(', ') || '' })}} + p.small {{$t('setting__sync_device', { devices: syncDevices })}} + p + base-btn.btn(min :disabled="!sync.status.status" @click="refreshSyncCode") {{$t('setting__sync_refresh_code')}} +dd + h3#sync_port {{$t('setting__sync_port')}} + div + p + base-input.gap-left(v-model.trim="currentStting.sync.port" :placeholder="$t('setting__sync_port_tip')") + + + + + diff --git a/src/renderer/views/setting/components/Update.vue b/src/renderer/views/setting/components/Update.vue new file mode 100644 index 00000000..ee70edd1 --- /dev/null +++ b/src/renderer/views/setting/components/Update.vue @@ -0,0 +1,53 @@ + +dt#update {{$t('setting__update')}} +dd + p.small + | {{$t('setting__update_latest_label')}}{{versionInfo.newVersion ? versionInfo.newVersion.version : $t('setting__update_unknown')}} + p.small {{$t('setting__update_current_label')}}{{versionInfo.version}} + p.small(v-if="versionInfo.downloadProgress" style="line-height: 1.5;") + | {{$t('setting__update_downloading')}} + br + | {{$t('setting__update_progress')}}{{downloadProgress}} + p(v-if="versionInfo.newVersion") + span(v-if="versionInfo.isLatestVer") {{$t('setting__update_latest')}} + base-btn.btn.gap-left(v-else min @click="showUpdateModal") {{$t('setting__update_open_version_modal_btn')}} + p.small(v-else) {{$t('setting__update_checking')}} + + + + + diff --git a/src/renderer/components/material/UserApiModal.vue b/src/renderer/views/setting/components/UserApiModal.vue similarity index 75% rename from src/renderer/components/material/UserApiModal.vue rename to src/renderer/views/setting/components/UserApiModal.vue index afe94b68..5a2a006d 100644 --- a/src/renderer/components/material/UserApiModal.vue +++ b/src/renderer/views/setting/components/UserApiModal.vue @@ -1,63 +1,57 @@ -material-modal(:show="visible" bg-close @close="handleClose") +material-modal(:show="modelValue" bg-close @close="handleClose") main(:class="$style.main") - h2 {{$t('material.user_api_modal.title')}} + h2 {{$t('user_api__title')}} ul.scroll(v-if="apiList.length" :class="$style.content") - li(:class="[$style.listItem, setting.apiSource == api.id ? $style.active : null]" v-for="(api, index) in apiList" :key="api.id") + li(:class="[$style.listItem, {[$style.active]: setting.apiSource == api.id}]" v-for="(api, index) in apiList" :key="api.id") div(:class="$style.listLeft") h3 {{api.name}} p {{api.description}} - material-btn(:class="$style.listBtn" outline :tips="$t('material.user_api_modal.btn_remove')" @click.stop="handleRemove(index)") + base-btn(:class="$style.listBtn" outline :tips="$t('user_api__btn_remove')" @click.stop="handleRemove(index)") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' viewBox='0 0 212.982 212.982' space='preserve' v-once) use(xlink:href='#icon-delete') div(v-else :class="$style.content") - div(:class="$style.noitem") {{$t('material.user_api_modal.noitem')}} + div(:class="$style.noitem") {{$t('user_api__noitem')}} div(:class="$style.note") p(:class="[$style.ruleLink]") - | {{$t('material.user_api_modal.readme')}} + | {{$t('user_api__readme')}} span.hover.underline(@click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%BA%90%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E8%AF%B4%E6%98%8E')" tips="https://github.com/lyswhut/lx-music-desktop/blob/master/FAQ.md") FAQ.md - p {{$t('material.user_api_modal.note')}} + p {{$t('user_api__note')}} div(:class="$style.footer") - material-btn(:class="$style.footerBtn" @click="handleImport") {{$t('material.user_api_modal.btn_import')}} - //- material-btn(:class="$style.footerBtn" @click="handleExport") {{$t('material.user_api_modal.btn_export')}} + base-btn(:class="$style.footerBtn" @click="handleImport") {{$t('user_api__btn_import')}} + //- base-btn(:class="$style.footerBtn" @click="handleExport") {{$t('user_api__btn_export')}}