├── .editorconfig ├── .env ├── .env.example ├── .eslintignore ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md ├── resources │ └── vutron-sample.webp └── workflows │ ├── app-test.yml │ ├── build.yml │ └── documents.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .vscode └── settings.json ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── build.sqlite3.js ├── buildAssets ├── builder │ └── config.js └── icons │ ├── icon-1.icns │ ├── icon-1.ico │ ├── icon-1.png │ ├── icon.icns │ ├── icon.ico │ └── icon.png ├── docs ├── .gitignore ├── package-lock.json ├── package.json ├── src │ ├── .vitepress │ │ └── config.mts │ ├── electron-how-to │ │ ├── main-and-renderer-process.md │ │ └── preload-script.md │ ├── installation-and-build │ │ ├── automated-testing.md │ │ ├── build-configuration.md │ │ ├── getting-started.md │ │ ├── install-local-documentation.md │ │ └── npm-scripts.md │ ├── other-projects.md │ ├── project-structures │ │ ├── pre-configured-components.md │ │ └── project-structure.md │ └── public │ │ ├── favicon.ico │ │ └── icon.png └── tsconfig.json ├── fix-sandbox.js ├── images ├── comment-page.jpg ├── explore.jpg ├── home.jpg ├── library.jpg ├── like-page.jpg ├── local-music.jpg ├── localMusic.jpg ├── media-control-lyric.png ├── mv.jpg ├── play-page.jpg ├── playlists.jpg ├── search-lyric.jpg ├── setConvolver.jpg ├── tray-TouchBar-lyric.jpg └── user.jpg ├── package.json ├── playwright.config.ts ├── rebuild.js ├── src ├── main │ ├── IPCs.ts │ ├── appServer │ │ ├── appServer.ts │ │ ├── audio.ts │ │ ├── netease.ts │ │ └── request.ts │ ├── cache.ts │ ├── checkUpdate.ts │ ├── db.ts │ ├── dbus.ts │ ├── dbusClient.ts │ ├── dbusService.ts │ ├── dock.ts │ ├── globalShortcut.ts │ ├── index.dev.ts │ ├── index.ts │ ├── log.ts │ ├── menu.ts │ ├── mpris.ts │ ├── store.ts │ ├── streaming │ │ ├── emby.ts │ │ └── navidrome.ts │ ├── touchBar.ts │ ├── tray.ts │ └── utils │ │ ├── CacheApis.ts │ │ ├── Constants.ts │ │ ├── shortcuts.ts │ │ └── utils.ts ├── preload │ ├── index.ts │ └── osdWin.ts ├── public │ ├── images │ │ ├── default.jpg │ │ ├── touchbar │ │ │ ├── backward.png │ │ │ ├── forward.png │ │ │ ├── like.png │ │ │ ├── like_fill.png │ │ │ ├── next_up.png │ │ │ ├── page_next.png │ │ │ ├── page_prev.png │ │ │ ├── pause.png │ │ │ ├── play.png │ │ │ ├── search.png │ │ │ └── thumbs_down.png │ │ ├── tray │ │ │ ├── left_black.png │ │ │ ├── left_white.png │ │ │ ├── like_black.png │ │ │ ├── like_white.png │ │ │ ├── lock_black.png │ │ │ ├── lock_white.png │ │ │ ├── lrc_black.png │ │ │ ├── lrc_white.png │ │ │ ├── menu_black.png │ │ │ ├── menu_black_1.png │ │ │ ├── menu_white.png │ │ │ ├── menu_white_1.png │ │ │ ├── pause_black.png │ │ │ ├── pause_white.png │ │ │ ├── play_black.png │ │ │ ├── play_white.png │ │ │ ├── quit_black.png │ │ │ ├── quit_white.png │ │ │ ├── repeat_black.png │ │ │ ├── repeat_white.png │ │ │ ├── right_black.png │ │ │ ├── right_white.png │ │ │ ├── unlike_black.png │ │ │ ├── unlike_white.png │ │ │ ├── unlock_black.png │ │ │ ├── unlock_white.png │ │ │ └── vutronmusic-icon.png │ │ └── vutron-logo.webp │ └── migrations │ │ ├── 1.5.0.sql │ │ └── init.sql ├── renderer │ ├── App.vue │ ├── api │ │ ├── album.ts │ │ ├── artist.ts │ │ ├── auth.ts │ │ ├── comment.ts │ │ ├── mv.ts │ │ ├── other.ts │ │ ├── playlist.ts │ │ ├── track.ts │ │ └── user.ts │ ├── assets │ │ ├── css │ │ │ ├── global.scss │ │ │ ├── osdlyric.scss │ │ │ └── plyr.css │ │ ├── icons │ │ │ ├── arrow-down.svg │ │ │ ├── arrow-left.svg │ │ │ ├── arrow-right.svg │ │ │ ├── arrow-up-alt.svg │ │ │ ├── arrow-up.svg │ │ │ ├── back5s.svg │ │ │ ├── checkbox.svg │ │ │ ├── checked.svg │ │ │ ├── close.svg │ │ │ ├── collect.svg │ │ │ ├── collected.svg │ │ │ ├── color-plate.svg │ │ │ ├── comment.svg │ │ │ ├── decreaseFont.svg │ │ │ ├── dropdown.svg │ │ │ ├── explicit.svg │ │ │ ├── explore.svg │ │ │ ├── floor-comment.svg │ │ │ ├── floorComment.svg │ │ │ ├── fm.svg │ │ │ ├── forward5s.svg │ │ │ ├── github.svg │ │ │ ├── heart-bar.svg │ │ │ ├── heart-solid.svg │ │ │ ├── heart.svg │ │ │ ├── increaseFont.svg │ │ │ ├── library.svg │ │ │ ├── like.svg │ │ │ ├── liked.svg │ │ │ ├── list.svg │ │ │ ├── local-music.svg │ │ │ ├── local-track-bg.svg │ │ │ ├── lock.svg │ │ │ ├── log.svg │ │ │ ├── login.svg │ │ │ ├── logo.svg │ │ │ ├── logout.svg │ │ │ ├── lyric.svg │ │ │ ├── lyricChange.svg │ │ │ ├── mail.svg │ │ │ ├── mini-mode.svg │ │ │ ├── mobile.svg │ │ │ ├── more.svg │ │ │ ├── mv.svg │ │ │ ├── next.svg │ │ │ ├── normal-mode.svg │ │ │ ├── offTop.svg │ │ │ ├── onTop.svg │ │ │ ├── options.svg │ │ │ ├── osd-lyrics.svg │ │ │ ├── pause.svg │ │ │ ├── play.svg │ │ │ ├── plus.svg │ │ │ ├── previous.svg │ │ │ ├── recovery.svg │ │ │ ├── repeat-1.svg │ │ │ ├── repeat.svg │ │ │ ├── search.svg │ │ │ ├── settings.svg │ │ │ ├── shuffle.svg │ │ │ ├── sort-up.svg │ │ │ ├── stream-icon.svg │ │ │ ├── target.svg │ │ │ ├── thumbs-down.svg │ │ │ ├── user.svg │ │ │ ├── volume-half.svg │ │ │ ├── volume-mute.svg │ │ │ ├── volume.svg │ │ │ ├── web.svg │ │ │ └── x.svg │ │ ├── images │ │ │ ├── auto.png │ │ │ ├── dark.jpg │ │ │ ├── default.jpg │ │ │ ├── emby.png │ │ │ ├── jellyfin.png │ │ │ ├── light.jpg │ │ │ ├── navidrome.png │ │ │ ├── navidrome.webp │ │ │ ├── netease-music.png │ │ │ ├── vutronmusic-icon.png │ │ │ └── vutronmusic-white.png │ │ ├── lottie │ │ │ └── snow.json │ │ ├── medias │ │ │ ├── Silence02s.mp3 │ │ │ ├── bright-hall.wav │ │ │ ├── cardiod-35-10-spread.wav │ │ │ ├── cinema-diningroom.wav │ │ │ ├── dining-living-true-stereo.wav │ │ │ ├── feedback-spring.wav │ │ │ ├── filter-telephone.wav │ │ │ ├── living-bedroom-leveled.wav │ │ │ ├── matrix-reverb1.wav │ │ │ ├── matrix-reverb2.wav │ │ │ ├── medium-room1.wav │ │ │ ├── s2_r4_bd.wav │ │ │ ├── s3_r1_bd.wav │ │ │ ├── spreader50-65ms.wav │ │ │ └── tim-omni-35-10-magnetic.wav │ │ └── tray │ │ │ ├── icon.png │ │ │ ├── icon_white.png │ │ │ ├── like.png │ │ │ ├── like_fill.png │ │ │ ├── like_fill_white.png │ │ │ ├── like_white.png │ │ │ ├── menu_black.png │ │ │ ├── menu_white.png │ │ │ ├── pause.png │ │ │ ├── pause_white.png │ │ │ ├── play_arrow.png │ │ │ ├── play_arrow_white.png │ │ │ ├── skip_next.png │ │ │ ├── skip_next_white.png │ │ │ ├── skip_previous.png │ │ │ ├── skip_previous_white.png │ │ │ ├── thumbs_down.png │ │ │ └── thumbs_down_white.png │ ├── components │ │ ├── AlbumList.vue │ │ ├── AlbumListItem.vue │ │ ├── ArtistList.vue │ │ ├── ArtistListItem.vue │ │ ├── ArtistsInLine.vue │ │ ├── BaseModal.vue │ │ ├── ButtonIcon.vue │ │ ├── ButtonTwoTone.vue │ │ ├── CheckBox.vue │ │ ├── CommentFloor.vue │ │ ├── CommentList.vue │ │ ├── CommentPage.vue │ │ ├── ContextMenu.vue │ │ ├── CoverBox.vue │ │ ├── CoverRow.vue │ │ ├── DailyTracksCard.vue │ │ ├── ExplicitSymbol.vue │ │ ├── FMCard.vue │ │ ├── InfoBG.vue │ │ ├── LatestVersion.vue │ │ ├── LinuxTitleBar.vue │ │ ├── LoadingButton.vue │ │ ├── LyricPage.vue │ │ ├── ModalAccurateMatch.vue │ │ ├── ModalAddTrackToPlaylist.vue │ │ ├── ModalConvolver.vue │ │ ├── ModalNewPlaylist.vue │ │ ├── ModalPitch.vue │ │ ├── ModalPlayback.vue │ │ ├── MvRow.vue │ │ ├── NavBar.vue │ │ ├── OsdHeader.vue │ │ ├── OsdLyricContainer.vue │ │ ├── PlayerBar.vue │ │ ├── ScrollBar.vue │ │ ├── SearchBox.vue │ │ ├── ShowToast.vue │ │ ├── SideNav.vue │ │ ├── SvgIcon.vue │ │ ├── TrackListItem.vue │ │ ├── VirtualCoverRow.vue │ │ ├── VirtualScrollNoHeight.vue │ │ ├── VirtualTrackList.vue │ │ ├── Win32TitleBar.vue │ │ ├── WriteComment.vue │ │ └── layout │ │ │ ├── DefaultLayout.vue │ │ │ ├── HeaderLayout.vue │ │ │ └── index.ts │ ├── index.html │ ├── locales │ │ ├── de.json │ │ ├── en.json │ │ ├── es.json │ │ ├── fr.json │ │ ├── ja.json │ │ ├── ko.json │ │ ├── nl.json │ │ ├── pt.json │ │ ├── ru.json │ │ ├── zh-hans.json │ │ └── zh-hant.json │ ├── main.ts │ ├── osdLyric.ts │ ├── osdlyric.html │ ├── plugins │ │ ├── i18n.ts │ │ └── vuetify.ts │ ├── router │ │ └── index.ts │ ├── store │ │ ├── data.ts │ │ ├── localMusic.ts │ │ ├── osdLyric.ts │ │ ├── player.ts │ │ ├── settings.ts │ │ ├── state.ts │ │ └── streamingMusic.ts │ ├── utils │ │ ├── auth.ts │ │ ├── canvas.ts │ │ ├── common.ts │ │ ├── convolver.ts │ │ ├── db.ts │ │ ├── debounceRef.ts │ │ ├── eventBus.ts │ │ ├── index.ts │ │ ├── lyric.ts │ │ ├── lyricController.ts │ │ ├── playlist.ts │ │ ├── request.ts │ │ ├── settingImg.dataurl │ │ ├── shortcuts.ts │ │ ├── soundtouch-worklet.js │ │ ├── trayLyrics.ts │ │ └── tricklingProgress.ts │ └── views │ │ ├── AlbumPage.vue │ │ ├── ArtistMv.vue │ │ ├── ArtistPage.vue │ │ ├── DailyTracks.vue │ │ ├── ErrorScreen.vue │ │ ├── ExplorePage.vue │ │ ├── HomePage.vue │ │ ├── LibraryMusic.vue │ │ ├── LocalMusic.vue │ │ ├── LoginAccount.vue │ │ ├── LottiePlayer.vue │ │ ├── MainScreen.vue │ │ ├── MvPage.vue │ │ ├── NextUp.vue │ │ ├── OSDLyric.vue │ │ ├── PlayPage.vue │ │ ├── PlaylistPage.vue │ │ ├── SearchPage.vue │ │ ├── StreamLogin.vue │ │ ├── StreamPage.vue │ │ ├── SystemSettings.vue │ │ ├── UserPage.vue │ │ └── index.ts └── vue-shim.d.ts ├── tests └── app.spec.ts ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.mts ├── vite.config.ts └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | indent_size = 2 9 | insert_final_newline = true 10 | max_line_length = 100 11 | trim_trailing_whitespace = true 12 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | VITE_BETTER_SQLITE3_BINDING_arm64=dist-native/better_sqlite3-arm64.node -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | VITE_BETTER_SQLITE3_BINDING=dist-native/better_sqlite3.node -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | buildAssets/icons/ 3 | dist/ 4 | release/ 5 | docs/ 6 | 7 | .idea/ 8 | .vscode/ 9 | .github/ 10 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "vue-eslint-parser", 4 | "parserOptions": { 5 | "parser": "@typescript-eslint/parser", 6 | "sourceType": "module", 7 | "ecmaVersion": 2022, 8 | "ecmaFeatures": { 9 | "jsx": true 10 | } 11 | }, 12 | "env": { 13 | "node": true, 14 | "es6": true 15 | }, 16 | "extends": ["standard", "plugin:vue/vue3-recommended", "prettier"], 17 | "globals": { 18 | "__static": true 19 | }, 20 | "plugins": ["vue"], 21 | "rules": { 22 | "arrow-parens": 0, 23 | "generator-star-spacing": 0, 24 | "no-case-declarations": 0, 25 | "array-callback-return": 0, 26 | "no-trailing-spaces": 1, 27 | "no-control-regex": 0, 28 | "no-useless-constructor": 0, 29 | "node/no-deprecated-api": 0 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: stark81 7 | 8 | --- 9 | 10 | **问题描述(Describe the bug)** 11 | 清晰简明地描述遇到的问题(A clear and concise description of what the bug is.) 12 | 13 | **重现步骤(To Reproduce)** 14 | 问题复现步骤:(Steps to reproduce the behavior:) 15 | 16 | **预期行为(Expected behavior)** 17 | 明确说明你期望发生的正常情况。(A clear and concise description of what you expected to happen.) 18 | 19 | **设备信息 -- 请完善下面的信息:(Device information -- please complete the following information:)** 20 | - 操作系统名称: [例: Windows, macOS, Linux...] (OS Name: [e.g. Windows, macOS, Linux...]) 21 | - 操作系统版本: [例: 11, 10.14...] (OS Version: [e.g. 11, 10.14...]) 22 | 23 | **截图与附件(Screenshots & Attachments)** 24 | 如果可以的话,请添加截图或文件附件帮助说明问题。(If applicable, add screenshots to help explain your problem.) 25 | 26 | **其他补充(Additional context)** 27 | 在此添加关于该问题的其他补充说明。(Add any other context about the problem here.) 28 | -------------------------------------------------------------------------------- /.github/resources/vutron-sample.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/.github/resources/vutron-sample.webp -------------------------------------------------------------------------------- /.github/workflows/app-test.yml: -------------------------------------------------------------------------------- 1 | name: app-test 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - '**' 8 | - '!README.md' 9 | - '!LICENSE' 10 | - '!CODE_OF_CONDUCT.md' 11 | - '!docs/**' 12 | - '!.github/**' 13 | - '.github/workflows/app-test.yml' 14 | pull_request: 15 | branches: [master] 16 | workflow_dispatch: 17 | 18 | jobs: 19 | app-test: 20 | runs-on: ${{ matrix.os }} 21 | name: Test NodeJS ${{ matrix.node_version }} on ${{ matrix.os }} 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | node_version: ['18', '20'] 26 | os: [windows-latest, macos-latest, ubuntu-latest] 27 | 28 | steps: 29 | - uses: actions/checkout@v4 30 | with: 31 | fetch-depth: 0 32 | 33 | - name: Install xvfb 34 | if: runner.os == 'Linux' 35 | run: | 36 | sudo apt install -y -q --no-install-recommends xvfb 37 | 38 | - name: Setup NodeJS ${{ matrix.node_version }} 39 | uses: actions/setup-node@v4 40 | with: 41 | node-version: ${{ matrix.node_version }} 42 | cache: npm 43 | cache-dependency-path: '**/package-lock.json' 44 | 45 | - name: Cache dependencies 46 | uses: actions/cache@v3 47 | id: npm-cache 48 | with: 49 | path: | 50 | **/node_modules 51 | key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} 52 | restore-keys: | 53 | ${{ runner.os }}-npm- 54 | 55 | - name: Install dependencies 56 | if: steps.npm-cache.outputs.cache-hit != 'true' 57 | run: npm i 58 | 59 | - name: Test module script (Windows or macOS) 60 | if: runner.os != 'Linux' 61 | run: | 62 | npm run test 63 | 64 | - name: Test module script (Linux) 65 | if: runner.os == 'Linux' 66 | run: | 67 | npm run test:linux 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore for NodeJS Projects 2 | # ---------- Start of common ignore files 3 | 4 | # Node artifact files 5 | node_modules/ 6 | yarn.lock 7 | 8 | # Log files 9 | *.log 10 | 11 | # dotenv environment variables file 12 | .env 13 | 14 | # JetBrains IDEs 15 | .idea/ 16 | *.iml 17 | 18 | # Visual Studio Code IDE 19 | .vscode/* 20 | !.vscode/settings.json 21 | !.vscode/tasks.json 22 | !.vscode/launch.json 23 | !.vscode/extensions.json 24 | !.vscode/*.code-snippets 25 | 26 | # Local History for Visual Studio Code 27 | .history/ 28 | 29 | # Built Visual Studio Code Extensions 30 | *.vsix 31 | 32 | # Generated by MacOS 33 | .DS_Store 34 | .AppleDouble 35 | .LSOverride 36 | 37 | # Generated by Windows 38 | Thumbs.db 39 | [Dd]esktop.ini 40 | $RECYCLE.BIN/ 41 | 42 | # Applications 43 | *.app 44 | *.pkg 45 | *.dmg 46 | *.exe 47 | *.war 48 | *.deb 49 | 50 | # Large media files 51 | *.mp4 52 | *.tiff 53 | *.avi 54 | *.flv 55 | *.mov 56 | *.wmv 57 | 58 | # ---------- End of common ignore files 59 | 60 | # Project Files 61 | dist/ 62 | dist-native/ 63 | release/ 64 | tests/results/ 65 | npm-debug.log 66 | npm-debug.log.* 67 | vite-plugin-electron.log 68 | tmp/ 69 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Lock files 2 | *-lock.json 3 | 4 | # IDEs 5 | .idea/ 6 | # .vscode/ 7 | 8 | # Project files 9 | .github/* 10 | !.github/workflows/ 11 | buildAssets/icons/ 12 | dist/ 13 | release/ 14 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "semi": false, 4 | "vueIndentScriptAndStyle": false, 5 | "singleQuote": true, 6 | "quoteProps": "as-needed", 7 | "trailingComma": "none", 8 | "bracketSpacing": true, 9 | "bracketSameLine": false, 10 | "jsxSingleQuote": false, 11 | "arrowParens": "always", 12 | "insertPragma": false, 13 | "requirePragma": false, 14 | "proseWrap": "never", 15 | "htmlWhitespaceSensitivity": "strict", 16 | "endOfLine": "lf" 17 | } 18 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[ignore]": { 3 | "editor.defaultFormatter": "foxundermoon.shell-format" 4 | }, 5 | "editor.codeActionsOnSave": { 6 | "source.fixAll.eslint": "explicit" 7 | }, 8 | "editor.defaultFormatter": "esbenp.prettier-vscode", 9 | "editor.formatOnSave": true, 10 | "editor.tabSize": 2, 11 | "editor.wordWrap": "on", 12 | "emmet.syntaxProfiles": {}, 13 | "eslint.codeActionsOnSave.rules": null, 14 | "eslint.validate": ["javascript", "typescript"], 15 | "eslint.workingDirectories": [{ "mode": "auto" }], 16 | // "files.autoSave": "off", 17 | "i18n-ally.localesPaths": ["src/renderer/locales"], 18 | "workbench.settings.useSplitJSON": true 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-2024 jooy2 (https://jooy2.com). 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | Logo 4 | 5 |

