From 9830e51fa040837a1b52bfcfa18d34b967d678a2 Mon Sep 17 00:00:00 2001 From: sqj Date: Mon, 25 Aug 2025 19:04:57 +0800 Subject: [PATCH] =?UTF-8?q?add:=20=E6=B7=BB=E5=8A=A0=E4=BA=86=E9=A6=96?= =?UTF-8?q?=E9=A1=B5=E7=83=AD=E9=97=A8=E6=AD=8C=E5=8D=95&=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=A4=9A=E5=B9=B3=E5=8F=B0=E6=AD=8C=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 28 +- .prettierrc.yaml | 2 +- eslint.config.mjs | 50 +- package.json | 2 + .../logs/36e8a2339944464684ff81e9be5799c4.txt | 133 + .../logs/92f1f743bf4e4ef2897f1697a0ccde8f.txt | 861 ++++ .../logs/a4f47a9638be4caba2eb850e4a0a7230.txt | 4556 ++++++++--------- plugin/logs/temp.txt | 104 + src/common/utils/lyricUtils/kg.js | 23 +- src/main/index.ts | 1 - src/main/services/music/net-ease-service.ts | 2 +- src/main/services/musicSdk/index.ts | 28 +- src/main/services/musicSdk/service.ts | 52 +- src/main/services/musicSdk/type.ts | 48 +- src/main/services/plugin/index.ts | 2 +- src/main/services/plugin/logger.ts | 20 +- .../plugin/manager/CeruMusicPluginHost.ts | 4 +- src/main/utils/musicSdk/kg/lyric.js | 2 +- src/main/utils/musicSdk/kw/lyric.js | 14 +- src/main/utils/musicSdk/kw/util.js | 63 +- src/main/utils/musicSdk/mg/lyric.js | 4 +- src/main/utils/musicSdk/wy/lyric.js | 27 +- src/main/utils/musicSdk/wy/songList.js | 2 +- src/main/utils/request.js | 341 +- src/preload/index.d.ts | 11 +- src/preload/index.ts | 3 +- src/renderer/components.d.ts | 5 +- .../src/components/Music/SongVirtualList.vue | 632 +++ src/renderer/src/components/Play/FullPlay.vue | 200 +- .../src/components/Play/PlayMusic.vue | 14 +- .../components/Settings/PlaylistSettings.vue | 7 +- src/renderer/src/router/index.ts | 5 + src/renderer/src/utils/playlistManager.ts | 59 +- src/renderer/src/views/home/index.vue | 37 +- src/renderer/src/views/music/find.vue | 185 +- src/renderer/src/views/music/list.vue | 640 +-- src/renderer/src/views/music/search.vue | 573 +-- yarn.lock | 10 + 38 files changed, 5156 insertions(+), 3594 deletions(-) create mode 100644 src/renderer/src/components/Music/SongVirtualList.vue diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5677136..767abd3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,31 +1,31 @@ -name: AutoBuild # 工作流的名称 +name: AutoBuild # 工作流的名称 permissions: - contents: write # 给予写入仓库内容的权限 + contents: write # 给予写入仓库内容的权限 on: push: tags: - - 'v*' # 当推送以v开头的标签时触发此工作流 + - 'v*' # 当推送以v开头的标签时触发此工作流 workflow_dispatch: jobs: release: - name: build and release electron app # 任务名称 - runs-on: ${{ matrix.os }} # 在matrix.os定义的操作系统上运行 + name: build and release electron app # 任务名称 + runs-on: ${{ matrix.os }} # 在matrix.os定义的操作系统上运行 strategy: - fail-fast: false # 如果一个任务失败,其他任务继续运行 + fail-fast: false # 如果一个任务失败,其他任务继续运行 matrix: - os: [windows-latest, macos-latest] # 在Windows和macOS上运行任务 + os: [windows-latest, macos-latest] # 在Windows和macOS上运行任务 steps: - name: Check out git repository - uses: actions/checkout@v4 # 检出代码仓库 + uses: actions/checkout@v4 # 检出代码仓库 - name: Install Node.js uses: actions/setup-node@v4 with: - node-version: 22 # 安装Node.js 22 (这里node环境是能够运行代码的环境) + node-version: 22 # 安装Node.js 22 (这里node环境是能够运行代码的环境) - name: Install Dependencies run: | @@ -33,11 +33,11 @@ jobs: yarn install # 安装项目依赖 - name: Build Electron App for windows - if: matrix.os == 'windows-latest' # 只在Windows上运行 - run: yarn run build:win # 构建Windows版应用 + if: matrix.os == 'windows-latest' # 只在Windows上运行 + run: yarn run build:win # 构建Windows版应用 - name: Build Electron App for macos - if: matrix.os == 'macos-latest' # 只在macOS上运行 + if: matrix.os == 'macos-latest' # 只在macOS上运行 run: | yarn run build:mac @@ -55,9 +55,9 @@ jobs: uses: actions/upload-artifact@v4 with: name: ${{ matrix.os }} - path: dist # 上传构建产物作为工作流artifact + path: dist # 上传构建产物作为工作流artifact - name: release uses: softprops/action-gh-release@v1 with: - files: 'dist/**' # 将dist目录下所有文件添加到release + files: 'dist/**' # 将dist目录下所有文件添加到release diff --git a/.prettierrc.yaml b/.prettierrc.yaml index 6caec94..2ed0c0e 100644 --- a/.prettierrc.yaml +++ b/.prettierrc.yaml @@ -2,4 +2,4 @@ singleQuote: true semi: false printWidth: 100 trailingComma: none -endOfLine: "auto" \ No newline at end of file +endOfLine: 'auto' diff --git a/eslint.config.mjs b/eslint.config.mjs index 68879d6..0bf3787 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -13,7 +13,7 @@ const baseRule = { 'prefer-const': 'off', 'no-labels': 'off', 'node/no-callback-literal': 'off', - 'multiline-ternary': 'off', + 'multiline-ternary': 'off' } const typescriptRule = { ...baseRule, @@ -21,55 +21,59 @@ const typescriptRule = { '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/space-before-function-paren': 'off', '@typescript-eslint/no-non-null-assertion': 'off', - '@typescript-eslint/restrict-template-expressions': [1, { - allowBoolean: true, - allowAny: true, - }], - '@typescript-eslint/restrict-plus-operands': [1, { - allowBoolean: true, - allowAny: true, - }], + '@typescript-eslint/restrict-template-expressions': [ + 1, + { + allowBoolean: true, + allowAny: true + } + ], + '@typescript-eslint/restrict-plus-operands': [ + 1, + { + allowBoolean: true, + allowAny: true + } + ], '@typescript-eslint/no-misused-promises': [ 'error', { checksVoidReturn: { arguments: false, - attributes: false, - }, - }, + attributes: false + } + } ], '@typescript-eslint/naming-convention': 'off', '@typescript-eslint/return-await': 'off', '@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/comma-dangle': 'off', - '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/no-unsafe-argument': 'off' } const vueRule = { ...typescriptRule, 'vue/multi-word-component-names': 'off', 'vue/max-attributes-per-line': 'off', 'vue/singleline-html-element-content-newline': 'off', - 'vue/use-v-on-exact': 'off', + 'vue/use-v-on-exact': 'off' } exports.base = { extends: ['standard'], rules: baseRule, - parser: '@babel/eslint-parser', + parser: '@babel/eslint-parser' } exports.html = { files: ['*.html'], - plugins: ['html'], + plugins: ['html'] } exports.typescript = { files: ['*.ts'], rules: typescriptRule, parser: '@typescript-eslint/parser', - extends: [ - 'standard-with-typescript', - ], + extends: ['standard-with-typescript'] } exports.vue = { @@ -82,7 +86,7 @@ exports.vue = { 'plugin:vue/vue3-recommended', 'plugin:vue-pug/vue3-recommended', // "plugin:vue/strongly-recommended" - 'standard-with-typescript', + 'standard-with-typescript' ], parserOptions: { sourceType: 'module', @@ -91,8 +95,8 @@ exports.vue = { js: '@typescript-eslint/parser', // Script parser for ` + + diff --git a/src/renderer/src/components/Play/FullPlay.vue b/src/renderer/src/components/Play/FullPlay.vue index 6c6c7cb..399f74a 100644 --- a/src/renderer/src/components/Play/FullPlay.vue +++ b/src/renderer/src/components/Play/FullPlay.vue @@ -10,23 +10,20 @@ import { } from '@applemusic-like-lyrics/vue' import type { SongList } from '@renderer/types/audio' import type { LyricLine } from '@applemusic-like-lyrics/core' -import { ref, computed, onMounted, watch, reactive, onBeforeUnmount } from 'vue' +import { ref, computed, onMounted, watch, reactive, onBeforeUnmount, toRaw } from 'vue' import { shouldUseBlackText } from '@renderer/utils/contrastColor' import { ControlAudioStore } from '@renderer/store/ControlAudio' import { Fullscreen1Icon, FullscreenExit1Icon, ChevronDownIcon } from 'tdesign-icons-vue-next' -// 导入歌词请求函数 -import musicService from '@renderer/services/music' // 直接从包路径导入,避免 WebAssembly 导入问题 import { parseYrc, parseLrc, parseTTML } from '@applemusic-like-lyrics/lyric/pkg/amll_lyric.js' - +import _ from 'lodash' import { storeToRefs } from 'pinia' - interface Props { show?: boolean coverImage?: string - songId?: string|null - songInfo: SongList|{songmid:number|null} + songId?: string | null + songInfo: SongList | { songmid: number | null } } const props = withDefaults(defineProps(), { @@ -53,8 +50,6 @@ const toggleFullscreen = () => { onMounted(async () => { // 添加事件监听器检测全屏状态变化 document.addEventListener('fullscreenchange', handleFullscreenChange) - - }) onBeforeUnmount(() => { @@ -88,46 +83,86 @@ const state = reactive({ watch( () => props.songId, async (newId) => { - if (!newId) return + if (!newId || !props.songInfo) return let lyricText = '' let parsedLyrics: LyricLine[] = [] // 创建一个符合 MusicItem 接口的对象,只包含必要的基本属性 try { - // 请求歌词数据,设置yv=true获取逐字歌词 - try { - const res = (await ( - await fetch(`https://amll.bikonoo.com/ncm-lyrics/${newId}.ttml`) - ).text()) as any - if (!res || res.length < 100) throw new Error('ttml 无歌词') - parsedLyrics = parseTTML(res).lines - console.log('搜索到ttml歌词', parsedLyrics) - } catch { - const lyricData = await musicService.request('getLyric', { - id: newId, - yv: true, - lv: true, - tv: true - }) - console.log(lyricData) - // 优先使用逐字歌词(yrc),如果没有则回退到普通歌词(lrc) + // 检查是否为网易云音乐,只有网易云才使用ttml接口 + const isNetease = + props.songInfo && 'source' in props.songInfo && props.songInfo.source === 'wy' + const songinfo: any = _.cloneDeep(toRaw(props.songInfo)) + console.log(songinfo) + if (isNetease) { + // 网易云音乐优先尝试ttml接口 + try { + const res = (await ( + await fetch(`https://amll.bikonoo.com/ncm-lyrics/${newId}.ttml`) + ).text()) as any + if (!res || res.length < 100) throw new Error('ttml 无歌词') + parsedLyrics = parseTTML(res).lines + console.log('搜索到ttml歌词', parsedLyrics) + } catch { + // ttml失败后使用新的歌词API + const lyricData = await window.api.music.requestSdk('getLyric', { + source: 'wy', + songInfo: songinfo + }) + console.log('网易云歌词数据:', lyricData) - if (lyricData.yrc?.lyric) { - lyricText = lyricData.yrc.lyric - parsedLyrics = parseYrc(lyricText) - console.log('使用逐字歌词', parsedLyrics) - } else if (lyricData.lrc?.lyric) { - lyricText = lyricData.lrc.lyric - parsedLyrics = parseLrc(lyricText) - console.log('使用普通歌词', parsedLyrics) - } - if (lyricData.tlyric && lyricData.tlyric.lyric) { - const translatedline = parseLrc(lyricData.tlyric.lyric) - console.log(translatedline) - for (let i = 0; i < parsedLyrics.length; i++) { - parsedLyrics[i].translatedLyric = translatedline[i].words[0].word + if (lyricData.crlyric) { + // 使用逐字歌词 + lyricText = lyricData.crlyric + console.log('网易云逐字歌词', lyricText) + parsedLyrics = parseYrc(lyricText) + console.log('使用网易云逐字歌词', parsedLyrics) + } else if (lyricData.lyric) { + lyricText = lyricData.lyric + parsedLyrics = parseLrc(lyricText) + console.log('使用网易云普通歌词', parsedLyrics) + } + + if (lyricData.tlyric) { + const translatedline = parseLrc(lyricData.tlyric) + console.log('网易云翻译歌词:', translatedline) + for (let i = 0; i < parsedLyrics.length; i++) { + if (translatedline[i] && translatedline[i].words[0]) { + parsedLyrics[i].translatedLyric = translatedline[i].words[0].word + } + } + } + } + } else { + // 其他音乐平台直接使用新的歌词API + const source = props.songInfo && 'source' in props.songInfo ? props.songInfo.source : 'kg' + // 创建一个纯净的对象,避免Vue响应式对象序列化问题 + const cleanSongInfo = JSON.parse(JSON.stringify(toRaw(props.songInfo))) + const lyricData = await window.api.music.requestSdk('getLyric', { + source: source, + songInfo: cleanSongInfo + }) + console.log(`${source}歌词数据:`, lyricData) + + if (lyricData.crlyric) { + // 使用逐字歌词 + lyricText = lyricData.crlyric + parsedLyrics = parseYrc(lyricText) + console.log(`使用${source}逐字歌词`, parsedLyrics) + } else if (lyricData.lyric) { + lyricText = lyricData.lyric + parsedLyrics = parseLrc(lyricText) + console.log(`使用${source}普通歌词`, parsedLyrics) + } + + if (lyricData.tlyric) { + const translatedline = parseLrc(lyricData.tlyric) + console.log(`${source}翻译歌词:`, translatedline) + for (let i = 0; i < parsedLyrics.length; i++) { + if (translatedline[i] && translatedline[i].words[0]) { + parsedLyrics[i].translatedLyric = translatedline[i].words[0].word + } } - console.log('使用翻译歌词', translatedline) } } @@ -212,20 +247,39 @@ watch(