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(