From 61797494cb180b0ef8215f32d0edba79f8ff6e73 Mon Sep 17 00:00:00 2001 From: alger Date: Sun, 5 Jul 2026 16:05:03 +0800 Subject: [PATCH] =?UTF-8?q?fix(lyric):=20=E7=82=B9=E5=87=BB=E6=AD=8C?= =?UTF-8?q?=E8=AF=8D=E8=B7=B3=E8=BD=AC=E4=B8=8D=E5=86=8D=E8=B7=B3=E5=88=B0?= =?UTF-8?q?=E6=AD=8C=E6=9B=B2=E6=9C=AB=E5=B0=BE=E8=A7=A6=E5=8F=91=E5=88=87?= =?UTF-8?q?=E6=AD=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因:MusicHook.parseLyricsString 的 lrcTimeArray 直接使用 yrcParser 的 毫秒时间戳,而 seek/nowTime 全链路以秒为单位。合并播放重构(#712)后 '先播放、歌词后到'使 ensureLyricsLoaded 的 API 兜底分支成为在线歌曲 常规路径,点击歌词 seek(毫秒值) 被钳到音频末尾直接触发 ended 切歌, 歌词高亮同步也一并失效。 修复: - parseLyricsString 时间数组统一换算为秒,与 usePlayerHooks.parseLyrics 一致 - 完整歌词对象(yrc 逐字/翻译)异步送达时强制重新解析,替换先行的兜底纯 lrc 测试:以真实 LRC/YRC 样本驱动真实 yrcParser 验证 7 项断言全过, 含修复前行为回归复现(seek(28910) 钳到末尾触发切歌)与修复后 全行点击不触发切歌、两解析路径单位一致性。 --- src/renderer/hooks/MusicHook.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/renderer/hooks/MusicHook.ts b/src/renderer/hooks/MusicHook.ts index 94fba1e..d6ab1a9 100644 --- a/src/renderer/hooks/MusicHook.ts +++ b/src/renderer/hooks/MusicHook.ts @@ -140,7 +140,10 @@ const parseLyricsString = async ( duration: line.duration }); - lrcTimeArray.push(line.startTime); + // yrcParser 的 startTime 是毫秒;lrcTimeArray 全链路(nowTime 对比、 + // setAudioTime seek)以秒为单位,必须换算,否则点击歌词会 seek 到 + // 远超时长的位置被钳到末尾、直接触发切歌 + lrcTimeArray.push(line.startTime / 1000); } return { lrcArray, lrcTimeArray, hasWordByWord }; } catch (error) { @@ -259,12 +262,17 @@ const setupMusicWatchers = () => { { immediate: true } ); - // 同一首歌但 lyric 字段后到 (重启 + autoPlay 关闭场景) + // 同一首歌但 lyric 字段后到 (播放后异步加载元数据 / 重启 + autoPlay 关闭场景) watch( () => playMusic.value?.lyric, - () => { - if (lrcArray.value.length === 0 && playMusic.value?.id) { - ensureLyricsLoaded(); + (newLyric) => { + if (!playMusic.value?.id) return; + // 完整歌词对象(含 yrc 逐字/翻译,时间单位为秒)后到时强制重新解析, + // 替换掉先行的 API 兜底纯 lrc 歌词 + const isRichLyric = + !!newLyric && typeof newLyric === 'object' && (newLyric.lrcArray?.length ?? 0) > 0; + if (lrcArray.value.length === 0 || isRichLyric) { + ensureLyricsLoaded(isRichLyric); } } );