From 968116ce18d94dda175c59b1710899b95170edb1 Mon Sep 17 00:00:00 2001 From: alger Date: Sun, 5 Jul 2026 16:29:58 +0800 Subject: [PATCH] =?UTF-8?q?fix(player):=20=E5=88=87=E6=AD=8C=E6=97=B6?= =?UTF-8?q?=E5=B0=81=E9=9D=A2/=E6=AD=8C=E8=AF=8D/=E8=83=8C=E6=99=AF?= =?UTF-8?q?=E8=89=B2=E4=B8=8E=E9=9F=B3=E9=A2=91=E5=90=8C=E6=AD=A5=E5=B0=B1?= =?UTF-8?q?=E7=BB=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复 #712 重构引入的体验回退:全屏页点下一首会'先响歌、后换脸'—— 歌词/背景色在播放成功后才开始加载。 - playTrack 的元数据加载(歌词+背景色)提前到与取 URL/加载音频并行: 不阻塞出声,视觉信息通常在起播前就绪;playMusic 被 getSongDetail 替换后重新应用已到达的元数据,避免丢失;generation 拦截过期请求 - 预加载下一首时顺带预热背景色(歌词已有预热),切歌时元数据全部 缓存命中、立即应用 测试:时序状态机模拟 3 场景全过(元数据先到不丢失/后到补上/快速 切歌不污染),typecheck 与构建通过。 --- src/renderer/services/playbackController.ts | 44 ++++++++++++++------- src/renderer/store/modules/playlist.ts | 15 +++++++ 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/renderer/services/playbackController.ts b/src/renderer/services/playbackController.ts index 9cfd4fc..5739cdc 100644 --- a/src/renderer/services/playbackController.ts +++ b/src/renderer/services/playbackController.ts @@ -200,6 +200,32 @@ export const playTrack = async ( // 保存原始歌曲数据 const originalMusic = { ...music }; + // 4.5 立即并行加载元数据(歌词 + 背景色),与取 URL/加载音频同时进行: + // 不阻塞出声,但让全屏页的封面/歌词/背景色尽量在音频起播前就绪, + // 避免"先响歌、后换脸"的割裂感。预加载过的歌曲两者都是缓存命中,立即应用 + let loadedMetadata: { + lyrics: SongResult['lyric']; + backgroundColor: string; + primaryColor: string; + } | null = null; + const applyLoadedMetadata = () => { + if (!loadedMetadata || gen !== generation) return; + playerCore.playMusic.lyric = loadedMetadata.lyrics; + playerCore.playMusic.backgroundColor = loadedMetadata.backgroundColor; + playerCore.playMusic.primaryColor = loadedMetadata.primaryColor; + // 触发 watcher 更新 + playerCore.playMusic = { ...playerCore.playMusic }; + }; + loadMetadata(originalMusic) + .then((result) => { + loadedMetadata = result; + applyLoadedMetadata(); + }) + .catch((error) => { + // 元数据加载失败不阻塞播放 + console.warn('[playbackController] 元数据加载失败:', error); + }); + // 5. 添加到播放历史 try { const playHistoryStore = await getPlayHistoryStore(); @@ -228,6 +254,8 @@ export const playTrack = async ( playerCore.playMusic = updatedPlayMusic; playerCore.playMusicUrl = updatedPlayMusic.playMusicUrl as string; music.playMusicUrl = updatedPlayMusic.playMusicUrl as string; + // playMusic 被替换为新对象,若元数据已先行到达则重新应用,避免歌词/背景色丢失 + applyLoadedMetadata(); } catch (error) { if (gen !== generation) return false; console.error('[playbackController] 获取歌曲详情失败:', error); @@ -251,21 +279,7 @@ export const playTrack = async ( } if (success) { - // 8. 播放成功后,异步加载元数据(不阻塞) - loadMetadata(playerCore.playMusic).then(({ lyrics, backgroundColor, primaryColor }) => { - // 检查 generation(确保还是当前歌曲) - if (gen !== generation) { - console.log(`[playbackController] gen=${gen} 已过期,跳过元数据更新`); - return; - } - playerCore.playMusic.lyric = lyrics; - playerCore.playMusic.backgroundColor = backgroundColor; - playerCore.playMusic.primaryColor = primaryColor; - // 触发 watcher 更新 - playerCore.playMusic = { ...playerCore.playMusic }; - }).catch((error) => { - console.warn('[playbackController] 元数据加载失败:', error); - }); + // 8. 元数据已在 4.5 步与播放并行加载,此处无需再处理 // 9. 播放成功 playerCore.playMusic.playLoading = false; diff --git a/src/renderer/store/modules/playlist.ts b/src/renderer/store/modules/playlist.ts index fc4938e..54ed229 100644 --- a/src/renderer/store/modules/playlist.ts +++ b/src/renderer/store/modules/playlist.ts @@ -84,6 +84,21 @@ export const usePlaylistStore = defineStore( } } + // 预热下一首的背景色:切歌时 playTrack 的元数据加载可直接缓存命中, + // 全屏页封面/背景色与音频同步就绪,不再"先响歌、后换背景" + if (nextSong?.picUrl && !(nextSong.backgroundColor && nextSong.primaryColor)) { + try { + const { getImageLinearBackground } = await import('@/utils/linearColor'); + const { backgroundColor, primaryColor } = await getImageLinearBackground( + getImgUrl(nextSong.picUrl, '30y30') + ); + nextSong.backgroundColor = backgroundColor; + nextSong.primaryColor = primaryColor; + } catch (error) { + console.warn('预热背景色失败:', error); + } + } + detailedSongs.forEach((song, index) => { if (song && startIndex + index < playList.value.length) { playList.value[startIndex + index] = song;