VutronMusic

6 |

高颜值的第三方网易云播放器

7 |
8 | 9 | [![LocalMusic][localMusic-screenShot]](https://github.com/stark81/VutronMusic) 10 | 11 | ## 说明 12 | 13 | - 本项目为本人个人项目,仅用于个人学习研究,请勿用于商业用途。 14 | - 本项目大部份界面和功能参考 [YesPlayMusic](https://github.com/qier222/YesPlayMusic),侧边导航栏设计参考"方格音乐",本地音乐top部分的信息统计参考 [NSMusicS](https://github.com/Super-Badmen-Viper/NSMusicS)。 15 | - 本地歌曲的内嵌歌词以及外挂lrc歌词支持从[LDDC](https://github.com/chenmozhijin/LDDC)下载的逐字歌词歌词格式。 16 | 17 | ## 特点 18 | 19 | - ⚡️ 手脚架为:[vutron](https://github.com/jooy2/vutron); 20 | - ⚡️ 使用 Vue3 + ts + pinia + fastify + better-sqlite3 进行开发; 21 | - ⚡️ 支持本地歌曲、离线歌单功能,本地歌曲支持读取外挂和内嵌封面歌词,支持逐字歌词功能,支持线上信息匹配; 22 | - ⚡️ 支持Mac状态栏歌词、TouchBar歌词等;Linux下可通过[media-controls](https://github.com/stark81/media-controls)插件将歌词显示在TopBar里; 23 | - ⚡️ 支持云盘、歌曲评论等功能; 24 | 25 | ## 配置开发环境 26 | 27 | ``` 28 | # 安装依赖,建议使用node21 + python3.9,其他的python版本可能会导致依赖安装失败的问题; 29 | yarn install 30 | 31 | # 运行 32 | yarn run dev(开发) 33 | yarn run build(构建) 34 | ``` 35 | 36 | ## 开源许可 37 | 38 | 本项目仅供个人学习研究使用,禁止用于商业及非法用途。 39 | 40 | 基于 [MIT license](https://opensource.org/licenses/MIT) 许可进行开源。 41 | 42 | ## 截图 43 | 44 | ![home-screenShot][home-screenShot] ![explore-screenShot][explore-screenShot] ![library-screenShot][library-screenShot] ![likepage-screenShot][likepage-screenShot] ![local-music-screenShot][local-music-screenShot] ![playlist-screenShot][playlist-screenShot] ![playpage-screenShot][playpage-screenShot] ![comment-screenShot][comment-screenShot] ![search-screenShot][search-screenShot] ![user-screenShot][user-screenShot] ![mv-screenShot][mv-screenShot] ![tray-lyric-screenShot][tray-lyric-screenShot] ![media-controls-screenShot][media-controls-screenShot] 45 | 46 | [localMusic-screenShot]: images/localMusic.jpg 47 | [home-screenShot]: images/home.jpg 48 | [explore-screenShot]: images/explore.jpg 49 | [library-screenShot]: images/library.jpg 50 | [likepage-screenShot]: images/like-page.jpg 51 | [local-music-screenShot]: images/local-music.jpg 52 | [playlist-screenShot]: images/playlists.jpg 53 | [playpage-screenShot]: images/play-page.jpg 54 | [comment-screenShot]: images/comment-page.jpg 55 | [search-screenShot]: images/search-lyric.jpg 56 | [setConvolver-screenShot]: images/setConvolver.jpg 57 | [user-screenShot]: images/user.jpg 58 | [tray-lyric-screenShot]: images/tray-TouchBar-lyric.jpg 59 | [mv-screenShot]: images/mv.jpg 60 | [media-controls-screenShot]: images/media-control-lyric.png 61 | -------------------------------------------------------------------------------- /buildAssets/icons/icon-1.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/buildAssets/icons/icon-1.icns -------------------------------------------------------------------------------- /buildAssets/icons/icon-1.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/buildAssets/icons/icon-1.ico -------------------------------------------------------------------------------- /buildAssets/icons/icon-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/buildAssets/icons/icon-1.png -------------------------------------------------------------------------------- /buildAssets/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/buildAssets/icons/icon.icns -------------------------------------------------------------------------------- /buildAssets/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/buildAssets/icons/icon.ico -------------------------------------------------------------------------------- /buildAssets/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/buildAssets/icons/icon.png -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Node artifact files 2 | node_modules/ 3 | 4 | # VitePress files 5 | dist 6 | dist/* 7 | src/.vitepress/.temp 8 | src/.vitepress/.temp/* 9 | src/.vitepress/cache 10 | src/.vitepress/cache/* 11 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vutron-docs", 3 | "private": true, 4 | "version": "1.0.0", 5 | "scripts": { 6 | "dev": "vitepress dev src", 7 | "build": "vitepress build src", 8 | "serve": "vitepress serve src" 9 | }, 10 | "author": "jooy2 ", 11 | "license": "MIT", 12 | "engines": { 13 | "node": ">=18.0.0" 14 | }, 15 | "dependencies": { 16 | "vitepress": "^1.0.0-rc.40", 17 | "vitepress-sidebar": "^1.18.6", 18 | "vue": "^3.4.15" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /docs/src/.vitepress/config.mts: -------------------------------------------------------------------------------- 1 | import { generateSidebar } from 'vitepress-sidebar' 2 | import { name, description, repository } from '../../../package.json' 3 | import { defineConfig } from 'vitepress' 4 | 5 | const capitalizeFirst = (str: string): string => str.charAt(0).toUpperCase() + str.slice(1) 6 | 7 | export default defineConfig({ 8 | title: capitalizeFirst(name), 9 | description, 10 | outDir: '../dist', 11 | head: [ 12 | ['link', { rel: 'icon', href: '/logo.png' }], 13 | ['link', { rel: 'shortcut icon', href: '/favicon.ico' }] 14 | ], 15 | cleanUrls: true, 16 | themeConfig: { 17 | logo: { src: '/icon.png', width: 24, height: 24 }, 18 | search: { 19 | provider: 'local' 20 | }, 21 | sidebar: generateSidebar({ 22 | documentRootPath: 'src', 23 | collapsed: false, 24 | useTitleFromFileHeading: true, 25 | useTitleFromFrontmatter: true, 26 | sortMenusByFrontmatterOrder: true, 27 | hyphenToSpace: true, 28 | capitalizeEachWords: true, 29 | manualSortFileNameByPriority: [ 30 | 'installation-and-build', 31 | 'project-structures', 32 | 'electron-how-to' 33 | ] 34 | }), 35 | socialLinks: [{ icon: 'github', link: repository.url.replace('.git', '') }] 36 | } 37 | }) 38 | -------------------------------------------------------------------------------- /docs/src/electron-how-to/main-and-renderer-process.md: -------------------------------------------------------------------------------- 1 | # Main vs Renderer Process 2 | 3 | A **Vutron** application is divided into code into a Main process and a Renderer process. 4 | 5 | **"Main"** is the code of `src/main` and is mainly the process code handled by Electron. **"Renderer"** is the code of `src/renderer`, mainly for front-end rendering process like Vue. 6 | 7 | In general, **NodeJS** scripts cannot be run in the renderer process. Examples include modules that contain APIs used by NodeJS, or native modules of **NodeJS** such as `path` or `net`, `os` or `crypto`. 8 | 9 | Preload scripts are run before the renderer is loaded. It creates a bridge to the main process to keep the execution of NodeJS scripts in the renderer area separate and isolated for security reasons. 10 | 11 | For secure script execution, it is recommended that the main process executes the Node scripts, and the renderer receives the execution results via messaging. This can be implemented via **IPC communication**. 12 | 13 | For more information on this, see the following articles: https://www.electronjs.org/docs/latest/tutorial/ipc 14 | 15 | ### How to run NodeJS on a renderer? 16 | 17 | If you want to skip the security issues and use NodeJS scripts in your renderer, you need to set `nodeIntegration` to `true` in your `vite.config.ts` file. 18 | 19 | ```javascript 20 | rendererPlugin({ 21 | nodeIntegration: true 22 | }) 23 | ``` 24 | 25 | For more information on this, see the following articles: https://github.com/electron-vite/vite-plugin-electron-renderer 26 | -------------------------------------------------------------------------------- /docs/src/electron-how-to/preload-script.md: -------------------------------------------------------------------------------- 1 | # Preload Script 2 | 3 | The preload script in Electron.js is a secure area designed for communication between the main and renderer processes. It is typically used for **[IPC communication](https://www.electronjs.org/docs/latest/tutorial/ipc)**. 4 | 5 | For more information, see the following articles https://www.electronjs.org/docs/latest/tutorial/tutorial-preload 6 | 7 | For compatibility and security with the latest version of Electron, we do not recommend using the old `electron/remote` module. If you want to utilize system events or Node scripts, it is recommended to do so in the main process, not the renderer. 8 | 9 | Vutron's preload script is located in the `src/preload` folder. To create a new IPC communication channel, add the channel name to the following variable to whitelist it for communication. 10 | 11 | - `mainAvailChannels`: Send an event from main to renderer. (`window.mainApi?.send('channelName')`) 12 | - `rendererAvailChannels`: Send an event from renderer to main. (`mainWindow.webContents.send('channelName')`) 13 | 14 | When sending events from renderer to main, you access the `window.mainApi` object instead of `ipcRenderer.send`. The `mainApi` is the name you set in your Vutron template and can be changed. 15 | 16 | Here are the supported functions for mainApi: 17 | 18 | - `send`: Send an event to main. 19 | - `on`: A listener to receive events sent by main. 20 | - `once`: A listener to receive events sent by main. (Handle only one call) 21 | - `off`: Remove an event listener 22 | - `invoke`: Functions that can send events to main and receive data asynchronously. 23 | 24 | To change and modify this, you need to modify `exposeInMainWorld` in `src/preload/index.ts`. 25 | -------------------------------------------------------------------------------- /docs/src/installation-and-build/automated-testing.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 4 3 | --- 4 | 5 | # Automated Testing 6 | 7 | **Vutron** includes automated testing. The testing framework uses Microsoft's **[Playwright](https://playwright.dev)**. 8 | 9 | **Playwright** is optimized for web application testing and has full support for the **Electron** framework. It is simple to install, requires no configuration to start testing immediately, and is cross-platform. You can learn more about **Playwright** here: https://github.com/microsoft/playwright 10 | 11 | Only very simple launch and behavioral tests for the template main screen have been implemented in this template. Advanced testing will depend on the scope of your application. 12 | 13 | Currently, the test specification file is located in the `tests` directory and the test results file is located in `tests/results`. (The built-in test specification file does not generate a separate results file.) 14 | 15 | The Playwright configuration is `playwright.config.ts` in the project root, see the following documentation for more information on this: https://playwright.dev/docs/test-configuration 16 | 17 | Once everything is configured, you can run a test with the following command. 18 | 19 | ```shell 20 | $ npm run test 21 | ``` 22 | 23 | Before running the test, empty the build directory (`dist`) and compile the package for the test. 24 | -------------------------------------------------------------------------------- /docs/src/installation-and-build/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 1 3 | --- 4 | 5 | # Getting Started 6 | 7 | ## Clone project 8 | 9 | ### Method 1: `npm init` (Recommend) 10 | 11 | You can easily clone a repository with just the npm command. 12 | 13 | ```shell 14 | $ npm init vutron 15 | ``` 16 | 17 | The above method will not create unnecessary documentation and `.github` related files for your project. 18 | 19 | ### Method 2: Use this template 20 | 21 | Click **[Use this template](https://github.com/jooy2/vutron/generate)** to instantly create your own project. 22 | 23 | This method creates a repository on GitHub immediately, but you will need to clone the project locally before you can use it. 24 | 25 | ### Method 3: Clone this repository 26 | 27 | Clone this repo using below command. This method is suitable for direct contributions to the Vutron repository. 28 | 29 | ```shell 30 | $ git clone https://github.com/jooy2/vutron 31 | ``` 32 | 33 | ## Installation 34 | 35 | After cloning the project, run the following command in the terminal: 36 | 37 | ```shell 38 | # via npm 39 | $ npm i 40 | 41 | # via yarn (https://yarnpkg.com) 42 | $ yarn install 43 | 44 | # via pnpm (https://pnpm.io) 45 | $ pnpm i 46 | ``` 47 | 48 | ## Run in development environment 49 | 50 | Applications in the development environment run through **[Vite](https://vitejs.dev)**. 51 | 52 | ```shell 53 | $ npm run dev 54 | ``` 55 | 56 | If your application doesn't appear after running command line commands, you may need to review if the default port is being used by another app. 57 | 58 | Vite uses port `5173` by default. 59 | -------------------------------------------------------------------------------- /docs/src/installation-and-build/install-local-documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 5 3 | --- 4 | 5 | # Manage Local Documentation 6 | 7 | Documents from `Vutron` can be viewed in the local environment through the `VitePress` viewer. 8 | 9 | This function works only when the entire project is cloned. If you created the project with `npm init vutron`, the `docs` folder is not included. 10 | 11 | ## Installation 12 | 13 | Everything in the instructions below should be done in the `docs` folder. 14 | 15 | ```shell 16 | $ cd docs 17 | ``` 18 | 19 | Install the relevant packages using the following commands: 20 | 21 | ```shell 22 | # via npm 23 | $ npm i 24 | 25 | # via yarn (https://yarnpkg.com) 26 | $ yarn install 27 | 28 | # via pnpm (https://pnpm.io) 29 | $ pnpm i 30 | ``` 31 | 32 | You can run the local server where the documents are hosted via the command below. 33 | 34 | ```shell 35 | $ npm run dev 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/src/installation-and-build/npm-scripts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: NPM Scripts 3 | order: 3 4 | --- 5 | 6 | # Npm Scripts 7 | 8 | > $ npm run %SCRIPT_NAME% 9 | 10 | ## General 11 | 12 | | Script Name | Description | 13 | | --- | --- | 14 | | `dev` | Start Electron as a development environment | 15 | | `dev:debug` | Start Electron as a development environment (with vite debug) | 16 | | `dev:debug:force` | Start Electron as a development environment (with vite debug + clean vite cache) | 17 | | `build:pre` | Commands commonly run at build time. This script does not need to be run separately. | 18 | | `build` | Build the package for the current operating system. | 19 | | `build:all` | Build a specified package for the entire operating system (Requires cross-platform build configuration) | 20 | | `build:dir` | `electron-builder` directory build | 21 | | `build:mac` | Build preconfigured packages for macOS | 22 | | `build:linux` | Build preconfigured packages for Linux | 23 | | `build:win` | Build preconfigured packages for Windows | 24 | | `lint` | ESLint code inspection. It does not modify the code. | 25 | | `lint:fix` | ESLint code inspection. Use auto-fix to fix your code. | 26 | | `format` | Prettier code inspection. It does not modify the code. | 27 | | `format:fix` | Prettier code inspection. Use auto-fix to fix your code. | 28 | | `test` | Build a package for testing and run tests against the test specification file. | 29 | | `test:linux` | Build a package for testing and run tests against the test specification file. (for linux ci only) | 30 | 31 | ## For Documentation 32 | 33 | Used only for contributing to project documentation. Must be run from the `docs` directory location. 34 | 35 | | Script Name | Description | 36 | | ----------- | ------------------------------------------------------------------ | 37 | | `dev` | Start the local document server. (For development) | 38 | | `build` | Build a local document server. Used only for GitHub page builders. | 39 | | `serve` | Start the local document server. | 40 | -------------------------------------------------------------------------------- /docs/src/other-projects.md: -------------------------------------------------------------------------------- 1 | # Other Projects 2 | 3 | ## Looking for Electron templates made with React? 4 | 5 | Also check out the `Retron` project, which consists of Vite + React + Material-UI + Electron. 6 | 7 | https://github.com/jooy2/retron 8 | 9 | ## (Deprecated) Looking for `Vutron` with Webpack 5 compiler? 10 | 11 | By using the Vite compiler, we achieved our goals of reducing project and bundle size, and improving development environment and build speed. 12 | 13 | The old **Vutron** using the Webpack 5 compiler has been split into the repositories below and will end support soon. 14 | 15 | https://github.com/jooy2/vutron-webpack 16 | -------------------------------------------------------------------------------- /docs/src/project-structures/pre-configured-components.md: -------------------------------------------------------------------------------- 1 | --- 2 | order: 2 3 | --- 4 | 5 | # Pre-configured Components 6 | 7 | ## Web app frameworks 8 | 9 | - [Vite](https://vitejs.dev) 10 | - [Electron](https://www.electronjs.org) 11 | - [Electron Builder](https://www.electron.build) 12 | 13 | ## Development help tools 14 | 15 | - [TypeScript](https://www.typescriptlang.org) 16 | - [ESLint](https://eslint.org) 17 | - [Prettier](https://prettier.io) 18 | 19 | ## Front-end frameworks (Vue) 20 | 21 | - [Vue](https://vuejs.org) 22 | - [Vue-i18n](https://kazupon.github.io/vue-i18n) 23 | - [Vue-router](https://router.vuejs.org) 24 | - [Pinia](https://pinia.vuejs.org) 25 | 26 | ## Design frameworks 27 | 28 | - [Vuetify](https://vuetifyjs.com) 29 | 30 | ## Testing 31 | 32 | - [Playwright](https://playwright.dev) 33 | -------------------------------------------------------------------------------- /docs/src/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/docs/src/public/favicon.ico -------------------------------------------------------------------------------- /docs/src/public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/docs/src/public/icon.png -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "resolveJsonModule": true, 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["src/.vitepress/config.mts", "package.json"] 10 | } 11 | -------------------------------------------------------------------------------- /fix-sandbox.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process') 2 | 3 | if (process.platform === 'linux') { 4 | // You need to make sure that 5 | // /home/runner/work/VutronMusic/VutronMusic/node_modules/electron/dist/chrome-sandbox 6 | // is owned by root and has mode 4755. 7 | execSync('sudo chown root:root ./node_modules/electron/dist/chrome-sandbox') 8 | execSync('sudo chmod 4755 ./node_modules/electron/dist/chrome-sandbox') 9 | } 10 | -------------------------------------------------------------------------------- /images/comment-page.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/images/comment-page.jpg -------------------------------------------------------------------------------- /images/explore.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/images/explore.jpg -------------------------------------------------------------------------------- /images/home.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/images/home.jpg -------------------------------------------------------------------------------- /images/library.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/images/library.jpg -------------------------------------------------------------------------------- /images/like-page.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/images/like-page.jpg -------------------------------------------------------------------------------- /images/local-music.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/images/local-music.jpg -------------------------------------------------------------------------------- /images/localMusic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/images/localMusic.jpg -------------------------------------------------------------------------------- /images/media-control-lyric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/images/media-control-lyric.png -------------------------------------------------------------------------------- /images/mv.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/images/mv.jpg -------------------------------------------------------------------------------- /images/play-page.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/images/play-page.jpg -------------------------------------------------------------------------------- /images/playlists.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/images/playlists.jpg -------------------------------------------------------------------------------- /images/search-lyric.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/images/search-lyric.jpg -------------------------------------------------------------------------------- /images/setConvolver.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/images/setConvolver.jpg -------------------------------------------------------------------------------- /images/tray-TouchBar-lyric.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/images/tray-TouchBar-lyric.jpg -------------------------------------------------------------------------------- /images/user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/images/user.jpg -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@playwright/test' 2 | 3 | export default defineConfig({ 4 | outputDir: 'tests/results', 5 | retries: process.env.CI ? 2 : 0, 6 | workers: process.env.CI ? 1 : undefined, 7 | timeout: 60000, 8 | expect: { 9 | timeout: 10000 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /rebuild.js: -------------------------------------------------------------------------------- 1 | const child = require('child_process') 2 | const fs = require('fs') 3 | const { resolve, posix, join } = require('path') 4 | 5 | const normalizePath = (path) => { 6 | return path.replace(/\\/g, '/') 7 | } 8 | 9 | const cp = child.spawn( 10 | process.platform === 'win32' 11 | ? '.\\node_modules\\.bin\\electron-rebuild.cmd' // Windows 12 | : './node_modules/.bin/electron-rebuild', // Mac/Linux 13 | ['--force', '--module-dir=node_modules/better-sqlite3'], 14 | { 15 | shell: true, // 这很重要,允许shell语法执行 16 | stdio: 'inherit' 17 | } 18 | ) 19 | 20 | cp.on('exit', (code) => { 21 | if (code === 0) { 22 | console.log('Rebuild native modules success.') 23 | const out = 'dist-native' 24 | const denstination = `better_sqlite3-${process.arch}.node` 25 | 26 | const resolvedRoot = normalizePath(process.cwd()) 27 | const output = normalizePath(resolve(resolvedRoot, out)) 28 | const betterSqlite3 = normalizePath(require.resolve('better-sqlite3')) 29 | const betterSqlite3Root = posix.join( 30 | betterSqlite3.slice(0, betterSqlite3.lastIndexOf('node_modules')), 31 | 'node_modules/better-sqlite3' 32 | ) 33 | const betterSqlite3Node = normalizePath( 34 | posix.join(betterSqlite3Root, 'build/Release/better_sqlite3.node') 35 | ) 36 | const betterSqlite3Copy = normalizePath(posix.join(output, denstination)) 37 | if (!fs.existsSync(output)) { 38 | fs.mkdirSync(output, { recursive: true }) 39 | } 40 | fs.copyFileSync(betterSqlite3Node, betterSqlite3Copy) 41 | 42 | const BETTER_SQLITE3_BINDING = join(out, denstination) 43 | fs.writeFileSync( 44 | join(resolvedRoot, '.env'), 45 | `VITE_BETTER_SQLITE3_BINDING_${process.arch}=${BETTER_SQLITE3_BINDING}` 46 | ) 47 | console.log(`binding to ${BETTER_SQLITE3_BINDING}`) 48 | } 49 | process.exit(code) 50 | }) 51 | -------------------------------------------------------------------------------- /src/main/appServer/appServer.ts: -------------------------------------------------------------------------------- 1 | import fastify from 'fastify' 2 | import netease from './netease' 3 | import Constants from '../utils/Constants' 4 | 5 | const initAppserver = async () => { 6 | const server = fastify({ 7 | ignoreTrailingSlash: true 8 | }) 9 | server.register(netease) 10 | 11 | const port = Number( 12 | Constants.IS_DEV_ENV 13 | ? Constants.ELECTRON_DEV_NETEASE_API_PORT || 40001 14 | : Constants.ELECTRON_WEB_SERVER_PORT || 41830 15 | ) 16 | await server.listen({ port }) 17 | console.log(`AppServer is running at http://localhost:${port}`) 18 | return server 19 | } 20 | 21 | export default initAppserver 22 | -------------------------------------------------------------------------------- /src/main/appServer/audio.ts: -------------------------------------------------------------------------------- 1 | // import { FastifyInstance, FastifyRequest } from 'fastify' 2 | // import NeteaseCloudMusicApi, { SoundQualityType } from 'NeteaseCloudMusicApi' 3 | // import cache from '../cache' 4 | // import { CacheAPIs } from '../utils/CacheApis' 5 | 6 | // async function audio(fastify: FastifyInstance) { 7 | // fastify.get( 8 | // '/netease/song/url/v1', 9 | // async ( 10 | // req: FastifyRequest<{ Querystring: { id: string | number; level: SoundQualityType } }>, 11 | // reply 12 | // ) => {} 13 | // ) 14 | 15 | // fastify.get('') 16 | // } 17 | 18 | // export default audio 19 | -------------------------------------------------------------------------------- /src/main/appServer/netease.ts: -------------------------------------------------------------------------------- 1 | import { pathCase } from 'change-case' 2 | import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify' 3 | import cache from '../cache' 4 | import { CacheAPIs } from '../utils/CacheApis' 5 | import { handleNeteaseResult } from '../utils/utils' 6 | 7 | async function netease(fastify: FastifyInstance) { 8 | const NeteaseCloudMusicApi = require('NeteaseCloudMusicApi') 9 | const getHandler = (name: string, neteaseApi: (params: any) => any) => { 10 | return async ( 11 | req: FastifyRequest<{ Querystring: { [key: string]: string } }>, 12 | reply: FastifyReply 13 | ) => { 14 | try { 15 | const { localID, ...params } = req.query 16 | if (!params.cookie) params.cookie = (req as any).cookies 17 | const result = await neteaseApi(params) 18 | 19 | result.body = handleNeteaseResult(name as CacheAPIs, result.body) 20 | cache.set(name as CacheAPIs, result.body, req.query) 21 | return reply.send(result.body) 22 | } catch (error: any) { 23 | if ([400, 301, 250].includes(error.status)) { 24 | return reply.status(error.status).send(error.body) 25 | } 26 | return reply.status(500) 27 | } 28 | } 29 | } 30 | Object.entries(NeteaseCloudMusicApi).forEach(([nameInSnakeCase, neteaseApi]: [string, any]) => { 31 | if (['serveNcmApi', 'getModulesDefinitions'].includes(nameInSnakeCase)) return 32 | const name = pathCase(nameInSnakeCase) 33 | const handler = getHandler(name, neteaseApi) 34 | fastify.get(`/netease/${name}`, handler) 35 | fastify.post(`/netease/${name}`, handler) 36 | }) 37 | fastify.get('/netease', () => 'NeteaseCloudMusicApi') 38 | } 39 | 40 | export default netease 41 | -------------------------------------------------------------------------------- /src/main/appServer/request.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' 2 | import Constants from '../utils/Constants' 3 | import { session } from 'electron' 4 | 5 | const port = Number( 6 | Constants.IS_DEV_ENV 7 | ? Constants.ELECTRON_DEV_NETEASE_API_PORT || 40001 8 | : Constants.ELECTRON_WEB_SERVER_PORT || 41830 9 | ) 10 | 11 | const baseUrl = `http://localhost:${port}/netease` 12 | 13 | const service: AxiosInstance = axios.create({ 14 | baseURL: baseUrl, 15 | withCredentials: true, 16 | timeout: 15000 17 | }) 18 | 19 | service.interceptors.request.use(async (config: any) => { 20 | if (!config.params) config.params = {} 21 | const cookieString = await session.defaultSession.cookies.get({}) 22 | const cookie = cookieString.find((cookie: any) => cookie.name === 'MUSIC_U') 23 | if (cookie) config.params.cookie = `MUSIC_U=${cookie.value}` 24 | return config 25 | }) 26 | 27 | service.interceptors.response.use( 28 | (response: AxiosResponse) => { 29 | const res = response 30 | return res 31 | }, 32 | (error: AxiosError) => { 33 | const { response } = error 34 | const data = response?.data as any 35 | if (data?.code === 301 && data?.message === '未登录') { 36 | console.log('未登录') 37 | } 38 | return Promise.reject(error) 39 | } 40 | ) 41 | 42 | const request = async (config: AxiosRequestConfig) => { 43 | const { data } = await service.request(config) 44 | return data as any 45 | } 46 | 47 | export default request 48 | -------------------------------------------------------------------------------- /src/main/checkUpdate.ts: -------------------------------------------------------------------------------- 1 | import { autoUpdater } from 'electron-updater' 2 | import { parse } from 'node-html-parser' 3 | import { BrowserWindow, app, dialog, shell } from 'electron' 4 | import Constants from './utils/Constants' 5 | 6 | const isMac = Constants.IS_MAC 7 | 8 | autoUpdater.setFeedURL({ 9 | provider: 'github', 10 | owner: 'stark81', 11 | repo: 'VutronMusic', 12 | private: false 13 | }) 14 | 15 | autoUpdater.autoDownload = false 16 | 17 | export const downloadUpdate = () => { 18 | autoUpdater.downloadUpdate() 19 | } 20 | 21 | const handleUpdateAvailable = (win: BrowserWindow, info: any) => { 22 | const plainNode = info.releaseNotes 23 | ? parse(info.releaseNotes as string).text.trim() 24 | : '无更新说明' 25 | 26 | dialog 27 | .showMessageBox(win, { 28 | type: 'info', 29 | title: '发现新版本', 30 | message: `发现新版本 ${info.version} \n是否立即下载?`, 31 | detail: isMac ? '' : plainNode, 32 | buttons: [isMac ? '前往下载' : '立即下载', '稍后下载'] 33 | }) 34 | .then((result) => { 35 | if (result.response === 0) { 36 | if (isMac) { 37 | shell.openExternal('https://github.com/stark81/VutronMusic/releases/') 38 | } else { 39 | downloadUpdate() 40 | } 41 | } 42 | }) 43 | } 44 | 45 | export const initAutoUpdater = (win: BrowserWindow) => { 46 | if (!app.isPackaged) return 47 | 48 | autoUpdater.on('update-available', (info) => { 49 | handleUpdateAvailable(win, info) 50 | }) 51 | 52 | autoUpdater.on('update-not-available', (info) => { 53 | win.webContents.send('update-not-available', info) 54 | }) 55 | 56 | autoUpdater.on('download-progress', (info) => { 57 | win.webContents.send('download-progress', info) 58 | }) 59 | 60 | autoUpdater.on('update-downloaded', (info) => { 61 | dialog 62 | .showMessageBox(win, { 63 | type: 'info', 64 | title: '下载完成', 65 | message: `新版本 ${info.version} 下载完成,是否立即安装?`, 66 | buttons: ['立即安装', '稍后安装'] 67 | }) 68 | .then((result) => { 69 | if (result.response === 0) { 70 | autoUpdater.quitAndInstall() 71 | } 72 | }) 73 | }) 74 | 75 | autoUpdater.on('error', () => { 76 | win.webContents.send('update-error') 77 | }) 78 | } 79 | 80 | export const checkUpdate = async () => { 81 | const info = await autoUpdater.checkForUpdates() 82 | return info 83 | } 84 | -------------------------------------------------------------------------------- /src/main/dbus.ts: -------------------------------------------------------------------------------- 1 | export type DBusNativeImport = typeof import('@httptoolkit/dbus-native') 2 | 3 | declare module '@httptoolkit/dbus-native' { 4 | export let sessionBus: DBusNativeImport['createClient'] 5 | 6 | export interface DBusClient { 7 | requestName(name: string, flags: number): Promise 8 | exportInterface(obj: any, path: string, iface: any): void 9 | getInterface(path: string, objname: string, name: string): Promise 10 | } 11 | 12 | export interface DBusService {} 13 | } 14 | -------------------------------------------------------------------------------- /src/main/dbusService.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | // 本文件为dbus服务端,主动向dbus发送歌词信息 4 | // 第三方插件可监听本服务,获取歌词信息,实现tray歌词显示等功能 5 | 6 | import { BrowserWindow } from 'electron' 7 | import type { DBusClient } from '@httptoolkit/dbus-native' 8 | import type { DBusNativeImport } from './dbus' 9 | 10 | export interface DBusImpl {} 11 | 12 | export enum signalNameEnum { 13 | currentLrc = 'CurrentLrc', 14 | updateLikeStatus = 'UpdateLikeStatus' 15 | } 16 | 17 | export interface interFace { 18 | LikeThisTrack: () => void 19 | emit: (signalName: signalNameEnum, ...args: any) => void 20 | } 21 | 22 | const serviceName = 'org.vutronmusic.Lyric' 23 | const objectPath = `/${serviceName.replace(/\./g, '/')}` 24 | 25 | class DBus implements DBusImpl { 26 | private dbus: DBusNativeImport = require('@httptoolkit/dbus-native') 27 | private sessionBus: DBusClient 28 | private win: BrowserWindow 29 | iface: interFace 30 | constructor(win: BrowserWindow) { 31 | this.win = win 32 | this.sessionBus = this.dbus.sessionBus({}) 33 | 34 | this.requestName() 35 | } 36 | 37 | requestName() { 38 | this.sessionBus 39 | .requestName(serviceName, 0x4) 40 | .then((retCode) => { 41 | if (retCode === 1) this.exportDBus() 42 | }) 43 | .catch((err) => { 44 | console.log(err) 45 | }) 46 | } 47 | 48 | exportDBus() { 49 | const ifaceDesc = { 50 | name: serviceName, 51 | methods: { 52 | LikeThisTrack: ['', '', [], []] 53 | }, 54 | signals: { 55 | CurrentLrc: ['s', 'currentLyric'], 56 | UpdateLikeStatus: ['b', 'likeStatus'] 57 | } 58 | } 59 | 60 | this.iface = { 61 | LikeThisTrack: () => { 62 | this.win.webContents.send('like') 63 | }, 64 | emit: function (signalName: signalNameEnum, ...args: any) {} 65 | } 66 | 67 | this.sessionBus.exportInterface(this.iface, objectPath, ifaceDesc) 68 | } 69 | } 70 | 71 | export const createDBus = (win: BrowserWindow) => { 72 | return new DBus(win) 73 | } 74 | -------------------------------------------------------------------------------- /src/main/globalShortcut.ts: -------------------------------------------------------------------------------- 1 | import { globalShortcut, BrowserWindow } from 'electron' 2 | import defaultShortcuts from './utils/shortcuts' 3 | import store from './store' 4 | 5 | export const registerGlobalShortcuts = (win: BrowserWindow) => { 6 | let shortcuts = store.get('settings.shortcuts') as 7 | | { id: string; name: string; shortcut: string; globalShortcut: string }[] 8 | | undefined 9 | if (shortcuts === undefined) { 10 | shortcuts = defaultShortcuts 11 | } 12 | 13 | globalShortcut.register(shortcuts.find((s) => s.id === 'play').globalShortcut, () => { 14 | win.webContents.send('play') 15 | }) 16 | globalShortcut.register(shortcuts.find((s) => s.id === 'next').globalShortcut, () => { 17 | win.webContents.send('next') 18 | }) 19 | globalShortcut.register(shortcuts.find((s) => s.id === 'previous').globalShortcut, () => { 20 | win.webContents.send('previous') 21 | }) 22 | globalShortcut.register(shortcuts.find((s) => s.id === 'increaseVolume').globalShortcut, () => { 23 | win.webContents.send('increaseVolume') 24 | }) 25 | globalShortcut.register(shortcuts.find((s) => s.id === 'decreaseVolume').globalShortcut, () => { 26 | win.webContents.send('decreaseVolume') 27 | }) 28 | globalShortcut.register(shortcuts.find((s) => s.id === 'like').globalShortcut, () => { 29 | win.webContents.send('like') 30 | }) 31 | globalShortcut.register(shortcuts.find((s) => s.id === 'minimize').globalShortcut, () => { 32 | win.isVisible() ? win.hide() : win.show() 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /src/main/index.dev.ts: -------------------------------------------------------------------------------- 1 | // Warning: This file is only used in the development environment 2 | // and is removed at build time. 3 | // Do not edit the file unless necessary. 4 | import { installExtension, VUEJS_DEVTOOLS } from 'electron-extension-installer' 5 | 6 | installExtension(VUEJS_DEVTOOLS, { 7 | loadExtensionOptions: { 8 | allowFileAccess: true 9 | } 10 | }) 11 | -------------------------------------------------------------------------------- /src/main/log.ts: -------------------------------------------------------------------------------- 1 | /** By default, it writes logs to the following locations: 2 | * on Linux: ~/.config/R3PLAY/logs/main.log 3 | * on macOS: ~/Library/Logs/r3playx/main.log 4 | * on Windows: %USERPROFILE%\AppData\Roaming\r3play\logs\main.log 5 | * @see https://www.npmjs.com/package/electron-log 6 | */ 7 | 8 | import log from 'electron-log' 9 | import pc from 'picocolors' 10 | import Constants from './utils/Constants' 11 | import { ipcMain } from 'electron' 12 | 13 | Object.assign(console, log.functions) 14 | log.variables.process = 'main' 15 | if (log.transports.ipc) log.transports.ipc.level = false 16 | log.transports.console.format = `${Constants.IS_DEV_ENV ? '' : pc.dim('{h}:{i}:{s}{scope} ')}{level} › {text}` 17 | log.transports.file.level = 'info' 18 | 19 | ipcMain.removeAllListeners('openLogFile') 20 | 21 | ipcMain.on('openLogFile', () => { 22 | const { shell } = require('electron') 23 | const logFilePath = log.transports.file.getFile().path 24 | shell.showItemInFolder(logFilePath) 25 | }) 26 | 27 | export default log 28 | -------------------------------------------------------------------------------- /src/main/utils/CacheApis.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | 3 | export enum CacheAPIs { 4 | Album = 'album', 5 | Artist = 'artists', 6 | ArtistAlbum = 'artist/album', 7 | Likelist = 'likelist', 8 | Lyric = 'lyric', 9 | Personalized = 'personalized', 10 | Playlist = 'playlist/detail', 11 | RecommendResource = 'recommend/resource', 12 | SongUrl = 'song/url/v1', 13 | Track = 'song/detail', 14 | UserAccount = 'user/account', 15 | UserAlbums = 'album/sublist', 16 | UserArtists = 'artist/sublist', 17 | UserPlaylist = 'user/playlist', 18 | SimilarArtist = 'simi/artist', 19 | ArtistSongs = 'artist/songs', 20 | ListenedRecords = 'user/record', 21 | Unblock = 'unblock', 22 | searchMatch = 'search/match', 23 | loginStatus = 'login/status', 24 | recommendTracks = 'recommend/songs', 25 | 26 | // not netease api 27 | LocalMusic = 'local_music', 28 | LocalPlaylist = 'local_playlist', 29 | CoverColor = 'cover_color', 30 | AppleMusicAlbum = 'apple_music_album', 31 | AppleMusicArtist = 'apple_music_artist' 32 | } 33 | -------------------------------------------------------------------------------- /src/main/utils/Constants.ts: -------------------------------------------------------------------------------- 1 | import { join, dirname } from 'path' 2 | import { name, version } from '../../../package.json' 3 | import { fileURLToPath } from 'url' 4 | 5 | const __dirname = dirname(fileURLToPath(import.meta.url)) 6 | 7 | export default class Constants { 8 | // Display app name (uppercase first letter) 9 | static APP_NAME = name.charAt(0).toUpperCase() + name.slice(1) 10 | 11 | static APP_VERSION = version 12 | 13 | static IS_DEV_ENV = process.env.NODE_ENV === 'development' 14 | 15 | static IS_MAC = process.platform === 'darwin' 16 | 17 | static IS_WINDOWS = process.platform === 'win32' 18 | 19 | static IS_LINUX = process.platform === 'linux' 20 | 21 | static DEFAULT_WEB_PREFERENCES = { 22 | // webSecurity: false, 23 | nodeIntegration: false, 24 | contextIsolation: true, 25 | enableRemoteModule: false, 26 | preload: join(__dirname, '../preload/index.js') 27 | } 28 | 29 | static DEFAULT_OSD_PREFERENCES = { 30 | // webSecurity: false, 31 | nodeIntegration: false, 32 | contextIsolation: true, 33 | enableRemoteModule: false, 34 | preload: join(__dirname, '../preload/osdWin.js') 35 | } 36 | 37 | static ELECTRON_WEB_SERVER_PORT = 41830 38 | static ELECTRON_DEV_NETEASE_API_PORT = 40001 39 | 40 | static APP_INDEX_URL_DEV = 'http://localhost:41830/index.html' 41 | static APP_INDEX_URL_PROD = 'http://localhost:41830/#/index.html' 42 | // static APP_OSD_URL_PROD = join(__dirname, '../../osdlyric.html') 43 | static APP_OSD_URL = 'http://localhost:41830/osdlyric.html' 44 | // static APP_OSD_URL_PROD = join(__dirname, '../index.html') 45 | } 46 | -------------------------------------------------------------------------------- /src/main/utils/shortcuts.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | id: 'play', 4 | name: '播放/暂停', 5 | shortcut: 'CommandOrControl+P', 6 | globalShortcut: 'Alt+CommandOrControl+P' 7 | }, 8 | { 9 | id: 'next', 10 | name: '下一首', 11 | shortcut: 'CommandOrControl+Right', 12 | globalShortcut: 'Alt+CommandOrControl+Right' 13 | }, 14 | { 15 | id: 'previous', 16 | name: '上一首', 17 | shortcut: 'CommandOrControl+Left', 18 | globalShortcut: 'Alt+CommandOrControl+Left' 19 | }, 20 | { 21 | id: 'increaseVolume', 22 | name: '增加音量', 23 | shortcut: 'CommandOrControl+Up', 24 | globalShortcut: 'Alt+CommandOrControl+Up' 25 | }, 26 | { 27 | id: 'decreaseVolume', 28 | name: '减少音量', 29 | shortcut: 'CommandOrControl+Down', 30 | globalShortcut: 'Alt+CommandOrControl+Down' 31 | }, 32 | { 33 | id: 'like', 34 | name: '喜欢歌曲', 35 | shortcut: 'CommandOrControl+L', 36 | globalShortcut: 'Alt+CommandOrControl+L' 37 | }, 38 | { 39 | id: 'minimize', 40 | name: '隐藏/显示播放器', 41 | shortcut: 'CommandOrControl+M', 42 | globalShortcut: 'Alt+CommandOrControl+M' 43 | } 44 | ] 45 | -------------------------------------------------------------------------------- /src/public/images/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/default.jpg -------------------------------------------------------------------------------- /src/public/images/touchbar/backward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/touchbar/backward.png -------------------------------------------------------------------------------- /src/public/images/touchbar/forward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/touchbar/forward.png -------------------------------------------------------------------------------- /src/public/images/touchbar/like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/touchbar/like.png -------------------------------------------------------------------------------- /src/public/images/touchbar/like_fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/touchbar/like_fill.png -------------------------------------------------------------------------------- /src/public/images/touchbar/next_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/touchbar/next_up.png -------------------------------------------------------------------------------- /src/public/images/touchbar/page_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/touchbar/page_next.png -------------------------------------------------------------------------------- /src/public/images/touchbar/page_prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/touchbar/page_prev.png -------------------------------------------------------------------------------- /src/public/images/touchbar/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/touchbar/pause.png -------------------------------------------------------------------------------- /src/public/images/touchbar/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/touchbar/play.png -------------------------------------------------------------------------------- /src/public/images/touchbar/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/touchbar/search.png -------------------------------------------------------------------------------- /src/public/images/touchbar/thumbs_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/touchbar/thumbs_down.png -------------------------------------------------------------------------------- /src/public/images/tray/left_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/left_black.png -------------------------------------------------------------------------------- /src/public/images/tray/left_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/left_white.png -------------------------------------------------------------------------------- /src/public/images/tray/like_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/like_black.png -------------------------------------------------------------------------------- /src/public/images/tray/like_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/like_white.png -------------------------------------------------------------------------------- /src/public/images/tray/lock_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/lock_black.png -------------------------------------------------------------------------------- /src/public/images/tray/lock_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/lock_white.png -------------------------------------------------------------------------------- /src/public/images/tray/lrc_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/lrc_black.png -------------------------------------------------------------------------------- /src/public/images/tray/lrc_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/lrc_white.png -------------------------------------------------------------------------------- /src/public/images/tray/menu_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/menu_black.png -------------------------------------------------------------------------------- /src/public/images/tray/menu_black_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/menu_black_1.png -------------------------------------------------------------------------------- /src/public/images/tray/menu_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/menu_white.png -------------------------------------------------------------------------------- /src/public/images/tray/menu_white_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/menu_white_1.png -------------------------------------------------------------------------------- /src/public/images/tray/pause_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/pause_black.png -------------------------------------------------------------------------------- /src/public/images/tray/pause_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/pause_white.png -------------------------------------------------------------------------------- /src/public/images/tray/play_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/play_black.png -------------------------------------------------------------------------------- /src/public/images/tray/play_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/play_white.png -------------------------------------------------------------------------------- /src/public/images/tray/quit_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/quit_black.png -------------------------------------------------------------------------------- /src/public/images/tray/quit_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/quit_white.png -------------------------------------------------------------------------------- /src/public/images/tray/repeat_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/repeat_black.png -------------------------------------------------------------------------------- /src/public/images/tray/repeat_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/repeat_white.png -------------------------------------------------------------------------------- /src/public/images/tray/right_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/right_black.png -------------------------------------------------------------------------------- /src/public/images/tray/right_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/right_white.png -------------------------------------------------------------------------------- /src/public/images/tray/unlike_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/unlike_black.png -------------------------------------------------------------------------------- /src/public/images/tray/unlike_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/unlike_white.png -------------------------------------------------------------------------------- /src/public/images/tray/unlock_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/unlock_black.png -------------------------------------------------------------------------------- /src/public/images/tray/unlock_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/unlock_white.png -------------------------------------------------------------------------------- /src/public/images/tray/vutronmusic-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/tray/vutronmusic-icon.png -------------------------------------------------------------------------------- /src/public/images/vutron-logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/public/images/vutron-logo.webp -------------------------------------------------------------------------------- /src/public/migrations/1.5.0.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX IF EXISTS filePath; 2 | DROP INDEX IF EXISTS deleted; 3 | 4 | CREATE TABLE IF NOT EXISTS Track_new ( 5 | "id" INTEGER NOT NULL, 6 | "type" TEXT DEFAULT "", 7 | "json" TEXT NOT NULL, 8 | "updatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 9 | PRIMARY KEY (id) 10 | ); 11 | 12 | INSERT INTO Track_new (id, type, json, updatedAt) 13 | SELECT id, 'local', json_set( 14 | json_remove(json, '$.isLocal', '$.deleted', '$.show'), 15 | '$.type', 'local' 16 | ), updatedAt FROM Track; 17 | 18 | DROP TABLE Track; 19 | 20 | ALTER TABLE Track_new RENAME TO Track; 21 | 22 | CREATE INDEX IF NOT EXISTS "type" ON "Track" ("type"); 23 | -------------------------------------------------------------------------------- /src/renderer/api/album.ts: -------------------------------------------------------------------------------- 1 | import request from '../utils/request' 2 | 3 | /** 4 | * 获取专辑内容 5 | * 说明 : 调用此接口 , 传入专辑 id, 可获得专辑内容 6 | * @param {number} id 7 | */ 8 | export function getAlbum(id: number) { 9 | return request({ 10 | url: '/album', 11 | method: 'get', 12 | params: { 13 | id 14 | } 15 | }) 16 | } 17 | 18 | /** 19 | * 全部新碟 20 | * 说明 : 登录后调用此接口 ,可获取全部新碟 21 | * - limit - 返回数量 , 默认为 30 22 | * - offset - 偏移数量,用于分页 , 如 :( 页数 -1)*30, 其中 30 为 limit 的值 , 默认为 0 23 | * - area - ALL:全部,ZH:华语,EA:欧美,KR:韩国,JP:日本 24 | * @param {Object} params 25 | * @param {number} params.limit 26 | * @param {number=} params.offset 27 | * @param {string} params.area 28 | */ 29 | export function newAlbums(params: { limit: number; offset?: number | undefined; area: string }) { 30 | return request({ 31 | url: '/album/new', 32 | method: 'get', 33 | params 34 | }) 35 | } 36 | 37 | /** 38 | * 专辑动态信息 39 | * 说明 : 调用此接口 , 传入专辑 id, 可获得专辑动态信息,如是否收藏,收藏数,评论数,分享数 40 | * - id - 专辑id 41 | * @param {number} id 42 | */ 43 | export function albumDynamicDetail(id) { 44 | return request({ 45 | url: '/album/detail/dynamic', 46 | method: 'get', 47 | params: { id, timestamp: new Date().getTime() } 48 | }) 49 | } 50 | 51 | export function likeAAlbum(params: { id: number; t: number }) { 52 | return request({ 53 | url: '/album/sub', 54 | method: 'post', 55 | params 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /src/renderer/api/artist.ts: -------------------------------------------------------------------------------- 1 | import request from '../utils/request' 2 | 3 | /** 4 | * 获取歌手单曲 5 | * 说明 : 调用此接口 , 传入歌手 id, 可获得歌手部分信息和热门歌曲 6 | * @param {number} id - 歌手 id, 可由搜索接口获得 7 | */ 8 | export function getArtist(id: number) { 9 | return request({ 10 | url: '/artists', 11 | method: 'get', 12 | params: { 13 | id, 14 | timestamp: Date.now() 15 | } 16 | }) 17 | } 18 | 19 | /** 20 | * 获取歌手专辑 21 | * 说明 : 调用此接口 , 传入歌手 id, 可获得歌手专辑内容 22 | * - id: 歌手 id 23 | * - limit: 取出数量 , 默认为 50 24 | * - offset: 偏移数量 , 用于分页 , 如 :( 页数 -1)*50, 其中 50 为 limit 的值 , 默认为 0 25 | * @param {Object} params 26 | * @param {number} params.id 27 | * @param {number=} params.limit 28 | * @param {number=} params.offset 29 | */ 30 | export function getArtistAlbum(params) { 31 | return request({ 32 | url: '/artist/album', 33 | method: 'get', 34 | params 35 | }) 36 | } 37 | 38 | /** 39 | * 歌手榜 40 | * 说明 : 调用此接口 , 可获取排行榜中的歌手榜 41 | * - type : 地区 42 | * 1: 华语 43 | * 2: 欧美 44 | * 3: 韩国 45 | * 4: 日本 46 | * @param {number=} type 47 | */ 48 | export function toplistOfArtists(type = null) { 49 | const params: { [key: string]: any } = {} 50 | if (type) { 51 | params.type = type 52 | } 53 | return request({ 54 | url: '/toplist/artist', 55 | method: 'get', 56 | params 57 | }) 58 | } 59 | 60 | /** 61 | * 获取歌手 mv 62 | * 说明 : 调用此接口 , 传入歌手 id, 可获得歌手 mv 信息 , 具体 mv 播放地址可调 用/mv传入此接口获得的 mvid 来拿到 , 如 : /artist/mv?id=6452,/mv?mvid=5461064 63 | * @param {number} params.id 歌手 id, 可由搜索接口获得 64 | * @param {number} params.offset 65 | * @param {number} params.limit 66 | */ 67 | export function artistMv(params) { 68 | return request({ 69 | url: '/artist/mv', 70 | method: 'get', 71 | params 72 | }) 73 | } 74 | 75 | /** 76 | * 相似歌手 77 | * 说明 : 调用此接口 , 传入歌手 id, 可获得相似歌手 78 | * - id: 歌手 id 79 | * @param {number} id 80 | */ 81 | export function similarArtists(id: number) { 82 | return request({ 83 | url: '/simi/artist', 84 | method: 'post', 85 | params: { id } 86 | }) 87 | } 88 | 89 | /** 90 | * 收藏歌手 91 | * 说明 : 调用此接口 , 传入歌手 id, 可收藏歌手 92 | * - id: 歌手 id 93 | * - t: 操作,1 为收藏,其他为取消收藏 94 | * @param {Object} params 95 | * @param {number} params.id 96 | * @param {number} params.t 97 | */ 98 | export function followAnArtist(params) { 99 | return request({ 100 | url: '/artist/sub', 101 | method: 'post', 102 | params 103 | }) 104 | } 105 | 106 | export function getArtistList(params) { 107 | return request({ 108 | url: '/artist/list', 109 | method: 'get', 110 | params 111 | }) 112 | } 113 | -------------------------------------------------------------------------------- /src/renderer/api/comment.ts: -------------------------------------------------------------------------------- 1 | import request from '../utils/request' 2 | 3 | export function getComment(params: { 4 | id: number 5 | type: number 6 | pageNo?: number 7 | pageSize?: number 8 | sortType?: number 9 | cursor?: number 10 | }) { 11 | return request({ 12 | url: '/comment/new', 13 | method: 'get', 14 | params 15 | }) 16 | } 17 | 18 | export function likeComment(params: { id: number; cid: number; t: number; type: number }) { 19 | return request({ 20 | url: '/comment/like', 21 | method: 'get', 22 | params 23 | }) 24 | } 25 | 26 | export function getFloorComment(params: { 27 | parentCommentId: number 28 | id: number 29 | type: number 30 | limit?: number 31 | time?: number 32 | }) { 33 | return request({ 34 | url: '/comment/floor', 35 | method: 'get', 36 | params 37 | }) 38 | } 39 | 40 | export function submitComment(params: { 41 | t: number 42 | type: number 43 | id: number 44 | content?: string 45 | commentId?: number 46 | }) { 47 | return request({ 48 | url: '/comment', 49 | method: 'post', 50 | params 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /src/renderer/api/mv.ts: -------------------------------------------------------------------------------- 1 | import request from '../utils/request' 2 | 3 | /** 4 | * 获取 mv 数据 5 | * 说明 : 调用此接口 , 传入 mvid ( 在搜索音乐的时候传 type=1004 获得 ) , 可获取对应 MV 数据 , 数据包含 mv 名字 , 歌手 , 发布时间 , mv 视频地址等数据 , 6 | * 其中 mv 视频 网易做了防盗链处理 , 可能不能直接播放 , 需要播放的话需要调用 ' mv 地址' 接口 7 | * - 调用例子 : /mv/detail?mvid=5436712 8 | * @param {number} mvid mv 的 id 9 | */ 10 | export function mvDetail(mvid: number) { 11 | return request({ 12 | url: '/mv/detail', 13 | method: 'get', 14 | params: { 15 | mvid, 16 | timestamp: Date.now() 17 | } 18 | }) 19 | } 20 | /** 21 | * 获取 mv 点赞转发评论数数据 22 | * 说明 : 调用此接口 , 传入 mvid ( 在搜索音乐的时候传 type=1004 获得 ) , 可获取对应 MV 点赞转发评论数数据 23 | * @param {number} mvid mv 的 id 24 | */ 25 | export function mvDetailInfo(mvid: number) { 26 | return request({ 27 | url: '/mv/detail/info', 28 | method: 'get', 29 | params: { 30 | mvid, 31 | timestamp: Date.now() 32 | } 33 | }) 34 | } 35 | 36 | /** 37 | * mv 地址 38 | * 说明 : 调用此接口 , 传入 mv id,可获取 mv 播放地址 39 | * - id: mv id 40 | * - r: 分辨率,默认1080,可从 /mv/detail 接口获取分辨率列表 41 | * - 调用例子 : /mv/url?id=5436712 /mv/url?id=10896407&r=1080 42 | * @param {Object} params 43 | * @param {number} params.id 44 | * @param {number=} params.r 45 | */ 46 | export function mvUrl(params: { id: number; r?: number }) { 47 | return request({ 48 | url: '/mv/url', 49 | method: 'get', 50 | params 51 | }) 52 | } 53 | 54 | /** 55 | * 相似 mv 56 | * 说明 : 调用此接口 , 传入 mvid 可获取相似 mv 57 | * @param {number} mvid 58 | */ 59 | export function simiMv(mvid: number) { 60 | return request({ 61 | url: '/simi/mv', 62 | method: 'get', 63 | params: { mvid } 64 | }) 65 | } 66 | 67 | /** 68 | * 收藏/取消收藏 MV 69 | * 说明 : 调用此接口,可收藏/取消收藏 MV 70 | * - mvid: mv id 71 | * - t: 1 为收藏,其他为取消收藏 72 | * @param {Object} params 73 | * @param {number} params.mvid 74 | * @param {number=} params.t 75 | */ 76 | 77 | export function subAMV(params: { mvid: number; t?: number; timestamp?: number }) { 78 | params.timestamp = new Date().getTime() 79 | return request({ 80 | url: '/mv/sub', 81 | method: 'post', 82 | params 83 | }) 84 | } 85 | 86 | export function likeAMV(params: { id: number; t: number; type: number }) { 87 | return request({ 88 | url: '/resource/like', 89 | method: 'get', 90 | params 91 | }) 92 | } 93 | -------------------------------------------------------------------------------- /src/renderer/api/other.ts: -------------------------------------------------------------------------------- 1 | import request from '../utils/request' 2 | 3 | /** 4 | * 本地音乐匹配 5 | * @param {Object} params 6 | * @param {string} params.title 7 | * @param {string} params.album 8 | * @param {string} params.artist 9 | * @param {number} params.duration 10 | * @param {string=} params.md5 11 | * @param {number=} params.localID 12 | */ 13 | export function searchMatch(params: { 14 | title: string 15 | album: string 16 | artist: string 17 | duration: number 18 | md5?: string 19 | localID?: number 20 | }): Promise { 21 | return request({ 22 | url: '/search/match', 23 | method: 'get', 24 | params 25 | }) 26 | } 27 | 28 | /** 29 | * 搜索 30 | * 说明 : 调用此接口 , 传入搜索关键词可以搜索该音乐 / 专辑 / 歌手 / 歌单 / 用户 , 关键词可以多个 , 以空格隔开 , 31 | * 如 " 周杰伦 搁浅 "( 不需要登录 ), 搜索获取的 mp3url 不能直接用 , 可通过 /song/url 接口传入歌曲 id 获取具体的播放链接 32 | * - keywords : 关键词 33 | * - limit : 返回数量 , 默认为 30 34 | * - offset : 偏移数量,用于分页 , 如 : 如 :( 页数 -1)*30, 其中 30 为 limit 的值 , 默认为 0 35 | * - type: 搜索类型;默认为 1 即单曲 , 取值意义 : 1: 单曲, 10: 专辑, 100: 歌手, 1000: 歌单, 1002: 用户, 1004: MV, 1006: 歌词, 1009: 电台, 1014: 视频, 1018:综合 36 | * - 调用例子 : /search?keywords=海阔天空 /cloudsearch?keywords=海阔天空(更全) 37 | * @param {Object} params 38 | * @param {string} params.keywords 39 | * @param {number=} params.limit 40 | * @param {number=} params.offset 41 | * @param {number=} params.type 42 | */ 43 | export function search(params: { 44 | keywords: string 45 | limit?: number 46 | offset?: number 47 | type?: number 48 | }) { 49 | return request({ 50 | url: '/search', 51 | method: 'get', 52 | params 53 | }) 54 | } 55 | 56 | /** 57 | * 获取banner图 58 | * @param {Object} params 59 | * @param {number=} params.type 60 | */ 61 | export function getBanner(params: { type?: number } = { type: 0 }) { 62 | return request({ 63 | url: '/banner', 64 | method: 'get', 65 | params 66 | }) 67 | } 68 | 69 | export function personalFM() { 70 | return request({ 71 | url: '/personal/fm', 72 | method: 'get', 73 | params: { 74 | timestamp: new Date().getTime() 75 | } 76 | }) 77 | } 78 | 79 | export function fmTrash(id: number) { 80 | return request({ 81 | url: '/fm_trash', 82 | method: 'post', 83 | params: { 84 | timestamp: new Date().getTime(), 85 | id 86 | } 87 | }) 88 | } 89 | 90 | /** 91 | * 获取歌曲副歌时间 92 | */ 93 | export function songChorus(id: number) { 94 | return request({ 95 | url: '/song/chorus', 96 | method: 'get', 97 | params: { 98 | id 99 | } 100 | }) 101 | } 102 | -------------------------------------------------------------------------------- /src/renderer/api/track.ts: -------------------------------------------------------------------------------- 1 | import request from '../utils/request' 2 | 3 | export function getLyric(id: number) { 4 | return request({ 5 | url: '/lyric', 6 | method: 'get', 7 | params: { 8 | id 9 | } 10 | }) as Promise<{ 11 | lrc: { lyric: any[] } 12 | tlyric: { lyric: any[] } 13 | romalrc: { lyric: any[] } 14 | yrc: { lyric: any[] } 15 | ytlrc: { lyric: any[] } 16 | yromalrc: { lyric: any[] } 17 | }> 18 | } 19 | 20 | /** 21 | * 喜欢音乐 22 | * 说明 : 调用此接口 , 传入音乐 id, 可喜欢该音乐 23 | * - id - 歌曲 id 24 | * - like - 默认为 true 即喜欢 , 若传 false, 则取消喜欢 25 | * @param {Object} params 26 | * @param {number} params.id 27 | * @param {boolean=} [params.like] 28 | */ 29 | export function likeTrack(params: any) { 30 | params.timestamp = new Date().getTime() 31 | return request({ 32 | url: '/like', 33 | method: 'get', 34 | params 35 | }) 36 | } 37 | 38 | /** 39 | * 获取歌曲详情 40 | * 说明 : 调用此接口 , 传入音乐 id(支持多个 id, 用 , 隔开), 可获得歌曲详情(注意:歌曲封面现在需要通过专辑内容接口获取) 41 | * @param {string} ids - 音乐 id, 例如 ids=405998841,33894312 42 | */ 43 | export function getTrackDetail(ids: string) { 44 | return request({ 45 | url: '/song/detail', 46 | method: 'get', 47 | params: { 48 | ids 49 | } 50 | }) 51 | } 52 | 53 | /** 54 | * 听歌打卡 55 | * 说明 : 调用此接口 , 传入音乐 id, 来源 id,歌曲时间 time,更新听歌排行数据 56 | * - id - 歌曲 id 57 | * - sourceid - 歌单或专辑 id 58 | * - time - 歌曲播放时间,单位为秒 59 | * @param {Object} params 60 | * @param {number} params.id 61 | * @param {number} params.sourceid 62 | * @param {number=} params.time 63 | */ 64 | export function scrobble(params) { 65 | params.timestamp = new Date().getTime() 66 | return request({ 67 | url: '/scrobble', 68 | method: 'get', 69 | params 70 | }) 71 | } 72 | 73 | /** 74 | * 新歌速递 75 | * 说明 : 调用此接口 , 可获取新歌速递 76 | * @param {number} type - 地区类型 id, 对应以下: 全部:0 华语:7 欧美:96 日本:8 韩国:16 77 | */ 78 | export function topSong(type: number) { 79 | return request({ 80 | url: '/top/song', 81 | method: 'get', 82 | params: { 83 | type 84 | } 85 | }) 86 | } 87 | 88 | /** 89 | * 新碟上架 90 | * 说明 : 调用此接口 , 可获取新碟上架 91 | * @param {string} params.area - ALL: 全部,ZH: 华语,EA: 欧美, KR: 韩国, JP: 日本 92 | * @param {string=} params.type - new: 全部,hot:热门,默认为new 93 | * @param {number=} params.year - 年,默认本年 94 | * @param {number=} params.month -月,默认本月 95 | * @param {number} params.limit 96 | * @param {number} params.offset 97 | */ 98 | export function topAlbum(params: any) { 99 | return request({ 100 | url: '/top/album', 101 | method: 'get', 102 | params 103 | }) 104 | } 105 | -------------------------------------------------------------------------------- /src/renderer/assets/css/osdlyric.scss: -------------------------------------------------------------------------------- 1 | body { 2 | scrollbar-width: none; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/arrow-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/arrow-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/arrow-up-alt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/arrow-up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/back5s.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/checkbox.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/checked.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/close.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/collect.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/collected.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/color-plate.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/comment.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/decreaseFont.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/dropdown.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/explicit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/explore.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/floor-comment.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/floorComment.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/fm.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/forward5s.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 5 7 | 8 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/heart-bar.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/heart-solid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/heart.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/increaseFont.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/library.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/like.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/liked.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/local-music.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/local-track-bg.svg: -------------------------------------------------------------------------------- 1 |  2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/log.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/login.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/logo.svg: -------------------------------------------------------------------------------- 1 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/logout.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/lyric.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/mail.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/mini-mode.svg: -------------------------------------------------------------------------------- 1 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/mobile.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/more.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/mv.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/normal-mode.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/offTop.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/onTop.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/options.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/osd-lyrics.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/pause.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/play.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/plus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/previous.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/repeat-1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/repeat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/settings.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/shuffle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/sort-up.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/stream-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/target.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/thumbs-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/volume-half.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/volume-mute.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/volume.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/icons/x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/images/auto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/images/auto.png -------------------------------------------------------------------------------- /src/renderer/assets/images/dark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/images/dark.jpg -------------------------------------------------------------------------------- /src/renderer/assets/images/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/images/default.jpg -------------------------------------------------------------------------------- /src/renderer/assets/images/emby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/images/emby.png -------------------------------------------------------------------------------- /src/renderer/assets/images/jellyfin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/images/jellyfin.png -------------------------------------------------------------------------------- /src/renderer/assets/images/light.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/images/light.jpg -------------------------------------------------------------------------------- /src/renderer/assets/images/navidrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/images/navidrome.png -------------------------------------------------------------------------------- /src/renderer/assets/images/navidrome.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/images/navidrome.webp -------------------------------------------------------------------------------- /src/renderer/assets/images/netease-music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/images/netease-music.png -------------------------------------------------------------------------------- /src/renderer/assets/images/vutronmusic-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/images/vutronmusic-icon.png -------------------------------------------------------------------------------- /src/renderer/assets/images/vutronmusic-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/images/vutronmusic-white.png -------------------------------------------------------------------------------- /src/renderer/assets/medias/Silence02s.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/medias/Silence02s.mp3 -------------------------------------------------------------------------------- /src/renderer/assets/medias/bright-hall.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/medias/bright-hall.wav -------------------------------------------------------------------------------- /src/renderer/assets/medias/cardiod-35-10-spread.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/medias/cardiod-35-10-spread.wav -------------------------------------------------------------------------------- /src/renderer/assets/medias/cinema-diningroom.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/medias/cinema-diningroom.wav -------------------------------------------------------------------------------- /src/renderer/assets/medias/dining-living-true-stereo.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/medias/dining-living-true-stereo.wav -------------------------------------------------------------------------------- /src/renderer/assets/medias/feedback-spring.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/medias/feedback-spring.wav -------------------------------------------------------------------------------- /src/renderer/assets/medias/filter-telephone.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/medias/filter-telephone.wav -------------------------------------------------------------------------------- /src/renderer/assets/medias/living-bedroom-leveled.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/medias/living-bedroom-leveled.wav -------------------------------------------------------------------------------- /src/renderer/assets/medias/matrix-reverb1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/medias/matrix-reverb1.wav -------------------------------------------------------------------------------- /src/renderer/assets/medias/matrix-reverb2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/medias/matrix-reverb2.wav -------------------------------------------------------------------------------- /src/renderer/assets/medias/medium-room1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/medias/medium-room1.wav -------------------------------------------------------------------------------- /src/renderer/assets/medias/s2_r4_bd.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/medias/s2_r4_bd.wav -------------------------------------------------------------------------------- /src/renderer/assets/medias/s3_r1_bd.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/medias/s3_r1_bd.wav -------------------------------------------------------------------------------- /src/renderer/assets/medias/spreader50-65ms.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/medias/spreader50-65ms.wav -------------------------------------------------------------------------------- /src/renderer/assets/medias/tim-omni-35-10-magnetic.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/medias/tim-omni-35-10-magnetic.wav -------------------------------------------------------------------------------- /src/renderer/assets/tray/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/icon.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/icon_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/icon_white.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/like.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/like_fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/like_fill.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/like_fill_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/like_fill_white.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/like_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/like_white.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/menu_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/menu_black.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/menu_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/menu_white.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/pause.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/pause_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/pause_white.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/play_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/play_arrow.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/play_arrow_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/play_arrow_white.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/skip_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/skip_next.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/skip_next_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/skip_next_white.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/skip_previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/skip_previous.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/skip_previous_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/skip_previous_white.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/thumbs_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/thumbs_down.png -------------------------------------------------------------------------------- /src/renderer/assets/tray/thumbs_down_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stark81/VutronMusic/60bdd9fb15d63f25165d8bb5105ac233626229ce/src/renderer/assets/tray/thumbs_down_white.png -------------------------------------------------------------------------------- /src/renderer/components/ArtistListItem.vue: -------------------------------------------------------------------------------- 1 | 12 | 37 | 38 | 102 | -------------------------------------------------------------------------------- /src/renderer/components/ArtistsInLine.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 42 | 43 | 59 | -------------------------------------------------------------------------------- /src/renderer/components/ButtonIcon.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 32 | -------------------------------------------------------------------------------- /src/renderer/components/ButtonTwoTone.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 58 | 59 | 94 | -------------------------------------------------------------------------------- /src/renderer/components/CheckBox.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 76 | 77 | 109 | -------------------------------------------------------------------------------- /src/renderer/components/CommentPage.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 42 | 43 | 48 | -------------------------------------------------------------------------------- /src/renderer/components/ExplicitSymbol.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/renderer/components/LatestVersion.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | 38 | -------------------------------------------------------------------------------- /src/renderer/components/LoadingButton.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 25 | -------------------------------------------------------------------------------- /src/renderer/components/ModalPitch.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 64 | 65 | 97 | -------------------------------------------------------------------------------- /src/renderer/components/ModalPlayback.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 65 | 66 | 98 | -------------------------------------------------------------------------------- /src/renderer/components/ShowToast.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | 49 | -------------------------------------------------------------------------------- /src/renderer/components/SvgIcon.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 30 | 31 | 38 | -------------------------------------------------------------------------------- /src/renderer/components/Win32TitleBar.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 42 | 43 | 99 | -------------------------------------------------------------------------------- /src/renderer/components/WriteComment.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 34 | 35 | 69 | -------------------------------------------------------------------------------- /src/renderer/components/layout/DefaultLayout.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 17 | -------------------------------------------------------------------------------- /src/renderer/components/layout/HeaderLayout.vue: -------------------------------------------------------------------------------- 1 | 15 | 28 | 50 | -------------------------------------------------------------------------------- /src/renderer/components/layout/index.ts: -------------------------------------------------------------------------------- 1 | import DefaultLayout from './layout/DefaultLayout.vue' 2 | import HeaderLayout from './layout/HeaderLayout.vue' 3 | 4 | export { DefaultLayout, HeaderLayout } 5 | -------------------------------------------------------------------------------- /src/renderer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/renderer/locales/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": { 3 | "welcome-title": "Hallo Vutron! Alles ist bereit.", 4 | "welcome-desc": "Sie können jetzt plattformübergreifende Webanwendungen schreiben. Sehen Sie Ihre Änderungen in Echtzeit und erstellen Sie mit einem einzigen Befehl auf mehreren Plattformen. Wenn Sie damit noch nicht vertraut sind, können Sie Hilfe von der Dokumentationsseite unten erhalten oder vorgefertigten Beispielcode ausführen.", 5 | "second-desc": "Auf den zweiten Bildschirm verschoben! Hier gibt es keine!", 6 | "selected-file": "Ausgewählte Datei: {filePath}." 7 | }, 8 | "title": { 9 | "main": "Hauptbildschirm", 10 | "second": "Zweiter Bildschirm", 11 | "error": "Ein Fehler ist aufgetreten" 12 | }, 13 | "menu": { 14 | "change-theme": "Thema wechseln", 15 | "change-language": "Sprache ändern", 16 | "increase-count": "Zählerstand um 1 erhöhen", 17 | "documentation": "dokumentieren", 18 | "github": "Quellcode", 19 | "open-file": "Öffnen Sie eine Textdatei" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/renderer/locales/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": { 3 | "welcome-title": "Hola Vutron! Todo está listo.", 4 | "welcome-desc": "Ahora puede escribir aplicaciones web multiplataforma. Vea sus cambios en tiempo real y cree en múltiples plataformas con un solo comando. Si aún no está familiarizado con él, puede obtener ayuda en la página de documentación a continuación o ejecutar un código de ejemplo preescrito.", 5 | "second-desc": "¡Movido a la segunda pantalla! ¡Aquí no hay ninguno!", 6 | "selected-file": "Archivo seleccionado: {filePath}." 7 | }, 8 | "title": { 9 | "main": "Pantalla principal", 10 | "second": "segunda pantalla", 11 | "error": "Se produjo un error" 12 | }, 13 | "menu": { 14 | "change-theme": "cambiar de tema", 15 | "change-language": "cambiar idioma", 16 | "increase-count": "aumentar la cuenta en 1", 17 | "documentation": "documento", 18 | "github": "código fuente", 19 | "open-file": "Abrir un archivo de texto" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/renderer/locales/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": { 3 | "welcome-title": "Bonjour Vutron ! Tout est prêt.", 4 | "welcome-desc": "Vous pouvez maintenant écrire des applications Web multiplateformes. Visualisez vos modifications en temps réel et créez sur plusieurs plates-formes avec une seule commande. Si vous ne le connaissez pas encore, vous pouvez obtenir de l'aide sur la page de documentation ci-dessous ou exécuter un exemple de code pré-écrit.", 5 | "second-desc": "Déplacé vers le deuxième écran! Il n'y en a pas ici !", 6 | "selected-file": "Fichier sélectionné: {filePath}." 7 | }, 8 | "title": { 9 | "main": "écran principal", 10 | "second": "Deuxième écran", 11 | "error": "Erreur est survenue" 12 | }, 13 | "menu": { 14 | "change-theme": "Change le thème", 15 | "change-language": "Changer de langue", 16 | "increase-count": "Augmenter le nombre de 1", 17 | "documentation": "Document", 18 | "github": "Code source", 19 | "open-file": "Ouvrir un fichier texte" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/renderer/locales/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": { 3 | "welcome-title": "こんにちはVutron! すべての準備が完了しました。", 4 | "welcome-desc": "これで、クロスプラットフォームのWebアプリケーションを作成できます。 変更内容をリアルタイムで確認し、一度のコマンドで複数のプラットフォームにビルドします。 まだ慣れていない場合は、以下のドキュメントページでヘルプを入手するか、事前に作成されたサンプルコードを実行してみてください。", 5 | "second-desc": "2番目の画面に移動しました! ここには何もありません!", 6 | "selected-file": "選択したファイル: {filePath}." 7 | }, 8 | "title": { 9 | "main": "メイン画面", 10 | "second": "2番目の画面", 11 | "error": "エラー発生" 12 | }, 13 | "menu": { 14 | "change-theme": "テーマの変更", 15 | "change-language": "言語の変更", 16 | "increase-count": "カウント1増加", 17 | "documentation": "文書", 18 | "github": "ソースコード", 19 | "open-file": "テキストファイルを開く" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/renderer/locales/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": { 3 | "welcome-title": "안녕 Vutron! 모든 준비를 마쳤습니다.", 4 | "welcome-desc": "이제 크로스플랫폼 웹 애플리케이션을 작성할 수 있습니다. 변경사항을 실시간으로 확인하고 한 번의 명령으로 여러 플랫폼으로 빌드하세요. 아직 익숙하지 않다면, 아래 문서 페이지에서 도움을 받아보거나 사전에 작성된 예제 코드를 실행해볼 수 있습니다.", 5 | "second-desc": "두번째 화면으로 이동하였습니다! 여기는 아무 것도 없습니다!", 6 | "selected-file": "선택한 파일: {filePath}." 7 | }, 8 | "title": { 9 | "main": "메인 화면", 10 | "second": "두번째 화면", 11 | "error": "에러 발생" 12 | }, 13 | "menu": { 14 | "change-theme": "테마 변경", 15 | "change-language": "언어 변경", 16 | "increase-count": "카운트 1 증가", 17 | "documentation": "문서", 18 | "github": "소스코드", 19 | "open-file": "텍스트 파일 열기" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/renderer/locales/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": { 3 | "welcome-title": "Hallo Vutron! Alles is klaar.", 4 | "welcome-desc": "U kunt nu platformonafhankelijke webapplicaties schrijven. Bekijk wijzigingen in realtime en bouw met één opdracht naar meerdere platforms. Als u hiermee nog niet bekend bent, kunt u hulp krijgen via de onderstaande documentatiepagina, of een vooraf geschreven voorbeeldcode uitvoeren.", 5 | "second-desc": "U bent naar het tweede scherm gegaan! Hier is niks!", 6 | "selected-file": "Geselecteerde bestand: {filePath}." 7 | }, 8 | "title": { 9 | "main": "Hoofdscherm", 10 | "second": "Tweede scherm", 11 | "error": "Onbekende Error" 12 | }, 13 | "menu": { 14 | "change-theme": "Thema veranderen", 15 | "change-language": "Taal wijzigen", 16 | "increase-count": "Één stap optellen", 17 | "documentation": "Documentatie", 18 | "github": "Broncode", 19 | "open-file": "Open een bestand" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/renderer/locales/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": { 3 | "welcome-title": "Olá Vutron! Tudo está pronto.", 4 | "welcome-desc": "Agora você pode escrever aplicativos da Web de plataforma cruzada. Veja suas alterações em tempo real e crie para várias plataformas com um único comando. Se ainda não estiver familiarizado com ele, você pode obter ajuda na página de documentação abaixo ou executar algum código de exemplo pré-escrito.", 5 | "second-desc": "Movido para a segunda tela! Não há nenhum aqui!", 6 | "selected-file": "Arquivo selecionado: {filePath}." 7 | }, 8 | "title": { 9 | "main": "Tela principal", 10 | "second": "Segunda tela", 11 | "error": "Erro desconhecido" 12 | }, 13 | "menu": { 14 | "change-theme": "Mudar Tema", 15 | "change-language": "Mudar Idioma", 16 | "increase-count": "Aumentar a contagem em 1", 17 | "documentation": "Documento", 18 | "github": "Código fonte", 19 | "open-file": "Abra um arquivo de texto" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/renderer/locales/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": { 3 | "welcome-title": "Привет Vutron! Все готово.", 4 | "welcome-desc": "Теперь вы можете писать кроссплатформенные веб-приложения. Просматривайте свои изменения в режиме реального времени и выполняйте сборку для нескольких платформ с помощью одной команды. Если вы еще не знакомы с ним, вы можете получить помощь на странице документации ниже или запустить предварительно написанный пример кода.", 5 | "second-desc": "Перенесено на второй экран! Здесь их нет!", 6 | "selected-file": "Выбранный файл: {filePath}." 7 | }, 8 | "title": { 9 | "main": "Главный экран", 10 | "second": "Второй экран", 11 | "error": "Неизвестная ошибка" 12 | }, 13 | "menu": { 14 | "change-theme": "Поменять тему", 15 | "change-language": "Изменить язык", 16 | "increase-count": "Увеличить количество на 1", 17 | "documentation": "Документ", 18 | "github": "Исходный код", 19 | "open-file": "Открыть текстовый файл" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/renderer/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { createPinia } from 'pinia' 3 | 4 | import App from './App.vue' 5 | import router from './router' 6 | import vuetify from './plugins/vuetify' 7 | import i18n from './plugins/i18n' 8 | import 'virtual:svg-icons-register' 9 | import './assets/css/global.scss' 10 | import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' 11 | import DOMPurify from 'dompurify' 12 | import { dailyTask } from './utils' 13 | import vue3lottie from 'vue3-lottie' 14 | 15 | // Add API key defined in contextBridge to window object type 16 | declare global { 17 | // eslint-disable-next-line no-unused-vars 18 | interface Window { 19 | mainApi?: { 20 | send: (channel: string, ...data: any[]) => void 21 | on: (channel: string, func: (...data: any[]) => void) => void 22 | once: (channel: string, func: (...data: any[]) => void) => void 23 | off: (channel: string, func: (...data: any[]) => void) => void 24 | invoke: (channel: string, ...data: any[]) => Promise 25 | sendMessage: (message: Record) => void 26 | closeMessagePort: () => void 27 | } 28 | env?: { 29 | isElectron: boolean 30 | isEnableTitlebar: boolean 31 | isLinux: boolean 32 | isMac: boolean 33 | isWindows: boolean 34 | } 35 | LottieAnimation: (typeof import('vue3-lottie'))['Vue3Lottie'] 36 | } 37 | } 38 | 39 | const app = createApp(App) 40 | 41 | app.directive('focus', { 42 | mounted(el) { 43 | el.focus() 44 | } 45 | }) 46 | app.directive('same-html', (el, binding) => { 47 | el.innerHTML = DOMPurify.sanitize(binding.value) 48 | }) 49 | 50 | const pinia = createPinia() 51 | pinia.use(piniaPluginPersistedstate) 52 | 53 | app.use(vuetify).use(vue3lottie).use(i18n).use(router).use(pinia) 54 | 55 | app.mount('#app') 56 | 57 | dailyTask() 58 | -------------------------------------------------------------------------------- /src/renderer/osdLyric.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import OSDLyric from './views/OSDLyric.vue' 3 | import 'virtual:svg-icons-register' 4 | import { createPinia } from 'pinia' 5 | import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' 6 | import './assets/css/osdlyric.scss' 7 | 8 | declare global { 9 | // eslint-disable-next-line no-unused-vars 10 | interface Window { 11 | mainApi?: { 12 | send: (channel: string, ...data: any[]) => void 13 | on: (channel: string, func: (...data: any[]) => void) => void 14 | once: (channel: string, func: (...data: any[]) => void) => void 15 | off: (channel: string, func: (...data: any[]) => void) => void 16 | invoke: (channel: string, ...data: any[]) => Promise 17 | sendMessage: (message: Record) => void 18 | closeMessagePort: () => void 19 | } 20 | env?: { 21 | isElectron: boolean 22 | isEnableTitlebar: boolean 23 | isLinux: boolean 24 | isMac: boolean 25 | isWindows: boolean 26 | } 27 | } 28 | } 29 | 30 | const app = createApp(OSDLyric) 31 | const pinia = createPinia() 32 | pinia.use(piniaPluginPersistedstate) 33 | app.use(pinia) 34 | app.mount('#lyric') 35 | -------------------------------------------------------------------------------- /src/renderer/osdlyric.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/renderer/plugins/i18n.ts: -------------------------------------------------------------------------------- 1 | import { createI18n } from 'vue-i18n' 2 | import en from '../locales/en.json' 3 | import zh from '../locales/zh-hans.json' 4 | import zht from '../locales/zh-hant.json' 5 | // import { getCurrentLocale } from '../utils' 6 | 7 | const settings = JSON.parse(localStorage.getItem('settings') || '{}') 8 | const language = settings?.general?.language || 'zh' 9 | 10 | // getCurrentLocale() 11 | export default createI18n({ 12 | legacy: false, 13 | locale: language, 14 | fallbackLocale: 'en', 15 | globalInjection: true, 16 | messages: { 17 | en, 18 | zh, 19 | zht 20 | } 21 | }) 22 | -------------------------------------------------------------------------------- /src/renderer/plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | import { createVuetify } from 'vuetify' 2 | import { en, zhHans } from 'vuetify/locale' 3 | import { aliases, mdi } from 'vuetify/iconsets/mdi' 4 | import 'vuetify/styles' 5 | import '@mdi/font/css/materialdesignicons.min.css' 6 | 7 | import colors from 'vuetify/lib/util/colors.mjs' 8 | 9 | export default createVuetify({ 10 | locale: { 11 | messages: { en, zhHans }, 12 | locale: 'en', 13 | fallback: 'en' 14 | }, 15 | icons: { 16 | defaultSet: 'mdi', 17 | aliases, 18 | sets: { 19 | mdi 20 | } 21 | }, 22 | theme: { 23 | themes: { 24 | light: { 25 | dark: false, 26 | colors: { 27 | // primary: colors.green.darken2 28 | primary: colors.blue.darken1 29 | } 30 | }, 31 | dark: { 32 | dark: true, 33 | colors: { 34 | // primary: colors.green.darken4 35 | primary: colors.blue.darken3 36 | } 37 | } 38 | } 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /src/renderer/store/osdLyric.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import { ref } from 'vue' 3 | 4 | export type Type = 'small' | 'normal' 5 | export type Mode = 'oneLine' | 'twoLines' 6 | export type TranslationMode = 'none' | 'tlyric' | 'rlyric' 7 | 8 | export const useOsdLyricStore = defineStore( 9 | 'osdLyric', 10 | () => { 11 | const show = ref(false) 12 | const type = ref('small') 13 | const mode = ref('twoLines') 14 | const isLock = ref(false) 15 | const alwaysOnTop = ref(false) 16 | const fontSize = ref(26) 17 | const staticTime = ref(1500) 18 | const isWordByWord = ref(true) 19 | const translationMode = ref('tlyric') 20 | const backgroundColor = ref('rgba(0, 0, 0, 0)') 21 | const playedLrcColor = ref('#37cf88') 22 | const unplayLrcColor = ref('rgba(210, 210, 210, 1)') 23 | const textShadow = ref('rgba(0, 0, 0, 0.2)') 24 | 25 | window.addEventListener('storage', (event) => { 26 | if (event.key === 'osdLyric') { 27 | const newState = JSON.parse(event.newValue || '{}') 28 | if (Object.keys(newState).length === 0 && newState.constructor === Object) return 29 | show.value = newState.show 30 | type.value = newState.type 31 | mode.value = newState.mode 32 | isLock.value = newState.isLock 33 | alwaysOnTop.value = newState.alwaysOnTop 34 | fontSize.value = newState.fontSize 35 | staticTime.value = newState.staticTime 36 | isWordByWord.value = newState.isWordByWord 37 | backgroundColor.value = newState.backgroundColor 38 | playedLrcColor.value = newState.playedLrcColor 39 | unplayLrcColor.value = newState.unplayLrcColor 40 | textShadow.value = newState.textShadow 41 | translationMode.value = newState.translationMode 42 | } 43 | }) 44 | 45 | // onMounted(() => { 46 | // window.mainApi?.send('updateOsdState', { show: show.value }) 47 | // }) 48 | 49 | return { 50 | show, 51 | type, 52 | mode, 53 | isLock, 54 | alwaysOnTop, 55 | fontSize, 56 | staticTime, 57 | isWordByWord, 58 | backgroundColor, 59 | playedLrcColor, 60 | unplayLrcColor, 61 | textShadow, 62 | translationMode 63 | } 64 | }, 65 | { 66 | persist: true 67 | } 68 | ) 69 | -------------------------------------------------------------------------------- /src/renderer/utils/auth.ts: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | import { logout } from '../api/auth' 3 | import { useDataStore } from '../store/data' 4 | import { storeToRefs } from 'pinia' 5 | 6 | export function setCookies(string: string) { 7 | const cookies = string.split(';;') 8 | cookies.map((cookie) => { 9 | document.cookie = cookie 10 | const cookieKeyValue = cookie.split(';')[0].split('=') 11 | localStorage.setItem(`cookie-${cookieKeyValue[0]}`, cookieKeyValue[1]) 12 | }) 13 | } 14 | 15 | export function getCookie(key: string) { 16 | return Cookies.get(key) ?? localStorage.getItem(`cookie-${key}`) 17 | } 18 | 19 | export function removeCookie(key: string) { 20 | Cookies.remove(key) 21 | localStorage.removeItem(`cookie-${key}`) 22 | } 23 | 24 | // MUSIC_U 只有在账户登录的情况下才有 25 | // export function isLoggedIn() { 26 | // return getCookie('MUSIC_U') !== null 27 | // } 28 | 29 | // 账号登录 30 | export function isAccountLoggedIn() { 31 | return getCookie('MUSIC_U') !== null 32 | } 33 | 34 | // 用户名搜索(用户数据为只读) 35 | // export function isUsernameLoggedIn() { 36 | // return store.state.data.loginMode === 'username'; 37 | // } 38 | 39 | // 账户登录或者用户名搜索都判断为登录,宽松检查 40 | export function isLooseLoggedIn() { 41 | return isAccountLoggedIn() 42 | // return isAccountLoggedIn() || isUsernameLoggedIn(); 43 | } 44 | 45 | export function doLogout() { 46 | const { resetUserInfo, resetLiked } = useDataStore() 47 | const { user } = storeToRefs(useDataStore()) 48 | window.mainApi?.invoke('logout', user.value.userId).then((res: boolean) => { 49 | if (res) { 50 | logout() 51 | removeCookie('MUSIC_U') 52 | removeCookie('__csrf') 53 | resetUserInfo() 54 | resetLiked() 55 | } else { 56 | console.log('logout failed') 57 | } 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /src/renderer/utils/db.ts: -------------------------------------------------------------------------------- 1 | import Dexie, { Table } from 'dexie' 2 | import { serviceName } from '../store/streamingMusic' 3 | 4 | export interface StreamInfo { 5 | name: serviceName 6 | stream: { 7 | url: string 8 | username: string 9 | password: string 10 | clientID?: string 11 | authorization?: string 12 | userId?: string 13 | accessToken?: string 14 | } 15 | updateTime: number 16 | } 17 | 18 | class VutronMusicDB extends Dexie { 19 | stream!: Table 20 | 21 | constructor() { 22 | super('VutronMusic') 23 | this.version(1).stores({ 24 | stream: '&name, updateTime' 25 | }) 26 | } 27 | } 28 | 29 | const db = new VutronMusicDB() 30 | 31 | export const setStreamInfo = (name: serviceName, stream: StreamInfo['stream']) => { 32 | return db.stream.put({ 33 | name, 34 | stream, 35 | updateTime: Date.now() 36 | }) 37 | } 38 | 39 | export const getStreamInfo = async (name: serviceName) => { 40 | const result = await db.stream.get(name) 41 | if (!result) return null 42 | return result.stream 43 | } 44 | 45 | export const updateStreamInfo = async ( 46 | name: serviceName, 47 | partialStream: Partial 48 | ) => { 49 | const existing = await db.stream.get(name) 50 | if (!existing) return 51 | 52 | await db.stream.put({ 53 | name, 54 | stream: { 55 | ...existing.stream, 56 | ...partialStream 57 | }, 58 | updateTime: Date.now() 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /src/renderer/utils/debounceRef.ts: -------------------------------------------------------------------------------- 1 | import { customRef } from 'vue' 2 | 3 | export function debounceRef(value: any, delay = 500) { 4 | let timer: any 5 | return customRef((track, trigger) => { 6 | return { 7 | get() { 8 | track() // 通知Vue追踪value的变化 9 | return value 10 | }, 11 | set(val) { 12 | clearTimeout(timer) 13 | timer = setTimeout(() => { 14 | value = val 15 | trigger() // 通知Vue去重新解析模板 16 | }, delay) 17 | } 18 | } 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /src/renderer/utils/eventBus.ts: -------------------------------------------------------------------------------- 1 | import mitt from 'mitt' 2 | 3 | export default mitt() 4 | -------------------------------------------------------------------------------- /src/renderer/utils/request.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' 2 | import { doLogout } from './auth' 3 | 4 | // let baseUrl = '' 5 | 6 | // if (window.env?.isElectron) { 7 | // // 8 | // } else { 9 | // baseUrl = process.env. 10 | // } 11 | 12 | // const baseUrl = window.env?.isElectron ? '/netease' : '' 13 | const baseUrl = '/netease' 14 | 15 | const service: AxiosInstance = axios.create({ 16 | baseURL: baseUrl, 17 | withCredentials: true, 18 | timeout: 15000 19 | }) 20 | 21 | service.interceptors.request.use((config: any) => { 22 | return config 23 | }) 24 | 25 | service.interceptors.response.use( 26 | (response: AxiosResponse) => { 27 | const res = response 28 | return res 29 | }, 30 | (error: AxiosError) => { 31 | const { response } = error 32 | const data = response?.data as any 33 | if (data?.code === 301 && data?.message === '未登录') { 34 | console.log('未登录') 35 | doLogout() 36 | } 37 | return Promise.reject(error) 38 | } 39 | ) 40 | 41 | const request = async (config: AxiosRequestConfig) => { 42 | const { data } = await service.request(config) 43 | return data as any 44 | } 45 | 46 | export default request 47 | -------------------------------------------------------------------------------- /src/renderer/utils/shortcuts.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | id: 'play', 4 | name: '播放/暂停', 5 | shortcut: 'CommandOrControl+P', 6 | globalShortcut: 'Alt+CommandOrControl+P' 7 | }, 8 | { 9 | id: 'next', 10 | name: '下一首', 11 | shortcut: 'CommandOrControl+Right', 12 | globalShortcut: 'Alt+CommandOrControl+Right' 13 | }, 14 | { 15 | id: 'previous', 16 | name: '上一首', 17 | shortcut: 'CommandOrControl+Left', 18 | globalShortcut: 'Alt+CommandOrControl+Left' 19 | }, 20 | { 21 | id: 'increaseVolume', 22 | name: '增加音量', 23 | shortcut: 'CommandOrControl+Up', 24 | globalShortcut: 'Alt+CommandOrControl+Up' 25 | }, 26 | { 27 | id: 'decreaseVolume', 28 | name: '减少音量', 29 | shortcut: 'CommandOrControl+Down', 30 | globalShortcut: 'Alt+CommandOrControl+Down' 31 | }, 32 | { 33 | id: 'like', 34 | name: '喜欢歌曲', 35 | shortcut: 'CommandOrControl+L', 36 | globalShortcut: 'Alt+CommandOrControl+L' 37 | }, 38 | { 39 | id: 'minimize', 40 | name: '隐藏/显示播放器', 41 | shortcut: 'CommandOrControl+M', 42 | globalShortcut: 'Alt+CommandOrControl+M' 43 | } 44 | ] 45 | -------------------------------------------------------------------------------- /src/renderer/utils/tricklingProgress.ts: -------------------------------------------------------------------------------- 1 | import 'trickling/lib/style.css' 2 | import { createTrickling } from 'trickling' 3 | 4 | export const tricklingProgress = createTrickling({ 5 | showSpinner: false, 6 | color: 'var(--primary-color)' 7 | // color: '#335eea' 8 | }) 9 | -------------------------------------------------------------------------------- /src/renderer/views/ArtistMv.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 70 | 71 | 85 | -------------------------------------------------------------------------------- /src/renderer/views/ErrorScreen.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/renderer/views/LottiePlayer.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 42 | -------------------------------------------------------------------------------- /src/renderer/views/index.ts: -------------------------------------------------------------------------------- 1 | import ErrorScreen from './ErrorScreen.vue' 2 | import MainScreen from './MainScreen.vue' 3 | import ExplorePage from './ExplorePage.vue' 4 | import LocalMusicScreen from './LocalMusic.vue' 5 | import SystemSettings from './SystemSettings.vue' 6 | import LoginAccount from './LoginAccount.vue' 7 | import LibraryMusic from './LibraryMusic.vue' 8 | import AlbumPage from './AlbumPage.vue' 9 | import ArtistPage from './ArtistPage.vue' 10 | import SearchPage from './SearchPage.vue' 11 | import HomePage from './HomePage.vue' 12 | import PlaylistPage from './PlaylistPage.vue' 13 | import DailyTracks from './DailyTracks.vue' 14 | import UserPage from './UserPage.vue' 15 | import MvPage from './MvPage.vue' 16 | import ArtistMv from './ArtistMv.vue' 17 | import NextUp from './NextUp.vue' 18 | import StreamLogin from './StreamLogin.vue' 19 | import StreamPage from './StreamPage.vue' 20 | import LottiePlayer from './LottiePlayer.vue' 21 | 22 | export { 23 | ErrorScreen, 24 | MainScreen, 25 | ExplorePage, 26 | LocalMusicScreen, 27 | SystemSettings, 28 | LoginAccount, 29 | LibraryMusic, 30 | AlbumPage, 31 | ArtistPage, 32 | SearchPage, 33 | HomePage, 34 | PlaylistPage, 35 | DailyTracks, 36 | UserPage, 37 | MvPage, 38 | ArtistMv, 39 | NextUp, 40 | StreamLogin, 41 | StreamPage, 42 | LottiePlayer 43 | } 44 | -------------------------------------------------------------------------------- /src/vue-shim.d.ts: -------------------------------------------------------------------------------- 1 | // For fix typescript import errors 2 | declare module '*.vue' { 3 | import { defineComponent } from 'vue' 4 | const component: ReturnType 5 | export default component 6 | } 7 | -------------------------------------------------------------------------------- /tests/app.spec.ts: -------------------------------------------------------------------------------- 1 | import { Page, _electron as electron } from 'playwright' 2 | import { ElectronApplication } from 'playwright-core' 3 | import { test, expect } from '@playwright/test' 4 | 5 | let appWindow: Page 6 | let appElectron: ElectronApplication 7 | 8 | test.beforeAll(async () => { 9 | // Open Electron app from build directory 10 | appElectron = await electron.launch({ 11 | args: ['dist/main/index.js'], 12 | locale: 'en-US', 13 | colorScheme: 'light', 14 | env: { 15 | ...process.env, 16 | NODE_ENV: 'production' 17 | } 18 | }) 19 | appWindow = await appElectron.firstWindow() 20 | 21 | await appWindow.waitForEvent('load') 22 | }) 23 | 24 | test('Environment check', async () => { 25 | const isPackaged = await appElectron.evaluate(async ({ app }) => { 26 | return app.isPackaged 27 | }) 28 | 29 | expect(isPackaged, 'Confirm that is in development mode').toBe(false) 30 | }) 31 | 32 | test.afterAll(async () => { 33 | await appWindow.waitForTimeout(2000) 34 | await appElectron.close() 35 | }) 36 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "outDir": "./dist", 5 | "target": "esnext", 6 | "module": "esnext", 7 | "moduleResolution": "node", 8 | "jsx": "preserve", 9 | "noImplicitAny": false, 10 | "allowSyntheticDefaultImports": true, 11 | "declaration": true, 12 | "resolveJsonModule": true, 13 | "esModuleInterop": true, 14 | "sourceMap": true, 15 | "strict": true, 16 | "skipLibCheck": true, 17 | "paths": { 18 | "@/*": ["./src/*"] 19 | }, 20 | "lib": ["esnext", "dom"], 21 | "types": ["node"], 22 | "typeRoots": ["node_modules/@types"], 23 | "allowJs": true 24 | }, 25 | "include": ["src/*.ts", "src/*.d.ts", "src/renderer"], 26 | "references": [ 27 | { 28 | "path": "./tsconfig.node.json" 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "jsx": "preserve", 6 | "moduleResolution": "node", 7 | "composite": true, 8 | "resolveJsonModule": true, 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true 11 | }, 12 | "include": ["src/main", "src/preload", "package.json", "vite.config.ts", "buildAssets/builder"] 13 | } 14 | --------------------------------------------------------------------------------