refactor: move resource URL refresh from favorites loading to playback/preload
Replace passive resource checking on playlist item mount with active checking during playback and preload operations. This improves performance by reducing unnecessary network requests and ensures resources are validated only when needed. Changes: - Create songResourceChecker utility for centralized resource validation - Remove resource checking from PlayListItem component - Add resource validation in Player component before playback - Add resource validation in usePlayQueueStore before preload - Maintain data consistency between play queue and favorites 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
fcf8362a15
commit
dcf13b2f07
|
@ -2,11 +2,8 @@
|
|||
import { artistsOrganize } from '../utils'
|
||||
import { ref } from 'vue'
|
||||
import { useFavourites } from '../stores/useFavourites'
|
||||
import apis from '../apis'
|
||||
import axios from 'axios'
|
||||
|
||||
import StarSlashIcon from '../assets/icons/starslash.vue'
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
const favourites = useFavourites()
|
||||
|
||||
|
@ -20,27 +17,6 @@ const props = defineProps<{
|
|||
const emit = defineEmits<{
|
||||
(e: 'play', index: number): void
|
||||
}>()
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
// 添加缓存控制头和随机参数来避免缓存
|
||||
await axios.head(props.item.song.sourceUrl ?? '', {
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
||||
'Pragma': 'no-cache',
|
||||
'Expires': '0'
|
||||
},
|
||||
params: {
|
||||
_t: Date.now() // 添加时间戳参数避免缓存
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
// 刷新资源地址
|
||||
const updatedSong = await apis.getSong(props.item.song.cid)
|
||||
console.log('Updated song:', updatedSong)
|
||||
favourites.updateSongInFavourites(props.item.song.cid, updatedSong)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -4,12 +4,14 @@
|
|||
import { usePlayQueueStore } from '../stores/usePlayQueueStore'
|
||||
import { useTemplateRef, watch, nextTick, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useFavourites } from '../stores/useFavourites'
|
||||
|
||||
import PlayIcon from '../assets/icons/play.vue'
|
||||
import LoadingIndicator from '../assets/icons/loadingindicator.vue'
|
||||
import { audioVisualizer } from '../utils'
|
||||
import { audioVisualizer, checkAndRefreshSongResource } from '../utils'
|
||||
|
||||
const playQueueStore = usePlayQueueStore()
|
||||
const favourites = useFavourites()
|
||||
const route = useRoute()
|
||||
const player = useTemplateRef('playerRef')
|
||||
|
||||
|
@ -53,9 +55,24 @@ watch(() => playQueueStore.currentIndex, async () => {
|
|||
const songId = track.song.cid
|
||||
|
||||
try {
|
||||
// 首先检查和刷新当前歌曲的资源
|
||||
console.log('[Player] 检查当前歌曲资源:', track.song.name)
|
||||
const updatedSong = await checkAndRefreshSongResource(
|
||||
track.song,
|
||||
(updated) => {
|
||||
// 更新播放队列中的歌曲信息
|
||||
if (playQueueStore.list[playQueueStore.currentIndex]) {
|
||||
playQueueStore.list[playQueueStore.currentIndex].song = updated
|
||||
}
|
||||
// 如果歌曲在收藏夹中,也更新收藏夹
|
||||
favourites.updateSongInFavourites(songId, updated)
|
||||
}
|
||||
)
|
||||
|
||||
// 使用更新后的歌曲信息
|
||||
const preloadedAudio = playQueueStore.getPreloadedAudio(songId)
|
||||
|
||||
if (preloadedAudio) {
|
||||
if (preloadedAudio && updatedSong.sourceUrl === track.song.sourceUrl) {
|
||||
console.log(`[Player] 使用预加载的音频: ${track.song.name}`)
|
||||
|
||||
// 直接使用预加载的音频数据
|
||||
|
@ -78,6 +95,11 @@ watch(() => playQueueStore.currentIndex, async () => {
|
|||
} else {
|
||||
console.log(`[Player] 正常加载音频: ${track.song.name}`)
|
||||
playQueueStore.isBuffering = true
|
||||
|
||||
// 如果资源地址已更新,清除旧的预加载音频
|
||||
if (updatedSong.sourceUrl !== track.song.sourceUrl) {
|
||||
playQueueStore.clearPreloadedAudio(songId)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Player] 处理预加载音频时出错:', error)
|
||||
|
@ -88,13 +110,21 @@ watch(() => playQueueStore.currentIndex, async () => {
|
|||
setMetadata()
|
||||
|
||||
// 延迟预加载下一首歌,避免影响当前歌曲加载
|
||||
setTimeout(() => {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
console.log('[Player] 尝试预加载下一首歌')
|
||||
|
||||
// 检查函数是否存在
|
||||
if (typeof playQueueStore.preloadNext === 'function') {
|
||||
playQueueStore.preloadNext()
|
||||
await playQueueStore.preloadNext()
|
||||
|
||||
// 预加载完成后,检查播放队列是否有更新,同步到收藏夹
|
||||
playQueueStore.list.forEach(item => {
|
||||
if (favourites.isFavourite(item.song.cid)) {
|
||||
favourites.updateSongInFavourites(item.song.cid, item.song)
|
||||
}
|
||||
})
|
||||
|
||||
playQueueStore.limitPreloadCache()
|
||||
} else {
|
||||
console.error('[Player] preloadNext 不是一个函数')
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { defineStore } from "pinia"
|
||||
import { ref, computed } from "vue"
|
||||
import { checkAndRefreshSongResource } from "../utils"
|
||||
|
||||
export const usePlayQueueStore = defineStore('queue', () => {
|
||||
const list = ref<QueueItem[]>([])
|
||||
|
@ -89,6 +90,26 @@ export const usePlayQueueStore = defineStore('queue', () => {
|
|||
isPreloading.value = true
|
||||
preloadProgress.value = 0
|
||||
|
||||
// 在预加载前检查和刷新资源
|
||||
console.log('[Store] 预加载前检查资源:', nextSong.song.name)
|
||||
const updatedSong = await checkAndRefreshSongResource(
|
||||
nextSong.song,
|
||||
(updated) => {
|
||||
// 更新播放队列中的歌曲信息
|
||||
const actualIndex = playMode.value.shuffle && shuffleList.value.length > 0
|
||||
? shuffleList.value[nextIndex]
|
||||
: nextIndex
|
||||
if (list.value[actualIndex]) {
|
||||
list.value[actualIndex].song = updated
|
||||
}
|
||||
|
||||
// 如果歌曲在收藏夹中,也更新收藏夹
|
||||
// 注意:这里不直接导入 favourites store 以避免循环依赖
|
||||
// 改为触发一个事件或者在调用方处理
|
||||
console.log('[Store] 预加载时需要更新收藏夹:', updated.name)
|
||||
}
|
||||
)
|
||||
|
||||
const audio = new Audio()
|
||||
audio.preload = 'auto'
|
||||
audio.crossOrigin = 'anonymous'
|
||||
|
@ -107,19 +128,21 @@ export const usePlayQueueStore = defineStore('queue', () => {
|
|||
preloadedAudio.value.set(songId, audio)
|
||||
isPreloading.value = false
|
||||
preloadProgress.value = 100
|
||||
console.log('[Store] 预加载完成:', updatedSong.name)
|
||||
})
|
||||
|
||||
// 监听加载错误
|
||||
audio.addEventListener('error', (e) => {
|
||||
console.error(`[Store] 预加载音频失败: ${e}`)
|
||||
console.error(`[Store] 预加载音频失败: ${updatedSong.name}`, e)
|
||||
isPreloading.value = false
|
||||
preloadProgress.value = 0
|
||||
})
|
||||
|
||||
// 设置音频源并开始加载
|
||||
audio.src = nextSong.song.sourceUrl
|
||||
// 使用更新后的音频源
|
||||
audio.src = updatedSong.sourceUrl!
|
||||
|
||||
} catch (error) {
|
||||
console.error('[Store] 预加载过程出错:', error)
|
||||
isPreloading.value = false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import artistsOrganize from "./artistsOrganize"
|
||||
import { audioVisualizer } from "./audioVisualizer"
|
||||
import cicdInfo from "./cicdInfo"
|
||||
import { checkAndRefreshSongResource, checkAndRefreshMultipleSongs } from "./songResourceChecker"
|
||||
|
||||
export { artistsOrganize, audioVisualizer, cicdInfo }
|
||||
export { artistsOrganize, audioVisualizer, cicdInfo, checkAndRefreshSongResource, checkAndRefreshMultipleSongs }
|
||||
|
|
80
src/utils/songResourceChecker.ts
Normal file
80
src/utils/songResourceChecker.ts
Normal file
|
@ -0,0 +1,80 @@
|
|||
import axios from 'axios'
|
||||
import apis from '../apis'
|
||||
|
||||
/**
|
||||
* 检查歌曲资源 URL 是否可用,如果不可用则刷新
|
||||
* @param song 要检查的歌曲对象
|
||||
* @param updateCallback 更新歌曲信息的回调函数
|
||||
* @returns 更新后的歌曲对象(如果需要更新)或原始歌曲对象
|
||||
*/
|
||||
export const checkAndRefreshSongResource = async (
|
||||
song: Song,
|
||||
updateCallback?: (updatedSong: Song) => void
|
||||
): Promise<Song> => {
|
||||
if (!song.sourceUrl) {
|
||||
console.warn('[ResourceChecker] 歌曲没有 sourceUrl:', song.name)
|
||||
return song
|
||||
}
|
||||
|
||||
try {
|
||||
// 检查资源是否可用
|
||||
await axios.head(song.sourceUrl, {
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
||||
'Pragma': 'no-cache',
|
||||
'Expires': '0'
|
||||
},
|
||||
params: {
|
||||
_t: Date.now() // 添加时间戳参数避免缓存
|
||||
},
|
||||
timeout: 5000 // 5秒超时
|
||||
})
|
||||
|
||||
// 资源可用,返回原始歌曲
|
||||
console.log('[ResourceChecker] 资源可用:', song.name)
|
||||
return song
|
||||
} catch (error) {
|
||||
// 资源不可用,刷新歌曲信息
|
||||
console.log('[ResourceChecker] 资源不可用,正在刷新:', song.name, error)
|
||||
|
||||
try {
|
||||
const updatedSong = await apis.getSong(song.cid)
|
||||
console.log('[ResourceChecker] 歌曲信息已刷新:', updatedSong.name)
|
||||
|
||||
// 调用更新回调(如果提供)
|
||||
if (updateCallback) {
|
||||
updateCallback(updatedSong)
|
||||
}
|
||||
|
||||
return updatedSong
|
||||
} catch (refreshError) {
|
||||
console.error('[ResourceChecker] 刷新歌曲信息失败:', refreshError)
|
||||
// 刷新失败,返回原始歌曲
|
||||
return song
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量检查多首歌曲的资源
|
||||
* @param songs 要检查的歌曲数组
|
||||
* @param updateCallback 更新单首歌曲信息的回调函数
|
||||
* @returns 更新后的歌曲数组
|
||||
*/
|
||||
export const checkAndRefreshMultipleSongs = async (
|
||||
songs: Song[],
|
||||
updateCallback?: (updatedSong: Song, originalIndex: number) => void
|
||||
): Promise<Song[]> => {
|
||||
const results: Song[] = []
|
||||
|
||||
for (let i = 0; i < songs.length; i++) {
|
||||
const originalSong = songs[i]
|
||||
const updatedSong = await checkAndRefreshSongResource(
|
||||
originalSong,
|
||||
updateCallback ? (updated) => updateCallback(updated, i) : undefined
|
||||
)
|
||||
results.push(updatedSong)
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
Loading…
Reference in New Issue
Block a user