fix(lyric): 点击歌词跳转不再跳到歌曲末尾触发切歌

根因:MusicHook.parseLyricsString 的 lrcTimeArray 直接使用 yrcParser 的
毫秒时间戳,而 seek/nowTime 全链路以秒为单位。合并播放重构(#712)后
'先播放、歌词后到'使 ensureLyricsLoaded 的 API 兜底分支成为在线歌曲
常规路径,点击歌词 seek(毫秒值) 被钳到音频末尾直接触发 ended 切歌,
歌词高亮同步也一并失效。

修复:
- parseLyricsString 时间数组统一换算为秒,与 usePlayerHooks.parseLyrics 一致
- 完整歌词对象(yrc 逐字/翻译)异步送达时强制重新解析,替换先行的兜底纯 lrc

测试:以真实 LRC/YRC 样本驱动真实 yrcParser 验证 7 项断言全过,
含修复前行为回归复现(seek(28910) 钳到末尾触发切歌)与修复后
全行点击不触发切歌、两解析路径单位一致性。
This commit is contained in:
alger
2026-07-05 16:05:03 +08:00
parent 8f1248c959
commit 3742ed5062
+13 -5
View File
@@ -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);
}
}
);