mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-07-15 07:07:31 +08:00
feat: 修复软件切歌慢问题
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
This commit is contained in:
@@ -372,11 +372,9 @@ export const useLyrics = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取歌曲详情
|
* 获取歌曲详情(优化版 - 只获取URL,背景色在播放后异步获取)
|
||||||
*/
|
*/
|
||||||
export const useSongDetail = () => {
|
export const useSongDetail = () => {
|
||||||
const { getSongUrl } = useSongUrl();
|
|
||||||
|
|
||||||
const getSongDetail = async (playMusic: SongResult, requestId?: string) => {
|
const getSongDetail = async (playMusic: SongResult, requestId?: string) => {
|
||||||
// 验证请求
|
// 验证请求
|
||||||
if (requestId && !playbackRequestManager.isRequestValid(requestId)) {
|
if (requestId && !playbackRequestManager.isRequestValid(requestId)) {
|
||||||
@@ -405,19 +403,10 @@ export const useSongDetail = () => {
|
|||||||
playMusic.createdAt = Date.now();
|
playMusic.createdAt = Date.now();
|
||||||
// 半小时后过期
|
// 半小时后过期
|
||||||
playMusic.expiredAt = playMusic.createdAt + 1800000;
|
playMusic.expiredAt = playMusic.createdAt + 1800000;
|
||||||
const { backgroundColor, primaryColor } =
|
|
||||||
playMusic.backgroundColor && playMusic.primaryColor
|
|
||||||
? playMusic
|
|
||||||
: await getImageLinearBackground(getImgUrl(playMusic?.picUrl, '30y30'));
|
|
||||||
|
|
||||||
// 验证请求
|
|
||||||
if (requestId && !playbackRequestManager.isRequestValid(requestId)) {
|
|
||||||
console.log(`[getSongDetail] 背景色获取后请求已失效: ${requestId}`);
|
|
||||||
throw new Error('Request cancelled');
|
|
||||||
}
|
|
||||||
|
|
||||||
playMusic.playLoading = false;
|
playMusic.playLoading = false;
|
||||||
return { ...playMusic, playMusicUrl, backgroundColor, primaryColor } as SongResult;
|
// 返回歌曲信息,背景色和歌词将在播放后异步加载
|
||||||
|
return { ...playMusic, playMusicUrl } as SongResult;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if ((error as Error).message === 'Request cancelled') {
|
if ((error as Error).message === 'Request cancelled') {
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ const loadAndPlayAudio = async (song: SongResult, shouldPlay: boolean): Promise<
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 触发预加载下一首/下下首歌曲
|
* 触发预加载下一首/下下首歌曲(立即触发)
|
||||||
*/
|
*/
|
||||||
const triggerPreload = async (song: SongResult): Promise<void> => {
|
const triggerPreload = async (song: SongResult): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
@@ -125,9 +125,8 @@ const triggerPreload = async (song: SongResult): Promise<void> => {
|
|||||||
(item: SongResult) => item.id === song.id && item.source === song.source
|
(item: SongResult) => item.id === song.id && item.source === song.source
|
||||||
);
|
);
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
setTimeout(() => {
|
// 立即触发预加载,不等待
|
||||||
playlistStore.preloadNextSongs(idx);
|
playlistStore.preloadNextSongs(idx);
|
||||||
}, 3000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -152,7 +151,7 @@ const updateDocumentTitle = (music: SongResult): void => {
|
|||||||
// ==================== 导出函数 ====================
|
// ==================== 导出函数 ====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 核心播放函数
|
* 核心播放函数(优化版)
|
||||||
*
|
*
|
||||||
* @param music 要播放的歌曲
|
* @param music 要播放的歌曲
|
||||||
* @param shouldPlay 是否立即播放(默认 true)
|
* @param shouldPlay 是否立即播放(默认 true)
|
||||||
@@ -193,30 +192,12 @@ export const playTrack = async (
|
|||||||
playerCore.isPlay = shouldPlay;
|
playerCore.isPlay = shouldPlay;
|
||||||
playerCore.userPlayIntent = shouldPlay;
|
playerCore.userPlayIntent = shouldPlay;
|
||||||
|
|
||||||
// 4. 加载元数据(歌词 + 背景色)
|
// 4. 先设置基本歌曲信息(立即显示UI)
|
||||||
try {
|
|
||||||
const { lyrics, backgroundColor, primaryColor } = await loadMetadata(music);
|
|
||||||
|
|
||||||
// 检查 generation
|
|
||||||
if (gen !== generation) {
|
|
||||||
console.log(`[playbackController] gen=${gen} 已过期(加载元数据后),当前 gen=${generation}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
music.lyric = lyrics;
|
|
||||||
music.backgroundColor = backgroundColor;
|
|
||||||
music.primaryColor = primaryColor;
|
|
||||||
} catch (error) {
|
|
||||||
if (gen !== generation) return false;
|
|
||||||
console.error('[playbackController] 加载元数据失败:', error);
|
|
||||||
// 元数据加载失败不阻塞播放,继续执行
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. 歌词已加载,现在设置 playMusic(触发 MusicHook 的歌词 watcher)
|
|
||||||
music.playLoading = true;
|
music.playLoading = true;
|
||||||
playerCore.playMusic = music;
|
playerCore.playMusic = music;
|
||||||
updateDocumentTitle(music);
|
updateDocumentTitle(music);
|
||||||
|
|
||||||
|
// 保存原始歌曲数据
|
||||||
const originalMusic = { ...music };
|
const originalMusic = { ...music };
|
||||||
|
|
||||||
// 5. 添加到播放历史
|
// 5. 添加到播放历史
|
||||||
@@ -233,7 +214,7 @@ export const playTrack = async (
|
|||||||
console.warn('[playbackController] 添加播放历史失败:', e);
|
console.warn('[playbackController] 添加播放历史失败:', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. 获取歌曲详情(解析 URL)
|
// 6. 获取歌曲详情(解析 URL)- 这是必须的,不能跳过
|
||||||
try {
|
try {
|
||||||
const { getSongDetail } = useSongDetail();
|
const { getSongDetail } = useSongDetail();
|
||||||
const updatedPlayMusic = await getSongDetail(originalMusic, requestId);
|
const updatedPlayMusic = await getSongDetail(originalMusic, requestId);
|
||||||
@@ -244,7 +225,6 @@ export const playTrack = async (
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedPlayMusic.lyric = music.lyric;
|
|
||||||
playerCore.playMusic = updatedPlayMusic;
|
playerCore.playMusic = updatedPlayMusic;
|
||||||
playerCore.playMusicUrl = updatedPlayMusic.playMusicUrl as string;
|
playerCore.playMusicUrl = updatedPlayMusic.playMusicUrl as string;
|
||||||
music.playMusicUrl = updatedPlayMusic.playMusicUrl as string;
|
music.playMusicUrl = updatedPlayMusic.playMusicUrl as string;
|
||||||
@@ -259,10 +239,7 @@ export const playTrack = async (
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. 触发预加载下一首(异步,不阻塞)
|
// 7. 加载并播放音频(优先播放)
|
||||||
triggerPreload(playerCore.playMusic);
|
|
||||||
|
|
||||||
// 8. 加载并播放音频
|
|
||||||
try {
|
try {
|
||||||
const success = await loadAndPlayAudio(playerCore.playMusic, shouldPlay);
|
const success = await loadAndPlayAudio(playerCore.playMusic, shouldPlay);
|
||||||
|
|
||||||
@@ -274,18 +251,38 @@ export const playTrack = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
// 9. 播放成功
|
// 9. 播放成功
|
||||||
playerCore.playMusic.playLoading = false;
|
playerCore.playMusic.playLoading = false;
|
||||||
playerCore.playMusic.isFirstPlay = false;
|
playerCore.playMusic.isFirstPlay = false;
|
||||||
playbackRequestManager.completeRequest(requestId);
|
playbackRequestManager.completeRequest(requestId);
|
||||||
console.log(`[playbackController] gen=${gen} 播放成功: ${music.name}`);
|
console.log(`[playbackController] gen=${gen} 播放成功: ${music.name}`);
|
||||||
|
|
||||||
|
// 10. 触发预加载下一首(立即触发,不等待)
|
||||||
|
triggerPreload(playerCore.playMusic);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
playbackRequestManager.failRequest(requestId);
|
playbackRequestManager.failRequest(requestId);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 10. 播放失败
|
// 11. 播放失败
|
||||||
if (gen !== generation) {
|
if (gen !== generation) {
|
||||||
console.log(`[playbackController] gen=${gen} 已过期(播放异常),静默返回`);
|
console.log(`[playbackController] gen=${gen} 已过期(播放异常),静默返回`);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -104,12 +104,12 @@ class PreloadService {
|
|||||||
testAudio.src = url;
|
testAudio.src = url;
|
||||||
testAudio.load();
|
testAudio.load();
|
||||||
|
|
||||||
// 5秒超时
|
// 3秒超时(优化预加载速度)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
cleanup();
|
cleanup();
|
||||||
// 超时不算失败,URL 可能是可用的只是服务器慢
|
// 超时不算失败,URL 可能是可用的只是服务器慢
|
||||||
resolve(url);
|
resolve(url);
|
||||||
}, 5000);
|
}, 3000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export const usePlaylistStore = defineStore(
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 智能预加载下一首歌曲
|
* 智能预加载下一首歌曲(立即执行,不等待)
|
||||||
*/
|
*/
|
||||||
const preloadNextSongs = (currentIndex: number) => {
|
const preloadNextSongs = (currentIndex: number) => {
|
||||||
if (playList.value.length <= 1) return;
|
if (playList.value.length <= 1) return;
|
||||||
@@ -171,9 +171,11 @@ export const usePlaylistStore = defineStore(
|
|||||||
nextIndex = (currentIndex + 1) % playList.value.length;
|
nextIndex = (currentIndex + 1) % playList.value.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 预加载下一首和下下首(最多2首)
|
||||||
const endIndex = Math.min(nextIndex + 2, playList.value.length);
|
const endIndex = Math.min(nextIndex + 2, playList.value.length);
|
||||||
|
|
||||||
if (nextIndex < playList.value.length) {
|
if (nextIndex < playList.value.length) {
|
||||||
|
// 立即执行预加载
|
||||||
fetchSongs(nextIndex, endIndex);
|
fetchSongs(nextIndex, endIndex);
|
||||||
|
|
||||||
// 循环模式且接近列表末尾,预加载列表开头
|
// 循环模式且接近列表末尾,预加载列表开头
|
||||||
@@ -182,9 +184,8 @@ export const usePlaylistStore = defineStore(
|
|||||||
nextIndex + 1 >= playList.value.length &&
|
nextIndex + 1 >= playList.value.length &&
|
||||||
playList.value.length > 2
|
playList.value.length > 2
|
||||||
) {
|
) {
|
||||||
setTimeout(() => {
|
// 立即预加载,不等待
|
||||||
fetchSongs(0, 1);
|
fetchSongs(0, 1);
|
||||||
}, 1000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -682,7 +683,8 @@ export const usePlaylistStore = defineStore(
|
|||||||
if (success) {
|
if (success) {
|
||||||
playerCore.isPlay = true;
|
playerCore.isPlay = true;
|
||||||
if (songIndex !== -1) {
|
if (songIndex !== -1) {
|
||||||
setTimeout(() => preloadNextSongs(playListIndex.value), 3000);
|
// 立即预加载,不等待
|
||||||
|
preloadNextSongs(playListIndex.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
|
|||||||
Reference in New Issue
Block a user