feat(Player): implement audio preloading functionality with state management
This commit is contained in:
		
							parent
							
								
									096e74a9bd
								
							
						
					
					
						commit
						7a0c638d2c
					
				| 
						 | 
				
			
			@ -1,7 +1,8 @@
 | 
			
		|||
<!-- Player.vue - 添加调试信息 -->
 | 
			
		||||
<!-- Player.vue - 添加预加载功能 -->
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
 | 
			
		||||
import { usePlayQueueStore } from '../stores/usePlayQueueStore'
 | 
			
		||||
import { useTemplateRef, watch, nextTick } from 'vue'
 | 
			
		||||
import { useTemplateRef, watch, nextTick, computed } from 'vue'
 | 
			
		||||
import { useRoute } from 'vue-router'
 | 
			
		||||
 | 
			
		||||
import PlayIcon from '../assets/icons/play.vue'
 | 
			
		||||
| 
						 | 
				
			
			@ -12,6 +13,28 @@ const playQueueStore = usePlayQueueStore()
 | 
			
		|||
const route = useRoute()
 | 
			
		||||
const player = useTemplateRef('playerRef')
 | 
			
		||||
 | 
			
		||||
// [调试] 检查 store 方法类型
 | 
			
		||||
console.log('[Player] 检查 store 方法:', {
 | 
			
		||||
	preloadNext: typeof playQueueStore.preloadNext,
 | 
			
		||||
	getPreloadedAudio: typeof playQueueStore.getPreloadedAudio,
 | 
			
		||||
	clearPreloadedAudio: typeof playQueueStore.clearPreloadedAudio
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// 获取当前歌曲的计算属性
 | 
			
		||||
const currentTrack = computed(() => {
 | 
			
		||||
	if (playQueueStore.playMode.shuffle && playQueueStore.shuffleList.length > 0) {
 | 
			
		||||
		return playQueueStore.list[playQueueStore.shuffleList[playQueueStore.currentIndex]]
 | 
			
		||||
	} else {
 | 
			
		||||
		return playQueueStore.list[playQueueStore.currentIndex]
 | 
			
		||||
	}
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// 获取当前歌曲的音频源
 | 
			
		||||
const currentAudioSrc = computed(() => {
 | 
			
		||||
	const track = currentTrack.value
 | 
			
		||||
	return track ? track.song.sourceUrl : ''
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
watch(() => playQueueStore.isPlaying, (newValue) => {
 | 
			
		||||
	if (newValue) {
 | 
			
		||||
		player.value?.play()
 | 
			
		||||
| 
						 | 
				
			
			@ -20,10 +43,66 @@ watch(() => playQueueStore.isPlaying, (newValue) => {
 | 
			
		|||
	else { player.value?.pause() }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
watch(() => playQueueStore.currentIndex, () => {
 | 
			
		||||
// 监听当前索引变化,处理预加载逻辑
 | 
			
		||||
watch(() => playQueueStore.currentIndex, async () => {
 | 
			
		||||
	console.log('[Player] 当前索引变化:', playQueueStore.currentIndex)
 | 
			
		||||
 | 
			
		||||
	// 检查是否可以使用预加载的音频
 | 
			
		||||
	const track = currentTrack.value
 | 
			
		||||
	if (track) {
 | 
			
		||||
		const songId = track.song.cid
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			const preloadedAudio = playQueueStore.getPreloadedAudio(songId)
 | 
			
		||||
 | 
			
		||||
			if (preloadedAudio) {
 | 
			
		||||
				console.log(`[Player] 使用预加载的音频: ${track.song.name}`)
 | 
			
		||||
 | 
			
		||||
				// 直接使用预加载的音频数据
 | 
			
		||||
				if (player.value) {
 | 
			
		||||
					// 复制预加载音频的状态到主播放器
 | 
			
		||||
					player.value.src = preloadedAudio.src
 | 
			
		||||
					player.value.currentTime = 0
 | 
			
		||||
 | 
			
		||||
					// 清理使用过的预加载音频
 | 
			
		||||
					playQueueStore.clearPreloadedAudio(songId)
 | 
			
		||||
 | 
			
		||||
					// 如果正在播放状态,立即播放
 | 
			
		||||
					if (playQueueStore.isPlaying) {
 | 
			
		||||
						await nextTick()
 | 
			
		||||
						player.value.play().catch(console.error)
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					playQueueStore.isBuffering = false
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				console.log(`[Player] 正常加载音频: ${track.song.name}`)
 | 
			
		||||
				playQueueStore.isBuffering = true
 | 
			
		||||
			}
 | 
			
		||||
		} catch (error) {
 | 
			
		||||
			console.error('[Player] 处理预加载音频时出错:', error)
 | 
			
		||||
			playQueueStore.isBuffering = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	setMetadata()
 | 
			
		||||
	playQueueStore.isBuffering = true
 | 
			
		||||
 | 
			
		||||
	// 延迟预加载下一首歌,避免影响当前歌曲加载
 | 
			
		||||
	setTimeout(() => {
 | 
			
		||||
		try {
 | 
			
		||||
			console.log('[Player] 尝试预加载下一首歌')
 | 
			
		||||
 | 
			
		||||
			// 检查函数是否存在
 | 
			
		||||
			if (typeof playQueueStore.preloadNext === 'function') {
 | 
			
		||||
				playQueueStore.preloadNext()
 | 
			
		||||
				playQueueStore.limitPreloadCache()
 | 
			
		||||
			} else {
 | 
			
		||||
				console.error('[Player] preloadNext 不是一个函数')
 | 
			
		||||
			}
 | 
			
		||||
		} catch (error) {
 | 
			
		||||
			console.error('[Player] 预加载失败:', error)
 | 
			
		||||
		}
 | 
			
		||||
	}, 1000)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
function artistsOrganize(list: string[]) {
 | 
			
		||||
| 
						 | 
				
			
			@ -35,13 +114,9 @@ function artistsOrganize(list: string[]) {
 | 
			
		|||
 | 
			
		||||
function setMetadata() {
 | 
			
		||||
	if ('mediaSession' in navigator) {
 | 
			
		||||
		let current = (() => {
 | 
			
		||||
			if (playQueueStore.playMode.shuffle) {
 | 
			
		||||
				return playQueueStore.list[playQueueStore.shuffleList[playQueueStore.currentIndex]]
 | 
			
		||||
			} else {
 | 
			
		||||
				return playQueueStore.list[playQueueStore.currentIndex]
 | 
			
		||||
			}
 | 
			
		||||
		})()
 | 
			
		||||
		let current = currentTrack.value
 | 
			
		||||
		if (!current) return
 | 
			
		||||
 | 
			
		||||
		navigator.mediaSession.metadata = new MediaMetadata({
 | 
			
		||||
			title: current.song.name,
 | 
			
		||||
			artist: artistsOrganize(current.song.artists ?? []),
 | 
			
		||||
| 
						 | 
				
			
			@ -54,8 +129,8 @@ function setMetadata() {
 | 
			
		|||
		navigator.mediaSession.setActionHandler('previoustrack', playPrevious)
 | 
			
		||||
		navigator.mediaSession.setActionHandler('nexttrack', playNext)
 | 
			
		||||
 | 
			
		||||
		playQueueStore.duration = player.value?.duration?? 0
 | 
			
		||||
		playQueueStore.currentTime = player.value?.currentTime?? 0
 | 
			
		||||
		playQueueStore.duration = player.value?.duration ?? 0
 | 
			
		||||
		playQueueStore.currentTime = player.value?.currentTime ?? 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	watch(() => playQueueStore.updatedCurrentTime, (newValue) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -69,7 +144,7 @@ function playNext() {
 | 
			
		|||
	if (playQueueStore.currentIndex === playQueueStore.list.length - 1) {
 | 
			
		||||
		console.log("at the bottom, pause")
 | 
			
		||||
		playQueueStore.currentIndex = 0
 | 
			
		||||
		if(playQueueStore.playMode.repeat === 'all') {
 | 
			
		||||
		if (playQueueStore.playMode.repeat === 'all') {
 | 
			
		||||
			playQueueStore.currentIndex = 0
 | 
			
		||||
			playQueueStore.isPlaying = true
 | 
			
		||||
		} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -92,18 +167,43 @@ function playPrevious() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
function updateCurrentTime() {
 | 
			
		||||
	playQueueStore.currentTime = player.value?.currentTime?? 0
 | 
			
		||||
	playQueueStore.currentTime = player.value?.currentTime ?? 0
 | 
			
		||||
 | 
			
		||||
	// 智能预加载策略:支持动态配置
 | 
			
		||||
	if (playQueueStore.duration > 0) {
 | 
			
		||||
		const progress = playQueueStore.currentTime / playQueueStore.duration
 | 
			
		||||
		const remainingTime = playQueueStore.duration - playQueueStore.currentTime
 | 
			
		||||
 | 
			
		||||
		// 从 localStorage 获取配置,如果没有则使用默认值
 | 
			
		||||
		const config = JSON.parse(localStorage.getItem('preloadConfig') || '{}')
 | 
			
		||||
		const preloadTrigger = (config.preloadTrigger || 50) / 100 // 转换为小数
 | 
			
		||||
		const remainingTimeThreshold = config.remainingTimeThreshold || 30
 | 
			
		||||
 | 
			
		||||
		if ((progress > preloadTrigger || remainingTime < remainingTimeThreshold) && !playQueueStore.isPreloading) {
 | 
			
		||||
			console.log(`[Player] 触发预加载 - 进度: ${Math.round(progress * 100)}%, 剩余: ${Math.round(remainingTime)}s`)
 | 
			
		||||
 | 
			
		||||
			try {
 | 
			
		||||
				if (typeof playQueueStore.preloadNext === 'function') {
 | 
			
		||||
					playQueueStore.preloadNext()
 | 
			
		||||
				} else {
 | 
			
		||||
					console.error('[Player] preloadNext 不是一个函数')
 | 
			
		||||
				}
 | 
			
		||||
			} catch (error) {
 | 
			
		||||
				console.error('[Player] 智能预加载失败:', error)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
console.log('[Player] 初始化 audioVisualizer')
 | 
			
		||||
const { barHeights, connectAudio, isAnalyzing, error } = audioVisualizer({
 | 
			
		||||
  sensitivity: 1.5,
 | 
			
		||||
  barCount: 6,
 | 
			
		||||
  maxDecibels: -10,
 | 
			
		||||
  bassBoost: 0.8,
 | 
			
		||||
  midBoost: 1.2,
 | 
			
		||||
  trebleBoost: 1.4,
 | 
			
		||||
  threshold: 0
 | 
			
		||||
	sensitivity: 1.5,
 | 
			
		||||
	barCount: 6,
 | 
			
		||||
	maxDecibels: -10,
 | 
			
		||||
	bassBoost: 0.8,
 | 
			
		||||
	midBoost: 1.2,
 | 
			
		||||
	trebleBoost: 1.4,
 | 
			
		||||
	threshold: 0
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
console.log('[Player] audioVisualizer 返回值:', { barHeights: barHeights.value, isAnalyzing: isAnalyzing.value })
 | 
			
		||||
| 
						 | 
				
			
			@ -111,15 +211,15 @@ console.log('[Player] audioVisualizer 返回值:', { barHeights: barHeights.valu
 | 
			
		|||
// 监听播放列表变化
 | 
			
		||||
watch(() => playQueueStore.list.length, async (newLength) => {
 | 
			
		||||
	console.log('[Player] 播放列表长度变化:', newLength)
 | 
			
		||||
	if (newLength === 0) { 
 | 
			
		||||
	if (newLength === 0) {
 | 
			
		||||
		console.log('[Player] 播放列表为空,跳过连接')
 | 
			
		||||
		return 
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	// 等待下一帧,确保 audio 元素已经渲染
 | 
			
		||||
	await nextTick()
 | 
			
		||||
	
 | 
			
		||||
	if (player.value) { 
 | 
			
		||||
 | 
			
		||||
	if (player.value) {
 | 
			
		||||
		console.log('[Player] 连接音频元素到可视化器')
 | 
			
		||||
		console.log('[Player] 音频元素状态:', {
 | 
			
		||||
			src: player.value.src?.substring(0, 50) + '...',
 | 
			
		||||
| 
						 | 
				
			
			@ -130,8 +230,13 @@ watch(() => playQueueStore.list.length, async (newLength) => {
 | 
			
		|||
	} else {
 | 
			
		||||
		console.log('[Player] ❌ 音频元素不存在')
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	playQueueStore.visualizer = barHeights.value
 | 
			
		||||
 | 
			
		||||
	// 开始预加载第一首歌的下一首
 | 
			
		||||
	setTimeout(() => {
 | 
			
		||||
		playQueueStore.preloadNext()
 | 
			
		||||
	}, 2000)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// 监听音频元素变化
 | 
			
		||||
| 
						 | 
				
			
			@ -185,60 +290,62 @@ watch(() => playQueueStore.playMode.shuffle, (isShuffle) => {
 | 
			
		|||
		// 将当前播放的歌曲的原来的索引赋给 currentIndex
 | 
			
		||||
		playQueueStore.currentIndex = playQueueStore.shuffleList[playQueueStore.currentIndex]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 切换播放模式后重新预加载
 | 
			
		||||
	setTimeout(() => {
 | 
			
		||||
		playQueueStore.clearAllPreloadedAudio()
 | 
			
		||||
		playQueueStore.preloadNext()
 | 
			
		||||
	}, 500)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
function getCurrentTrack() {
 | 
			
		||||
	if (playQueueStore.playMode.shuffle) {
 | 
			
		||||
		return playQueueStore.list[playQueueStore.shuffleList[playQueueStore.currentIndex]] 
 | 
			
		||||
	} else {
 | 
			
		||||
		return playQueueStore.list[playQueueStore.currentIndex]
 | 
			
		||||
	}
 | 
			
		||||
	return currentTrack.value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 组件卸载时清理预加载
 | 
			
		||||
// onUnmounted(() => {
 | 
			
		||||
//   playQueueStore.clearAllPreloadedAudio()
 | 
			
		||||
// })
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
	<div>
 | 
			
		||||
		<audio
 | 
			
		||||
			:src="(() => {
 | 
			
		||||
				if (playQueueStore.playMode.shuffle) {
 | 
			
		||||
					return playQueueStore.list[playQueueStore.shuffleList[playQueueStore.currentIndex]] ? playQueueStore.list[playQueueStore.shuffleList[playQueueStore.currentIndex]].song.sourceUrl : ''
 | 
			
		||||
				} else {
 | 
			
		||||
					return playQueueStore.list[playQueueStore.currentIndex] ? playQueueStore.list[playQueueStore.currentIndex].song.sourceUrl : ''
 | 
			
		||||
				}
 | 
			
		||||
			})()"
 | 
			
		||||
			ref="playerRef" 
 | 
			
		||||
			:autoplay="playQueueStore.isPlaying" 
 | 
			
		||||
			v-if="playQueueStore.list.length !== 0" 
 | 
			
		||||
			@ended="() => {
 | 
			
		||||
		<audio :src="currentAudioSrc" ref="playerRef" :autoplay="playQueueStore.isPlaying"
 | 
			
		||||
			v-if="playQueueStore.list.length !== 0" @ended="() => {
 | 
			
		||||
				if (playQueueStore.playMode.repeat === 'single') { playQueueStore.isPlaying = true }
 | 
			
		||||
				else { playNext() }
 | 
			
		||||
			}"
 | 
			
		||||
			@pause="playQueueStore.isPlaying = false" 
 | 
			
		||||
			@play="playQueueStore.isPlaying = true" 
 | 
			
		||||
			@playing="() => {
 | 
			
		||||
			}" @pause="playQueueStore.isPlaying = false" @play="playQueueStore.isPlaying = true" @playing="() => {
 | 
			
		||||
				console.log('[Player] 音频开始播放事件')
 | 
			
		||||
				playQueueStore.isBuffering = false
 | 
			
		||||
				setMetadata()
 | 
			
		||||
			}" 
 | 
			
		||||
			@waiting="playQueueStore.isBuffering = true"
 | 
			
		||||
			@loadeddata="() => console.log('[Player] 音频数据加载完成')"
 | 
			
		||||
			@canplay="() => console.log('[Player] 音频可以播放')"
 | 
			
		||||
			@error="(e) => console.error('[Player] 音频错误:', e)"
 | 
			
		||||
			crossorigin="anonymous"
 | 
			
		||||
			@timeupdate="updateCurrentTime">
 | 
			
		||||
			}" @waiting="playQueueStore.isBuffering = true" @loadeddata="() => {
 | 
			
		||||
				console.log('[Player] 音频数据加载完成')
 | 
			
		||||
				playQueueStore.isBuffering = false
 | 
			
		||||
			}" @canplay="() => {
 | 
			
		||||
				console.log('[Player] 音频可以播放')
 | 
			
		||||
				playQueueStore.isBuffering = false
 | 
			
		||||
			}" @error="(e) => {
 | 
			
		||||
				console.error('[Player] 音频错误:', e)
 | 
			
		||||
				playQueueStore.isBuffering = false
 | 
			
		||||
			}" crossorigin="anonymous" @timeupdate="updateCurrentTime">
 | 
			
		||||
		</audio>
 | 
			
		||||
 | 
			
		||||
		<!-- 预加载进度指示器(可选显示) -->
 | 
			
		||||
		<div v-if="playQueueStore.isPreloading"
 | 
			
		||||
			class="fixed top-4 right-4 bg-black/80 text-white px-3 py-1 rounded text-xs z-50">
 | 
			
		||||
			预加载中... {{ Math.round(playQueueStore.preloadProgress) }}%
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
		<div
 | 
			
		||||
			class="text-white h-9 bg-neutral-800/80 border border-[#ffffff39] rounded-full text-center backdrop-blur-3xl flex gap-2 overflow-hidden select-none"
 | 
			
		||||
			v-if="playQueueStore.list.length !== 0 && route.path !== '/playroom'"
 | 
			
		||||
			>
 | 
			
		||||
			v-if="playQueueStore.list.length !== 0 && route.path !== '/playroom'">
 | 
			
		||||
			<RouterLink to="/playroom">
 | 
			
		||||
				<img :src="getCurrentTrack().album?.coverUrl ?? ''" class="rounded-full h-9 w-9" />
 | 
			
		||||
				<img :src="getCurrentTrack()?.album?.coverUrl ?? ''" class="rounded-full h-9 w-9" />
 | 
			
		||||
			</RouterLink>
 | 
			
		||||
 | 
			
		||||
			<RouterLink to="/playroom">
 | 
			
		||||
				<div class="flex items-center w-32 h-9">
 | 
			
		||||
					<span class="truncate">{{ getCurrentTrack().song.name }}</span>
 | 
			
		||||
					<span class="truncate">{{ getCurrentTrack()?.song.name }}</span>
 | 
			
		||||
				</div>
 | 
			
		||||
			</RouterLink>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -248,9 +355,10 @@ function getCurrentTrack() {
 | 
			
		|||
				<div v-if="playQueueStore.isPlaying">
 | 
			
		||||
					<LoadingIndicator v-if="playQueueStore.isBuffering === true" :size="4" />
 | 
			
		||||
					<div v-else class="h-4 flex justify-center items-center gap-[.125rem]">
 | 
			
		||||
						<div class="bg-white/75 w-[.125rem] rounded-full" v-for="(bar, index) in playQueueStore.visualizer" :key="index" :style="{
 | 
			
		||||
							height: `${Math.max(10, bar)}%`
 | 
			
		||||
						}" />
 | 
			
		||||
						<div class="bg-white/75 w-[.125rem] rounded-full" v-for="(bar, index) in playQueueStore.visualizer"
 | 
			
		||||
							:key="index" :style="{
 | 
			
		||||
								height: `${Math.max(10, bar)}%`
 | 
			
		||||
							}" />
 | 
			
		||||
					</div>
 | 
			
		||||
				</div>
 | 
			
		||||
				<PlayIcon v-else :size="4" />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,9 @@
 | 
			
		|||
import { defineStore } from "pinia"
 | 
			
		||||
import { ref } from "vue"
 | 
			
		||||
import { ref, computed } from "vue"
 | 
			
		||||
 | 
			
		||||
export const usePlayQueueStore = defineStore('queue', () =>{
 | 
			
		||||
  const list = ref<QueueItem[]>([])
 | 
			
		||||
  const currentIndex = ref<number>(0)
 | 
			
		||||
export const usePlayQueueStore = defineStore('queue', () => {
 | 
			
		||||
	const list = ref<QueueItem[]>([])
 | 
			
		||||
	const currentIndex = ref<number>(0)
 | 
			
		||||
	const isPlaying = ref<boolean>(false)
 | 
			
		||||
	const queueReplaceLock = ref<boolean>(false)
 | 
			
		||||
	const isBuffering = ref<boolean>(false)
 | 
			
		||||
| 
						 | 
				
			
			@ -21,5 +21,183 @@ export const usePlayQueueStore = defineStore('queue', () =>{
 | 
			
		|||
	})
 | 
			
		||||
	const shuffleCurrent = ref<boolean | undefined>(undefined)
 | 
			
		||||
 | 
			
		||||
  return { list, currentIndex, isPlaying, queueReplaceLock, isBuffering, currentTime, duration, updatedCurrentTime, visualizer, shuffleList, playMode, shuffleCurrent }
 | 
			
		||||
	// 预加载相关状态
 | 
			
		||||
	const preloadedAudio = ref<Map<string, HTMLAudioElement>>(new Map())
 | 
			
		||||
	const isPreloading = ref<boolean>(false)
 | 
			
		||||
	const preloadProgress = ref<number>(0)
 | 
			
		||||
 | 
			
		||||
	// 获取下一首歌的索引
 | 
			
		||||
	const getNextIndex = computed(() => {
 | 
			
		||||
		if (list.value.length === 0) return -1
 | 
			
		||||
 | 
			
		||||
		if (playMode.value.repeat === 'single') {
 | 
			
		||||
			return currentIndex.value
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (playMode.value.shuffle && shuffleList.value.length > 0) {
 | 
			
		||||
			const currentShuffleIndex = shuffleList.value.indexOf(currentIndex.value)
 | 
			
		||||
			if (currentShuffleIndex < shuffleList.value.length - 1) {
 | 
			
		||||
				return shuffleList.value[currentShuffleIndex + 1]
 | 
			
		||||
			} else if (playMode.value.repeat === 'all') {
 | 
			
		||||
				return shuffleList.value[0]
 | 
			
		||||
			}
 | 
			
		||||
			return -1
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (currentIndex.value < list.value.length - 1) {
 | 
			
		||||
			return currentIndex.value + 1
 | 
			
		||||
		} else if (playMode.value.repeat === 'all') {
 | 
			
		||||
			return 0
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return -1
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// 预加载下一首歌
 | 
			
		||||
	const preloadNext = async () => {
 | 
			
		||||
		console.log('[Store] preloadNext 被调用')
 | 
			
		||||
		
 | 
			
		||||
		const nextIndex = getNextIndex.value
 | 
			
		||||
		if (nextIndex === -1) {
 | 
			
		||||
			console.log('[Store] 没有下一首歌,跳过预加载')
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 获取下一首歌曲对象
 | 
			
		||||
		let nextSong
 | 
			
		||||
		if (playMode.value.shuffle && shuffleList.value.length > 0) {
 | 
			
		||||
			nextSong = list.value[shuffleList.value[nextIndex]]
 | 
			
		||||
		} else {
 | 
			
		||||
			nextSong = list.value[nextIndex]
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!nextSong || !nextSong.song) {
 | 
			
		||||
			console.log('[Store] 下一首歌曲不存在,跳过预加载')
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const songId = nextSong.song.cid
 | 
			
		||||
 | 
			
		||||
		// 如果已经预加载过,跳过
 | 
			
		||||
		if (preloadedAudio.value.has(songId)) {
 | 
			
		||||
			console.log(`[Store] 歌曲 ${songId} 已预加载`)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// 检查是否有有效的音频源
 | 
			
		||||
		if (!nextSong.song.sourceUrl) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			isPreloading.value = true
 | 
			
		||||
			preloadProgress.value = 0
 | 
			
		||||
 | 
			
		||||
			const audio = new Audio()
 | 
			
		||||
			audio.preload = 'auto'
 | 
			
		||||
			audio.crossOrigin = 'anonymous'
 | 
			
		||||
 | 
			
		||||
			// 监听加载进度
 | 
			
		||||
			audio.addEventListener('progress', () => {
 | 
			
		||||
				if (audio.buffered.length > 0) {
 | 
			
		||||
					const buffered = audio.buffered.end(0)
 | 
			
		||||
					const total = audio.duration || 1
 | 
			
		||||
					preloadProgress.value = (buffered / total) * 100
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
			// 监听加载完成
 | 
			
		||||
			audio.addEventListener('canplaythrough', () => {
 | 
			
		||||
				preloadedAudio.value.set(songId, audio)
 | 
			
		||||
				isPreloading.value = false
 | 
			
		||||
				preloadProgress.value = 100
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
			// 监听加载错误
 | 
			
		||||
			audio.addEventListener('error', (e) => {
 | 
			
		||||
				console.error(`[Store] 预加载音频失败: ${e}`)
 | 
			
		||||
				isPreloading.value = false
 | 
			
		||||
				preloadProgress.value = 0
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
			// 设置音频源并开始加载
 | 
			
		||||
			audio.src = nextSong.song.sourceUrl
 | 
			
		||||
 | 
			
		||||
		} catch (error) {
 | 
			
		||||
			isPreloading.value = false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 获取预加载的音频对象
 | 
			
		||||
	const getPreloadedAudio = (songId: string): HTMLAudioElement | null => {
 | 
			
		||||
		const audio = preloadedAudio.value.get(songId) || null
 | 
			
		||||
		return audio
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 清理预加载的音频
 | 
			
		||||
	const clearPreloadedAudio = (songId: string) => {
 | 
			
		||||
		const audio = preloadedAudio.value.get(songId)
 | 
			
		||||
		if (audio) {
 | 
			
		||||
			audio.pause()
 | 
			
		||||
			audio.src = ''
 | 
			
		||||
			preloadedAudio.value.delete(songId)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 清理所有预加载的音频
 | 
			
		||||
	const clearAllPreloadedAudio = () => {
 | 
			
		||||
		preloadedAudio.value.forEach((_audio, songId) => {
 | 
			
		||||
			clearPreloadedAudio(songId)
 | 
			
		||||
		})
 | 
			
		||||
		preloadedAudio.value.clear()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 限制预加载缓存大小(最多保留3首歌)
 | 
			
		||||
	const limitPreloadCache = () => {
 | 
			
		||||
		while (preloadedAudio.value.size > 3) {
 | 
			
		||||
			const oldestKey = preloadedAudio.value.keys().next().value
 | 
			
		||||
			if (oldestKey) { 
 | 
			
		||||
				clearPreloadedAudio(oldestKey) 
 | 
			
		||||
			} else {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 调试函数:打印当前状态
 | 
			
		||||
	const debugPreloadState = () => {
 | 
			
		||||
		console.log('[Store] 预加载状态:', {
 | 
			
		||||
			isPreloading: isPreloading.value,
 | 
			
		||||
			progress: preloadProgress.value,
 | 
			
		||||
			cacheSize: preloadedAudio.value.size,
 | 
			
		||||
			cachedSongs: Array.from(preloadedAudio.value.keys()),
 | 
			
		||||
			nextIndex: getNextIndex.value
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return {
 | 
			
		||||
		list,
 | 
			
		||||
		currentIndex,
 | 
			
		||||
		isPlaying,
 | 
			
		||||
		queueReplaceLock,
 | 
			
		||||
		isBuffering,
 | 
			
		||||
		currentTime,
 | 
			
		||||
		duration,
 | 
			
		||||
		updatedCurrentTime,
 | 
			
		||||
		visualizer,
 | 
			
		||||
		shuffleList,
 | 
			
		||||
		playMode,
 | 
			
		||||
		shuffleCurrent,
 | 
			
		||||
		// 预加载相关 - 确保所有函数都在返回对象中
 | 
			
		||||
		preloadedAudio,
 | 
			
		||||
		isPreloading,
 | 
			
		||||
		preloadProgress,
 | 
			
		||||
		getNextIndex,
 | 
			
		||||
		preloadNext,
 | 
			
		||||
		getPreloadedAudio,
 | 
			
		||||
		clearPreloadedAudio,
 | 
			
		||||
		clearAllPreloadedAudio,
 | 
			
		||||
		limitPreloadCache,
 | 
			
		||||
		debugPreloadState
 | 
			
		||||
	}
 | 
			
		||||
})
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user