mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-28 02:47:22 +08:00
@@ -455,24 +455,8 @@ const setupAudioListeners = () => {
|
|||||||
if (sound.value) {
|
if (sound.value) {
|
||||||
replayMusic();
|
replayMusic();
|
||||||
}
|
}
|
||||||
} else if (getPlayerStore().playMode === 2) {
|
|
||||||
// 随机播放模式
|
|
||||||
|
|
||||||
if (getPlayerStore().playList.length <= 1) {
|
|
||||||
replayMusic();
|
|
||||||
} else {
|
|
||||||
let randomIndex;
|
|
||||||
do {
|
|
||||||
randomIndex = Math.floor(Math.random() * getPlayerStore().playList.length);
|
|
||||||
} while (
|
|
||||||
randomIndex === getPlayerStore().playListIndex &&
|
|
||||||
getPlayerStore().playList.length > 1
|
|
||||||
);
|
|
||||||
getPlayerStore().playListIndex = randomIndex;
|
|
||||||
getPlayerStore().setPlay(getPlayerStore().playList[randomIndex]);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// 列表循环模式
|
// 顺序播放、列表循环、随机播放模式都使用统一的nextPlay方法
|
||||||
getPlayerStore().nextPlay();
|
getPlayerStore().nextPlay();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -414,6 +414,115 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
// 音量状态管理
|
// 音量状态管理
|
||||||
const volume = ref(parseFloat(getLocalStorageItem('volume', '1')));
|
const volume = ref(parseFloat(getLocalStorageItem('volume', '1')));
|
||||||
|
|
||||||
|
// 原始播放列表 - 保存切换到随机模式前的顺序
|
||||||
|
const originalPlayList = ref<SongResult[]>(getLocalStorageItem('originalPlayList', []));
|
||||||
|
|
||||||
|
// 通用洗牌函数 - Fisher-Yates 算法
|
||||||
|
const performShuffle = (list: SongResult[], currentSong?: SongResult): SongResult[] => {
|
||||||
|
if (list.length <= 1) return [...list];
|
||||||
|
|
||||||
|
const result: SongResult[] = [];
|
||||||
|
const remainingSongs = [...list];
|
||||||
|
|
||||||
|
// 如果指定了当前歌曲,先把它放在第一位
|
||||||
|
if (currentSong && currentSong.id) {
|
||||||
|
const currentSongIndex = remainingSongs.findIndex(song => song.id === currentSong.id);
|
||||||
|
if (currentSongIndex !== -1) {
|
||||||
|
// 把当前歌曲放在第一位
|
||||||
|
result.push(remainingSongs.splice(currentSongIndex, 1)[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对剩余歌曲进行洗牌
|
||||||
|
if (remainingSongs.length > 0) {
|
||||||
|
// Fisher-Yates 洗牌算法
|
||||||
|
for (let i = remainingSongs.length - 1; i > 0; i--) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1));
|
||||||
|
[remainingSongs[i], remainingSongs[j]] = [remainingSongs[j], remainingSongs[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 把洗牌后的歌曲添加到结果中
|
||||||
|
result.push(...remainingSongs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 应用随机播放到当前播放列表
|
||||||
|
const shufflePlayList = () => {
|
||||||
|
if (playList.value.length <= 1) return;
|
||||||
|
|
||||||
|
// 保存原始播放列表(如果还没保存)
|
||||||
|
if (originalPlayList.value.length === 0) {
|
||||||
|
originalPlayList.value = [...playList.value];
|
||||||
|
localStorage.setItem('originalPlayList', JSON.stringify(originalPlayList.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentSong = playList.value[playListIndex.value];
|
||||||
|
const shuffledList = performShuffle(playList.value, currentSong);
|
||||||
|
|
||||||
|
// 更新播放列表和索引
|
||||||
|
playList.value = shuffledList;
|
||||||
|
playListIndex.value = 0;
|
||||||
|
localStorage.setItem('playList', JSON.stringify(shuffledList));
|
||||||
|
localStorage.setItem('playListIndex', '0');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 恢复原始播放列表顺序
|
||||||
|
const restoreOriginalOrder = () => {
|
||||||
|
if (originalPlayList.value.length === 0) return;
|
||||||
|
|
||||||
|
const currentSong = playMusic.value;
|
||||||
|
const originalIndex = originalPlayList.value.findIndex(song => song.id === currentSong.id);
|
||||||
|
|
||||||
|
playList.value = [...originalPlayList.value];
|
||||||
|
playListIndex.value = Math.max(0, originalIndex);
|
||||||
|
|
||||||
|
localStorage.setItem('playList', JSON.stringify(playList.value));
|
||||||
|
localStorage.setItem('playListIndex', playListIndex.value.toString());
|
||||||
|
|
||||||
|
// 清空原始播放列表
|
||||||
|
originalPlayList.value = [];
|
||||||
|
localStorage.removeItem('originalPlayList');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 智能预加载下一首歌曲
|
||||||
|
const preloadNextSongs = (currentIndex: number) => {
|
||||||
|
if (playList.value.length <= 1) return;
|
||||||
|
|
||||||
|
// 计算下一首歌曲的索引
|
||||||
|
let nextIndex: number;
|
||||||
|
|
||||||
|
if (playMode.value === 0) {
|
||||||
|
// 顺序播放模式:下一首,如果是最后一首则不预加载
|
||||||
|
if (currentIndex >= playList.value.length - 1) {
|
||||||
|
return; // 顺序播放模式下最后一首不预加载
|
||||||
|
}
|
||||||
|
nextIndex = currentIndex + 1;
|
||||||
|
} else {
|
||||||
|
// 循环播放模式和随机播放模式:都是循环的
|
||||||
|
nextIndex = (currentIndex + 1) % playList.value.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预加载下一首和下下首
|
||||||
|
const endIndex = Math.min(nextIndex + 2, playList.value.length);
|
||||||
|
|
||||||
|
// 如果需要循环到开头,分两次预加载
|
||||||
|
if (nextIndex < playList.value.length) {
|
||||||
|
fetchSongs(playList.value, nextIndex, endIndex);
|
||||||
|
|
||||||
|
// 如果是循环模式且接近列表末尾,也预加载列表开头的歌曲
|
||||||
|
if ((playMode.value === 1 || playMode.value === 2) &&
|
||||||
|
nextIndex + 1 >= playList.value.length &&
|
||||||
|
playList.value.length > 2) {
|
||||||
|
// 预加载列表开头的第一首
|
||||||
|
setTimeout(() => {
|
||||||
|
fetchSongs(playList.value, 0, 1);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 清空播放列表
|
// 清空播放列表
|
||||||
const clearPlayAll = async () => {
|
const clearPlayAll = async () => {
|
||||||
audioService.pause();
|
audioService.pause();
|
||||||
@@ -422,10 +531,12 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
playMusicUrl.value = '';
|
playMusicUrl.value = '';
|
||||||
playList.value = [];
|
playList.value = [];
|
||||||
playListIndex.value = 0;
|
playListIndex.value = 0;
|
||||||
|
originalPlayList.value = [];
|
||||||
localStorage.removeItem('currentPlayMusic');
|
localStorage.removeItem('currentPlayMusic');
|
||||||
localStorage.removeItem('currentPlayMusicUrl');
|
localStorage.removeItem('currentPlayMusicUrl');
|
||||||
localStorage.removeItem('playList');
|
localStorage.removeItem('playList');
|
||||||
localStorage.removeItem('playListIndex');
|
localStorage.removeItem('playListIndex');
|
||||||
|
localStorage.removeItem('originalPlayList');
|
||||||
}, 500);
|
}, 500);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -521,10 +632,11 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
localStorage.setItem('currentPlayMusicUrl', playMusicUrl.value);
|
localStorage.setItem('currentPlayMusicUrl', playMusicUrl.value);
|
||||||
localStorage.setItem('isPlaying', play.value.toString());
|
localStorage.setItem('isPlaying', play.value.toString());
|
||||||
|
|
||||||
// 无论如何都预加载更多歌曲
|
// 预加载下一首歌曲
|
||||||
if (songIndex !== -1) {
|
if (songIndex !== -1) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
fetchSongs(playList.value, songIndex + 1, songIndex + 2);
|
// 使用最新的 playListIndex 而不是 songIndex,确保预加载索引正确
|
||||||
|
preloadNextSongs(playListIndex.value);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
} else {
|
} else {
|
||||||
console.warn('当前歌曲未在播放列表中找到');
|
console.warn('当前歌曲未在播放列表中找到');
|
||||||
@@ -706,12 +818,58 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const setPlayList = (list: SongResult[], keepIndex: boolean = false) => {
|
const setPlayList = (list: SongResult[], keepIndex: boolean = false) => {
|
||||||
// 如果指定保持当前索引,则不重新计算索引
|
if (list.length === 0) {
|
||||||
if (!keepIndex) {
|
playList.value = [];
|
||||||
playListIndex.value = list.findIndex((item) => item.id === playMusic.value.id);
|
playListIndex.value = 0;
|
||||||
|
originalPlayList.value = [];
|
||||||
|
localStorage.setItem('playList', JSON.stringify([]));
|
||||||
|
localStorage.setItem('playListIndex', '0');
|
||||||
|
localStorage.removeItem('originalPlayList');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
playList.value = list;
|
|
||||||
localStorage.setItem('playList', JSON.stringify(list));
|
// 根据当前播放模式处理新的播放列表
|
||||||
|
if (playMode.value === 2) {
|
||||||
|
// 随机模式:保存原始顺序并洗牌
|
||||||
|
console.log('随机模式下设置新播放列表,保存原始顺序并洗牌');
|
||||||
|
|
||||||
|
// 保存原始播放列表
|
||||||
|
originalPlayList.value = [...list];
|
||||||
|
localStorage.setItem('originalPlayList', JSON.stringify(originalPlayList.value));
|
||||||
|
|
||||||
|
// 洗牌新列表,优先保持当前歌曲在第一位
|
||||||
|
const currentSong = playMusic.value;
|
||||||
|
const shuffledList = performShuffle(list, currentSong);
|
||||||
|
|
||||||
|
// 计算新的播放索引
|
||||||
|
if (currentSong && currentSong.id) {
|
||||||
|
const currentSongIndex = shuffledList.findIndex(song => song.id === currentSong.id);
|
||||||
|
playListIndex.value = currentSongIndex !== -1 ? 0 : (keepIndex ? playListIndex.value : 0);
|
||||||
|
} else {
|
||||||
|
playListIndex.value = keepIndex ? playListIndex.value : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
playList.value = shuffledList;
|
||||||
|
} else {
|
||||||
|
// 顺序模式和循环模式:直接设置播放列表
|
||||||
|
console.log('顺序/循环模式下设置新播放列表');
|
||||||
|
|
||||||
|
// 清除原始播放列表状态(如果有的话)
|
||||||
|
if (originalPlayList.value.length > 0) {
|
||||||
|
originalPlayList.value = [];
|
||||||
|
localStorage.removeItem('originalPlayList');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算播放索引
|
||||||
|
if (!keepIndex) {
|
||||||
|
playListIndex.value = list.findIndex((item) => item.id === playMusic.value.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
playList.value = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存到 localStorage
|
||||||
|
localStorage.setItem('playList', JSON.stringify(playList.value));
|
||||||
localStorage.setItem('playListIndex', playListIndex.value.toString());
|
localStorage.setItem('playListIndex', playListIndex.value.toString());
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -719,13 +877,22 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
const list = [...playList.value];
|
const list = [...playList.value];
|
||||||
const currentIndex = playListIndex.value;
|
const currentIndex = playListIndex.value;
|
||||||
|
|
||||||
|
// 如果歌曲已在播放列表中,先移除它
|
||||||
const existingIndex = list.findIndex((item) => item.id === song.id);
|
const existingIndex = list.findIndex((item) => item.id === song.id);
|
||||||
if (existingIndex !== -1) {
|
if (existingIndex !== -1) {
|
||||||
list.splice(existingIndex, 1);
|
list.splice(existingIndex, 1);
|
||||||
|
// 如果移除的歌曲在当前歌曲之前,需要调整当前索引
|
||||||
|
if (existingIndex <= currentIndex) {
|
||||||
|
playListIndex.value = Math.max(0, playListIndex.value - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list.splice(currentIndex + 1, 0, song);
|
// 插入到当前播放歌曲的下一个位置
|
||||||
setPlayList(list);
|
const insertIndex = playListIndex.value + 1;
|
||||||
|
list.splice(insertIndex, 0, song);
|
||||||
|
|
||||||
|
// 更新播放列表
|
||||||
|
setPlayList(list, true); // 保持当前索引不变
|
||||||
};
|
};
|
||||||
|
|
||||||
// 睡眠定时器功能
|
// 睡眠定时器功能
|
||||||
@@ -919,103 +1086,25 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
|
|
||||||
// 保存当前索引,用于错误恢复
|
// 保存当前索引,用于错误恢复
|
||||||
const currentIndex = playListIndex.value;
|
const currentIndex = playListIndex.value;
|
||||||
let nowPlayListIndex: number;
|
|
||||||
|
// 计算下一首歌曲的索引(所有播放模式都使用顺序播放,因为随机模式下列表已经是随机的)
|
||||||
if (playMode.value === 2) {
|
const nowPlayListIndex = (playListIndex.value + 1) % playList.value.length;
|
||||||
// 随机播放模式
|
|
||||||
do {
|
|
||||||
nowPlayListIndex = Math.floor(Math.random() * playList.value.length);
|
|
||||||
} while (nowPlayListIndex === playListIndex.value && playList.value.length > 1);
|
|
||||||
} else {
|
|
||||||
// 顺序播放或循环播放模式
|
|
||||||
nowPlayListIndex = (playListIndex.value + 1) % playList.value.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取下一首歌曲
|
// 获取下一首歌曲
|
||||||
let nextSong = { ...playList.value[nowPlayListIndex] };
|
const nextSong = { ...playList.value[nowPlayListIndex] };
|
||||||
|
|
||||||
// 记录尝试播放过的索引,防止无限循环
|
// 更新当前播放索引
|
||||||
const attemptedIndices = new Set<number>();
|
|
||||||
attemptedIndices.add(nowPlayListIndex);
|
|
||||||
|
|
||||||
// 先更新当前播放索引
|
|
||||||
playListIndex.value = nowPlayListIndex;
|
playListIndex.value = nowPlayListIndex;
|
||||||
|
|
||||||
// 尝试播放
|
// 尝试播放
|
||||||
let success = false;
|
const success = await handlePlayMusic(nextSong, true);
|
||||||
let retryCount = 0;
|
|
||||||
const maxRetries = Math.min(3, playList.value.length);
|
|
||||||
|
|
||||||
// 尝试播放,最多尝试maxRetries次
|
|
||||||
while (!success && retryCount < maxRetries) {
|
|
||||||
success = await handlePlayMusic(nextSong, true);
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
retryCount++;
|
|
||||||
console.error(`播放失败,尝试 ${retryCount}/${maxRetries}`);
|
|
||||||
|
|
||||||
if (retryCount >= maxRetries) {
|
|
||||||
console.error('多次尝试播放失败,将从播放列表中移除此歌曲');
|
|
||||||
// 从播放列表中移除失败的歌曲
|
|
||||||
const newPlayList = [...playList.value];
|
|
||||||
newPlayList.splice(nowPlayListIndex, 1);
|
|
||||||
|
|
||||||
if (newPlayList.length > 0) {
|
|
||||||
// 更新播放列表,但保持当前索引不变
|
|
||||||
const keepCurrentIndexPosition = true;
|
|
||||||
setPlayList(newPlayList, keepCurrentIndexPosition);
|
|
||||||
|
|
||||||
// 继续尝试下一首
|
|
||||||
if (playMode.value === 2) {
|
|
||||||
// 随机模式,随机选择一首未尝试过的
|
|
||||||
const availableIndices = Array.from(
|
|
||||||
{ length: newPlayList.length },
|
|
||||||
(_, i) => i
|
|
||||||
).filter((i) => !attemptedIndices.has(i));
|
|
||||||
|
|
||||||
if (availableIndices.length > 0) {
|
|
||||||
// 随机选择一个未尝试过的索引
|
|
||||||
nowPlayListIndex =
|
|
||||||
availableIndices[Math.floor(Math.random() * availableIndices.length)];
|
|
||||||
} else {
|
|
||||||
// 如果所有歌曲都尝试过了,选择下一个索引
|
|
||||||
nowPlayListIndex = (playListIndex.value + 1) % newPlayList.length;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 顺序播放,选择下一首
|
|
||||||
// 如果当前索引已经是最后一首,循环到第一首
|
|
||||||
nowPlayListIndex =
|
|
||||||
playListIndex.value >= newPlayList.length ? 0 : playListIndex.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
playListIndex.value = nowPlayListIndex;
|
|
||||||
attemptedIndices.add(nowPlayListIndex);
|
|
||||||
|
|
||||||
if (newPlayList[nowPlayListIndex]) {
|
|
||||||
nextSong = { ...newPlayList[nowPlayListIndex] };
|
|
||||||
retryCount = 0; // 重置重试计数器,为新歌曲准备
|
|
||||||
} else {
|
|
||||||
// 处理索引无效的情况
|
|
||||||
console.error('无效的播放索引,停止尝试');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 播放列表为空,停止尝试
|
|
||||||
console.error('播放列表为空,停止尝试');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 歌曲切换成功,触发歌曲变更处理(用于定时关闭功能)
|
|
||||||
if (success) {
|
if (success) {
|
||||||
handleSongChange();
|
handleSongChange();
|
||||||
} else {
|
} else {
|
||||||
console.error('所有尝试都失败,无法播放下一首歌曲');
|
console.error('播放下一首失败');
|
||||||
// 如果尝试了所有可能的歌曲仍然失败,恢复到原始索引
|
|
||||||
playListIndex.value = currentIndex;
|
playListIndex.value = currentIndex;
|
||||||
setIsPlay(false); // 停止播放
|
setIsPlay(false);
|
||||||
message.error(i18n.global.t('player.playFailed'));
|
message.error(i18n.global.t('player.playFailed'));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -1110,8 +1199,24 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
const prevPlay = useThrottleFn(_prevPlay, 500);
|
const prevPlay = useThrottleFn(_prevPlay, 500);
|
||||||
|
|
||||||
const togglePlayMode = () => {
|
const togglePlayMode = () => {
|
||||||
playMode.value = (playMode.value + 1) % 3;
|
const newMode = (playMode.value + 1) % 3;
|
||||||
|
const wasRandom = playMode.value === 2;
|
||||||
|
const isRandom = newMode === 2;
|
||||||
|
|
||||||
|
playMode.value = newMode;
|
||||||
localStorage.setItem('playMode', JSON.stringify(playMode.value));
|
localStorage.setItem('playMode', JSON.stringify(playMode.value));
|
||||||
|
|
||||||
|
// 当切换到随机模式时,直接洗牌播放列表
|
||||||
|
if (isRandom && !wasRandom && playList.value.length > 0) {
|
||||||
|
shufflePlayList();
|
||||||
|
console.log('切换到随机模式,洗牌播放列表');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当从随机模式切换出去时,恢复原始顺序
|
||||||
|
if (!isRandom && wasRandom) {
|
||||||
|
restoreOriginalOrder();
|
||||||
|
console.log('切换出随机模式,恢复原始顺序');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const addToFavorite = async (id: number | string) => {
|
const addToFavorite = async (id: number | string) => {
|
||||||
@@ -1183,6 +1288,17 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
|
|
||||||
if (savedPlayList.length > 0) {
|
if (savedPlayList.length > 0) {
|
||||||
setPlayList(savedPlayList);
|
setPlayList(savedPlayList);
|
||||||
|
|
||||||
|
// 重启后恢复随机播放状态
|
||||||
|
if (playMode.value === 2) {
|
||||||
|
// 如果当前是随机模式但没有保存的原始播放列表,说明需要重新洗牌
|
||||||
|
if (originalPlayList.value.length === 0) {
|
||||||
|
console.log('重启后恢复随机播放模式,重新洗牌播放列表');
|
||||||
|
shufflePlayList();
|
||||||
|
} else {
|
||||||
|
console.log('重启后恢复随机播放模式,播放列表已是洗牌状态');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savedPlayMusic && Object.keys(savedPlayMusic).length > 0) {
|
if (savedPlayMusic && Object.keys(savedPlayMusic).length > 0) {
|
||||||
@@ -1530,6 +1646,12 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
setVolume,
|
setVolume,
|
||||||
getVolume,
|
getVolume,
|
||||||
increaseVolume,
|
increaseVolume,
|
||||||
decreaseVolume
|
decreaseVolume,
|
||||||
|
|
||||||
|
// 原始播放列表和洗牌相关
|
||||||
|
originalPlayList,
|
||||||
|
shufflePlayList,
|
||||||
|
restoreOriginalOrder,
|
||||||
|
preloadNextSongs
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user