fix: 修复随机播放模式下预加载顺序错误的问题

- 修正 getNextIndex 在随机播放模式下返回正确的原始列表索引
- 简化预加载逻辑,因为 nextIndex 已经是正确的列表索引
- 保持 Player.vue 中更新歌曲信息时的索引计算逻辑

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Astrian Zheng 2025-06-04 22:14:44 +10:00
parent dcf13b2f07
commit ae2d8875ad
Signed by: Astrian
SSH Key Fingerprint: SHA256:rVnhx3DAKjujCwWE13aDl7uV6+9U1MvydLkNRXJrBiA
2 changed files with 285 additions and 229 deletions

View File

@ -1,13 +1,12 @@
<!-- Player.vue - 添加预加载功能 --> <!-- Player.vue - 添加预加载功能 -->
<script setup lang="ts"> <script setup lang="ts">
import { computed, nextTick, useTemplateRef, watch } from 'vue'
import { usePlayQueueStore } from '../stores/usePlayQueueStore'
import { useTemplateRef, watch, nextTick, computed } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { useFavourites } from '../stores/useFavourites' import { useFavourites } from '../stores/useFavourites'
import { usePlayQueueStore } from '../stores/usePlayQueueStore'
import PlayIcon from '../assets/icons/play.vue'
import LoadingIndicator from '../assets/icons/loadingindicator.vue' import LoadingIndicator from '../assets/icons/loadingindicator.vue'
import PlayIcon from '../assets/icons/play.vue'
import { audioVisualizer, checkAndRefreshSongResource } from '../utils' import { audioVisualizer, checkAndRefreshSongResource } from '../utils'
const playQueueStore = usePlayQueueStore() const playQueueStore = usePlayQueueStore()
@ -19,13 +18,18 @@ const player = useTemplateRef('playerRef')
console.log('[Player] 检查 store 方法:', { console.log('[Player] 检查 store 方法:', {
preloadNext: typeof playQueueStore.preloadNext, preloadNext: typeof playQueueStore.preloadNext,
getPreloadedAudio: typeof playQueueStore.getPreloadedAudio, getPreloadedAudio: typeof playQueueStore.getPreloadedAudio,
clearPreloadedAudio: typeof playQueueStore.clearPreloadedAudio clearPreloadedAudio: typeof playQueueStore.clearPreloadedAudio,
}) })
// //
const currentTrack = computed(() => { const currentTrack = computed(() => {
if (playQueueStore.playMode.shuffle && playQueueStore.shuffleList.length > 0) { if (
return playQueueStore.list[playQueueStore.shuffleList[playQueueStore.currentIndex]] playQueueStore.playMode.shuffle &&
playQueueStore.shuffleList.length > 0
) {
return playQueueStore.list[
playQueueStore.shuffleList[playQueueStore.currentIndex]
]
} }
return playQueueStore.list[playQueueStore.currentIndex] return playQueueStore.list[playQueueStore.currentIndex]
@ -37,16 +41,22 @@ const currentAudioSrc = computed(() => {
return track ? track.song.sourceUrl : '' return track ? track.song.sourceUrl : ''
}) })
watch(() => playQueueStore.isPlaying, (newValue) => { watch(
() => playQueueStore.isPlaying,
(newValue) => {
if (newValue) { if (newValue) {
player.value?.play() player.value?.play()
setMetadata() setMetadata()
} else {
player.value?.pause()
} }
else { player.value?.pause() } },
}) )
// //
watch(() => playQueueStore.currentIndex, async () => { watch(
() => playQueueStore.currentIndex,
async () => {
console.log('[Player] 当前索引变化:', playQueueStore.currentIndex) console.log('[Player] 当前索引变化:', playQueueStore.currentIndex)
// 使 // 使
@ -61,12 +71,19 @@ watch(() => playQueueStore.currentIndex, async () => {
track.song, track.song,
(updated) => { (updated) => {
// //
if (playQueueStore.list[playQueueStore.currentIndex]) { // currentIndex shuffleList
playQueueStore.list[playQueueStore.currentIndex].song = updated // shuffleList[currentIndex] list
const actualIndex =
playQueueStore.playMode.shuffle &&
playQueueStore.shuffleList.length > 0
? playQueueStore.shuffleList[playQueueStore.currentIndex]
: playQueueStore.currentIndex
if (playQueueStore.list[actualIndex]) {
playQueueStore.list[actualIndex].song = updated
} }
// //
favourites.updateSongInFavourites(songId, updated) favourites.updateSongInFavourites(songId, updated)
} },
) )
// 使 // 使
@ -119,7 +136,7 @@ watch(() => playQueueStore.currentIndex, async () => {
await playQueueStore.preloadNext() await playQueueStore.preloadNext()
// //
playQueueStore.list.forEach(item => { playQueueStore.list.forEach((item) => {
if (favourites.isFavourite(item.song.cid)) { if (favourites.isFavourite(item.song.cid)) {
favourites.updateSongInFavourites(item.song.cid, item.song) favourites.updateSongInFavourites(item.song.cid, item.song)
} }
@ -133,18 +150,23 @@ watch(() => playQueueStore.currentIndex, async () => {
console.error('[Player] 预加载失败:', error) console.error('[Player] 预加载失败:', error)
} }
}, 1000) }, 1000)
}) },
)
function artistsOrganize(list: string[]) { function artistsOrganize(list: string[]) {
if (list.length === 0) { return '未知音乐人' } if (list.length === 0) {
return list.map((artist) => { return '未知音乐人'
}
return list
.map((artist) => {
return artist return artist
}).join(' / ') })
.join(' / ')
} }
function setMetadata() { function setMetadata() {
if ('mediaSession' in navigator) { if ('mediaSession' in navigator) {
let current = currentTrack.value const current = currentTrack.value
if (!current) return if (!current) return
navigator.mediaSession.metadata = new MediaMetadata({ navigator.mediaSession.metadata = new MediaMetadata({
@ -152,8 +174,12 @@ function setMetadata() {
artist: artistsOrganize(current.song.artists ?? []), artist: artistsOrganize(current.song.artists ?? []),
album: current.album?.name, album: current.album?.name,
artwork: [ artwork: [
{ src: current.album?.coverUrl ?? '', sizes: '500x500', type: 'image/png' }, {
] src: current.album?.coverUrl ?? '',
sizes: '500x500',
type: 'image/png',
},
],
}) })
navigator.mediaSession.setActionHandler('previoustrack', playPrevious) navigator.mediaSession.setActionHandler('previoustrack', playPrevious)
@ -163,16 +189,21 @@ function setMetadata() {
playQueueStore.currentTime = player.value?.currentTime ?? 0 playQueueStore.currentTime = player.value?.currentTime ?? 0
} }
watch(() => playQueueStore.updatedCurrentTime, (newValue) => { watch(
if (newValue === null) { return } () => playQueueStore.updatedCurrentTime,
(newValue) => {
if (newValue === null) {
return
}
if (player.value) player.value.currentTime = newValue if (player.value) player.value.currentTime = newValue
playQueueStore.updatedCurrentTime = null playQueueStore.updatedCurrentTime = null
}) },
)
} }
function playNext() { function playNext() {
if (playQueueStore.currentIndex === playQueueStore.list.length - 1) { if (playQueueStore.currentIndex === playQueueStore.list.length - 1) {
console.log("at the bottom, pause") console.log('at the bottom, pause')
playQueueStore.currentIndex = 0 playQueueStore.currentIndex = 0
if (playQueueStore.playMode.repeat === 'all') { if (playQueueStore.playMode.repeat === 'all') {
playQueueStore.currentIndex = 0 playQueueStore.currentIndex = 0
@ -188,11 +219,17 @@ function playNext() {
} }
function playPrevious() { function playPrevious() {
if (player.value && (player.value.currentTime ?? 0) < 5 && playQueueStore.currentIndex > 0) { if (
player.value &&
(player.value.currentTime ?? 0) < 5 &&
playQueueStore.currentIndex > 0
) {
playQueueStore.currentIndex-- playQueueStore.currentIndex--
playQueueStore.isPlaying = true playQueueStore.isPlaying = true
} else { } else {
if (player.value) { player.value.currentTime = 0 } if (player.value) {
player.value.currentTime = 0
}
} }
} }
@ -209,8 +246,10 @@ function updateCurrentTime() {
const preloadTrigger = (config.preloadTrigger || 50) / 100 // const preloadTrigger = (config.preloadTrigger || 50) / 100 //
const remainingTimeThreshold = config.remainingTimeThreshold || 30 const remainingTimeThreshold = config.remainingTimeThreshold || 30
if ((progress > preloadTrigger || remainingTime < remainingTimeThreshold) && !playQueueStore.isPreloading) { if (
(progress > preloadTrigger || remainingTime < remainingTimeThreshold) &&
!playQueueStore.isPreloading
) {
try { try {
if (typeof playQueueStore.preloadNext === 'function') { if (typeof playQueueStore.preloadNext === 'function') {
playQueueStore.preloadNext() playQueueStore.preloadNext()
@ -232,13 +271,18 @@ const { barHeights, connectAudio, isAnalyzing, error } = audioVisualizer({
bassBoost: 0.8, bassBoost: 0.8,
midBoost: 1.2, midBoost: 1.2,
trebleBoost: 1.4, trebleBoost: 1.4,
threshold: 0 threshold: 0,
}) })
console.log('[Player] audioVisualizer 返回值:', { barHeights: barHeights.value, isAnalyzing: isAnalyzing.value }) console.log('[Player] audioVisualizer 返回值:', {
barHeights: barHeights.value,
isAnalyzing: isAnalyzing.value,
})
// //
watch(() => playQueueStore.list.length, async (newLength) => { watch(
() => playQueueStore.list.length,
async (newLength) => {
console.log('[Player] 播放列表长度变化:', newLength) console.log('[Player] 播放列表长度变化:', newLength)
if (newLength === 0) { if (newLength === 0) {
console.log('[Player] 播放列表为空,跳过连接') console.log('[Player] 播放列表为空,跳过连接')
@ -253,7 +297,7 @@ watch(() => playQueueStore.list.length, async (newLength) => {
console.log('[Player] 音频元素状态:', { console.log('[Player] 音频元素状态:', {
src: player.value.src?.substring(0, 50) + '...', src: player.value.src?.substring(0, 50) + '...',
readyState: player.value.readyState, readyState: player.value.readyState,
paused: player.value.paused paused: player.value.paused,
}) })
connectAudio(player.value) connectAudio(player.value)
} else { } else {
@ -271,29 +315,42 @@ watch(() => playQueueStore.list.length, async (newLength) => {
if (player.value) { if (player.value) {
initializeVolume() initializeVolume()
} }
}) },
)
// //
watch(() => player.value, (audioElement) => { watch(
() => player.value,
(audioElement) => {
if (audioElement && playQueueStore.list.length > 0) { if (audioElement && playQueueStore.list.length > 0) {
connectAudio(audioElement) connectAudio(audioElement)
} }
}) },
)
// //
watch(() => barHeights.value, (newHeights) => { watch(
() => barHeights.value,
(newHeights) => {
playQueueStore.visualizer = newHeights playQueueStore.visualizer = newHeights
}, { deep: true }) },
{ deep: true },
)
// //
watch(() => error.value, (newError) => { watch(
() => error.value,
(newError) => {
if (newError) { if (newError) {
console.error('[Player] 可视化器错误:', newError) console.error('[Player] 可视化器错误:', newError)
} }
}) },
)
// //
watch(() => playQueueStore.playMode.shuffle, (isShuffle) => { watch(
() => playQueueStore.playMode.shuffle,
(isShuffle) => {
if (isShuffle) { if (isShuffle) {
const currentIndex = playQueueStore.currentIndex const currentIndex = playQueueStore.currentIndex
const trackCount = playQueueStore.list.length const trackCount = playQueueStore.list.length
@ -302,8 +359,10 @@ watch(() => playQueueStore.playMode.shuffle, (isShuffle) => {
let shuffledList = [...Array(currentIndex).keys()] let shuffledList = [...Array(currentIndex).keys()]
// 2. // 2.
let shuffleSpace = [...Array(trackCount).keys()].filter(index => const shuffleSpace = [...Array(trackCount).keys()].filter((index) =>
playQueueStore.shuffleCurrent ? index >= currentIndex : index > currentIndex playQueueStore.shuffleCurrent
? index >= currentIndex
: index > currentIndex,
) )
// 3. // 3.
@ -324,7 +383,8 @@ watch(() => playQueueStore.playMode.shuffle, (isShuffle) => {
playQueueStore.shuffleCurrent = undefined playQueueStore.shuffleCurrent = undefined
} else { } else {
// 退 // 退
playQueueStore.currentIndex = playQueueStore.shuffleList[playQueueStore.currentIndex] playQueueStore.currentIndex =
playQueueStore.shuffleList[playQueueStore.currentIndex]
} }
// //
@ -332,7 +392,8 @@ watch(() => playQueueStore.playMode.shuffle, (isShuffle) => {
playQueueStore.clearAllPreloadedAudio() playQueueStore.clearAllPreloadedAudio()
playQueueStore.preloadNext() playQueueStore.preloadNext()
}, 500) }, 500)
}) },
)
function getCurrentTrack() { function getCurrentTrack() {
return currentTrack.value return currentTrack.value
@ -343,7 +404,7 @@ function initializeVolume() {
if (player.value) { if (player.value) {
const savedVolume = localStorage.getItem('audioVolume') const savedVolume = localStorage.getItem('audioVolume')
if (savedVolume) { if (savedVolume) {
const volumeValue = parseFloat(savedVolume) const volumeValue = Number.parseFloat(savedVolume)
player.value.volume = volumeValue player.value.volume = volumeValue
console.log('[Player] 初始化音量:', volumeValue) console.log('[Player] 初始化音量:', volumeValue)
} else { } else {
@ -369,7 +430,7 @@ function syncVolumeFromStorage() {
if (player.value) { if (player.value) {
const savedVolume = localStorage.getItem('audioVolume') const savedVolume = localStorage.getItem('audioVolume')
if (savedVolume) { if (savedVolume) {
const volumeValue = parseFloat(savedVolume) const volumeValue = Number.parseFloat(savedVolume)
if (player.value.volume !== volumeValue) { if (player.value.volume !== volumeValue) {
player.value.volume = volumeValue player.value.volume = volumeValue
} }

View File

@ -1,6 +1,6 @@
import { defineStore } from "pinia" import { defineStore } from 'pinia'
import { ref, computed } from "vue" import { computed, ref } from 'vue'
import { checkAndRefreshSongResource } from "../utils" import { checkAndRefreshSongResource } from '../utils'
export const usePlayQueueStore = defineStore('queue', () => { export const usePlayQueueStore = defineStore('queue', () => {
const list = ref<QueueItem[]>([]) const list = ref<QueueItem[]>([])
@ -14,11 +14,11 @@ export const usePlayQueueStore = defineStore('queue', () => {
const visualizer = ref<number[]>([0, 0, 0, 0, 0, 0]) const visualizer = ref<number[]>([0, 0, 0, 0, 0, 0])
const shuffleList = ref<number[]>([]) const shuffleList = ref<number[]>([])
const playMode = ref<{ const playMode = ref<{
shuffle: boolean, shuffle: boolean
repeat: 'off' | 'single' | 'all' repeat: 'off' | 'single' | 'all'
}>({ }>({
shuffle: false, shuffle: false,
repeat: 'off' repeat: 'off',
}) })
const shuffleCurrent = ref<boolean | undefined>(undefined) const shuffleCurrent = ref<boolean | undefined>(undefined)
@ -36,10 +36,13 @@ export const usePlayQueueStore = defineStore('queue', () => {
} }
if (playMode.value.shuffle && shuffleList.value.length > 0) { if (playMode.value.shuffle && shuffleList.value.length > 0) {
const currentShuffleIndex = shuffleList.value.indexOf(currentIndex.value) // 当前在 shuffleList 中的位置
const currentShuffleIndex = currentIndex.value
if (currentShuffleIndex < shuffleList.value.length - 1) { if (currentShuffleIndex < shuffleList.value.length - 1) {
// 返回下一个位置对应的原始 list 索引
return shuffleList.value[currentShuffleIndex + 1] return shuffleList.value[currentShuffleIndex + 1]
} else if (playMode.value.repeat === 'all') { } else if (playMode.value.repeat === 'all') {
// 返回第一个位置对应的原始 list 索引
return shuffleList.value[0] return shuffleList.value[0]
} }
return -1 return -1
@ -56,19 +59,14 @@ export const usePlayQueueStore = defineStore('queue', () => {
// 预加载下一首歌 // 预加载下一首歌
const preloadNext = async () => { const preloadNext = async () => {
const nextIndex = getNextIndex.value const nextIndex = getNextIndex.value
if (nextIndex === -1) { if (nextIndex === -1) {
return return
} }
// 获取下一首歌曲对象 // 获取下一首歌曲对象
let nextSong // nextIndex 已经是原始 list 中的索引
if (playMode.value.shuffle && shuffleList.value.length > 0) { const nextSong = list.value[nextIndex]
nextSong = list.value[shuffleList.value[nextIndex]]
} else {
nextSong = list.value[nextIndex]
}
if (!nextSong || !nextSong.song) { if (!nextSong || !nextSong.song) {
return return
@ -96,18 +94,16 @@ export const usePlayQueueStore = defineStore('queue', () => {
nextSong.song, nextSong.song,
(updated) => { (updated) => {
// 更新播放队列中的歌曲信息 // 更新播放队列中的歌曲信息
const actualIndex = playMode.value.shuffle && shuffleList.value.length > 0 // nextIndex 已经是原始 list 中的索引
? shuffleList.value[nextIndex] if (list.value[nextIndex]) {
: nextIndex list.value[nextIndex].song = updated
if (list.value[actualIndex]) {
list.value[actualIndex].song = updated
} }
// 如果歌曲在收藏夹中,也更新收藏夹 // 如果歌曲在收藏夹中,也更新收藏夹
// 注意:这里不直接导入 favourites store 以避免循环依赖 // 注意:这里不直接导入 favourites store 以避免循环依赖
// 改为触发一个事件或者在调用方处理 // 改为触发一个事件或者在调用方处理
console.log('[Store] 预加载时需要更新收藏夹:', updated.name) console.log('[Store] 预加载时需要更新收藏夹:', updated.name)
} },
) )
const audio = new Audio() const audio = new Audio()
@ -140,7 +136,6 @@ export const usePlayQueueStore = defineStore('queue', () => {
// 使用更新后的音频源 // 使用更新后的音频源
audio.src = updatedSong.sourceUrl! audio.src = updatedSong.sourceUrl!
} catch (error) { } catch (error) {
console.error('[Store] 预加载过程出错:', error) console.error('[Store] 预加载过程出错:', error)
isPreloading.value = false isPreloading.value = false
@ -190,7 +185,7 @@ export const usePlayQueueStore = defineStore('queue', () => {
progress: preloadProgress.value, progress: preloadProgress.value,
cacheSize: preloadedAudio.value.size, cacheSize: preloadedAudio.value.size,
cachedSongs: Array.from(preloadedAudio.value.keys()), cachedSongs: Array.from(preloadedAudio.value.keys()),
nextIndex: getNextIndex.value nextIndex: getNextIndex.value,
}) })
} }
@ -217,6 +212,6 @@ export const usePlayQueueStore = defineStore('queue', () => {
clearPreloadedAudio, clearPreloadedAudio,
clearAllPreloadedAudio, clearAllPreloadedAudio,
limitPreloadCache, limitPreloadCache,
debugPreloadState debugPreloadState,
} }
}) })