fix(lyric): 重启后桌面歌词显示无歌词

playerCore.playMusic 整体替换 (id 不变) 时, lyric watcher 的响应式追踪不可靠
点击播放走 playTrack 流程也未必能可靠触发解析

- 提取 ensureLyricsLoaded 到 module 级别, 直接读 playMusic.value 解析
- openLyric 入口主动调用 (重启首次打开桌面歌词场景)
- onLyricWindowReady 兜底 (窗口异步就绪时 lyric 字段可能刚到位)
- audioService.on('play') 兜底 (重启后首次点击播放场景)
- 在线歌曲 lyric 字段缺失时, 主动调 getMusicLrc API 兜底
This commit is contained in:
alger
2026-04-19 15:56:00 +08:00
parent 7282e876f4
commit 97220761cf
+66 -65
View File
@@ -149,37 +149,21 @@ const parseLyricsString = async (
} }
}; };
// 设置音乐相关的监听器 // 解析当前 playMusic.lyric 写入 lrcArray, 供 watcher / openLyric / onLyricWindowReady 共用
const setupMusicWatchers = () => { const ensureLyricsLoaded = async (force = false) => {
const store = getPlayerStore(); const songId = playMusic.value?.id;
if (!songId) {
// 监听 playerStore.playMusic 的变化以更新歌词数据
watch(
() => store.playMusic.id,
async (newId, oldId) => {
// 如果没有歌曲ID,清空歌词
if (!newId) {
lrcArray.value = []; lrcArray.value = [];
lrcTimeArray.value = []; lrcTimeArray.value = [];
nowIndex.value = 0; nowIndex.value = 0;
return; return;
} }
if (!force && lrcArray.value.length > 0) return;
// 避免相同ID的重复执行(但允许初始化时执行) await nextTick();
if (newId === oldId && lrcArray.value.length > 0) return;
// 歌曲切换时重置歌词索引
if (newId !== oldId) {
nowIndex.value = 0;
}
await nextTick(async () => {
console.log('歌曲切换,更新歌词数据');
// 检查是否有原始歌词字符串需要解析
const lyricData = playMusic.value.lyric; const lyricData = playMusic.value.lyric;
if (lyricData && typeof lyricData === 'string') { if (lyricData && typeof lyricData === 'string') {
// 如果歌词是字符串格式,使用新的解析器
const { const {
lrcArray: parsedLrcArray, lrcArray: parsedLrcArray,
lrcTimeArray: parsedTimeArray, lrcTimeArray: parsedTimeArray,
@@ -188,12 +172,10 @@ const setupMusicWatchers = () => {
lrcArray.value = parsedLrcArray; lrcArray.value = parsedLrcArray;
lrcTimeArray.value = parsedTimeArray; lrcTimeArray.value = parsedTimeArray;
// 更新歌曲的歌词数据结构
if (playMusic.value.lyric && typeof playMusic.value.lyric === 'object') { if (playMusic.value.lyric && typeof playMusic.value.lyric === 'object') {
playMusic.value.lyric.hasWordByWord = hasWordByWord; playMusic.value.lyric.hasWordByWord = hasWordByWord;
} }
} else if (lyricData && typeof lyricData === 'object' && lyricData.lrcArray?.length > 0) { } else if (lyricData && typeof lyricData === 'object' && lyricData.lrcArray?.length > 0) {
// 使用现有的歌词数据结构
const rawLrc = lyricData.lrcArray || []; const rawLrc = lyricData.lrcArray || [];
lrcTimeArray.value = lyricData.lrcTimeArray || []; lrcTimeArray.value = lyricData.lrcTimeArray || [];
@@ -205,11 +187,8 @@ const setupMusicWatchers = () => {
lrcArray.value = rawLrc as any; lrcArray.value = rawLrc as any;
} }
} else if (isElectron && playMusic.value.playMusicUrl?.startsWith('local:///')) { } else if (isElectron && playMusic.value.playMusicUrl?.startsWith('local:///')) {
// 从下载/本地文件的 ID3/FLAC 元数据中提取嵌入歌词
try { try {
let filePath = decodeURIComponent( let filePath = decodeURIComponent(playMusic.value.playMusicUrl.replace('local:///', ''));
playMusic.value.playMusicUrl.replace('local:///', '')
);
// 处理 Windows 路径:/C:/... → C:/... // 处理 Windows 路径:/C:/... → C:/...
if (/^\/[a-zA-Z]:\//.test(filePath)) { if (/^\/[a-zA-Z]:\//.test(filePath)) {
filePath = filePath.slice(1); filePath = filePath.slice(1);
@@ -226,16 +205,14 @@ const setupMusicWatchers = () => {
if (playMusic.value.lyric && typeof playMusic.value.lyric === 'object') { if (playMusic.value.lyric && typeof playMusic.value.lyric === 'object') {
(playMusic.value.lyric as any).hasWordByWord = hasWordByWord; (playMusic.value.lyric as any).hasWordByWord = hasWordByWord;
} }
} else { } else if (typeof songId === 'number') {
// 无嵌入歌词 — 若有数字 ID,尝试 API 兜底
const songId = playMusic.value.id;
if (songId && typeof songId === 'number') {
try { try {
const { getMusicLrc } = await import('@/api/music'); const { getMusicLrc } = await import('@/api/music');
const res = await getMusicLrc(songId); const res = await getMusicLrc(songId);
if (res?.data?.lrc?.lyric) { if (res?.data?.lrc?.lyric) {
const { lrcArray: apiLrcArray, lrcTimeArray: apiTimeArray } = const { lrcArray: apiLrcArray, lrcTimeArray: apiTimeArray } = await parseLyricsString(
await parseLyricsString(res.data.lrc.lyric); res.data.lrc.lyric
);
lrcArray.value = apiLrcArray; lrcArray.value = apiLrcArray;
lrcTimeArray.value = apiTimeArray; lrcTimeArray.value = apiTimeArray;
} }
@@ -243,30 +220,54 @@ const setupMusicWatchers = () => {
console.error('API lyrics fallback failed:', apiErr); console.error('API lyrics fallback failed:', apiErr);
} }
} }
}
} catch (err) { } catch (err) {
console.error('Failed to extract embedded lyrics:', err); console.error('Failed to extract embedded lyrics:', err);
} }
} else { } else if (typeof songId === 'number') {
// 无歌词数据 // 在线歌曲但 lyric 字段尚未加载, 主动调 API 兜底
lrcArray.value = []; try {
lrcTimeArray.value = []; const { getMusicLrc } = await import('@/api/music');
const res = await getMusicLrc(songId);
if (res?.data?.lrc?.lyric) {
const { lrcArray: apiLrcArray, lrcTimeArray: apiTimeArray } = await parseLyricsString(
res.data.lrc.lyric
);
lrcArray.value = apiLrcArray;
lrcTimeArray.value = apiTimeArray;
}
} catch (apiErr) {
console.error('API lyrics fallback failed:', apiErr);
}
} }
// 当歌词数据更新时,如果歌词窗口打开,则发送数据
if (isElectron && isLyricWindowOpen.value) {
console.log('歌词窗口已打开,同步最新歌词数据');
// 不管歌词数组是否为空,都发送最新数据
sendLyricToWin();
// 再次延迟发送,确保歌词窗口已完全加载 if (isElectron && isLyricWindowOpen.value) {
setTimeout(() => {
sendLyricToWin(); sendLyricToWin();
}, 500); setTimeout(() => sendLyricToWin(), 500);
} }
}); };
const setupMusicWatchers = () => {
const store = getPlayerStore();
// 切歌时 id 变化, 强制重新解析
watch(
() => store.playMusic.id,
async (newId, oldId) => {
if (newId !== oldId) nowIndex.value = 0;
await ensureLyricsLoaded(true);
}, },
{ immediate: true } { immediate: true }
); );
// 同一首歌但 lyric 字段后到 (重启 + autoPlay 关闭场景)
watch(
() => playMusic.value?.lyric,
() => {
if (lrcArray.value.length === 0 && playMusic.value?.id) {
ensureLyricsLoaded();
}
}
);
}; };
const setupAudioListeners = () => { const setupAudioListeners = () => {
@@ -486,7 +487,10 @@ const setupAudioListeners = () => {
if (isElectron) { if (isElectron) {
window.api.sendSong(cloneDeep(getPlayerStore().playMusic)); window.api.sendSong(cloneDeep(getPlayerStore().playMusic));
} }
// 启动进度更新 // 兜底: 重启后首次点播放时 lrcArray 仍为空则主动加载
if (lrcArray.value.length === 0 && playMusic.value?.id) {
ensureLyricsLoaded();
}
startProgressInterval(); startProgressInterval();
}); });
@@ -893,28 +897,20 @@ const stopLyricSync = () => {
} }
}; };
// 修改openLyric函数,添加定时同步 export const openLyric = async () => {
export const openLyric = () => {
if (!isElectron) return; if (!isElectron) return;
// 检查是否有播放中的歌曲
if (!playMusic.value || !playMusic.value.id) { if (!playMusic.value || !playMusic.value.id) {
console.log('没有正在播放的歌曲,无法打开歌词窗口'); console.log('没有正在播放的歌曲,无法打开歌词窗口');
return; return;
} }
console.log('Opening lyric window with current song:', playMusic.value?.name);
isLyricWindowOpen.value = !isLyricWindowOpen.value; isLyricWindowOpen.value = !isLyricWindowOpen.value;
if (isLyricWindowOpen.value) { if (isLyricWindowOpen.value) {
// 立即打开窗口
window.api.openLyric(); window.api.openLyric();
// 确保有歌词数据,如果没有,则使用默认的"无歌词"提示 // 先发"加载中"占位, 防止窗口启动期间显示"无歌词"
if (!lrcArray.value || lrcArray.value.length === 0) { if (!lrcArray.value || lrcArray.value.length === 0) {
// 如果当前播放的歌曲有ID但没有歌词,则尝试加载歌词
console.log('尝试加载歌词数据...');
// 发送默认的"无歌词"数据
const emptyLyricData = { const emptyLyricData = {
type: 'empty', type: 'empty',
nowIndex: 0, nowIndex: 0,
@@ -928,12 +924,15 @@ export const openLyric = () => {
playMusic: playMusic.value playMusic: playMusic.value
}; };
window.api.sendLyric(JSON.stringify(emptyLyricData)); window.api.sendLyric(JSON.stringify(emptyLyricData));
// 关键: 主动加载歌词, 不依赖 watcher
// (重启场景下 playerCore.playMusic 整体替换可能未触发 lyric watcher)
await ensureLyricsLoaded(true);
} else { } else {
// 发送完整歌词数据
sendLyricToWin(); sendLyricToWin();
} }
// 延迟重发一次,以防窗口加载 // 延迟重发, 防窗口加载慢丢消息
setTimeout(() => { setTimeout(() => {
if (isLyricWindowOpen.value) { if (isLyricWindowOpen.value) {
sendLyricToWin(); sendLyricToWin();
@@ -1055,11 +1054,13 @@ export const initAudioListeners = async () => {
window.api.onLyricWindowClosed(() => { window.api.onLyricWindowClosed(() => {
isLyricWindowOpen.value = false; isLyricWindowOpen.value = false;
}); });
// 歌词窗口 Vue 加载完成后,发送完整歌词数据 window.api.onLyricWindowReady(async () => {
window.api.onLyricWindowReady(() => { if (!isLyricWindowOpen.value) return;
if (isLyricWindowOpen.value) { // 窗口加载完成时再兜底加载一次, 防止 openLyric 阶段 lyric 字段尚未到位
sendLyricToWin(); if (lrcArray.value.length === 0 && playMusic.value?.id) {
await ensureLyricsLoaded(true);
} }
sendLyricToWin();
}); });
} }