diff --git a/components.d.ts b/components.d.ts index cfe17c3..11a6d09 100644 --- a/components.d.ts +++ b/components.d.ts @@ -61,8 +61,11 @@ declare module 'vue' { NDrawerContent: typeof import('naive-ui')['NDrawerContent'] NDropdown: typeof import('naive-ui')['NDropdown'] NDynamicTags: typeof import('naive-ui')['NDynamicTags'] + NEllipsis: typeof import('naive-ui')['NEllipsis'] NEmpty: typeof import('naive-ui')['NEmpty'] NFlex: typeof import('naive-ui')['NFlex'] + NFloatButton: typeof import('naive-ui')['NFloatButton'] + NFloatButtonGroup: typeof import('naive-ui')['NFloatButtonGroup'] NForm: typeof import('naive-ui')['NForm'] NFormItem: typeof import('naive-ui')['NFormItem'] NFormItemGi: typeof import('naive-ui')['NFormItemGi'] @@ -70,6 +73,7 @@ declare module 'vue' { NGlobalStyle: typeof import('naive-ui')['NGlobalStyle'] NGrid: typeof import('naive-ui')['NGrid'] NH1: typeof import('naive-ui')['NH1'] + NH2: typeof import('naive-ui')['NH2'] NH3: typeof import('naive-ui')['NH3'] NIcon: typeof import('naive-ui')['NIcon'] NImage: typeof import('naive-ui')['NImage'] @@ -100,6 +104,7 @@ declare module 'vue' { NSelect: typeof import('naive-ui')['NSelect'] NSkeleton: typeof import('naive-ui')['NSkeleton'] NSlider: typeof import('naive-ui')['NSlider'] + NSpin: typeof import('naive-ui')['NSpin'] NSwitch: typeof import('naive-ui')['NSwitch'] NTabPane: typeof import('naive-ui')['NTabPane'] NTabs: typeof import('naive-ui')['NTabs'] diff --git a/package.json b/package.json index d8b3904..62fe5e7 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "@electron-toolkit/utils": "^4.0.0", "@imsyy/color-utils": "^1.0.2", "@material/material-color-utilities": "^0.3.0", - "@neteasecloudmusicapienhanced/api": "^4.29.15", + "@neteasecloudmusicapienhanced/api": "^4.29.16", "@pixi/app": "^7.4.3", "@pixi/core": "^7.4.3", "@pixi/display": "^7.4.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index da316ff..902225c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,8 +34,8 @@ importers: specifier: ^0.3.0 version: 0.3.0 '@neteasecloudmusicapienhanced/api': - specifier: ^4.29.15 - version: 4.29.15 + specifier: ^4.29.16 + version: 4.29.16 '@pixi/app': specifier: ^7.4.3 version: 7.4.3(@pixi/core@7.4.3)(@pixi/display@7.4.3(@pixi/core@7.4.3)) @@ -967,8 +967,8 @@ packages: '@material/material-color-utilities@0.3.0': resolution: {integrity: sha512-ztmtTd6xwnuh2/xu+Vb01btgV8SQWYCaK56CkRK8gEkWe5TuDyBcYJ0wgkMRn+2VcE9KUmhvkz+N9GHrqw/C0g==} - '@neteasecloudmusicapienhanced/api@4.29.15': - resolution: {integrity: sha512-/dw0ON90NDXJnULEyKzGEvKjjkJyHYb9fJgKa1dxsN5AIN/tXNAu4AV2vMJcPGfA/NV7iRnLDEPS8ocJCbCU2w==} + '@neteasecloudmusicapienhanced/api@4.29.16': + resolution: {integrity: sha512-ml6cTErjJ/fn+E0wvlNGGoW74rDj3mfogO85xySuTCuX/Mdhuayv+pPtoSm5YDM6j26hHmS8+rV2ma4o16pUiw==} engines: {node: '>=12'} hasBin: true @@ -5331,7 +5331,7 @@ snapshots: '@material/material-color-utilities@0.3.0': {} - '@neteasecloudmusicapienhanced/api@4.29.15': + '@neteasecloudmusicapienhanced/api@4.29.16': dependencies: '@unblockneteasemusic/server': 0.28.0 axios: 1.13.2 diff --git a/src/components/Player/MainLyric.vue b/src/components/Player/MainLyric.vue index 63cc6a1..4d64ce8 100644 --- a/src/components/Player/MainLyric.vue +++ b/src/components/Player/MainLyric.vue @@ -58,7 +58,10 @@ ]" :style="{ filter: settingStore.lyricsBlur - ? `blur(${Math.min(Math.abs(statusStore.lyricIndex - index) * 1.8, 10)}px)` + ? (playSeek >= item.time && playSeek < item.endTime) || + statusStore.lyricIndex === index + ? 'blur(0)' + : `blur(${Math.min(Math.abs(statusStore.lyricIndex - index) * 1.8, 10)}px)` : 'blur(0)', }" @click="jumpSeek(item.time)" diff --git a/src/components/Setting/LyricsSetting.vue b/src/components/Setting/LyricsSetting.vue index 6a4e123..de0d045 100644 --- a/src/components/Setting/LyricsSetting.vue +++ b/src/components/Setting/LyricsSetting.vue @@ -287,7 +287,10 @@
- Apple Music-like Lyrics + + Apple Music-like Lyrics + Beta +
使用 Apple Music-like Lyrics @@ -318,7 +321,10 @@
- 桌面歌词 + + 桌面歌词 + Beta +
开启桌面歌词 diff --git a/src/components/Setting/MainSetting.vue b/src/components/Setting/MainSetting.vue index 4baef95..fec84a4 100644 --- a/src/components/Setting/MainSetting.vue +++ b/src/components/Setting/MainSetting.vue @@ -195,6 +195,12 @@ const toGithub = () => { margin-bottom: 0; } } + .n-h { + display: inline-flex; + flex-direction: row; + align-items: center; + gap: 8px; + } .n-collapse-transition { margin-bottom: 12px; &:last-child { diff --git a/src/utils/player-utils/lyric.ts b/src/utils/player-utils/lyric.ts index 945ef45..6f5d529 100644 --- a/src/utils/player-utils/lyric.ts +++ b/src/utils/player-utils/lyric.ts @@ -26,61 +26,78 @@ export const getLyricData = async (id: number) => { try { // 检测本地歌词覆盖 const getLyric = getLyricFun(settingStore.localLyricPath, id); - // 先加载 LRC,不阻塞到 TTML 完成 + // 并发请求:如果 TTML 先到并且有效,则直接采用 TTML,不再等待或覆盖为 LRC const lrcPromise = getLyric("lrc", songLyric); const ttmlPromise = settingStore.enableTTMLLyric ? getLyric("ttml", songLyricTTML) : null; - const { lyric: lyricRes, isLocal: lyricLocal } = await lrcPromise; - parsedLyricsData(lyricRes, lyricLocal && !settingStore.enableExcludeLocalLyrics); - // LRC 到达后即可认为加载完成 - statusStore.lyricLoading = false; + let settled = false; // 是否已采用某一种歌词并结束加载状态 + let ttmlAdopted = false; // 是否已采用 TTML - // TTML 并行加载,完成后增量更新,不阻塞整体流程 - if (ttmlPromise) { - statusStore.usingTTMLLyric = false; - void ttmlPromise - .then(({ lyric: ttmlContent, isLocal: ttmlLocal }) => { - if (!ttmlContent) { - statusStore.usingTTMLLyric = false; - return; - } - const parsedResult = parseTTML(ttmlContent); - if (!parsedResult?.lines?.length) { - statusStore.usingTTMLLyric = false; - return; - } - const skipExcludeLocal = ttmlLocal && !settingStore.enableExcludeLocalLyrics; - const skipExcludeTTML = !settingStore.enableExcludeTTML; - const skipExclude = skipExcludeLocal || skipExcludeTTML; - const ttmlLyric = parseTTMLToAMLL(parsedResult, skipExclude); - const ttmlYrcLyric = parseTTMLToYrc(parsedResult, skipExclude); - console.log("TTML lyrics:", ttmlLyric, ttmlYrcLyric); - // 合并数据 - const updates: Partial<{ yrcAMData: LyricLine[]; yrcData: LyricType[] }> = {}; - if (ttmlLyric?.length) { - updates.yrcAMData = ttmlLyric; - console.log("✅ TTML AMLL lyrics success"); - } - if (ttmlYrcLyric?.length) { - updates.yrcData = ttmlYrcLyric; - console.log("✅ TTML Yrc lyrics success"); - } - if (Object.keys(updates).length) { - musicStore.setSongLyric(updates); - statusStore.usingTTMLLyric = true; - } else { - statusStore.usingTTMLLyric = false; - } - }) - .catch((err) => { - console.error("❌ Error loading TTML lyrics:", err); + const adoptTTML = async () => { + if (!ttmlPromise) { + statusStore.usingTTMLLyric = false; + return; + } + try { + const { lyric: ttmlContent, isLocal: ttmlLocal } = await ttmlPromise; + if (!ttmlContent) { statusStore.usingTTMLLyric = false; - }); - } else { - statusStore.usingTTMLLyric = false; - } + return; + } + const parsedResult = parseTTML(ttmlContent); + if (!parsedResult?.lines?.length) { + statusStore.usingTTMLLyric = false; + return; + } + const skipExcludeLocal = ttmlLocal && !settingStore.enableExcludeLocalLyrics; + const skipExcludeTTML = !settingStore.enableExcludeTTML; + const skipExclude = skipExcludeLocal || skipExcludeTTML; + const ttmlLyric = parseTTMLToAMLL(parsedResult, skipExclude); + const ttmlYrcLyric = parseTTMLToYrc(parsedResult, skipExclude); - console.log("Lyrics: ", musicStore.songLyric); + const updates: Partial<{ yrcAMData: LyricLine[]; yrcData: LyricType[] }> = {}; + if (ttmlLyric?.length) updates.yrcAMData = ttmlLyric; + if (ttmlYrcLyric?.length) updates.yrcData = ttmlYrcLyric; + + if (Object.keys(updates).length) { + musicStore.setSongLyric(updates); + statusStore.usingTTMLLyric = true; + ttmlAdopted = true; + if (!settled) { + statusStore.lyricLoading = false; + settled = true; + } + console.log("✅ TTML lyrics adopted (prefer TTML)"); + } else { + statusStore.usingTTMLLyric = false; + } + } catch (err) { + console.error("❌ Error loading TTML lyrics:", err); + statusStore.usingTTMLLyric = false; + } + }; + + const adoptLRC = async () => { + try { + const { lyric: lyricRes, isLocal: lyricLocal } = await lrcPromise; + // 如果 TTML 已采用,则忽略 LRC + if (ttmlAdopted) return; + parsedLyricsData(lyricRes, lyricLocal && !settingStore.enableExcludeLocalLyrics); + statusStore.usingTTMLLyric = false; + if (!settled) { + statusStore.lyricLoading = false; + settled = true; + } + console.log("✅ LRC lyrics adopted"); + } catch (err) { + console.error("❌ Error loading LRC lyrics:", err); + if (!settled) statusStore.lyricLoading = false; + } + }; + + // 启动并发任务:TTML 与 LRC 同时进行,哪个先成功就先用 + void adoptLRC(); + void adoptTTML(); } catch (error) { console.error("❌ Error loading lyrics:", error); statusStore.usingTTMLLyric = false; diff --git a/src/utils/request.ts b/src/utils/request.ts index b42e493..1e5ead3 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -3,7 +3,7 @@ import { isDev, isElectron } from "./env"; import { useSettingStore } from "@/stores"; import { getCookie } from "./cookie"; import { isLogin } from "./auth"; -import axiosRetry from "axios-retry"; +// import axiosRetry from "axios-retry"; // 全局地址 const baseURL: string = String(isDev ? "/api/netease" : import.meta.env["VITE_API_URL"]); @@ -18,10 +18,10 @@ const server: AxiosInstance = axios.create({ }); // 请求重试 -axiosRetry(server, { - // 重试次数 - retries: 3, -}); +// axiosRetry(server, { +// // 重试次数 +// retries: 3, +// }); // 请求拦截器 server.interceptors.request.use( diff --git a/src/views/DesktopLyric/index.vue b/src/views/DesktopLyric/index.vue index 2c596a3..5b0341d 100644 --- a/src/views/DesktopLyric/index.vue +++ b/src/views/DesktopLyric/index.vue @@ -118,7 +118,7 @@ @@ -570,9 +579,10 @@ onBeforeUnmount(() => { border-radius: 12px; overflow: hidden; transition: background-color 0.3s; - cursor: move; + cursor: default; .header { margin-bottom: 12px; + cursor: default; // 子内容三等分grid display: grid; grid-template-columns: 1fr 1fr 1fr; @@ -752,6 +762,9 @@ onBeforeUnmount(() => { } } } + .lyric-container { + cursor: move; + } }