diff --git a/electron.vite.config.ts b/electron.vite.config.ts index dc162b2..86e73a9 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -13,7 +13,7 @@ export default defineConfig({ resolve: { alias: { '@renderer': resolve('src/renderer/src'), - '@assets': resolve('src/renderer/assets') + '@assets': resolve('src/renderer/src/assets') } }, plugins: [vue(), vueJsx(), wasm()], diff --git a/package-lock.json b/package-lock.json index f32d652..34663c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "typescript": "^5.2.2", "vite": "^5.1.6", "vite-plugin-node-polyfills": "^0.25.0", + "vite-svg-loader": "^5.1.0", "vue-tsc": "^2.0.26" } }, @@ -2375,6 +2376,16 @@ "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": { "version": "6.0.3", "resolved": "https://registry.npmmirror.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -3408,6 +3419,13 @@ "dev": true, "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": { "version": "3.2.0", "resolved": "https://registry.npmmirror.com/boolean/-/boolean-3.2.0.tgz", @@ -4322,6 +4340,37 @@ "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": { "version": "2.0.8", "resolved": "https://registry.npmmirror.com/css-vendor/-/css-vendor-2.0.8.tgz", @@ -4332,6 +4381,55 @@ "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": { "version": "3.2.3", "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", @@ -4628,6 +4726,34 @@ "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": { "version": "4.22.0", "resolved": "https://registry.npmmirror.com/domain-browser/-/domain-browser-4.22.0.tgz", @@ -4641,6 +4767,50 @@ "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": { "version": "9.0.2", "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-9.0.2.tgz", @@ -7053,6 +7223,13 @@ "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": { "version": "6.0.0", "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz", @@ -7354,6 +7531,19 @@ "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==", "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": { "version": "1.13.4", "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz", @@ -8995,6 +9185,42 @@ "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": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/symbol-observable/-/symbol-observable-1.2.0.tgz", @@ -9528,6 +9754,19 @@ "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": { "version": "1.1.2", "resolved": "https://registry.npmmirror.com/vm-browserify/-/vm-browserify-1.1.2.tgz", diff --git a/package.json b/package.json index 04b0d64..50cebe7 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "typescript": "^5.2.2", "vite": "^5.1.6", "vite-plugin-node-polyfills": "^0.25.0", + "vite-svg-loader": "^5.1.0", "vue-tsc": "^2.0.26" }, "main": "out/main/index.js", diff --git a/src/main/index.ts b/src/main/index.ts index 252cd09..eb9bd5c 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -34,7 +34,8 @@ function createWindow() { webPreferences: { preload: path.join(__dirname, '../preload/index.js'), sandbox: false, - contextIsolation: true + contextIsolation: true, + webSecurity: false } }) diff --git a/src/renderer/src/assets/airplay.svg b/src/renderer/src/assets/airplay.svg new file mode 100644 index 0000000..6e4f8d3 --- /dev/null +++ b/src/renderer/src/assets/airplay.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/renderer/src/assets/icon_forward.svg b/src/renderer/src/assets/icon_forward.svg new file mode 100644 index 0000000..a74d786 --- /dev/null +++ b/src/renderer/src/assets/icon_forward.svg @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/src/renderer/src/assets/icon_pause.svg b/src/renderer/src/assets/icon_pause.svg new file mode 100644 index 0000000..1bc1d35 --- /dev/null +++ b/src/renderer/src/assets/icon_pause.svg @@ -0,0 +1,5 @@ + + + diff --git a/src/renderer/src/assets/icon_play.svg b/src/renderer/src/assets/icon_play.svg new file mode 100644 index 0000000..20a9bb3 --- /dev/null +++ b/src/renderer/src/assets/icon_play.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/renderer/src/assets/icon_rewind.svg b/src/renderer/src/assets/icon_rewind.svg new file mode 100644 index 0000000..561a9e8 --- /dev/null +++ b/src/renderer/src/assets/icon_rewind.svg @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/src/renderer/src/assets/icon_speaker.svg b/src/renderer/src/assets/icon_speaker.svg new file mode 100644 index 0000000..5dc78d0 --- /dev/null +++ b/src/renderer/src/assets/icon_speaker.svg @@ -0,0 +1,5 @@ + + + diff --git a/src/renderer/src/assets/icon_speaker_3.svg b/src/renderer/src/assets/icon_speaker_3.svg new file mode 100644 index 0000000..519369a --- /dev/null +++ b/src/renderer/src/assets/icon_speaker_3.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/src/renderer/src/assets/lyricfont.ttf b/src/renderer/src/assets/lyricfont.ttf new file mode 100644 index 0000000..5c925d1 Binary files /dev/null and b/src/renderer/src/assets/lyricfont.ttf differ diff --git a/src/renderer/src/assets/lyrics_off.svg b/src/renderer/src/assets/lyrics_off.svg new file mode 100644 index 0000000..429f2a2 --- /dev/null +++ b/src/renderer/src/assets/lyrics_off.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/renderer/src/assets/lyrics_on.svg b/src/renderer/src/assets/lyrics_on.svg new file mode 100644 index 0000000..0bb4484 --- /dev/null +++ b/src/renderer/src/assets/lyrics_on.svg @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/src/renderer/src/assets/playlist_off.svg b/src/renderer/src/assets/playlist_off.svg new file mode 100644 index 0000000..72f16ad --- /dev/null +++ b/src/renderer/src/assets/playlist_off.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/renderer/src/assets/playlist_on.svg b/src/renderer/src/assets/playlist_on.svg new file mode 100644 index 0000000..9745fae --- /dev/null +++ b/src/renderer/src/assets/playlist_on.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/renderer/src/assets/repeat-active.svg b/src/renderer/src/assets/repeat-active.svg new file mode 100644 index 0000000..d15c731 --- /dev/null +++ b/src/renderer/src/assets/repeat-active.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/renderer/src/assets/repeat-one-active.svg b/src/renderer/src/assets/repeat-one-active.svg new file mode 100644 index 0000000..89075da --- /dev/null +++ b/src/renderer/src/assets/repeat-one-active.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/renderer/src/assets/repeat.svg b/src/renderer/src/assets/repeat.svg new file mode 100644 index 0000000..53674fb --- /dev/null +++ b/src/renderer/src/assets/repeat.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/renderer/src/assets/shuffle-active.svg b/src/renderer/src/assets/shuffle-active.svg new file mode 100644 index 0000000..7f6cfa7 --- /dev/null +++ b/src/renderer/src/assets/shuffle-active.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/renderer/src/assets/shuffle.svg b/src/renderer/src/assets/shuffle.svg new file mode 100644 index 0000000..9ec5885 --- /dev/null +++ b/src/renderer/src/assets/shuffle.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/renderer/src/components/FullScreenPlayer.vue b/src/renderer/src/components/FullScreenPlayer.vue index 92c1c3e..6e6c2c5 100644 --- a/src/renderer/src/components/FullScreenPlayer.vue +++ b/src/renderer/src/components/FullScreenPlayer.vue @@ -1,225 +1,189 @@ diff --git a/src/renderer/src/components/player/BouncingSlider.vue b/src/renderer/src/components/player/BouncingSlider.vue new file mode 100644 index 0000000..63e4926 --- /dev/null +++ b/src/renderer/src/components/player/BouncingSlider.vue @@ -0,0 +1,223 @@ + + + + + diff --git a/src/renderer/src/components/player/ControlThumb.vue b/src/renderer/src/components/player/ControlThumb.vue new file mode 100644 index 0000000..5af7c2e --- /dev/null +++ b/src/renderer/src/components/player/ControlThumb.vue @@ -0,0 +1,47 @@ + + + + + \ No newline at end of file diff --git a/src/renderer/src/components/player/Cover.vue b/src/renderer/src/components/player/Cover.vue new file mode 100644 index 0000000..4241b56 --- /dev/null +++ b/src/renderer/src/components/player/Cover.vue @@ -0,0 +1,134 @@ + + + + + diff --git a/src/renderer/src/components/player/MediaButton.vue b/src/renderer/src/components/player/MediaButton.vue new file mode 100644 index 0000000..2c713a8 --- /dev/null +++ b/src/renderer/src/components/player/MediaButton.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/src/renderer/src/components/player/MusicInfo.vue b/src/renderer/src/components/player/MusicInfo.vue new file mode 100644 index 0000000..bf722c9 --- /dev/null +++ b/src/renderer/src/components/player/MusicInfo.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/src/renderer/src/components/PlayerBar.vue b/src/renderer/src/components/player/PlayerBar.vue similarity index 99% rename from src/renderer/src/components/PlayerBar.vue rename to src/renderer/src/components/player/PlayerBar.vue index 10b5d1e..fdaccb9 100644 --- a/src/renderer/src/components/PlayerBar.vue +++ b/src/renderer/src/components/player/PlayerBar.vue @@ -103,7 +103,7 @@ + + diff --git a/src/renderer/src/components/player/ToggleIconButton.vue b/src/renderer/src/components/player/ToggleIconButton.vue new file mode 100644 index 0000000..c9c4ac7 --- /dev/null +++ b/src/renderer/src/components/player/ToggleIconButton.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/src/renderer/src/components/player/VolumeControl.vue b/src/renderer/src/components/player/VolumeControl.vue new file mode 100644 index 0000000..80e7128 --- /dev/null +++ b/src/renderer/src/components/player/VolumeControl.vue @@ -0,0 +1,191 @@ + + + + + diff --git a/src/renderer/src/env.d.ts b/src/renderer/src/env.d.ts new file mode 100644 index 0000000..53758af --- /dev/null +++ b/src/renderer/src/env.d.ts @@ -0,0 +1,14 @@ +/// +/// + +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; +} diff --git a/src/renderer/src/layout/MainLayout.vue b/src/renderer/src/layout/MainLayout.vue index 2a7787d..5e3db62 100644 --- a/src/renderer/src/layout/MainLayout.vue +++ b/src/renderer/src/layout/MainLayout.vue @@ -19,7 +19,7 @@ import { computed } from 'vue'; import Sidebar from '../components/Sidebar.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'; const playerStore = usePlayerStore(); diff --git a/src/renderer/src/shims.d.ts b/src/renderer/src/shims.d.ts new file mode 100644 index 0000000..7d50869 --- /dev/null +++ b/src/renderer/src/shims.d.ts @@ -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; +} diff --git a/src/renderer/src/stores/player.ts b/src/renderer/src/stores/player.ts index 7822ca8..2a76370 100644 --- a/src/renderer/src/stores/player.ts +++ b/src/renderer/src/stores/player.ts @@ -1,8 +1,8 @@ import { defineStore } from 'pinia'; -import { ref, watch } from 'vue'; +import { ref, shallowRef, watch } from 'vue'; import { MessagePlugin } from 'tdesign-vue-next'; import type { Song } from '../types/song'; - +import { parseLyric } from '../utils/lyricUtil' export enum PlayMode { List = 'list', Single = 'single', @@ -44,6 +44,7 @@ export const usePlayerStore = defineStore('player', () => { // UI State const isPlayerFullScreen = ref(false); + const hideLyricView = ref(false); // Playlist State const savedPlaylist = localStorage.getItem('qz-player-playlist'); @@ -61,7 +62,7 @@ export const usePlayerStore = defineStore('player', () => { const hasRetriedWithFreshUrl = ref(false); // Lyrics State - const lyrics = ref<{ lines: any[] }>({ lines: [] }); + const lyrics = shallowRef<{ lines: any[] }>({ lines: [] }); // --- Helpers --- const activateDummyAudio = async () => { @@ -139,7 +140,7 @@ export const usePlayerStore = defineStore('player', () => { await activateDummyAudio(); updateMediaSession(song); - // fetchLyrics(song); + fetchLyrics(song); // Get URL (Cache -> Network) let playUrl = song.url; @@ -184,13 +185,14 @@ export const usePlayerStore = defineStore('player', () => { lyrics.value = { lines: [] }; // Reset if (!song || !song.id) return; try { - // Check if plugin API exists - // if (window.electronAPI?.plugin?.getLyric) { - // const rawLyric = await window.electronAPI.plugin.getLyric(song.source || 'kw', song.id.toString()); - // console.log(rawLyric) - // } else { - // MessagePlugin.warning("当前插件不支持歌词获取").then() - // } + //Check if plugin API exists + if (window.electronAPI?.plugin?.getLyric) { + const rawLyric = await window.electronAPI.plugin.getLyric(song.source || 'kw', song.id.toString()); + lyrics.value = { lines: parseLyric(rawLyric) } + console.log(lyrics.value) + } else { + MessagePlugin.warning("当前插件不支持歌词获取").then() + } } catch (e) { console.error('Failed to fetch lyrics:', e); } @@ -335,7 +337,7 @@ export const usePlayerStore = defineStore('player', () => { if (restoredSong) { // Use playSong with autoPlay=false to load the song into the engine setTimeout(() => { - playSong(restoredSong, false); + playSong(restoredSong, true); fetchLyrics(restoredSong); }, 0); } @@ -364,6 +366,7 @@ export const usePlayerStore = defineStore('player', () => { lyrics, fetchLyrics, addListMode, - playFromList + playFromList, + hideLyricView }; }); \ No newline at end of file diff --git a/src/renderer/src/styles/main.css b/src/renderer/src/styles/main.css index 4426550..5d89f6e 100644 --- a/src/renderer/src/styles/main.css +++ b/src/renderer/src/styles/main.css @@ -1,8 +1,9 @@ @import 'variables.css'; -* { +* , *::before, *::after { margin: 0; padding: 0; + box-sizing: border-box; } body { diff --git a/src/renderer/src/utils/lyricUtil.ts b/src/renderer/src/utils/lyricUtil.ts new file mode 100644 index 0000000..980e496 --- /dev/null +++ b/src/renderer/src/utils/lyricUtil.ts @@ -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); +} \ No newline at end of file diff --git a/tsconfig.web.json b/tsconfig.web.json new file mode 100644 index 0000000..c5fa8b4 --- /dev/null +++ b/tsconfig.web.json @@ -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/*" + ] + }, + }, + +}