fix: 播放页&功能&界面优化
- 播放页UI(全) - 音量控制优化 - 歌词组件 - 各种控件组件 - 图标打包 - 布局优化
@@ -13,7 +13,7 @@ export default defineConfig({
|
|||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@renderer': resolve('src/renderer/src'),
|
'@renderer': resolve('src/renderer/src'),
|
||||||
'@assets': resolve('src/renderer/assets')
|
'@assets': resolve('src/renderer/src/assets')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: [vue(), vueJsx(), wasm()],
|
plugins: [vue(), vueJsx(), wasm()],
|
||||||
|
|||||||
239
package-lock.json
generated
@@ -40,6 +40,7 @@
|
|||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^5.1.6",
|
"vite": "^5.1.6",
|
||||||
"vite-plugin-node-polyfills": "^0.25.0",
|
"vite-plugin-node-polyfills": "^0.25.0",
|
||||||
|
"vite-svg-loader": "^5.1.0",
|
||||||
"vue-tsc": "^2.0.26"
|
"vue-tsc": "^2.0.26"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2375,6 +2376,16 @@
|
|||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@trysound/sax": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@trysound/sax/-/sax-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/cacheable-request": {
|
"node_modules/@types/cacheable-request": {
|
||||||
"version": "6.0.3",
|
"version": "6.0.3",
|
||||||
"resolved": "https://registry.npmmirror.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
|
||||||
@@ -3408,6 +3419,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/boolbase": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/boolean": {
|
"node_modules/boolean": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmmirror.com/boolean/-/boolean-3.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/boolean/-/boolean-3.2.0.tgz",
|
||||||
@@ -4322,6 +4340,37 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/css-select": {
|
||||||
|
"version": "5.2.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/css-select/-/css-select-5.2.2.tgz",
|
||||||
|
"integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"boolbase": "^1.0.0",
|
||||||
|
"css-what": "^6.1.0",
|
||||||
|
"domhandler": "^5.0.2",
|
||||||
|
"domutils": "^3.0.1",
|
||||||
|
"nth-check": "^2.0.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/fb55"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/css-tree": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mdn-data": "2.0.30",
|
||||||
|
"source-map-js": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/css-vendor": {
|
"node_modules/css-vendor": {
|
||||||
"version": "2.0.8",
|
"version": "2.0.8",
|
||||||
"resolved": "https://registry.npmmirror.com/css-vendor/-/css-vendor-2.0.8.tgz",
|
"resolved": "https://registry.npmmirror.com/css-vendor/-/css-vendor-2.0.8.tgz",
|
||||||
@@ -4332,6 +4381,55 @@
|
|||||||
"is-in-browser": "^1.0.2"
|
"is-in-browser": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/css-what": {
|
||||||
|
"version": "6.2.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/css-what/-/css-what-6.2.2.tgz",
|
||||||
|
"integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/fb55"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/csso": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmmirror.com/csso/-/csso-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"css-tree": "~2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
|
||||||
|
"npm": ">=7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/csso/node_modules/css-tree": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mdn-data": "2.0.28",
|
||||||
|
"source-map-js": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
|
||||||
|
"npm": ">=7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/csso/node_modules/mdn-data": {
|
||||||
|
"version": "2.0.28",
|
||||||
|
"resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.28.tgz",
|
||||||
|
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "CC0-1.0"
|
||||||
|
},
|
||||||
"node_modules/csstype": {
|
"node_modules/csstype": {
|
||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
|
||||||
@@ -4628,6 +4726,34 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dom-serializer": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"domelementtype": "^2.3.0",
|
||||||
|
"domhandler": "^5.0.2",
|
||||||
|
"entities": "^4.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dom-serializer/node_modules/entities": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/domain-browser": {
|
"node_modules/domain-browser": {
|
||||||
"version": "4.22.0",
|
"version": "4.22.0",
|
||||||
"resolved": "https://registry.npmmirror.com/domain-browser/-/domain-browser-4.22.0.tgz",
|
"resolved": "https://registry.npmmirror.com/domain-browser/-/domain-browser-4.22.0.tgz",
|
||||||
@@ -4641,6 +4767,50 @@
|
|||||||
"url": "https://bevry.me/fund"
|
"url": "https://bevry.me/fund"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/domelementtype": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/fb55"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/domhandler": {
|
||||||
|
"version": "5.0.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-5.0.3.tgz",
|
||||||
|
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"domelementtype": "^2.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/domutils": {
|
||||||
|
"version": "3.2.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/domutils/-/domutils-3.2.2.tgz",
|
||||||
|
"integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"dom-serializer": "^2.0.0",
|
||||||
|
"domelementtype": "^2.3.0",
|
||||||
|
"domhandler": "^5.0.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dotenv": {
|
"node_modules/dotenv": {
|
||||||
"version": "9.0.2",
|
"version": "9.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-9.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-9.0.2.tgz",
|
||||||
@@ -7053,6 +7223,13 @@
|
|||||||
"safe-buffer": "^5.1.2"
|
"safe-buffer": "^5.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mdn-data": {
|
||||||
|
"version": "2.0.30",
|
||||||
|
"resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||||
|
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "CC0-1.0"
|
||||||
|
},
|
||||||
"node_modules/memoize-one": {
|
"node_modules/memoize-one": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||||
@@ -7354,6 +7531,19 @@
|
|||||||
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==",
|
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==",
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/nth-check": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"boolbase": "^1.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-inspect": {
|
"node_modules/object-inspect": {
|
||||||
"version": "1.13.4",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz",
|
"resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||||
@@ -8995,6 +9185,42 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/svgo": {
|
||||||
|
"version": "3.3.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/svgo/-/svgo-3.3.2.tgz",
|
||||||
|
"integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@trysound/sax": "0.2.0",
|
||||||
|
"commander": "^7.2.0",
|
||||||
|
"css-select": "^5.1.0",
|
||||||
|
"css-tree": "^2.3.1",
|
||||||
|
"css-what": "^6.1.0",
|
||||||
|
"csso": "^5.0.5",
|
||||||
|
"picocolors": "^1.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"svgo": "bin/svgo"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/svgo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/svgo/node_modules/commander": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/symbol-observable": {
|
"node_modules/symbol-observable": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmmirror.com/symbol-observable/-/symbol-observable-1.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/symbol-observable/-/symbol-observable-1.2.0.tgz",
|
||||||
@@ -9528,6 +9754,19 @@
|
|||||||
"vite": "^2 || ^3 || ^4 || ^5 || ^6 || ^7"
|
"vite": "^2 || ^3 || ^4 || ^5 || ^6 || ^7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vite-svg-loader": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/vite-svg-loader/-/vite-svg-loader-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-M/wqwtOEjgb956/+m5ZrYT/Iq6Hax0OakWbokj8+9PXOnB7b/4AxESHieEtnNEy7ZpjsjYW1/5nK8fATQMmRxw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"svgo": "^3.0.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": ">=3.2.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vm-browserify": {
|
"node_modules/vm-browserify": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmmirror.com/vm-browserify/-/vm-browserify-1.1.2.tgz",
|
"resolved": "https://registry.npmmirror.com/vm-browserify/-/vm-browserify-1.1.2.tgz",
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^5.1.6",
|
"vite": "^5.1.6",
|
||||||
"vite-plugin-node-polyfills": "^0.25.0",
|
"vite-plugin-node-polyfills": "^0.25.0",
|
||||||
|
"vite-svg-loader": "^5.1.0",
|
||||||
"vue-tsc": "^2.0.26"
|
"vue-tsc": "^2.0.26"
|
||||||
},
|
},
|
||||||
"main": "out/main/index.js",
|
"main": "out/main/index.js",
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ function createWindow() {
|
|||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: path.join(__dirname, '../preload/index.js'),
|
preload: path.join(__dirname, '../preload/index.js'),
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
contextIsolation: true
|
contextIsolation: true,
|
||||||
|
webSecurity: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
5
src/renderer/src/assets/airplay.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M11.8633 31.8984C11.8633 29.138 12.3841 26.5469 13.4258 24.125C14.4674 21.6901 15.9128 19.5482 17.7617 17.6992C19.6107 15.8372 21.7526 14.3854 24.1875 13.3438C26.6224 12.2891 29.2266 11.7617 32 11.7617C34.7734 11.7617 37.3776 12.2891 39.8125 13.3438C42.2474 14.3854 44.3893 15.8372 46.2383 17.6992C48.0872 19.5482 49.5326 21.6901 50.5742 24.125C51.6159 26.5469 52.1367 29.138 52.1367 31.8984C52.1367 34.6719 51.6029 37.2826 50.5352 39.7305C49.4674 42.1784 48.0156 44.3073 46.1797 46.1172C46.0365 46.2604 45.8802 46.332 45.7109 46.332C45.5547 46.332 45.4049 46.2539 45.2617 46.0977L44.3438 45.0625C44.0964 44.763 44.1094 44.4635 44.3828 44.1641C45.9453 42.6016 47.1823 40.7656 48.0938 38.6562C49.0182 36.5339 49.4805 34.2812 49.4805 31.8984C49.4805 29.5026 49.0247 27.2565 48.1133 25.1602C47.2018 23.0508 45.9388 21.1953 44.3242 19.5938C42.7227 17.9792 40.8672 16.7161 38.7578 15.8047C36.6484 14.8932 34.3958 14.4375 32 14.4375C29.6042 14.4375 27.3516 14.8932 25.2422 15.8047C23.1328 16.7161 21.2708 17.9792 19.6562 19.5938C18.0547 21.1953 16.7982 23.0508 15.8867 25.1602C14.9753 27.2565 14.5195 29.5026 14.5195 31.8984C14.5195 34.2812 14.9753 36.5273 15.8867 38.6367C16.7982 40.7461 18.0417 42.5885 19.6172 44.1641C19.8776 44.4635 19.8841 44.7565 19.6367 45.043L18.7188 46.0781C18.5885 46.2344 18.4323 46.3125 18.25 46.3125C18.0807 46.3125 17.9245 46.2409 17.7812 46.0977C15.9583 44.2878 14.513 42.1654 13.4453 39.7305C12.3906 37.2826 11.8633 34.6719 11.8633 31.8984ZM17.5469 31.8984C17.5469 29.9193 17.918 28.0573 18.6602 26.3125C19.4154 24.5677 20.457 23.0312 21.7852 21.7031C23.1133 20.375 24.6497 19.3333 26.3945 18.5781C28.1393 17.8229 30.0078 17.4453 32 17.4453C33.9792 17.4453 35.8411 17.8229 37.5859 18.5781C39.3438 19.3333 40.8867 20.375 42.2148 21.7031C43.543 23.0312 44.5781 24.5677 45.3203 26.3125C46.0755 28.0573 46.4531 29.9193 46.4531 31.8984C46.4531 33.8255 46.0885 35.6419 45.3594 37.3477C44.6432 39.0404 43.6667 40.5312 42.4297 41.8203C42.2865 41.9766 42.1237 42.0547 41.9414 42.0547C41.7721 42.0547 41.6224 41.9766 41.4922 41.8203L40.5547 40.7852C40.3073 40.5117 40.3073 40.2122 40.5547 39.8867C41.5573 38.8581 42.3451 37.6602 42.918 36.293C43.4909 34.9128 43.7773 33.4479 43.7773 31.8984C43.7773 30.2839 43.4714 28.7669 42.8594 27.3477C42.2474 25.9284 41.3945 24.6784 40.3008 23.5977C39.2201 22.5169 37.9701 21.6706 36.5508 21.0586C35.1315 20.4336 33.6146 20.1211 32 20.1211C30.3724 20.1211 28.849 20.4336 27.4297 21.0586C26.0104 21.6706 24.7604 22.5169 23.6797 23.5977C22.599 24.6784 21.7526 25.9284 21.1406 27.3477C20.5286 28.7669 20.2227 30.2839 20.2227 31.8984C20.2227 33.4349 20.5026 34.8932 21.0625 36.2734C21.6354 37.6536 22.4232 38.8581 23.4258 39.8867C23.6732 40.2122 23.6732 40.5117 23.4258 40.7852L22.5078 41.8008C22.3776 41.957 22.2214 42.0417 22.0391 42.0547C21.8568 42.0547 21.694 41.9701 21.5508 41.8008C20.3138 40.5117 19.3372 39.0208 18.6211 37.3281C17.9049 35.6224 17.5469 33.8125 17.5469 31.8984ZM23.2305 31.8984C23.2305 30.2969 23.6276 28.832 24.4219 27.5039C25.2161 26.1758 26.2708 25.1146 27.5859 24.3203C28.9141 23.526 30.3854 23.1289 32 23.1289C33.6146 23.1289 35.0794 23.526 36.3945 24.3203C37.7227 25.1146 38.7839 26.1758 39.5781 27.5039C40.3724 28.832 40.7695 30.2969 40.7695 31.8984C40.7695 32.9661 40.5807 33.9753 40.2031 34.9258C39.8255 35.8633 39.3047 36.7031 38.6406 37.4453C38.5104 37.6276 38.3542 37.7188 38.1719 37.7188C38.0026 37.7188 37.8398 37.6406 37.6836 37.4844L36.7266 36.4688C36.4792 36.2214 36.4596 35.9414 36.668 35.6289C37.1237 35.1341 37.4753 34.5677 37.7227 33.9297C37.9701 33.2917 38.0938 32.6146 38.0938 31.8984C38.0938 30.7917 37.8138 29.776 37.2539 28.8516C36.707 27.9271 35.9714 27.1914 35.0469 26.6445C34.1224 26.0846 33.1068 25.8047 32 25.8047C30.8932 25.8047 29.8776 26.0846 28.9531 26.6445C28.0286 27.1914 27.2865 27.9271 26.7266 28.8516C26.1797 29.776 25.9062 30.7917 25.9062 31.8984C25.9062 32.6016 26.0299 33.2721 26.2773 33.9102C26.5247 34.5482 26.8698 35.1146 27.3125 35.6094C27.5078 35.9219 27.4883 36.2018 27.2539 36.4492L26.2969 37.4648C26.1406 37.6211 25.9714 37.6992 25.7891 37.6992C25.6198 37.6992 25.4701 37.6146 25.3398 37.4453C24.6758 36.7031 24.1549 35.8568 23.7773 34.9062C23.4128 33.9557 23.2305 32.9531 23.2305 31.8984ZM19.8906 51.3711C19.4089 51.3711 19.0898 51.1758 18.9336 50.7852C18.7773 50.3945 18.8555 50.0299 19.168 49.6914L31.1016 36.1758C31.3359 35.9023 31.6289 35.7656 31.9805 35.7656C32.332 35.7656 32.625 35.9023 32.8594 36.1758L44.8125 49.6914C45.112 50.0299 45.1836 50.3945 45.0273 50.7852C44.8711 51.1758 44.5586 51.3711 44.0898 51.3711H19.8906Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.6 KiB |
12
src/renderer/src/assets/icon_forward.svg
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<svg id="vector" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 134 134"
|
||||||
|
fill="currentColor" color="#fff">
|
||||||
|
<path
|
||||||
|
d="M62 60.0717C65.938 62.3453 67.9069 63.4821 68.5677 64.9662C69.1441 66.2608 69.1441 67.7391 68.5677 69.0336C67.9069 70.5177 65.938 71.6545 62 73.9281L41 86.0525C37.062 88.326 35.0931 89.4628 33.4774 89.293C32.0681 89.1449 30.7878 88.4057 29.9549 87.2593C29 85.945 29 83.6714 29 79.1243V54.8755C29 50.3284 29 48.0548 29.9549 46.7405C30.7878 45.5941 32.0681 44.8549 33.4774 44.7068C35.0931 44.537 37.062 45.6738 41 47.9473L62 60.0717Z"
|
||||||
|
class="amll-forward-left-arrow"></path>
|
||||||
|
<path
|
||||||
|
d="M62 60.0717C65.938 62.3453 67.9069 63.4821 68.5677 64.9662C69.1441 66.2608 69.1441 67.7391 68.5677 69.0336C67.9069 70.5177 65.938 71.6545 62 73.9281L41 86.0525C37.062 88.326 35.0931 89.4628 33.4774 89.293C32.0681 89.1449 30.7878 88.4057 29.9549 87.2593C29 85.945 29 83.6714 29 79.1243V54.8755C29 50.3284 29 48.0548 29.9549 46.7405C30.7878 45.5941 32.0681 44.8549 33.4774 44.7068C35.0931 44.537 37.062 45.6738 41 47.9473L62 60.0717Z"
|
||||||
|
class="amll-forward-left-standby"></path>
|
||||||
|
<path
|
||||||
|
d="M102 60.0717C105.938 62.3453 107.907 63.4821 108.568 64.9662C109.144 66.2608 109.144 67.7391 108.568 69.0336C107.907 70.5177 105.938 71.6545 102 73.9281L81 86.0525C77.062 88.326 75.0931 89.4628 73.4774 89.293C72.0681 89.1449 70.7878 88.4057 69.9549 87.2593C69 85.945 69 83.6714 69 79.1243V54.8755C69 50.3284 69 48.0548 69.9549 46.7405C70.7878 45.5941 72.0681 44.8549 73.4774 44.7068C75.0931 44.537 77.062 45.6738 81 47.9473L102 60.0717Z"
|
||||||
|
class="amll-forward-right-arrow"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
5
src/renderer/src/assets/icon_pause.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg id="vector" width="38" height="38" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg" color="#fff">
|
||||||
|
<path
|
||||||
|
d="M8.46953 37C7.37801 37 6.56603 36.7271 6.03359 36.1814C5.51445 35.6489 5.25488 34.8502 5.25488 33.7854V4.21464C5.25488 3.14975 5.52111 2.35108 6.05355 1.81864C6.59931 1.27288 7.40463 1 8.46953 1H13.3813C14.4329 1 15.2249 1.27288 15.7574 1.81864C16.3031 2.35108 16.576 3.14975 16.576 4.21464V33.7854C16.576 34.8502 16.3031 35.6489 15.7574 36.1814C15.2249 36.7271 14.4329 37 13.3813 37H8.46953ZM24.6426 37C23.5644 37 22.759 36.7271 22.2266 36.1814C21.6942 35.6489 21.4279 34.8502 21.4279 33.7854V4.21464C21.4279 3.14975 21.6942 2.35108 22.2266 1.81864C22.7724 1.27288 23.5777 1 24.6426 1H29.5544C30.6193 1 31.4179 1.27288 31.9504 1.81864C32.4828 2.35108 32.7491 3.14975 32.7491 4.21464V33.7854C32.7491 34.8502 32.4828 35.6489 31.9504 36.1814C31.4179 36.7271 30.6193 37 29.5544 37H24.6426Z"
|
||||||
|
fill="currentColor" fill-rule="nonzero" id="path_0"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 989 B |
5
src/renderer/src/assets/icon_play.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg id="vector" width="38" height="38" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg" color="#fff">
|
||||||
|
<path
|
||||||
|
d="M5.80762 32.4896V5.4925C5.80762 4.305 6.12305 3.41438 6.75391 2.82063C7.38477 2.22688 8.13932 1.93 9.01758 1.93C9.78451 1.93 10.5391 2.14029 11.2812 2.56086L33.7324 15.6605C34.5859 16.1553 35.223 16.6562 35.6436 17.1634C36.0641 17.6582 36.2744 18.2705 36.2744 19.0003C36.2744 19.7054 36.0641 20.3177 35.6436 20.8372C35.223 21.3444 34.5859 21.8392 33.7324 22.3216L11.2812 35.4212C10.5391 35.8542 9.78451 36.0706 9.01758 36.0706C8.13932 36.0706 7.38477 35.7676 6.75391 35.1614C6.12305 34.5677 5.80762 33.6771 5.80762 32.4896Z"
|
||||||
|
fill="currentColor" fill-rule="nonzero"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 713 B |
12
src/renderer/src/assets/icon_rewind.svg
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<svg id="vector" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 134 134"
|
||||||
|
fill="currentColor" color="#fff">
|
||||||
|
<path
|
||||||
|
d="M72 60.0717C68.062 62.3453 66.0931 63.4821 65.4323 64.9662C64.8559 66.2608 64.8559 67.7391 65.4323 69.0336C66.0931 70.5177 68.062 71.6545 72 73.9281L93 86.0525C96.938 88.326 98.9069 89.4628 100.523 89.293C101.932 89.1449 103.212 88.4057 104.045 87.2593C105 85.945 105 83.6714 105 79.1243V54.8755C105 50.3284 105 48.0548 104.045 46.7405C103.212 45.5941 101.932 44.8549 100.523 44.7068C98.9069 44.537 96.938 45.6738 93 47.9473L72 60.0717Z"
|
||||||
|
class="amll-rewind-right-arrow" />
|
||||||
|
<path
|
||||||
|
d="M72 60.0717C68.062 62.3453 66.0931 63.4821 65.4323 64.9662C64.8559 66.2608 64.8559 67.7391 65.4323 69.0336C66.0931 70.5177 68.062 71.6545 72 73.9281L93 86.0525C96.938 88.326 98.9069 89.4628 100.523 89.293C101.932 89.1449 103.212 88.4057 104.045 87.2593C105 85.945 105 83.6714 105 79.1243V54.8755C105 50.3284 105 48.0548 104.045 46.7405C103.212 45.5941 101.932 44.8549 100.523 44.7068C98.9069 44.537 96.938 45.6738 93 47.9473L72 60.0717Z"
|
||||||
|
class="amll-rewind-right-standby" />
|
||||||
|
<path
|
||||||
|
d="M32 60.0717C28.062 62.3453 26.0931 63.4821 25.4323 64.9662C24.8559 66.2608 24.8559 67.7391 25.4323 69.0336C26.0931 70.5177 28.062 71.6545 32 73.9281L53 86.0525C56.938 88.326 58.9069 89.4628 60.5226 89.293C61.9319 89.1449 63.2122 88.4057 64.0451 87.2593C65 85.945 65 83.6714 65 79.1243V54.8755C65 50.3284 65 48.0548 64.0451 46.7405C63.2122 45.5941 61.9319 44.8549 60.5226 44.7068C58.9069 44.537 56.938 45.6738 53 47.9473L32 60.0717Z"
|
||||||
|
class="amll-rewind-left-arrow" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
5
src/renderer/src/assets/icon_speaker.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="32" height="40" viewBox="0 0 32 40" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M14.9042 27.1802C14.4202 27.1802 14.0473 26.9897 13.595 26.5612L10.3815 23.5461C10.3339 23.5065 10.2863 23.4906 10.2228 23.4906H8.01703C6.70778 23.4906 5.99365 22.7527 5.99365 21.38V18.4442C5.99365 17.0715 6.70778 16.3257 8.01703 16.3257H10.2307C10.2863 16.3257 10.3418 16.3019 10.3815 16.2622L13.595 13.2709C14.079 12.8107 14.4361 12.6282 14.8883 12.6282C15.6104 12.6282 16.142 13.1915 16.142 13.8977V25.9344C16.142 26.6406 15.6104 27.1802 14.9042 27.1802Z"
|
||||||
|
class="speaker-bounce-1"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 628 B |
14
src/renderer/src/assets/icon_speaker_3.svg
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<svg width="43" height="40" viewBox="0 0 43 40" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M24.0403 27.1802C23.5642 27.1802 23.1913 26.9897 22.739 26.5612L19.5176 23.5461C19.4779 23.5065 19.4224 23.4906 19.3668 23.4906H17.161C15.8518 23.4906 15.1377 22.7527 15.1377 21.38V18.4442C15.1377 17.0715 15.8518 16.3257 17.161 16.3257H19.3668C19.4303 16.3257 19.4779 16.3019 19.5255 16.2622L22.739 13.2709C23.223 12.8107 23.5721 12.6282 24.0324 12.6282C24.7544 12.6282 25.286 13.1915 25.286 13.8977V25.9344C25.286 26.6406 24.7544 27.1802 24.0403 27.1802Z"
|
||||||
|
class="speaker-bounce-1"/>
|
||||||
|
<path
|
||||||
|
d="M28.0948 23.6653C27.6028 23.3559 27.4996 22.7687 27.8964 22.1101C28.2931 21.4991 28.5232 20.7136 28.5232 19.8964C28.5232 19.0712 28.301 18.2856 27.8964 17.6826C27.4917 17.032 27.6028 16.4369 28.0948 16.1274C28.547 15.8418 29.1104 15.9529 29.404 16.3576C30.0863 17.3097 30.491 18.5713 30.491 19.8964C30.491 21.2214 30.0863 22.4831 29.404 23.4273C29.1104 23.8399 28.547 23.943 28.0948 23.6653Z"
|
||||||
|
class="speaker-bounce-2"/>
|
||||||
|
<path
|
||||||
|
d="M31.6733 25.8711C31.1576 25.5696 31.0942 24.9428 31.4432 24.3794C32.2526 23.1257 32.7207 21.5468 32.7207 19.8964C32.7207 18.2459 32.2605 16.6591 31.4432 15.4133C31.0942 14.8499 31.1576 14.2231 31.6733 13.9137C32.1415 13.6439 32.7128 13.755 33.0143 14.2152C34.0855 15.7783 34.6885 17.8016 34.6885 19.8964C34.6885 21.9911 34.0775 23.9985 33.0143 25.5775C32.7128 26.0377 32.1415 26.1488 31.6733 25.8711Z"
|
||||||
|
class="speaker-bounce-3"/>
|
||||||
|
<path
|
||||||
|
d="M35.2362 28.1007C34.7363 27.7992 34.6569 27.1803 34.9981 26.6249C36.1883 24.7286 36.9104 22.4196 36.9104 19.9122C36.9104 17.397 36.1883 15.0881 34.9981 13.1917C34.6569 12.6362 34.7363 12.0174 35.2362 11.7159C35.7123 11.4302 36.3073 11.5651 36.6088 12.0571C38.0133 14.2866 38.8702 16.9765 38.8702 19.9122C38.8702 22.8401 38.0291 25.5379 36.6088 27.7675C36.3073 28.2515 35.7123 28.3864 35.2362 28.1007Z"
|
||||||
|
class="speaker-bounce-4"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
BIN
src/renderer/src/assets/lyricfont.ttf
Normal file
5
src/renderer/src/assets/lyrics_off.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M22.8594 53.9102C22 53.9102 21.3229 53.6302 20.8281 53.0703C20.3464 52.5104 20.1055 51.7617 20.1055 50.8242V46.1953H18.9727C17.1237 46.1953 15.5156 45.8242 14.1484 45.082C12.7812 44.3398 11.7201 43.2721 10.9648 41.8789C10.2227 40.4857 9.85156 38.8125 9.85156 36.8594V21.5664C9.85156 19.6133 10.2161 17.9401 10.9453 16.5469C11.6875 15.1536 12.7552 14.0859 14.1484 13.3438C15.5417 12.5885 17.2214 12.2109 19.1875 12.2109H44.793C46.7721 12.2109 48.4518 12.5885 49.832 13.3438C51.2253 14.0859 52.2865 15.1536 53.0156 16.5469C53.7578 17.9401 54.1289 19.6133 54.1289 21.5664V36.8594C54.1289 38.8125 53.7578 40.4857 53.0156 41.8789C52.2865 43.2721 51.2253 44.3398 49.832 45.082C48.4518 45.8242 46.7721 46.1953 44.793 46.1953H33.0742L26.4336 52.0547C25.7044 52.6927 25.0729 53.1615 24.5391 53.4609C24.0182 53.7604 23.4583 53.9102 22.8594 53.9102ZM23.9141 49.0469L30.0859 42.9727C30.5156 42.5299 30.9193 42.237 31.2969 42.0938C31.6745 41.9375 32.1758 41.8594 32.8008 41.8594H44.5977C46.3424 41.8594 47.6445 41.4232 48.5039 40.5508C49.3633 39.6784 49.793 38.3828 49.793 36.6641V21.7422C49.793 20.0365 49.3633 18.7474 48.5039 17.875C47.6445 17.0026 46.3424 16.5664 44.5977 16.5664H19.3828C17.625 16.5664 16.3164 17.0026 15.457 17.875C14.6107 18.7474 14.1875 20.0365 14.1875 21.7422V36.6641C14.1875 38.3828 14.6107 39.6784 15.457 40.5508C16.3164 41.4232 17.625 41.8594 19.3828 41.8594H22.2344C22.8073 41.8594 23.2305 41.9896 23.5039 42.25C23.7773 42.4974 23.9141 42.9271 23.9141 43.5391V49.0469ZM22.4492 27.1914C22.4492 25.9935 22.8529 25.0104 23.6602 24.2422C24.4674 23.474 25.4701 23.0898 26.668 23.0898C28.0221 23.0898 29.1029 23.5716 29.9102 24.5352C30.7305 25.4857 31.1406 26.6576 31.1406 28.0508C31.1406 29.2096 30.9323 30.2383 30.5156 31.1367C30.112 32.0221 29.5911 32.7708 28.9531 33.3828C28.3281 33.9948 27.6771 34.457 27 34.7695C26.3229 35.082 25.7174 35.2383 25.1836 35.2383C24.8841 35.2383 24.6367 35.1536 24.4414 34.9844C24.2461 34.8151 24.1484 34.5938 24.1484 34.3203C24.1484 34.0859 24.2135 33.8906 24.3438 33.7344C24.487 33.5651 24.7214 33.4414 25.0469 33.3633C25.5677 33.2331 26.0625 33.0312 26.5312 32.7578C27.013 32.4714 27.4362 32.1328 27.8008 31.7422C28.1654 31.3385 28.4453 30.8828 28.6406 30.375H28.3867C28.1263 30.7005 27.7943 30.9284 27.3906 31.0586C26.987 31.1758 26.5573 31.2344 26.1016 31.2344C25.0078 31.2344 24.1224 30.8503 23.4453 30.082C22.7812 29.3008 22.4492 28.3372 22.4492 27.1914ZM33.0742 27.1914C33.0742 25.9935 33.4714 25.0104 34.2656 24.2422C35.0729 23.474 36.082 23.0898 37.293 23.0898C38.6471 23.0898 39.7279 23.5716 40.5352 24.5352C41.3555 25.4857 41.7656 26.6576 41.7656 28.0508C41.7656 29.2096 41.5573 30.2383 41.1406 31.1367C40.737 32.0221 40.2161 32.7708 39.5781 33.3828C38.9531 33.9948 38.2956 34.457 37.6055 34.7695C36.9284 35.082 36.3294 35.2383 35.8086 35.2383C35.5091 35.2383 35.2617 35.1536 35.0664 34.9844C34.8711 34.8151 34.7734 34.5938 34.7734 34.3203C34.7734 34.0859 34.8385 33.8906 34.9688 33.7344C35.112 33.5651 35.3529 33.4414 35.6914 33.3633C36.1992 33.2331 36.6875 33.0312 37.1562 32.7578C37.638 32.4714 38.0612 32.1328 38.4258 31.7422C38.7904 31.3385 39.0703 30.8828 39.2656 30.375H39.0117C38.7513 30.7005 38.4193 30.9284 38.0156 31.0586C37.612 31.1758 37.1823 31.2344 36.7266 31.2344C35.6328 31.2344 34.7474 30.8503 34.0703 30.082C33.4062 29.3008 33.0742 28.3372 33.0742 27.1914Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.4 KiB |
8
src/renderer/src/assets/lyrics_on.svg
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
|
d="M1.91256 7.67068C0 11.0858 0 15.6405 0 24.75V39.25C0 48.3595 0 52.9142 1.91256 56.3293C3.26425 58.7429 5.25707 60.7357 7.67068 62.0874C11.0858 64 15.6405 64 24.75 64H39.25C48.3595 64 52.9142 64 56.3293 62.0874C58.7429 60.7357 60.7357 58.7429 62.0874 56.3293C64 52.9142 64 48.3595 64 39.25V24.75C64 15.6405 64 11.0858 62.0874 7.67068C60.7357 5.25707 58.7429 3.26425 56.3293 1.91256C52.9142 0 48.3595 0 39.25 0H24.75C15.6405 0 11.0858 0 7.67068 1.91256C5.25707 3.26425 3.26425 5.25707 1.91256 7.67068ZM20.8281 53.0703C21.3229 53.6302 22 53.9102 22.8594 53.9102C23.4583 53.9102 24.0182 53.7604 24.5391 53.4609C25.0729 53.1615 25.7044 52.6927 26.4336 52.0547L33.0742 46.1953H44.793C46.7721 46.1953 48.4518 45.8242 49.832 45.082C51.2253 44.3398 52.2865 43.2721 53.0156 41.8789C53.7578 40.4857 54.1289 38.8125 54.1289 36.8594V21.5664C54.1289 19.6133 53.7578 17.9401 53.0156 16.5469C52.2865 15.1536 51.2253 14.0859 49.832 13.3438C48.4518 12.5885 46.7721 12.2109 44.793 12.2109H19.1875C17.2214 12.2109 15.5417 12.5885 14.1484 13.3438C12.7552 14.0859 11.6875 15.1536 10.9453 16.5469C10.2161 17.9401 9.85156 19.6133 9.85156 21.5664V36.8594C9.85156 38.8125 10.2227 40.4857 10.9648 41.8789C11.7201 43.2721 12.7812 44.3398 14.1484 45.082C15.5156 45.8242 17.1237 46.1953 18.9727 46.1953H20.1055V50.8242C20.1055 51.7617 20.3464 52.5104 20.8281 53.0703Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
<path
|
||||||
|
d="M22.4492 27.1914C22.4492 25.9935 22.8529 25.0104 23.6602 24.2422C24.4674 23.474 25.4701 23.0898 26.668 23.0898C28.0221 23.0898 29.1029 23.5716 29.9102 24.5352C30.7305 25.4857 31.1406 26.6576 31.1406 28.0508C31.1406 29.2096 30.9323 30.2383 30.5156 31.1367C30.112 32.0221 29.5911 32.7708 28.9531 33.3828C28.3281 33.9948 27.6771 34.457 27 34.7695C26.3229 35.082 25.7174 35.2383 25.1836 35.2383C24.8841 35.2383 24.6367 35.1536 24.4414 34.9844C24.2461 34.8151 24.1484 34.5938 24.1484 34.3203C24.1484 34.0859 24.2135 33.8906 24.3438 33.7344C24.487 33.5651 24.7214 33.4414 25.0469 33.3633C25.5677 33.2331 26.0625 33.0312 26.5312 32.7578C27.013 32.4714 27.4362 32.1328 27.8008 31.7422C28.1654 31.3385 28.4453 30.8828 28.6406 30.375H28.3867C28.1263 30.7005 27.7943 30.9284 27.3906 31.0586C26.987 31.1758 26.5573 31.2344 26.1016 31.2344C25.0078 31.2344 24.1224 30.8503 23.4453 30.082C22.7812 29.3008 22.4492 28.3372 22.4492 27.1914ZM33.0742 27.1914C33.0742 25.9935 33.4714 25.0104 34.2656 24.2422C35.0729 23.474 36.082 23.0898 37.293 23.0898C38.6471 23.0898 39.7279 23.5716 40.5352 24.5352C41.3555 25.4857 41.7656 26.6576 41.7656 28.0508C41.7656 29.2096 41.5573 30.2383 41.1406 31.1367C40.737 32.0221 40.2161 32.7708 39.5781 33.3828C38.9531 33.9948 38.2956 34.457 37.6055 34.7695C36.9284 35.082 36.3294 35.2383 35.8086 35.2383C35.5091 35.2383 35.2617 35.1536 35.0664 34.9844C34.8711 34.8151 34.7734 34.5938 34.7734 34.3203C34.7734 34.0859 34.8385 33.8906 34.9688 33.7344C35.112 33.5651 35.3529 33.4414 35.6914 33.3633C36.1992 33.2331 36.6875 33.0312 37.1562 32.7578C37.638 32.4714 38.0612 32.1328 38.4258 31.7422C38.7904 31.3385 39.0703 30.8828 39.2656 30.375H39.0117C38.7513 30.7005 38.4193 30.9284 38.0156 31.0586C37.612 31.1758 37.1823 31.2344 36.7266 31.2344C35.6328 31.2344 34.7474 30.8503 34.0703 30.082C33.4062 29.3008 33.0742 28.3372 33.0742 27.1914Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.4 KiB |
5
src/renderer/src/assets/playlist_off.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M23.9922 21.8594C23.4062 21.8594 22.9115 21.6641 22.5078 21.2734C22.1172 20.8698 21.9219 20.375 21.9219 19.7891C21.9219 19.2161 22.1172 18.7279 22.5078 18.3242C22.9115 17.9206 23.4062 17.7188 23.9922 17.7188H50.418C50.9909 17.7188 51.4792 17.9206 51.8828 18.3242C52.2865 18.7279 52.4883 19.2161 52.4883 19.7891C52.4883 20.375 52.2865 20.8698 51.8828 21.2734C51.4792 21.6641 50.9909 21.8594 50.418 21.8594H23.9922ZM23.9922 33.9883C23.4062 33.9883 22.9115 33.7865 22.5078 33.3828C22.1172 32.9792 21.9219 32.4909 21.9219 31.918C21.9219 31.3451 22.1172 30.8633 22.5078 30.4727C22.9115 30.069 23.4062 29.8672 23.9922 29.8672H50.418C50.9909 29.8672 51.4792 30.069 51.8828 30.4727C52.2865 30.8633 52.4883 31.3451 52.4883 31.918C52.4883 32.5039 52.2865 32.9987 51.8828 33.4023C51.4792 33.793 50.9909 33.9883 50.418 33.9883H23.9922ZM23.9922 46.1172C23.4062 46.1172 22.9115 45.9219 22.5078 45.5312C22.1172 45.1276 21.9219 44.6328 21.9219 44.0469C21.9219 43.474 22.1172 42.9857 22.5078 42.582C22.9115 42.1784 23.4062 41.9766 23.9922 41.9766H50.418C50.9909 41.9766 51.4792 42.1784 51.8828 42.582C52.2865 42.9857 52.4883 43.474 52.4883 44.0469C52.4883 44.6328 52.2865 45.1276 51.8828 45.5312C51.4792 45.9219 50.9909 46.1172 50.418 46.1172H23.9922ZM14.4805 22.7383C13.6602 22.7383 12.957 22.4518 12.3711 21.8789C11.7982 21.306 11.5117 20.6094 11.5117 19.7891C11.5117 18.9688 11.7982 18.2721 12.3711 17.6992C12.957 17.1263 13.6602 16.8398 14.4805 16.8398C15.2878 16.8398 15.9844 17.1263 16.5703 17.6992C17.1562 18.2721 17.4492 18.9688 17.4492 19.7891C17.4492 20.6094 17.1562 21.306 16.5703 21.8789C15.9844 22.4518 15.2878 22.7383 14.4805 22.7383ZM14.4805 34.8867C13.6602 34.8867 12.957 34.5938 12.3711 34.0078C11.7982 33.4219 11.5117 32.7253 11.5117 31.918C11.5117 31.1107 11.7982 30.4141 12.3711 29.8281C12.957 29.2422 13.6602 28.9492 14.4805 28.9492C15.2878 28.9492 15.9844 29.2422 16.5703 29.8281C17.1562 30.4141 17.4492 31.1107 17.4492 31.918C17.4492 32.7253 17.1562 33.4219 16.5703 34.0078C15.9844 34.5938 15.2878 34.8867 14.4805 34.8867ZM14.4805 47.0156C13.6602 47.0156 12.957 46.7227 12.3711 46.1367C11.7982 45.5638 11.5117 44.8672 11.5117 44.0469C11.5117 43.2266 11.7982 42.5299 12.3711 41.957C12.957 41.3841 13.6602 41.0977 14.4805 41.0977C15.2878 41.0977 15.9844 41.3841 16.5703 41.957C17.1562 42.5299 17.4492 43.2266 17.4492 44.0469C17.4492 44.8672 17.1562 45.5638 16.5703 46.1367C15.9844 46.7227 15.2878 47.0156 14.4805 47.0156Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
5
src/renderer/src/assets/playlist_on.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
|
d="M1.91256 7.67068C0 11.0858 0 15.6405 0 24.75V39.25C0 48.3595 0 52.9142 1.91256 56.3293C3.26425 58.7429 5.25707 60.7357 7.67068 62.0874C11.0858 64 15.6405 64 24.75 64H39.25C48.3595 64 52.9142 64 56.3293 62.0874C58.7429 60.7357 60.7357 58.7429 62.0874 56.3293C64 52.9142 64 48.3595 64 39.25V24.75C64 15.6405 64 11.0858 62.0874 7.67068C60.7357 5.25706 58.7429 3.26425 56.3293 1.91256C52.9142 0 48.3595 0 39.25 0H24.75C15.6405 0 11.0858 0 7.67068 1.91256C5.25707 3.26425 3.26425 5.25706 1.91256 7.67068ZM22.5078 21.2734C22.9115 21.6641 23.4062 21.8594 23.9922 21.8594H50.418C50.9909 21.8594 51.4792 21.6641 51.8828 21.2734C52.2865 20.8698 52.4883 20.375 52.4883 19.7891C52.4883 19.2161 52.2865 18.7279 51.8828 18.3242C51.4792 17.9206 50.9909 17.7188 50.418 17.7188H23.9922C23.4062 17.7188 22.9115 17.9206 22.5078 18.3242C22.1172 18.7279 21.9219 19.2161 21.9219 19.7891C21.9219 20.375 22.1172 20.8698 22.5078 21.2734ZM22.5078 33.3828C22.9115 33.7865 23.4062 33.9883 23.9922 33.9883H50.418C50.9909 33.9883 51.4792 33.793 51.8828 33.4023C52.2865 32.9987 52.4883 32.5039 52.4883 31.918C52.4883 31.3451 52.2865 30.8633 51.8828 30.4727C51.4792 30.069 50.9909 29.8672 50.418 29.8672H23.9922C23.4062 29.8672 22.9115 30.069 22.5078 30.4727C22.1172 30.8633 21.9219 31.3451 21.9219 31.918C21.9219 32.4909 22.1172 32.9792 22.5078 33.3828ZM22.5078 45.5312C22.9115 45.9219 23.4062 46.1172 23.9922 46.1172H50.418C50.9909 46.1172 51.4792 45.9219 51.8828 45.5312C52.2865 45.1276 52.4883 44.6328 52.4883 44.0469C52.4883 43.474 52.2865 42.9857 51.8828 42.582C51.4792 42.1784 50.9909 41.9766 50.418 41.9766H23.9922C23.4062 41.9766 22.9115 42.1784 22.5078 42.582C22.1172 42.9857 21.9219 43.474 21.9219 44.0469C21.9219 44.6328 22.1172 45.1276 22.5078 45.5312ZM12.3711 21.8789C12.957 22.4518 13.6602 22.7383 14.4805 22.7383C15.2878 22.7383 15.9844 22.4518 16.5703 21.8789C17.1562 21.306 17.4492 20.6094 17.4492 19.7891C17.4492 18.9688 17.1562 18.2721 16.5703 17.6992C15.9844 17.1263 15.2878 16.8398 14.4805 16.8398C13.6602 16.8398 12.957 17.1263 12.3711 17.6992C11.7982 18.2721 11.5117 18.9688 11.5117 19.7891C11.5117 20.6094 11.7982 21.306 12.3711 21.8789ZM12.3711 34.0078C12.957 34.5938 13.6602 34.8867 14.4805 34.8867C15.2878 34.8867 15.9844 34.5938 16.5703 34.0078C17.1562 33.4219 17.4492 32.7253 17.4492 31.918C17.4492 31.1107 17.1562 30.4141 16.5703 29.8281C15.9844 29.2422 15.2878 28.9492 14.4805 28.9492C13.6602 28.9492 12.957 29.2422 12.3711 29.8281C11.7982 30.4141 11.5117 31.1107 11.5117 31.918C11.5117 32.7253 11.7982 33.4219 12.3711 34.0078ZM12.3711 46.1367C12.957 46.7227 13.6602 47.0156 14.4805 47.0156C15.2878 47.0156 15.9844 46.7227 16.5703 46.1367C17.1562 45.5638 17.4492 44.8672 17.4492 44.0469C17.4492 43.2266 17.1562 42.5299 16.5703 41.957C15.9844 41.3841 15.2878 41.0977 14.4805 41.0977C13.6602 41.0977 12.957 41.3841 12.3711 41.957C11.7982 42.5299 11.5117 43.2266 11.5117 44.0469C11.5117 44.8672 11.7982 45.5638 12.3711 46.1367Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.0 KiB |
3
src/renderer/src/assets/repeat-active.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M37.5805 0H18.4195C12.0146 0 9.69208 0.666878 7.35056 1.91914C5.00904 3.1714 3.1714 5.00904 1.91914 7.35056C0.666879 9.69208 0 12.0146 0 18.4195V37.5805C0 43.9854 0.666879 46.3079 1.91914 48.6494C3.1714 50.991 5.00904 52.8286 7.35056 54.0809C9.69208 55.3331 12.0146 56 18.4195 56H37.5805C43.9854 56 46.3079 55.3331 48.6495 54.0809C50.991 52.8286 52.8286 50.991 54.0809 48.6494C55.3331 46.3079 56 43.9854 56 37.5805V18.4195C56 12.0146 55.3331 9.69208 54.0809 7.35056C52.8286 5.00904 50.991 3.1714 48.6495 1.91914C46.3079 0.666878 43.9854 0 37.5805 0ZM12.7334 28.376C13.1465 28.7891 13.6519 28.9956 14.2495 28.9956C14.8296 28.9956 15.3262 28.7891 15.7393 28.376C16.1523 27.9541 16.3589 27.4531 16.3589 26.873V25.9502C16.3589 24.7812 16.7236 23.8628 17.4531 23.1948C18.1826 22.5269 19.1846 22.1929 20.459 22.1929H30.3599V25.0669C30.3599 25.5415 30.4961 25.9194 30.7686 26.2007C31.041 26.4819 31.4146 26.6226 31.8892 26.6226C32.1177 26.6226 32.3198 26.5874 32.4956 26.5171C32.6714 26.4468 32.8296 26.3501 32.9702 26.2271L38.7578 21.3096C39.1094 21.0107 39.2852 20.6504 39.2852 20.2285C39.2939 19.7979 39.1182 19.4331 38.7578 19.1343L32.9702 14.2168C32.8296 14.1113 32.6714 14.0234 32.4956 13.9531C32.3198 13.874 32.1177 13.8345 31.8892 13.8345C31.4146 13.8345 31.041 13.9751 30.7686 14.2563C30.4961 14.5288 30.3599 14.9111 30.3599 15.4033V18.0532H20.6436C18.877 18.0532 17.3564 18.3477 16.082 18.9365C14.8076 19.5166 13.8276 20.3516 13.1421 21.4414C12.4565 22.5312 12.1138 23.832 12.1138 25.3438V26.873C12.1138 27.4531 12.3203 27.9541 12.7334 28.376ZM43.2402 27.3213C42.8271 26.9082 42.3306 26.7017 41.7505 26.7017C41.1528 26.7017 40.6475 26.9082 40.2344 27.3213C39.8301 27.7344 39.6279 28.2354 39.6279 28.8242V29.7471C39.6279 30.916 39.2632 31.8345 38.5337 32.5024C37.8042 33.1616 36.8022 33.4912 35.5278 33.4912H25.627V30.6172C25.627 30.125 25.4907 29.7427 25.2183 29.4702C24.9458 29.189 24.5723 29.0483 24.0977 29.0483C23.8691 29.0483 23.6626 29.0835 23.478 29.1538C23.3022 29.2241 23.1484 29.3164 23.0166 29.4307L17.2158 34.3481C16.8643 34.6558 16.6885 35.0249 16.6885 35.4556C16.6973 35.8862 16.873 36.2466 17.2158 36.5366L23.0166 41.4409C23.1484 41.5552 23.3022 41.6475 23.478 41.7178C23.6626 41.7969 23.8691 41.8364 24.0977 41.8364C24.5723 41.8364 24.9458 41.6958 25.2183 41.4146C25.4907 41.1333 25.627 40.751 25.627 40.2676V37.644H35.3301C37.1055 37.644 38.6304 37.354 39.9048 36.7739C41.1792 36.1851 42.1592 35.3457 42.8447 34.2559C43.5303 33.166 43.873 31.8652 43.873 30.3535V28.8242C43.873 28.2354 43.6621 27.7344 43.2402 27.3213Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
3
src/renderer/src/assets/repeat-one-active.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M37.5805 0H18.4195C12.0146 0 9.69208 0.666878 7.35056 1.91914C5.00904 3.1714 3.1714 5.00904 1.91914 7.35056C0.666879 9.69208 0 12.0146 0 18.4195V37.5805C0 43.9854 0.666879 46.3079 1.91914 48.6494C3.1714 50.991 5.00904 52.8286 7.35056 54.0809C9.69208 55.3331 12.0146 56 18.4195 56H37.5805C43.9854 56 46.3079 55.3331 48.6495 54.0809C50.991 52.8286 52.8286 50.991 54.0809 48.6494C55.3331 46.3079 56 43.9854 56 37.5805V18.4195C56 12.0146 55.3331 9.69208 54.0809 7.35056C52.8286 5.00904 50.991 3.1714 48.6495 1.91914C46.3079 0.666878 43.9854 0 37.5805 0ZM13.2212 28.4946C13.5552 28.8198 13.9858 28.9824 14.5132 28.9824C15.0405 28.9824 15.4668 28.8198 15.792 28.4946C16.1172 28.1606 16.2798 27.73 16.2798 27.2026V26.1743C16.2798 25.0142 16.6401 24.1001 17.3608 23.4321C18.0903 22.7554 19.0791 22.417 20.3271 22.417H26.5103V25.3701C26.5103 25.7744 26.6245 26.0952 26.853 26.3325C27.0903 26.561 27.4111 26.6753 27.8154 26.6753C28 26.6753 28.1714 26.6445 28.3296 26.583C28.4878 26.5215 28.6284 26.438 28.7515 26.3325L34.2622 21.6919C34.5698 21.437 34.7236 21.1294 34.7236 20.769C34.7236 20.3999 34.5698 20.0879 34.2622 19.833L28.7515 15.1924C28.6284 15.0869 28.4878 15.0034 28.3296 14.9419C28.1714 14.8804 28 14.8496 27.8154 14.8496C27.4111 14.8496 27.0903 14.9683 26.853 15.2056C26.6245 15.4341 26.5103 15.7549 26.5103 16.168V18.9629H20.5249C18.9165 18.9629 17.5322 19.2354 16.3721 19.7803C15.2119 20.3164 14.3154 21.0898 13.6826 22.1006C13.0498 23.1113 12.7334 24.3198 12.7334 25.7261V27.2026C12.7334 27.73 12.896 28.1606 13.2212 28.4946ZM42.7393 27.6509C42.4141 27.3257 41.9878 27.1631 41.4604 27.1631C40.9331 27.1631 40.5068 27.3257 40.1816 27.6509C39.8564 27.9761 39.6938 28.4067 39.6938 28.9429V29.9712C39.6938 31.1313 39.3291 32.0454 38.5996 32.7134C37.8789 33.3813 36.8945 33.7153 35.6465 33.7153H25.6138V30.7754C25.6138 30.3623 25.4995 30.0415 25.271 29.813C25.0425 29.5757 24.7261 29.457 24.3218 29.457C24.1284 29.457 23.9526 29.4878 23.7944 29.5493C23.6362 29.6108 23.4956 29.6943 23.3726 29.7998L17.8618 34.4404C17.563 34.7041 17.4136 35.0161 17.4136 35.3765C17.4136 35.7368 17.563 36.0444 17.8618 36.2993L23.3726 40.9399C23.4956 41.0454 23.6362 41.1289 23.7944 41.1904C23.9526 41.252 24.1284 41.2827 24.3218 41.2827C24.7261 41.2827 25.0425 41.1641 25.271 40.9268C25.4995 40.6982 25.6138 40.3818 25.6138 39.9775V37.1826H35.4487C37.0483 37.1826 38.4282 36.9146 39.5884 36.3784C40.7573 35.8335 41.6582 35.0557 42.291 34.0449C42.9238 33.0254 43.2402 31.8169 43.2402 30.4194V28.9429C43.2402 28.4067 43.0732 27.9761 42.7393 27.6509ZM40.2871 24.1968C40.6035 24.4868 41.021 24.6318 41.5396 24.6318C42.0669 24.6318 42.4844 24.4868 42.792 24.1968C43.1084 23.9067 43.2666 23.4629 43.2666 22.8652V16.603C43.2666 16.0142 43.0776 15.5396 42.6997 15.1792C42.3306 14.8188 41.856 14.6387 41.2759 14.6387C40.8188 14.6387 40.4189 14.709 40.0762 14.8496C39.7422 14.9902 39.3994 15.188 39.0479 15.4429L37.2812 16.7612C37.0176 16.9458 36.8374 17.1216 36.7407 17.2886C36.644 17.4556 36.5957 17.6533 36.5957 17.8818C36.5957 18.207 36.7012 18.4707 36.9121 18.6729C37.123 18.875 37.3911 18.9761 37.7163 18.9761C38.0151 18.9761 38.2832 18.8838 38.5205 18.6992L39.707 17.7632H39.8125V22.8652C39.8125 23.4629 39.9707 23.9067 40.2871 24.1968Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.3 KiB |
3
src/renderer/src/assets/repeat.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M14.2495 28.9956C13.6519 28.9956 13.1465 28.7891 12.7334 28.376C12.3203 27.9541 12.1138 27.4531 12.1138 26.873V25.3438C12.1138 23.832 12.4565 22.5312 13.1421 21.4414C13.8276 20.3516 14.8076 19.5166 16.082 18.9365C17.3564 18.3477 18.877 18.0532 20.6436 18.0532H30.3599V15.4033C30.3599 14.9111 30.4961 14.5288 30.7686 14.2563C31.041 13.9751 31.4146 13.8345 31.8892 13.8345C32.1177 13.8345 32.3198 13.874 32.4956 13.9531C32.6714 14.0234 32.8296 14.1113 32.9702 14.2168L38.7578 19.1343C39.1182 19.4331 39.2939 19.7979 39.2852 20.2285C39.2852 20.6504 39.1094 21.0107 38.7578 21.3096L32.9702 26.2271C32.8296 26.3501 32.6714 26.4468 32.4956 26.5171C32.3198 26.5874 32.1177 26.6226 31.8892 26.6226C31.4146 26.6226 31.041 26.4819 30.7686 26.2007C30.4961 25.9194 30.3599 25.5415 30.3599 25.0669V22.1929H20.459C19.1846 22.1929 18.1826 22.5269 17.4531 23.1948C16.7236 23.8628 16.3589 24.7812 16.3589 25.9502V26.873C16.3589 27.4531 16.1523 27.9541 15.7393 28.376C15.3262 28.7891 14.8296 28.9956 14.2495 28.9956ZM41.7505 26.7017C42.3306 26.7017 42.8271 26.9082 43.2402 27.3213C43.6621 27.7344 43.873 28.2354 43.873 28.8242V30.3535C43.873 31.8652 43.5303 33.166 42.8447 34.2559C42.1592 35.3457 41.1792 36.1851 39.9048 36.7739C38.6304 37.354 37.1055 37.644 35.3301 37.644H25.627V40.2676C25.627 40.751 25.4907 41.1333 25.2183 41.4146C24.9458 41.6958 24.5723 41.8364 24.0977 41.8364C23.8691 41.8364 23.6626 41.7969 23.478 41.7178C23.3022 41.6475 23.1484 41.5552 23.0166 41.4409L17.2158 36.5366C16.873 36.2466 16.6973 35.8862 16.6885 35.4556C16.6885 35.0249 16.8643 34.6558 17.2158 34.3481L23.0166 29.4307C23.1484 29.3164 23.3022 29.2241 23.478 29.1538C23.6626 29.0835 23.8691 29.0483 24.0977 29.0483C24.5723 29.0483 24.9458 29.189 25.2183 29.4702C25.4907 29.7427 25.627 30.125 25.627 30.6172V33.4912H35.5278C36.8022 33.4912 37.8042 33.1616 38.5337 32.5024C39.2632 31.8345 39.6279 30.916 39.6279 29.7471V28.8242C39.6279 28.2354 39.8301 27.7344 40.2344 27.3213C40.6475 26.9082 41.1528 26.7017 41.7505 26.7017Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
3
src/renderer/src/assets/shuffle-active.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M37.5805 0H18.4195C12.0146 0 9.69208 0.666878 7.35056 1.91914C5.00904 3.1714 3.1714 5.00904 1.91914 7.35056C0.666879 9.69208 0 12.0146 0 18.4195V37.5805C0 43.9854 0.666879 46.3079 1.91914 48.6494C3.1714 50.991 5.00904 52.8286 7.35056 54.0809C9.69208 55.3331 12.0146 56 18.4195 56H37.5805C43.9854 56 46.3079 55.3331 48.6495 54.0809C50.991 52.8286 52.8286 50.991 54.0809 48.6494C55.3331 46.3079 56 43.9854 56 37.5805V18.4195C56 12.0146 55.3331 9.69208 54.0809 7.35056C52.8286 5.00904 50.991 3.1714 48.6495 1.91914C46.3079 0.666878 43.9854 0 37.5805 0ZM11.2173 34.8887C10.8218 35.2754 10.624 35.75 10.624 36.3125C10.624 36.875 10.8218 37.3496 11.2173 37.7363C11.6216 38.123 12.1094 38.3164 12.6807 38.3164H16.082C17.3389 38.3164 18.3936 38.1099 19.2461 37.6968C20.0986 37.2749 20.8853 36.6113 21.606 35.7061L24.7747 31.75L27.7363 35.4556C28.5625 36.4927 29.5249 37.231 30.6235 37.6704C31.7222 38.1011 32.9043 38.3164 34.1699 38.3164H36.4243V41.23C36.4243 41.7134 36.5605 42.0957 36.833 42.377C37.1055 42.6582 37.479 42.7988 37.9536 42.7988C38.1821 42.7988 38.3843 42.7593 38.5601 42.6802C38.7446 42.6099 38.9072 42.5132 39.0479 42.3901L44.8223 37.4858C45.1826 37.187 45.3628 36.8223 45.3628 36.3916C45.3628 35.9609 45.1826 35.5962 44.8223 35.2974L39.0479 30.4062C38.9072 30.2832 38.7446 30.1865 38.5601 30.1162C38.3843 30.0371 38.1821 29.9976 37.9536 29.9976C37.479 29.9976 37.1055 30.1382 36.833 30.4194C36.5605 30.7007 36.4243 31.083 36.4243 31.5664V34.2954H34.2227C33.291 34.2954 32.5 34.1328 31.8496 33.8076C31.1992 33.4824 30.5972 32.9727 30.0435 32.2783L27.1993 28.7231L30.0962 25.1064C30.4829 24.6143 30.8521 24.2275 31.2036 23.9463C31.564 23.6562 31.9551 23.4497 32.377 23.3267C32.8076 23.2036 33.3218 23.1421 33.9194 23.1421H36.4243V25.8184C36.4243 26.3018 36.5605 26.6841 36.833 26.9653C37.1055 27.2466 37.479 27.3872 37.9536 27.3872C38.1821 27.3872 38.3843 27.3521 38.5601 27.2817C38.7446 27.2026 38.9072 27.106 39.0479 26.9917L44.8223 22.0742C45.1826 21.7754 45.3628 21.4106 45.3628 20.98C45.3628 20.5493 45.1826 20.1846 44.8223 19.8857L39.0479 14.9814C38.9072 14.8672 38.7446 14.7749 38.5601 14.7046C38.3843 14.6343 38.1821 14.5991 37.9536 14.5991C37.479 14.5991 37.1055 14.7397 36.833 15.021C36.5605 15.2935 36.4243 15.6714 36.4243 16.1548V19.1079H34.1963C32.79 19.1079 31.5552 19.332 30.4917 19.7803C29.4282 20.2285 28.3955 21.0811 27.3936 22.3379L24.7534 25.6657L21.606 21.7314C20.8853 20.8174 20.0371 20.1538 19.0615 19.7407C18.0947 19.3188 16.9829 19.1079 15.7261 19.1079H12.6807C12.1094 19.1079 11.6216 19.3057 11.2173 19.7012C10.8218 20.0879 10.624 20.5625 10.624 21.125C10.624 21.6787 10.8218 22.1533 11.2173 22.5488C11.6216 22.9443 12.1094 23.1421 12.6807 23.1421H15.1064C16.0381 23.1421 16.8599 23.3003 17.5718 23.6167C18.2925 23.9331 18.9341 24.4429 19.4966 25.146L22.3415 28.7056L19.4966 32.2915C18.9341 32.9946 18.3496 33.5044 17.7432 33.8208C17.1455 34.1372 16.3896 34.2954 15.4756 34.2954H12.6807C12.1094 34.2954 11.6216 34.4932 11.2173 34.8887Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
3
src/renderer/src/assets/shuffle.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M10.624 36.3125C10.624 35.75 10.8218 35.2754 11.2173 34.8887C11.6216 34.4932 12.1094 34.2954 12.6807 34.2954H15.4756C16.3896 34.2954 17.1455 34.1372 17.7432 33.8208C18.3496 33.5044 18.9341 32.9946 19.4966 32.2915L27.3936 22.3379C28.3955 21.0811 29.4282 20.2285 30.4917 19.7803C31.5552 19.332 32.79 19.1079 34.1963 19.1079H36.4243V16.1548C36.4243 15.6714 36.5605 15.2935 36.833 15.021C37.1055 14.7397 37.479 14.5991 37.9536 14.5991C38.1821 14.5991 38.3843 14.6343 38.5601 14.7046C38.7446 14.7749 38.9072 14.8672 39.0479 14.9814L44.8223 19.8857C45.1826 20.1846 45.3628 20.5493 45.3628 20.98C45.3628 21.4106 45.1826 21.7754 44.8223 22.0742L39.0479 26.9917C38.9072 27.106 38.7446 27.2026 38.5601 27.2817C38.3843 27.3521 38.1821 27.3872 37.9536 27.3872C37.479 27.3872 37.1055 27.2466 36.833 26.9653C36.5605 26.6841 36.4243 26.3018 36.4243 25.8184V23.1421H33.9194C33.3218 23.1421 32.8076 23.2036 32.377 23.3267C31.9551 23.4497 31.564 23.6562 31.2036 23.9463C30.8521 24.2275 30.4829 24.6143 30.0962 25.1064L21.606 35.7061C20.8853 36.6113 20.0986 37.2749 19.2461 37.6968C18.3936 38.1099 17.3389 38.3164 16.082 38.3164H12.6807C12.1094 38.3164 11.6216 38.123 11.2173 37.7363C10.8218 37.3496 10.624 36.875 10.624 36.3125ZM10.624 21.125C10.624 20.5625 10.8218 20.0879 11.2173 19.7012C11.6216 19.3057 12.1094 19.1079 12.6807 19.1079H15.7261C16.9829 19.1079 18.0947 19.3188 19.0615 19.7407C20.0371 20.1538 20.8853 20.8174 21.606 21.7314L30.0435 32.2783C30.5972 32.9727 31.1992 33.4824 31.8496 33.8076C32.5 34.1328 33.291 34.2954 34.2227 34.2954H36.4243V31.5664C36.4243 31.083 36.5605 30.7007 36.833 30.4194C37.1055 30.1382 37.479 29.9976 37.9536 29.9976C38.1821 29.9976 38.3843 30.0371 38.5601 30.1162C38.7446 30.1865 38.9072 30.2832 39.0479 30.4062L44.8223 35.2974C45.1826 35.5962 45.3628 35.9609 45.3628 36.3916C45.3628 36.8223 45.1826 37.187 44.8223 37.4858L39.0479 42.3901C38.9072 42.5132 38.7446 42.6099 38.5601 42.6802C38.3843 42.7593 38.1821 42.7988 37.9536 42.7988C37.479 42.7988 37.1055 42.6582 36.833 42.377C36.5605 42.0957 36.4243 41.7134 36.4243 41.23V38.3164H34.1699C32.9043 38.3164 31.7222 38.1011 30.6235 37.6704C29.5249 37.231 28.5625 36.4927 27.7363 35.4556L19.4966 25.146C18.9341 24.4429 18.2925 23.9331 17.5718 23.6167C16.8599 23.3003 16.0381 23.1421 15.1064 23.1421H12.6807C12.1094 23.1421 11.6216 22.9443 11.2173 22.5488C10.8218 22.1533 10.624 21.6787 10.624 21.125Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -1,225 +1,189 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fullscreen-player" :class="{ active: isPlayerFullScreen }">
|
<div class="fullscreen-player" :class="{ active: isPlayerFullScreen }">
|
||||||
<div class="player-content">
|
<div class="background-container">
|
||||||
<div class="dismiss-area" @click="toggleFullScreen">
|
<BackgroundRender
|
||||||
<svg class="dismiss-button" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
:album="playerStore.currentSong?.picUrl"
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
:album-is-video="false"
|
||||||
<polyline points="6 9 12 15 18 9"></polyline>
|
ref="bgRef"
|
||||||
</svg>
|
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="background-container">
|
<div v-if="isPlayerFullScreen" class="drag-bar"></div>
|
||||||
<BackgroundRender
|
|
||||||
:album="currentSong?.picUrl"
|
<div class="horizontal-layout" :class="{ hideLyric: playerStore.hideLyricView }">
|
||||||
:lowFreqVolume="1"
|
<div class="thumb">
|
||||||
:hasLyric="true"
|
<ControlThumb @click="toggleFullScreen" />
|
||||||
|
</div>
|
||||||
|
<Cover
|
||||||
|
class="cover"
|
||||||
|
:cover-url="playerStore.currentSong?.picUrl"
|
||||||
|
:music-paused="!isPlaying"
|
||||||
|
:cover-video-paused="!isPlaying"
|
||||||
|
:pause-shrink-aspect="0.75"
|
||||||
|
/>
|
||||||
|
<div class="controls">
|
||||||
|
<MusicInfo
|
||||||
|
:name="playerStore.currentSong?.name"
|
||||||
|
:artists="playerStore.currentSong?.artist.split('、')??undefined"
|
||||||
|
class="music-info-container"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<BouncingSlider
|
||||||
|
:value="playerStore.currentTime"
|
||||||
|
:min="0"
|
||||||
|
:max="playerStore.duration"
|
||||||
|
:is-playing="isPlaying"
|
||||||
|
@update:value="handleSeek"
|
||||||
|
/>
|
||||||
|
<div class="progressBarLabels">
|
||||||
|
<div class="time-label">
|
||||||
|
{{ formatTime(playerStore.currentTime) }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="time-label remaining"
|
||||||
|
@click="showRemaining = !showRemaining"
|
||||||
|
>
|
||||||
|
{{ showRemaining ? `-${formatTime(playerStore.duration - playerStore.currentTime)}` : formatTime(playerStore.duration) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mediaControlls">
|
||||||
|
<MediaButton class="songMediaButton" @click="playerStore.toggleMode">
|
||||||
|
<template v-if="playMode === 'random'">
|
||||||
|
<img :src="IconShuffleActive" :style="iconStyle"/>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<img :src="IconShuffle" :style="iconStyle"/>
|
||||||
|
</template>
|
||||||
|
</MediaButton>
|
||||||
|
|
||||||
|
<MediaButton class="songMediaButton" @click="()=>playerStore.prev()">
|
||||||
|
<img :src="IconRewind" />
|
||||||
|
</MediaButton>
|
||||||
|
|
||||||
|
<MediaButton class="songMediaPlayButton" @click="playerStore.togglePlay">
|
||||||
|
<img :src="isPlaying ? IconPause : IconPlay"/>
|
||||||
|
</MediaButton>
|
||||||
|
|
||||||
|
<MediaButton class="songMediaButton" @click="()=>playerStore.next()">
|
||||||
|
<img :src="IconForward" />
|
||||||
|
</MediaButton>
|
||||||
|
|
||||||
|
<MediaButton class="songMediaButton" @click="playerStore.toggleMode">
|
||||||
|
<template v-if="playMode === 'single'">
|
||||||
|
<img :src="IconRepeatOneActive" :style="iconStyle" />
|
||||||
|
</template>
|
||||||
|
<template v-else-if="playMode === 'list'">
|
||||||
|
<img :src="IconRepeatActive" :style="iconStyle" />
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<img :src="IconRepeat" :style="iconStyle" />
|
||||||
|
</template>
|
||||||
|
</MediaButton>
|
||||||
|
</div>
|
||||||
|
<div class="volumeControllBar">
|
||||||
|
<VolumeControl
|
||||||
|
v-model="playerStore.volume"
|
||||||
|
:min="0"
|
||||||
|
:max="100"
|
||||||
|
:on-update="value => {
|
||||||
|
playerStore.setVolume( Math.floor(value) )
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="lyric">
|
||||||
|
<LyricPlayer
|
||||||
|
v-if="isPlayerFullScreen"
|
||||||
|
ref="lyricPlayerRef"
|
||||||
|
:lyric-lines="toRaw(playerStore.lyrics.lines)"
|
||||||
|
:current-time="playerStore.currentTime"
|
||||||
:playing="isPlaying"
|
:playing="isPlaying"
|
||||||
|
:align-position="0.5"
|
||||||
|
:wordFadeWidth="0.5"
|
||||||
|
:enable-scale="false"
|
||||||
|
:enable-blur="true"
|
||||||
|
:enable-spring="true"
|
||||||
|
@line-click="jumpTime"
|
||||||
|
style="width:100%;height:100%;font-family: 'LyricFont',sans-serif"
|
||||||
|
>
|
||||||
|
</LyricPlayer>
|
||||||
|
</div>
|
||||||
|
<div class="bottomControls">
|
||||||
|
<ToggleIconButton
|
||||||
|
type="playlist"
|
||||||
|
/>
|
||||||
|
<ToggleIconButton
|
||||||
|
type="lyrics"
|
||||||
|
:checked="!playerStore.hideLyricView"
|
||||||
|
@click="playerStore.hideLyricView = !playerStore.hideLyricView"
|
||||||
|
/>
|
||||||
|
<div style="flex: 1" />
|
||||||
|
<ToggleIconButton
|
||||||
|
type="airplay"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="music-info">
|
|
||||||
<!-- Placeholder for song info, lyrics etc. -->
|
|
||||||
<h1>{{ currentSong?.name || 'No Music' }}</h1>
|
|
||||||
<p>{{ currentSong?.artist }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<canvas ref="canvasRef" class="spectrum-canvas"></canvas>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<canvas ref="canvasRef" class="spectrum-canvas"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, watch } from 'vue';
|
import { computed, toRaw, watch, ref, onMounted, onUnmounted } from 'vue';
|
||||||
import { usePlayerStore } from '../stores/player';
|
import { usePlayerStore } from '../stores/player';
|
||||||
import {
|
|
||||||
type AbstractBaseRenderer,
|
|
||||||
type BaseRenderer,
|
|
||||||
BackgroundRender as CoreBackgroundRender,
|
|
||||||
MeshGradientRenderer,
|
|
||||||
} from "@applemusic-like-lyrics/core";
|
|
||||||
import {
|
|
||||||
defineComponent,
|
|
||||||
onMounted,
|
|
||||||
onUnmounted,
|
|
||||||
type PropType,
|
|
||||||
type Ref,
|
|
||||||
ref,
|
|
||||||
type ShallowRef,
|
|
||||||
useTemplateRef,
|
|
||||||
watchEffect,
|
|
||||||
h,
|
|
||||||
} from "vue";
|
|
||||||
|
|
||||||
// --- BackgroundRender Component Definition (from User Request) ---
|
import "@applemusic-like-lyrics/core/style.css";
|
||||||
|
import { LyricPlayer, type LyricPlayerRef, BackgroundRender, type BackgroundRenderRef} from "@applemusic-like-lyrics/vue";
|
||||||
|
import { LyricLineMouseEvent } from "@applemusic-like-lyrics/core";
|
||||||
|
import Cover from './player/Cover.vue';
|
||||||
|
import ControlThumb from './player/ControlThumb.vue';
|
||||||
|
import BouncingSlider from './player/BouncingSlider.vue';
|
||||||
|
import MusicInfo from './player/MusicInfo.vue';
|
||||||
|
import MediaButton from './player/MediaButton.vue';
|
||||||
|
import VolumeControl from './player/VolumeControl.vue';
|
||||||
|
import ToggleIconButton from './player/ToggleIconButton.vue';
|
||||||
|
|
||||||
/**
|
// Icons
|
||||||
* 背景渲染组件的引用
|
import IconRewind from '@assets/icon_rewind.svg';
|
||||||
*/
|
import IconPlay from '@assets/icon_play.svg';
|
||||||
export interface BackgroundRenderRef {
|
import IconPause from '@assets/icon_pause.svg';
|
||||||
/**
|
import IconForward from '@assets/icon_forward.svg';
|
||||||
* 背景渲染实例引用
|
import IconShuffle from '@assets/shuffle.svg';
|
||||||
*/
|
import IconShuffleActive from '@assets/shuffle-active.svg';
|
||||||
bgRender?: Ref<AbstractBaseRenderer | undefined>;
|
import IconRepeat from '@assets/repeat.svg';
|
||||||
/**
|
import IconRepeatOneActive from '@assets/repeat-one-active.svg';
|
||||||
* 将背景渲染实例的元素包裹起来的 DIV 元素实例
|
import IconRepeatActive from '@assets/repeat-active.svg';
|
||||||
*/
|
|
||||||
wrapperEl: Readonly<ShallowRef<HTMLDivElement | null>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const backgroundRenderProps = {
|
const lyricPlayerRef = ref<LyricPlayerRef>()
|
||||||
/**
|
const bgRef = ref<BackgroundRenderRef>();
|
||||||
* 设置背景专辑资源
|
|
||||||
*/
|
|
||||||
album: {
|
|
||||||
type: [String, Object] as PropType<
|
|
||||||
string | HTMLImageElement | HTMLVideoElement
|
|
||||||
>,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 设置专辑资源是否为视频
|
|
||||||
*/
|
|
||||||
albumIsVideo: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 设置当前背景动画帧率,如果为 `undefined` 则默认为 `30`
|
|
||||||
*/
|
|
||||||
fps: {
|
|
||||||
type: Number,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 设置当前播放状态,如果为 `undefined` 则默认为 `true`
|
|
||||||
*/
|
|
||||||
playing: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 设置当前动画流动速度,如果为 `undefined` 则默认为 `2`
|
|
||||||
*/
|
|
||||||
flowSpeed: {
|
|
||||||
type: Number,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 设置背景是否根据“是否有歌词”这个特征调整自身效果,例如有歌词时会变得更加活跃
|
|
||||||
*
|
|
||||||
* 部分渲染器会根据这个特征调整自身效果
|
|
||||||
*
|
|
||||||
* 如果不确定是否需要赋值或无法知晓是否包含歌词,请传入 true 或不做任何处理(默认值为 true)
|
|
||||||
*/
|
|
||||||
hasLyric: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 设置低频的音量大小,范围在 80hz-120hz 之间为宜,取值范围在 [0.0-1.0] 之间
|
|
||||||
*
|
|
||||||
* 部分渲染器会根据音量大小调整背景效果(例如根据鼓点跳动)
|
|
||||||
*
|
|
||||||
* 如果无法获取到类似的数据,请传入 undefined 或 1.0 作为默认值,或不做任何处理(默认值即 1.0)
|
|
||||||
*/
|
|
||||||
lowFreqVolume: {
|
|
||||||
type: Number,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 设置当前渲染缩放比例,如果为 `undefined` 则默认为 `0.5`
|
|
||||||
*/
|
|
||||||
renderScale: {
|
|
||||||
type: Number,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 设置渲染器,如果为 `undefined` 则默认为 `MeshGradientRenderer`
|
|
||||||
* 默认渲染器有可能会随着版本更新而更换
|
|
||||||
*/
|
|
||||||
renderer: {
|
|
||||||
type: Object as PropType<{ // Use constructor type
|
|
||||||
new (...args: ConstructorParameters<typeof BaseRenderer>): BaseRenderer;
|
|
||||||
}>,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
const BackgroundRender = defineComponent({
|
const showRemaining = ref(false);
|
||||||
name: "BackgroundRender",
|
|
||||||
props: backgroundRenderProps,
|
|
||||||
setup(props, { expose }) {
|
|
||||||
const wrapperRef = useTemplateRef<HTMLDivElement>("wrapper-ref");
|
|
||||||
const bgRenderRef = ref<AbstractBaseRenderer>();
|
|
||||||
|
|
||||||
onMounted(() => {
|
const formatTime = (miliseconds: number) => {
|
||||||
if (wrapperRef.value) {
|
const seconds = miliseconds / 1000;
|
||||||
// @ts-ignore
|
const min = Math.floor(seconds / 60);
|
||||||
bgRenderRef.value = CoreBackgroundRender.new(
|
const sec = Math.floor(seconds % 60);
|
||||||
props.renderer ?? MeshGradientRenderer,
|
return `${min}:${sec.toString().padStart(2, '0')}`;
|
||||||
);
|
};
|
||||||
const el = bgRenderRef.value.getElement();
|
|
||||||
el.style.width = "100%";
|
|
||||||
el.style.height = "100%";
|
|
||||||
wrapperRef.value.appendChild(el);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
const handleSeek = (val: number) => {
|
||||||
if (bgRenderRef.value) {
|
playerStore.seek(val);
|
||||||
bgRenderRef.value.dispose();
|
lyricPlayerRef.value?.lyricPlayer.value?.setCurrentTime(val,true);
|
||||||
}
|
};
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
if (props.album)
|
|
||||||
bgRenderRef.value?.setAlbum(props.album, props.albumIsVideo);
|
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
if (props.fps) bgRenderRef.value?.setFPS(props.fps);
|
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
if (props.playing) bgRenderRef.value?.resume();
|
|
||||||
else bgRenderRef.value?.pause();
|
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
if (props.flowSpeed) bgRenderRef.value?.setFlowSpeed(props.flowSpeed);
|
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
if (props.renderScale)
|
|
||||||
bgRenderRef.value?.setRenderScale(props.renderScale);
|
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
if (props.lowFreqVolume !== undefined)
|
|
||||||
bgRenderRef.value?.setLowFreqVolume(props.lowFreqVolume);
|
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
if (props.hasLyric !== undefined)
|
|
||||||
bgRenderRef.value?.setHasLyric(props.hasLyric ?? true);
|
|
||||||
});
|
|
||||||
|
|
||||||
expose<BackgroundRenderRef>({
|
|
||||||
bgRender: bgRenderRef,
|
|
||||||
wrapperEl: wrapperRef,
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => h("div", { style: "display: contents;", ref: "wrapper-ref" });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// --- FullScreenPlayer Logic ---
|
|
||||||
|
|
||||||
const playerStore = usePlayerStore();
|
const playerStore = usePlayerStore();
|
||||||
const isPlayerFullScreen = computed(() => playerStore.isPlayerFullScreen);
|
const isPlayerFullScreen = computed(() => playerStore.isPlayerFullScreen);
|
||||||
const currentSong = computed(() => playerStore.currentSong);
|
//const currentSong = computed(() => playerStore.currentSong);
|
||||||
const isPlaying = computed(() => playerStore.isPlaying);
|
const isPlaying = computed(() => playerStore.isPlaying);
|
||||||
|
const playMode = computed(() => playerStore.playMode);
|
||||||
|
|
||||||
|
const iconStyle = {
|
||||||
|
width: "1.3em",
|
||||||
|
height: "1.3em",
|
||||||
|
};
|
||||||
const canvasRef = ref<HTMLCanvasElement | null>(null);
|
const canvasRef = ref<HTMLCanvasElement | null>(null);
|
||||||
let animationId: number | null = null;
|
let animationId: number | null = null;
|
||||||
let currentData: number[] = new Array(32).fill(0); // For temporal smoothing
|
let currentData: number[] = new Array(32).fill(0); // For temporal smoothing
|
||||||
@@ -335,37 +299,34 @@ const toggleFullScreen = () => {
|
|||||||
playerStore.toggleFullScreen();
|
playerStore.toggleFullScreen();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const jumpTime = (e: LyricLineMouseEvent) => {
|
||||||
|
playerStore.seek(e.line.getLine().startTime)
|
||||||
|
lyricPlayerRef.value?.lyricPlayer.value?.setCurrentTime(e.line.getLine().startTime,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// watch(()=>playerStore.currentTime,(t)=>{
|
||||||
|
// console.log(toRaw(t))
|
||||||
|
// })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.fullscreen-player {
|
.fullscreen-player {
|
||||||
|
--height: calc(100vh);
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 100%; /* Initially hidden below screen */
|
top: var(--height);
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100vw;
|
width: 100%;
|
||||||
height: 100vh;
|
height: var(--height);
|
||||||
background-color: #000;
|
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
transition: top 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
|
transition: top 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||||
|
background: black; /* Default background if image fails */
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fullscreen-player.active {
|
.fullscreen-player.active {
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-content {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 10;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background-container {
|
.background-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -374,44 +335,19 @@ const toggleFullScreen = () => {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
opacity: 0.6; /* Dim background slightly */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dismiss-area {
|
.drag-bar {
|
||||||
padding: 2.5%;
|
position: fixed;
|
||||||
cursor: pointer;
|
top: 0;
|
||||||
display: flex;
|
left: 0;
|
||||||
justify-content: start;
|
width: 100%;
|
||||||
align-items: center;
|
height: 50px;
|
||||||
color: rgba(255, 255, 255, 0.5);
|
z-index: 100;
|
||||||
transition: color 0.2s;
|
|
||||||
z-index: 20;
|
|
||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
}
|
background: transparent;
|
||||||
.dismiss-button {
|
|
||||||
-webkit-app-region: none;
|
|
||||||
}
|
|
||||||
.dismiss-area:hover {
|
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.music-info {
|
|
||||||
margin-top: auto;
|
|
||||||
padding: 40px;
|
|
||||||
text-align: left;
|
|
||||||
z-index: 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
.music-info h1 {
|
|
||||||
font-size: 2rem;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.music-info p {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spectrum-canvas {
|
.spectrum-canvas {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -421,9 +357,264 @@ const toggleFullScreen = () => {
|
|||||||
left: 0;
|
left: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 15;
|
z-index: 15;
|
||||||
/* Optional: Fade out at top */
|
|
||||||
mask-image: linear-gradient(to top, black 0%, transparent 100%);
|
mask-image: linear-gradient(to top, black 0%, transparent 100%);
|
||||||
-webkit-mask-image: linear-gradient(to top, black 0%, transparent 100%);
|
-webkit-mask-image: linear-gradient(to top, black 0%, transparent 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'LyricFont';
|
||||||
|
src: url('@assets/lyricfont.ttf');
|
||||||
|
font-weight: 350;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* New Horizontal Layout Styles */
|
||||||
|
.horizontal-layout {
|
||||||
|
/* --info-size-fract: 0.85fr; */
|
||||||
|
/* --player-size-fract: 1fr; */
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: [drag-area] minmax(20px, .20fr) [thumb] auto [cover] auto [music-info] 3fr [buttom-controls] 0fr .1fr;
|
||||||
|
grid-template-columns: [info-side] .50fr [player-side] .50fr [side-controls] 0fr;
|
||||||
|
gap: 8px;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
--hide-lyric-left: 50%;
|
||||||
|
|
||||||
|
--horizontal-layout-max-width: min(50vh, 38vw);
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-layout .thumb,
|
||||||
|
.horizontal-layout .cover,
|
||||||
|
.horizontal-layout .controls {
|
||||||
|
transition: left 0.5s cubic-bezier(0.5, 0, 0.5, 1);
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.horizontal-layout.hideLyric .lyric {
|
||||||
|
transition:
|
||||||
|
opacity 0.25s cubic-bezier(0.5, 0, 0.5, 1),
|
||||||
|
transform 0.5s cubic-bezier(0.5, 0, 0.5, 1);
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-layout.hideLyric .thumb,
|
||||||
|
.horizontal-layout.hideLyric .cover,
|
||||||
|
.horizontal-layout.hideLyric .controls {
|
||||||
|
left: var(--hide-lyric-left);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-height: 1000px) {
|
||||||
|
.horizontal-layout {
|
||||||
|
--horizontal-layout-max-width: min(45vh, 38vw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-height: 768px) {
|
||||||
|
.horizontal-layout {
|
||||||
|
font-size: 0.8em;
|
||||||
|
gap: 2px;
|
||||||
|
grid-template-rows: [drag-area] minmax(30px, 0.25fr) [thumb] auto [cover] auto [music-info] 3fr [buttom-controls] 0fr 0.2fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumb {
|
||||||
|
grid-column: info-side;
|
||||||
|
grid-row: thumb;
|
||||||
|
will-change: transform;
|
||||||
|
justify-self: center;
|
||||||
|
margin: 2vh;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover {
|
||||||
|
margin: 0;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
grid-column: info-side;
|
||||||
|
grid-row: cover;
|
||||||
|
align-self: center;
|
||||||
|
justify-self: center;
|
||||||
|
width: var(--horizontal-layout-max-width);
|
||||||
|
height: var(--horizontal-layout-max-width);
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
grid-area: music-info / info-side;
|
||||||
|
will-change: transform;
|
||||||
|
justify-self: center;
|
||||||
|
|
||||||
|
mix-blend-mode: plus-lighter;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
|
width: var(--horizontal-layout-max-width);
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
margin-top: calc(-8px + 1.75em);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressBarLabels {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: max(1.2vh, 0.8em);
|
||||||
|
opacity: 0.5;
|
||||||
|
margin-top: 4px;
|
||||||
|
|
||||||
|
@media screen and (max-height: 768px) {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > *:nth-child(2) {
|
||||||
|
flex: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > *:last-child {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-label {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-label.remaining {
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.music-info-container {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 2vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lyric {
|
||||||
|
box-sizing: border-box;
|
||||||
|
grid-column: player-side;
|
||||||
|
grid-row: 2 / 5;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
transition: opacity 0.5s 0.25s cubic-bezier(0.5, 0, 0.5, 1);
|
||||||
|
padding-right: 15%;
|
||||||
|
|
||||||
|
mask-image: linear-gradient(transparent, black 10%, black 90%, transparent);
|
||||||
|
|
||||||
|
contain: paint;
|
||||||
|
pointer-events: none; /* Allow clicks to pass through to elements below */
|
||||||
|
|
||||||
|
/* 修复呼吸点,不要删!*/
|
||||||
|
:deep(.amll-lyric-player) {
|
||||||
|
box-sizing: content-box;
|
||||||
|
pointer-events: auto; /* Re-enable pointer events for the actual lyric player */
|
||||||
|
[class*="interludeDots"] {
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1600px), (max-height: 1000px) {
|
||||||
|
.lyric {
|
||||||
|
padding-right: 8%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottomControls {
|
||||||
|
grid-area: buttom-controls / 1 / buttom-controls / 4;
|
||||||
|
gap: 2em;
|
||||||
|
padding-left: 2em;
|
||||||
|
padding-right: 2em;
|
||||||
|
mix-blend-mode: plus-lighter;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.songMediaButton,
|
||||||
|
.songMediaPlayButton {
|
||||||
|
width: 18%;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.songMediaButton > img {
|
||||||
|
display: block;
|
||||||
|
scale: 3;
|
||||||
|
transition: scale 0.3s;
|
||||||
|
|
||||||
|
@media screen and (max-height: 1080px) {
|
||||||
|
scale: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-height: 768px) {
|
||||||
|
scale: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-height: 512px) {
|
||||||
|
scale: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 480px) {
|
||||||
|
scale: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.songMediaPlayButton > img {
|
||||||
|
scale: 2;
|
||||||
|
transition: scale 0.3s;
|
||||||
|
|
||||||
|
@media screen and (max-height: 1080px) {
|
||||||
|
scale: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-height: 768px) {
|
||||||
|
scale: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-height: 512px) {
|
||||||
|
scale: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 480px) {
|
||||||
|
scale: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bigControls :deep(button) {
|
||||||
|
height: 10vh !important;
|
||||||
|
width: 10vh !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mediaControlls {
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
.volumeControllBar {
|
||||||
|
touch-action: none;
|
||||||
|
justify-content: stretch;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 24px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
223
src/renderer/src/components/player/BouncingSlider.vue
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="bouncing-slider"
|
||||||
|
ref="containerRef"
|
||||||
|
@mousedown="handlePanStart"
|
||||||
|
@touchstart="handlePanStart"
|
||||||
|
@mouseenter="handleHoverStart"
|
||||||
|
@mouseleave="handleHoverEnd"
|
||||||
|
:style="{
|
||||||
|
transform: `translateX(${bounceX}px)`
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="inner" :style="{ clipPath: clipPath }">
|
||||||
|
<div
|
||||||
|
class="thumb"
|
||||||
|
:style="{
|
||||||
|
transform: `scaleX(${progress})`
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, watch, onUnmounted } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
min: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
type: Number,
|
||||||
|
default: 100
|
||||||
|
},
|
||||||
|
isPlaying: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
changeOnDrag: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:value', 'drag-start', 'drag-end', 'seeking']);
|
||||||
|
|
||||||
|
const containerRef = ref<HTMLElement | null>(null);
|
||||||
|
const isHovering = ref(false);
|
||||||
|
const isDragging = ref(false);
|
||||||
|
const localValue = ref(props.value);
|
||||||
|
const bounceX = ref(0);
|
||||||
|
|
||||||
|
const THROTTLE_MS = 20;
|
||||||
|
let lastEmitTime = 0;
|
||||||
|
|
||||||
|
const MAX_HEIGHT = 20;
|
||||||
|
const MIN_HEIGHT = 8;
|
||||||
|
const INITIAL_INSET = (MAX_HEIGHT - MIN_HEIGHT) / 2;
|
||||||
|
const MAX_BOUNCE_DISTANCE = 12;
|
||||||
|
|
||||||
|
const inset = ref(INITIAL_INSET);
|
||||||
|
|
||||||
|
const clipPath = computed(() => {
|
||||||
|
return `inset(${inset.value}px 0px round 100px)`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const progress = computed(() => {
|
||||||
|
const range = props.max - props.min;
|
||||||
|
if (range === 0) return 0;
|
||||||
|
return Math.max(0, Math.min(1, (localValue.value - props.min) / range));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sync localValue with props.value when not dragging
|
||||||
|
watch(() => props.value, (newVal) => {
|
||||||
|
if (!isDragging.value) {
|
||||||
|
localValue.value = newVal;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const expand = () => {
|
||||||
|
inset.value = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const collapse = () => {
|
||||||
|
inset.value = INITIAL_INSET;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleHoverStart = () => {
|
||||||
|
isHovering.value = true;
|
||||||
|
if (!isDragging.value) {
|
||||||
|
expand();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleHoverEnd = () => {
|
||||||
|
isHovering.value = false;
|
||||||
|
if (!isDragging.value) {
|
||||||
|
collapse();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePanStart = (event: MouseEvent | TouchEvent) => {
|
||||||
|
isDragging.value = true;
|
||||||
|
expand();
|
||||||
|
emit('drag-start');
|
||||||
|
emit('seeking', true);
|
||||||
|
|
||||||
|
// Initial calculation
|
||||||
|
calculateValue(event);
|
||||||
|
|
||||||
|
window.addEventListener('mousemove', handlePan);
|
||||||
|
window.addEventListener('touchmove', handlePan);
|
||||||
|
window.addEventListener('mouseup', handlePanEnd);
|
||||||
|
window.addEventListener('touchend', handlePanEnd);
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateValue = (event: MouseEvent | TouchEvent) => {
|
||||||
|
if (!containerRef.value) return;
|
||||||
|
const rect = containerRef.value.getBoundingClientRect();
|
||||||
|
const clientX = 'touches' in event ? event.touches[0].clientX : (event as MouseEvent).clientX;
|
||||||
|
|
||||||
|
const relPos = (clientX - rect.left) / rect.width;
|
||||||
|
|
||||||
|
// Bounce effect
|
||||||
|
if (relPos < 0) {
|
||||||
|
bounceX.value = Math.tanh(relPos * 2) * MAX_BOUNCE_DISTANCE;
|
||||||
|
} else if (relPos > 1) {
|
||||||
|
bounceX.value = Math.tanh((relPos - 1) * 2) * MAX_BOUNCE_DISTANCE;
|
||||||
|
} else {
|
||||||
|
bounceX.value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const clampedPos = Math.max(0, Math.min(1, relPos));
|
||||||
|
const newValue = props.min + clampedPos * (props.max - props.min);
|
||||||
|
|
||||||
|
localValue.value = newValue;
|
||||||
|
|
||||||
|
if (props.changeOnDrag) {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - lastEmitTime >= THROTTLE_MS) {
|
||||||
|
lastEmitTime = now;
|
||||||
|
emit('update:value', newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePan = (event: Event) => {
|
||||||
|
// MouseEvent or TouchEvent
|
||||||
|
calculateValue(event as MouseEvent | TouchEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePanEnd = () => {
|
||||||
|
isDragging.value = false;
|
||||||
|
bounceX.value = 0;
|
||||||
|
|
||||||
|
if (isHovering.value) {
|
||||||
|
expand();
|
||||||
|
} else {
|
||||||
|
collapse();
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('seeking', false);
|
||||||
|
emit('drag-end');
|
||||||
|
|
||||||
|
// Commit value
|
||||||
|
emit('update:value', localValue.value);
|
||||||
|
|
||||||
|
window.removeEventListener('mousemove', handlePan);
|
||||||
|
window.removeEventListener('touchmove', handlePan);
|
||||||
|
window.removeEventListener('mouseup', handlePanEnd);
|
||||||
|
window.removeEventListener('touchend', handlePanEnd);
|
||||||
|
};
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('mousemove', handlePan);
|
||||||
|
window.removeEventListener('touchmove', handlePan);
|
||||||
|
window.removeEventListener('mouseup', handlePanEnd);
|
||||||
|
window.removeEventListener('touchend', handlePanEnd);
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.bouncing-slider {
|
||||||
|
touch-action: none;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: stretch;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 24px;
|
||||||
|
width: 100%; /* Ensure it takes full width */
|
||||||
|
transform: translateZ(0);
|
||||||
|
/* Transition for bounce effect on container */
|
||||||
|
transition: transform 0.1s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
height: 20px;
|
||||||
|
background-color: #ffffff26;
|
||||||
|
transition: clip-path 0.3s cubic-bezier(0.25, 0.8, 0.5, 1);
|
||||||
|
overflow: hidden; /* Needed for clip-path visualization in some contexts/fallbacks */
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumb {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: white;
|
||||||
|
opacity: 0.4;
|
||||||
|
transform-origin: left center;
|
||||||
|
transition: transform 0.1s linear; /* Smooth visual update during drag/play */
|
||||||
|
}
|
||||||
|
|
||||||
|
.bouncing-slider > svg {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
47
src/renderer/src/components/player/ControlThumb.vue
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<div class="controlThumb" @click="onClick">
|
||||||
|
<button type="button" class="knob"></button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const emit = defineEmits(['click']);
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
emit('click');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.controlThumb {
|
||||||
|
width: 100%;
|
||||||
|
height: 25px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 10;
|
||||||
|
position: relative;
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
|
|
||||||
|
.knob {
|
||||||
|
width: 70px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #ffffff2d;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
/* 可选:加一点反馈 */
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.knob:hover {
|
||||||
|
background-color: #ffffff4d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.knob:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
134
src/renderer/src/components/player/Cover.vue
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
ref="frameRef"
|
||||||
|
class="cover"
|
||||||
|
:class="{ musicPaused }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="coverInner"
|
||||||
|
:style="{
|
||||||
|
borderRadius: `${cornerRadius}px`,
|
||||||
|
backgroundImage: !coverIsVideo ? `url(${coverUrl})` : undefined,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<video
|
||||||
|
v-if="coverIsVideo"
|
||||||
|
ref="videoRef"
|
||||||
|
class="coverInner"
|
||||||
|
:src="coverUrl"
|
||||||
|
autoplay
|
||||||
|
loop
|
||||||
|
muted
|
||||||
|
playsinline
|
||||||
|
crossorigin="anonymous"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, onUnmounted, watch } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
coverUrl: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
coverIsVideo: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
coverVideoPaused: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
musicPaused: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const frameRef = ref<HTMLElement | null>(null);
|
||||||
|
const videoRef = ref<HTMLVideoElement | null>(null);
|
||||||
|
const cornerRadius = ref(20);
|
||||||
|
let resizeObserver: ResizeObserver | null = null;
|
||||||
|
|
||||||
|
watch(() => props.coverVideoPaused, (paused) => {
|
||||||
|
if (videoRef.value) {
|
||||||
|
if (paused) {
|
||||||
|
videoRef.value.pause();
|
||||||
|
} else {
|
||||||
|
videoRef.value.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const frameEl = frameRef.value;
|
||||||
|
if (frameEl) {
|
||||||
|
const onResize = () => {
|
||||||
|
const size = Math.min(frameEl.clientWidth, frameEl.clientHeight);
|
||||||
|
// Logic from React component: Math.max(size * 0.02, window.innerHeight * 0.007)
|
||||||
|
cornerRadius.value = Math.max(size * 0.02, window.innerHeight * 0.007);
|
||||||
|
};
|
||||||
|
resizeObserver = new ResizeObserver(onResize);
|
||||||
|
onResize();
|
||||||
|
resizeObserver.observe(frameEl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (resizeObserver) {
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.cover {
|
||||||
|
--base-box-shadow-v-0: rgba(0, 0, 0, 0.19);
|
||||||
|
--base-box-shadow-y-0: 1em;
|
||||||
|
--base-box-shadow-r-0: 1.2em;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
/* object-fit: cover; removed as it applies to img/video, here it's div container */
|
||||||
|
/* object-position: center; removed */
|
||||||
|
/* background-position: center; removed - moved to inner */
|
||||||
|
/* background-size: cover; removed - moved to inner */
|
||||||
|
|
||||||
|
/* border-radius: max(2%, 0.7vh); */
|
||||||
|
filter: drop-shadow(
|
||||||
|
var(--base-box-shadow-v-0) 0px var(--base-box-shadow-y-0)
|
||||||
|
var(--base-box-shadow-r-0)
|
||||||
|
);
|
||||||
|
transform: scale(1);
|
||||||
|
will-change: transform; /* Optimized for animation */
|
||||||
|
transition:
|
||||||
|
background-image 0.5s linear,
|
||||||
|
filter 0.5s ease,
|
||||||
|
transform 0.5s cubic-bezier(0.3, 0.2, 0.2, 1.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover.musicPaused {
|
||||||
|
/* --base-box-shadow-v-0: rgba(0, 0, 0, 0.19); */
|
||||||
|
/* --base-box-shadow-y-0: 0.8em; */
|
||||||
|
/* --base-box-shadow-r-0: 0.8em; */
|
||||||
|
/* transform: scale(var(--scale-level)); */
|
||||||
|
transition:
|
||||||
|
background-image 0.5s linear,
|
||||||
|
filter 0.5s ease,
|
||||||
|
transform 0.6s cubic-bezier(0.4, 0.2, 0.1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.coverInner {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-size: cover;
|
||||||
|
background-color: black;
|
||||||
|
transition: background-image 0.5s linear;
|
||||||
|
overflow: hidden; /* Important for border-radius clipping */
|
||||||
|
object-fit: cover; /* For video element */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
71
src/renderer/src/components/player/MediaButton.vue
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<template>
|
||||||
|
<button
|
||||||
|
class="mediaButton"
|
||||||
|
:class="className"
|
||||||
|
type="button"
|
||||||
|
@click="onClick"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
className?: string;
|
||||||
|
onClick?: (event: MouseEvent) => void;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
@keyframes pressed-animation {
|
||||||
|
0% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
20% {
|
||||||
|
transform: scale(0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mediaButton {
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: relative;
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mediaButton:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.133); /* #fff2 approx */
|
||||||
|
}
|
||||||
|
|
||||||
|
.mediaButton:active {
|
||||||
|
background-color: rgba(255, 255, 255, 0.133);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mediaButton:active :deep(*) {
|
||||||
|
animation-name: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mediaButton :deep(*) {
|
||||||
|
transition: transform 0.5s;
|
||||||
|
transform-origin: center;
|
||||||
|
animation: pressed-animation 0.7s;
|
||||||
|
animation-composition: accumulate;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
107
src/renderer/src/components/player/MusicInfo.vue
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<div class="musicInfo" :class="className">
|
||||||
|
<div class="info">
|
||||||
|
<TextMarquee v-if="name !== undefined" class="name">
|
||||||
|
{{ name }}
|
||||||
|
</TextMarquee>
|
||||||
|
<TextMarquee v-if="artists && artists.length > 0" class="artists">
|
||||||
|
<span v-for="(artist, index) in artists" :key="`artist-${artist}-${index}`">
|
||||||
|
<a @click.stop="onArtistClicked && onArtistClicked(artist, index)">{{ artist }}</a>
|
||||||
|
</span>
|
||||||
|
</TextMarquee>
|
||||||
|
<TextMarquee v-if="album !== undefined" class="album">
|
||||||
|
<a @click.stop="onAlbumClicked && onAlbumClicked()">{{ album }}</a>
|
||||||
|
</TextMarquee>
|
||||||
|
</div>
|
||||||
|
<!-- MenuButton placeholder or implementation -->
|
||||||
|
<div class="menu-button-placeholder" @click="onMenuButtonClicked">
|
||||||
|
<!-- Icon could go here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import TextMarquee from './TextMarquee.vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
name?: string;
|
||||||
|
artists?: string[];
|
||||||
|
album?: string;
|
||||||
|
onArtistClicked?: (artist: string, index: number) => void;
|
||||||
|
onAlbumClicked?: () => void;
|
||||||
|
onMenuButtonClicked?: () => void;
|
||||||
|
className?: string;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.musicInfo {
|
||||||
|
display: flex;
|
||||||
|
color: white;
|
||||||
|
font-size: max(2vh, 1em);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
/* Fixed line-height to prevent layout shifting */
|
||||||
|
line-height: 1.25em;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: text;
|
||||||
|
user-select: text;
|
||||||
|
min-width: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.4px;
|
||||||
|
opacity: 0.9;
|
||||||
|
mix-blend-mode: normal !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.artists,
|
||||||
|
.album {
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: center;
|
||||||
|
opacity: 0.45;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
letter-spacing: 0.4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.artists :deep(a),
|
||||||
|
.album :deep(a) {
|
||||||
|
text-decoration: none;
|
||||||
|
user-select: text;
|
||||||
|
cursor: default;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.artists :deep(a:hover),
|
||||||
|
.album :deep(a:hover) {
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.artists :deep(a:active),
|
||||||
|
.album :deep(a:active) {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add separators between artists */
|
||||||
|
.artists :deep(span::after) {
|
||||||
|
content: ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
.artists :deep(span:nth-last-child(2)::after) {
|
||||||
|
content: "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
.artists :deep(span:last-child::after) {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { Icon } from '@iconify/vue';
|
import { Icon } from '@iconify/vue';
|
||||||
import { usePlayerStore, PlayMode } from '../stores/player';
|
import { usePlayerStore, PlayMode } from '../../stores/player';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
const playerStore = usePlayerStore();
|
const playerStore = usePlayerStore();
|
||||||
105
src/renderer/src/components/player/TextMarquee.vue
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
ref="outerDiv"
|
||||||
|
class="textMarquee"
|
||||||
|
:class="{ [className || '']: true }"
|
||||||
|
@mouseenter="onMouseEnter"
|
||||||
|
@mouseleave="onMouseLeave"
|
||||||
|
>
|
||||||
|
<div ref="innerDiv">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onUnmounted } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
className?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const outerDiv = ref<HTMLDivElement | null>(null);
|
||||||
|
const innerDiv = ref<HTMLDivElement | null>(null);
|
||||||
|
const currentAnimations = new Set<Animation>();
|
||||||
|
|
||||||
|
const onMouseEnter = () => {
|
||||||
|
if (innerDiv.value && outerDiv.value) {
|
||||||
|
const outerWidth = outerDiv.value.clientWidth;
|
||||||
|
const innerWidth = innerDiv.value.clientWidth;
|
||||||
|
|
||||||
|
if (innerWidth <= outerWidth * 0.95) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
outerDiv.value.classList.add('animating');
|
||||||
|
|
||||||
|
const distance = innerWidth - outerWidth * 0.95;
|
||||||
|
|
||||||
|
const ani = innerDiv.value.animate(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
transform: "translateX(0px)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transform: `translateX(${-distance}px)`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
iterations: 2,
|
||||||
|
direction: "alternate",
|
||||||
|
easing: "linear",
|
||||||
|
duration: Math.max(0, ((distance * 2) / 64) * 1000),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
ani.finished.then(() => {
|
||||||
|
outerDiv.value?.classList.remove('animating');
|
||||||
|
});
|
||||||
|
|
||||||
|
currentAnimations.add(ani);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseLeave = () => {
|
||||||
|
for (const ani of currentAnimations) {
|
||||||
|
ani.finish();
|
||||||
|
}
|
||||||
|
outerDiv.value?.classList.remove('animating');
|
||||||
|
currentAnimations.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
onMouseLeave();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.textMarquee {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
-webkit-mask-image: linear-gradient(to right, #000 95%, #0000);
|
||||||
|
mask-image: linear-gradient(to right, #000 95%, #0000);
|
||||||
|
|
||||||
|
&.animating {
|
||||||
|
-webkit-mask-image: linear-gradient(
|
||||||
|
to left,
|
||||||
|
#0000,
|
||||||
|
#000 5%,
|
||||||
|
#000 95%,
|
||||||
|
#0000
|
||||||
|
);
|
||||||
|
mask-image: linear-gradient(
|
||||||
|
to left,
|
||||||
|
#0000,
|
||||||
|
#000 5%,
|
||||||
|
#000 95%,
|
||||||
|
#0000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
90
src/renderer/src/components/player/ToggleIconButton.vue
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<template>
|
||||||
|
<button
|
||||||
|
class="toggleIconButton"
|
||||||
|
:class="className"
|
||||||
|
type="button"
|
||||||
|
@click="handleClick"
|
||||||
|
>
|
||||||
|
<img :src="iconSrc" />
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
// Icons
|
||||||
|
import LyricsOffIcon from '@assets/lyrics_off.svg';
|
||||||
|
import LyricsOnIcon from '@assets/lyrics_on.svg';
|
||||||
|
import PlaylistOffIcon from '@assets/playlist_off.svg';
|
||||||
|
import PlaylistOnIcon from '@assets/playlist_on.svg';
|
||||||
|
import AirplayIcon from '@assets/airplay.svg';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
type: 'lyrics' | 'playlist' | 'airplay';
|
||||||
|
checked?: boolean;
|
||||||
|
className?: string;
|
||||||
|
onClick?: (event: MouseEvent) => void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const icons = computed(() => {
|
||||||
|
switch (props.type) {
|
||||||
|
case 'lyrics':
|
||||||
|
return [LyricsOffIcon, LyricsOnIcon];
|
||||||
|
case 'playlist':
|
||||||
|
return [PlaylistOffIcon, PlaylistOnIcon];
|
||||||
|
case 'airplay':
|
||||||
|
return [AirplayIcon, AirplayIcon];
|
||||||
|
default:
|
||||||
|
return [AirplayIcon, AirplayIcon];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const iconSrc = computed(() => {
|
||||||
|
return props.checked ? icons.value[1] : icons.value[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleClick = (event: MouseEvent) => {
|
||||||
|
if (props.onClick) {
|
||||||
|
props.onClick(event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.toggleIconButton {
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: currentColor;
|
||||||
|
|
||||||
|
opacity: 0.7;
|
||||||
|
width: 3em;
|
||||||
|
height: 3em;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
& img {
|
||||||
|
width: 3em;
|
||||||
|
height: 3em;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
filter: brightness(0) invert(1); /* Force white color */
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1600px), (max-height: 1000px) {
|
||||||
|
width: 2em;
|
||||||
|
height: 2em;
|
||||||
|
|
||||||
|
& img {
|
||||||
|
width: 2em;
|
||||||
|
height: 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleIconButton:active {
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
191
src/renderer/src/components/player/VolumeControl.vue
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
<template>
|
||||||
|
<div class="volumeControl" :class="className" :style="style">
|
||||||
|
<div class="icon-container" ref="minSpeakerRef" @click="onClickMin">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 32 40" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M14.9042 27.1802C14.4202 27.1802 14.0473 26.9897 13.595 26.5612L10.3815 23.5461C10.3339 23.5065 10.2863 23.4906 10.2228 23.4906H8.01703C6.70778 23.4906 5.99365 22.7527 5.99365 21.38V18.4442C5.99365 17.0715 6.70778 16.3257 8.01703 16.3257H10.2307C10.2863 16.3257 10.3418 16.3019 10.3815 16.2622L13.595 13.2709C14.079 12.8107 14.4361 12.6282 14.8883 12.6282C15.6104 12.6282 16.142 13.1915 16.142 13.8977V25.9344C16.142 26.6406 15.6104 27.1802 14.9042 27.1802Z"
|
||||||
|
class="speaker-bounce-1"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<BouncingSlider
|
||||||
|
:value="modelValue"
|
||||||
|
:min="min"
|
||||||
|
:max="max"
|
||||||
|
:change-on-drag="true"
|
||||||
|
@update:value="onUpdateValue"
|
||||||
|
class="slider"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="icon-container" ref="maxSpeakerRef" @click="onClickMax">
|
||||||
|
<svg width="32" height="32" viewBox="0 0 43 40" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M24.0403 27.1802C23.5642 27.1802 23.1913 26.9897 22.739 26.5612L19.5176 23.5461C19.4779 23.5065 19.4224 23.4906 19.3668 23.4906H17.161C15.8518 23.4906 15.1377 22.7527 15.1377 21.38V18.4442C15.1377 17.0715 15.8518 16.3257 17.161 16.3257H19.3668C19.4303 16.3257 19.4779 16.3019 19.5255 16.2622L22.739 13.2709C23.223 12.8107 23.5721 12.6282 24.0324 12.6282C24.7544 12.6282 25.286 13.1915 25.286 13.8977V25.9344C25.286 26.6406 24.7544 27.1802 24.0403 27.1802Z"
|
||||||
|
class="speaker-bounce-1"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M28.0948 23.6653C27.6028 23.3559 27.4996 22.7687 27.8964 22.1101C28.2931 21.4991 28.5232 20.7136 28.5232 19.8964C28.5232 19.0712 28.301 18.2856 27.8964 17.6826C27.4917 17.032 27.6028 16.4369 28.0948 16.1274C28.547 15.8418 29.1104 15.9529 29.404 16.3576C30.0863 17.3097 30.491 18.5713 30.491 19.8964C30.491 21.2214 30.0863 22.4831 29.404 23.4273C29.1104 23.8399 28.547 23.943 28.0948 23.6653Z"
|
||||||
|
class="speaker-bounce-2"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M31.6733 25.8711C31.1576 25.5696 31.0942 24.9428 31.4432 24.3794C32.2526 23.1257 32.7207 21.5468 32.7207 19.8964C32.7207 18.2459 32.2605 16.6591 31.4432 15.4133C31.0942 14.8499 31.1576 14.2231 31.6733 13.9137C32.1415 13.6439 32.7128 13.755 33.0143 14.2152C34.0855 15.7783 34.6885 17.8016 34.6885 19.8964C34.6885 21.9911 34.0775 23.9985 33.0143 25.5775C32.7128 26.0377 32.1415 26.1488 31.6733 25.8711Z"
|
||||||
|
class="speaker-bounce-3"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M35.2362 28.1007C34.7363 27.7992 34.6569 27.1803 34.9981 26.6249C36.1883 24.7286 36.9104 22.4196 36.9104 19.9122C36.9104 17.397 36.1883 15.0881 34.9981 13.1917C34.6569 12.6362 34.7363 12.0174 35.2362 11.7159C35.7123 11.4302 36.3073 11.5651 36.6088 12.0571C38.0133 14.2866 38.8702 16.9765 38.8702 19.9122C38.8702 22.8401 38.0291 25.5379 36.6088 27.7675C36.3073 28.2515 35.7123 28.3864 35.2362 28.1007Z"
|
||||||
|
class="speaker-bounce-4"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
import BouncingSlider from './BouncingSlider.vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: number;
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
className?: string;
|
||||||
|
onUpdate: (value: number) => void;
|
||||||
|
style?: any;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const minSpeakerRef = ref<HTMLElement | null>(null);
|
||||||
|
const maxSpeakerRef = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
|
const onUpdateValue = (value: number) => {
|
||||||
|
props.onUpdate(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const triggerAnimation = (ref: HTMLElement | null) => {
|
||||||
|
if(!ref) return;
|
||||||
|
ref.classList.remove('speakerAnimate');
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
ref.classList.add('speakerAnimate');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClickMin = () => {
|
||||||
|
onUpdateValue(props.min);
|
||||||
|
triggerAnimation(minSpeakerRef.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClickMax = () => {
|
||||||
|
onUpdateValue(props.max);
|
||||||
|
triggerAnimation(maxSpeakerRef.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.modelValue, (newVal, oldVal) => {
|
||||||
|
if (newVal === oldVal) return;
|
||||||
|
|
||||||
|
if (newVal <= props.min && minSpeakerRef.value) {
|
||||||
|
minSpeakerRef.value.classList.remove('speakerAnimate');
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
minSpeakerRef.value?.classList.add('speakerAnimate');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (newVal >= props.max && maxSpeakerRef.value) {
|
||||||
|
maxSpeakerRef.value.classList.remove('speakerAnimate');
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
maxSpeakerRef.value?.classList.add('speakerAnimate');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.volumeControl {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: row;
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
color: white; /* Ensure icons are white */
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure SVG fills container and maintains aspect ratio */
|
||||||
|
.icon-container svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
flex: 1;
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
@keyframes speaker-bounce-part1 {
|
||||||
|
to {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes speaker-bounce-part2 {
|
||||||
|
to {
|
||||||
|
transform: scale(0.85);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes speaker-bounce-part3 {
|
||||||
|
to {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Global scope needed for SVG internal classes if they are not scoped by Vue deep selector?
|
||||||
|
Actually, since SVGs are inlined, scoped CSS should work if we use deep selector or just global class.
|
||||||
|
The React code used :global, so we'll simulate that by not scoping these specific animations classes or using :deep()
|
||||||
|
*/
|
||||||
|
|
||||||
|
:deep(.speakerAnimate) .speaker-bounce-1 {
|
||||||
|
animation:
|
||||||
|
speaker-bounce-part1 0.2s ease-in-out 0s forwards,
|
||||||
|
speaker-bounce-part2 0.2s ease-in-out 0.2s forwards,
|
||||||
|
speaker-bounce-part3 0.2s ease-in-out 0.4s forwards;
|
||||||
|
transform-origin: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.speakerAnimate) .speaker-bounce-2 {
|
||||||
|
animation:
|
||||||
|
speaker-bounce-part1 0.2s ease-in-out 0.05s forwards,
|
||||||
|
speaker-bounce-part2 0.2s ease-in-out 0.25s forwards,
|
||||||
|
speaker-bounce-part3 0.2s ease-in-out 0.45s forwards;
|
||||||
|
transform-origin: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.speakerAnimate) .speaker-bounce-3 {
|
||||||
|
animation:
|
||||||
|
speaker-bounce-part1 0.2s ease-in-out 0.1s forwards,
|
||||||
|
speaker-bounce-part2 0.2s ease-in-out 0.3s forwards,
|
||||||
|
speaker-bounce-part3 0.2s ease-in-out 0.5s forwards;
|
||||||
|
transform-origin: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.speakerAnimate) .speaker-bounce-4 {
|
||||||
|
animation:
|
||||||
|
speaker-bounce-part1 0.2s ease-in-out 0.15s forwards,
|
||||||
|
speaker-bounce-part2 0.2s ease-in-out 0.35s forwards,
|
||||||
|
speaker-bounce-part3 0.2s ease-in-out 0.55s forwards;
|
||||||
|
transform-origin: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
14
src/renderer/src/env.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
/// <reference types="vite-svg-loader" />
|
||||||
|
|
||||||
|
declare module '*.svg' {
|
||||||
|
import { Component } from 'vue';
|
||||||
|
const content: Component;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.svg?component' {
|
||||||
|
import { Component } from 'vue';
|
||||||
|
const content: Component;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import Sidebar from '../components/Sidebar.vue';
|
import Sidebar from '../components/Sidebar.vue';
|
||||||
import TopBar from '../components/TopBar.vue';
|
import TopBar from '../components/TopBar.vue';
|
||||||
import PlayerBar from '../components/PlayerBar.vue';
|
import PlayerBar from '../components/player/PlayerBar.vue';
|
||||||
import { usePlayerStore } from '../stores/player';
|
import { usePlayerStore } from '../stores/player';
|
||||||
|
|
||||||
const playerStore = usePlayerStore();
|
const playerStore = usePlayerStore();
|
||||||
|
|||||||
14
src/renderer/src/shims.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
declare module '*.svg' {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.png' {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '*.jpg' {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { ref, watch } from 'vue';
|
import { ref, shallowRef, watch } from 'vue';
|
||||||
import { MessagePlugin } from 'tdesign-vue-next';
|
import { MessagePlugin } from 'tdesign-vue-next';
|
||||||
import type { Song } from '../types/song';
|
import type { Song } from '../types/song';
|
||||||
|
import { parseLyric } from '../utils/lyricUtil'
|
||||||
export enum PlayMode {
|
export enum PlayMode {
|
||||||
List = 'list',
|
List = 'list',
|
||||||
Single = 'single',
|
Single = 'single',
|
||||||
@@ -44,6 +44,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
|
|
||||||
// UI State
|
// UI State
|
||||||
const isPlayerFullScreen = ref(false);
|
const isPlayerFullScreen = ref(false);
|
||||||
|
const hideLyricView = ref(false);
|
||||||
|
|
||||||
// Playlist State
|
// Playlist State
|
||||||
const savedPlaylist = localStorage.getItem('qz-player-playlist');
|
const savedPlaylist = localStorage.getItem('qz-player-playlist');
|
||||||
@@ -61,7 +62,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
const hasRetriedWithFreshUrl = ref(false);
|
const hasRetriedWithFreshUrl = ref(false);
|
||||||
|
|
||||||
// Lyrics State
|
// Lyrics State
|
||||||
const lyrics = ref<{ lines: any[] }>({ lines: [] });
|
const lyrics = shallowRef<{ lines: any[] }>({ lines: [] });
|
||||||
|
|
||||||
// --- Helpers ---
|
// --- Helpers ---
|
||||||
const activateDummyAudio = async () => {
|
const activateDummyAudio = async () => {
|
||||||
@@ -139,7 +140,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
|
|
||||||
await activateDummyAudio();
|
await activateDummyAudio();
|
||||||
updateMediaSession(song);
|
updateMediaSession(song);
|
||||||
// fetchLyrics(song);
|
fetchLyrics(song);
|
||||||
|
|
||||||
// Get URL (Cache -> Network)
|
// Get URL (Cache -> Network)
|
||||||
let playUrl = song.url;
|
let playUrl = song.url;
|
||||||
@@ -184,13 +185,14 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
lyrics.value = { lines: [] }; // Reset
|
lyrics.value = { lines: [] }; // Reset
|
||||||
if (!song || !song.id) return;
|
if (!song || !song.id) return;
|
||||||
try {
|
try {
|
||||||
// Check if plugin API exists
|
//Check if plugin API exists
|
||||||
// if (window.electronAPI?.plugin?.getLyric) {
|
if (window.electronAPI?.plugin?.getLyric) {
|
||||||
// const rawLyric = await window.electronAPI.plugin.getLyric(song.source || 'kw', song.id.toString());
|
const rawLyric = await window.electronAPI.plugin.getLyric(song.source || 'kw', song.id.toString());
|
||||||
// console.log(rawLyric)
|
lyrics.value = { lines: parseLyric(rawLyric) }
|
||||||
// } else {
|
console.log(lyrics.value)
|
||||||
// MessagePlugin.warning("当前插件不支持歌词获取").then()
|
} else {
|
||||||
// }
|
MessagePlugin.warning("当前插件不支持歌词获取").then()
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to fetch lyrics:', e);
|
console.error('Failed to fetch lyrics:', e);
|
||||||
}
|
}
|
||||||
@@ -335,7 +337,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
if (restoredSong) {
|
if (restoredSong) {
|
||||||
// Use playSong with autoPlay=false to load the song into the engine
|
// Use playSong with autoPlay=false to load the song into the engine
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
playSong(restoredSong, false);
|
playSong(restoredSong, true);
|
||||||
fetchLyrics(restoredSong);
|
fetchLyrics(restoredSong);
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
@@ -364,6 +366,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
lyrics,
|
lyrics,
|
||||||
fetchLyrics,
|
fetchLyrics,
|
||||||
addListMode,
|
addListMode,
|
||||||
playFromList
|
playFromList,
|
||||||
|
hideLyricView
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
@import 'variables.css';
|
@import 'variables.css';
|
||||||
|
|
||||||
* {
|
* , *::before, *::after {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|||||||
57
src/renderer/src/utils/lyricUtil.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import {LyricLine, parseLrc, parseQrc, parseTTML, parseYrc} from "@applemusic-like-lyrics/lyric";
|
||||||
|
const sanitizeLyricLines = (lines: LyricLine[]): LyricLine[] => {
|
||||||
|
const defaultLineDuration = 3000
|
||||||
|
const toFiniteNumber = (v: any, fallback: number) => {
|
||||||
|
const n = typeof v === 'number' ? v : Number(v)
|
||||||
|
return Number.isFinite(n) ? n : fallback
|
||||||
|
}
|
||||||
|
const cleaned: LyricLine[] = []
|
||||||
|
for (const rawLine of lines || []) {
|
||||||
|
const rawWords = Array.isArray((rawLine as any).words) ? (rawLine as any).words : []
|
||||||
|
const fixedWords: any[] = []
|
||||||
|
let prevEnd = -1
|
||||||
|
for (const rawWord of rawWords) {
|
||||||
|
const rawStart = toFiniteNumber(rawWord?.startTime, Number.NaN)
|
||||||
|
const rawEnd = toFiniteNumber(rawWord?.endTime, Number.NaN)
|
||||||
|
if (!Number.isFinite(rawStart)) continue
|
||||||
|
let startTime = Math.max(0, rawStart)
|
||||||
|
if (startTime < prevEnd) startTime = prevEnd
|
||||||
|
let endTime = Number.isFinite(rawEnd) ? rawEnd : startTime + 1
|
||||||
|
if (endTime <= startTime) endTime = startTime + 1
|
||||||
|
prevEnd = endTime
|
||||||
|
fixedWords.push({ ...rawWord, startTime, endTime })
|
||||||
|
}
|
||||||
|
if (fixedWords.length === 0) continue
|
||||||
|
|
||||||
|
const firstWordStart = fixedWords[0].startTime
|
||||||
|
const lastWordEnd = fixedWords[fixedWords.length - 1].endTime
|
||||||
|
let startTime = toFiniteNumber((rawLine as any).startTime, firstWordStart)
|
||||||
|
startTime = Math.max(0, startTime)
|
||||||
|
let endTime = toFiniteNumber((rawLine as any).endTime, lastWordEnd)
|
||||||
|
if (!Number.isFinite(endTime) || endTime <= startTime) endTime = startTime + defaultLineDuration
|
||||||
|
if (endTime < lastWordEnd) endTime = lastWordEnd
|
||||||
|
|
||||||
|
cleaned.push({ ...(rawLine as any), startTime, endTime, words: fixedWords })
|
||||||
|
}
|
||||||
|
cleaned.sort((a: any, b: any) => (a?.startTime ?? 0) - (b?.startTime ?? 0))
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
interface LyricData {
|
||||||
|
ttml?: string,
|
||||||
|
yrc?: string,
|
||||||
|
lrc?: string,
|
||||||
|
qrc?: string
|
||||||
|
}
|
||||||
|
export function parseLyric(lyric: LyricData):LyricLine[] {
|
||||||
|
let parsed:LyricLine[] = []
|
||||||
|
if (lyric.ttml != undefined) {
|
||||||
|
parsed = parseTTML(lyric.ttml).lines;
|
||||||
|
} else if (lyric.yrc != undefined) {
|
||||||
|
parsed = parseYrc(lyric.yrc);
|
||||||
|
} else if (lyric.lrc != undefined) {
|
||||||
|
parsed = parseLrc(lyric.lrc);
|
||||||
|
} else if (lyric.qrc != undefined) {
|
||||||
|
parsed = parseQrc(lyric.qrc)
|
||||||
|
}
|
||||||
|
return sanitizeLyricLines(parsed);
|
||||||
|
}
|
||||||
19
tsconfig.web.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"extends": "@electron-toolkit/tsconfig/tsconfig.web.json",
|
||||||
|
"include": [
|
||||||
|
"src/renderer/src/env.d.ts",
|
||||||
|
"src/renderer/src/**/*",
|
||||||
|
"src/renderer/src/**/*.vue",
|
||||||
|
"src/preload/*.d.ts"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@renderer/*": [
|
||||||
|
"src/renderer/src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||