feat: 跳转至下一首功能
This commit is contained in:
		
							parent
							
								
									024a84b2eb
								
							
						
					
					
						commit
						61a99975b2
					
				| 
						 | 
				
			
			@ -7,6 +7,7 @@ import apis from '../apis'
 | 
			
		|||
const playQueue = usePlayQueueStore()
 | 
			
		||||
 | 
			
		||||
const resourcesUrl = ref<{ [key: string]: string }>({})
 | 
			
		||||
const audioRefs = ref<{ [key: string]: HTMLAudioElement }>({}) // audio 元素的引用
 | 
			
		||||
 | 
			
		||||
// 监听播放列表变化
 | 
			
		||||
watch(() => playQueue.queue, async () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -20,9 +21,10 @@ watch(() => playQueue.queue, async () => {
 | 
			
		|||
	resourcesUrl.value = newResourcesUrl
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// 在播放曲目变动时,将元数据更新到浏览器和操作系统中
 | 
			
		||||
watch(() => playQueue.currentTrack, async () => {
 | 
			
		||||
watch(() => playQueue.currentTrack, async (newTrack, oldTrack) => {
 | 
			
		||||
	if (!playQueue.currentTrack) return
 | 
			
		||||
 | 
			
		||||
	// 更新元数据
 | 
			
		||||
	navigator.mediaSession.metadata = new MediaMetadata({
 | 
			
		||||
		title: playQueue.currentTrack.song.name,
 | 
			
		||||
		artist: artistsOrganize(playQueue.currentTrack.song.artists ?? []),
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +37,30 @@ watch(() => playQueue.currentTrack, async () => {
 | 
			
		|||
			},
 | 
			
		||||
		],
 | 
			
		||||
	})
 | 
			
		||||
	navigator.mediaSession.setActionHandler('previoustrack', () => {})
 | 
			
		||||
	navigator.mediaSession.setActionHandler('nexttrack', playQueue.skipToNext)
 | 
			
		||||
 | 
			
		||||
	// 如果目前歌曲变动时正在播放,则激活对应的 audio 组件,并将播放时间进度重置为零
 | 
			
		||||
	if (!playQueue.isPlaying) return
 | 
			
		||||
	debugPlayer("正在播放,变更至下一首歌")
 | 
			
		||||
	if (oldTrack) {
 | 
			
		||||
		const oldAudio = getAudioElement(oldTrack.song.cid)
 | 
			
		||||
		if (oldAudio && !oldAudio.paused) {
 | 
			
		||||
			oldAudio.pause()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const newAudio = getAudioElement(newTrack.song.cid)
 | 
			
		||||
	if (newAudio) {
 | 
			
		||||
		try {
 | 
			
		||||
			await newAudio.play()
 | 
			
		||||
			debugPlayer(`开始播放: audio-${newTrack.song.cid}`)
 | 
			
		||||
		} catch (error) {
 | 
			
		||||
			console.error(`播放失败: audio-${newTrack.song.cid}`, error)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		console.warn(`找不到音频元素: audio-${newTrack.song.cid}`)
 | 
			
		||||
	}
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// 优化音乐人字符串显示
 | 
			
		||||
| 
						 | 
				
			
			@ -65,6 +91,32 @@ function isAutoPlay(cid: string) {
 | 
			
		|||
 | 
			
		||||
	return true	
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 获取 audio 元素的 ref
 | 
			
		||||
function getAudioElement(cid: string): HTMLAudioElement | null {
 | 
			
		||||
	debugPlayer('Getting audio element for:', cid, audioRefs.value)
 | 
			
		||||
	return audioRefs.value[cid] || null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// audio 元素结束播放事件
 | 
			
		||||
function endOfPlay() {
 | 
			
		||||
	debugPlayer("结束播放")
 | 
			
		||||
	if (playQueue.loopingMode !== "single") {
 | 
			
		||||
		const next = playQueue.queue[playQueue.currentIndex + 1]
 | 
			
		||||
		debugPlayer(next.song.cid)
 | 
			
		||||
		debugPlayer(audioRefs.value[next.song.cid])
 | 
			
		||||
		audioRefs.value[next.song.cid].play()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function setAudioRef(cid: string, el: HTMLAudioElement | null) {
 | 
			
		||||
	if (el) {
 | 
			
		||||
		audioRefs.value[cid] = el
 | 
			
		||||
		debugPlayer(`Audio element for ${cid} registered`, el)
 | 
			
		||||
	} else {
 | 
			
		||||
		delete audioRefs.value[cid]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
| 
						 | 
				
			
			@ -74,9 +126,12 @@ function isAutoPlay(cid: string) {
 | 
			
		|||
				v-if="resourcesUrl[track.song.cid]" 
 | 
			
		||||
				:src="resourcesUrl[track.song.cid]" 
 | 
			
		||||
				preload="auto" 
 | 
			
		||||
				:ref="`audio-${track.song.cid}`"
 | 
			
		||||
				:ref="el => setAudioRef(track.song.cid, el as HTMLAudioElement)"
 | 
			
		||||
				:autoplay="isAutoPlay(track.song.cid)"
 | 
			
		||||
				@ended="endOfPlay"
 | 
			
		||||
				@timeupdate=""
 | 
			
		||||
			/>
 | 
			
		||||
				{{track.song.cid}}
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</template>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
import { defineStore } from 'pinia'
 | 
			
		||||
import { ref, computed } from 'vue'
 | 
			
		||||
import { debugStore } from '../utils/debug'
 | 
			
		||||
 | 
			
		||||
export const usePlayQueueStore = defineStore('queue', () => {
 | 
			
		||||
	// 内部状态
 | 
			
		||||
| 
						 | 
				
			
			@ -10,6 +11,7 @@ export const usePlayQueueStore = defineStore('queue', () => {
 | 
			
		|||
	const currentPlaying = ref(0) // 当前播放指针,指针在 queueOrder 中寻址(无论是否开启了随机播放)
 | 
			
		||||
	const queueOrder = ref<number[]>([]) // 播放队列顺序
 | 
			
		||||
	const isPlaying = ref(false)
 | 
			
		||||
	const playProgress = ref(0) // 当前曲目的播放时间指针
 | 
			
		||||
 | 
			
		||||
	// 暴露给外部的响应式只读引用
 | 
			
		||||
	const queueState = computed(() =>
 | 
			
		||||
| 
						 | 
				
			
			@ -27,6 +29,9 @@ export const usePlayQueueStore = defineStore('queue', () => {
 | 
			
		|||
		return queue.value[actualIndex] || null
 | 
			
		||||
	})
 | 
			
		||||
	
 | 
			
		||||
	// 获取当前播放时间
 | 
			
		||||
	const playProgressState = computed(() => playProgress.value)
 | 
			
		||||
	
 | 
			
		||||
	// 获取当前是否正在播放
 | 
			
		||||
	const playingState = computed(() => isPlaying.value)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +67,6 @@ export const usePlayQueueStore = defineStore('queue', () => {
 | 
			
		|||
 | 
			
		||||
	/***********
 | 
			
		||||
	 * 播放控制相关
 | 
			
		||||
	 *
 | 
			
		||||
	 **********/
 | 
			
		||||
	// 控制播放
 | 
			
		||||
	const togglePlay = (turnTo?: boolean) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -71,6 +75,33 @@ export const usePlayQueueStore = defineStore('queue', () => {
 | 
			
		|||
		isPlaying.value = newPlayState
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 跳转至队列的某首歌曲
 | 
			
		||||
	const toggleQueuePlay = (turnTo: number) => {
 | 
			
		||||
		if (turnTo < 0 || turnTo >= queue.value.length) return
 | 
			
		||||
		currentPlaying.value = turnTo
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 跳至下一首(通常为用户点按下一首按钮)
 | 
			
		||||
	const skipToNext = () => {
 | 
			
		||||
		currentPlaying.value = currentPlaying.value + 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 继续播放接下来的曲目
 | 
			
		||||
	// 通常为当前曲目播放完毕,需要通过循环模式判断应该重置进度或队列指针 +1
 | 
			
		||||
	const continueToNext = () => {
 | 
			
		||||
		debugStore(loopingMode.value)
 | 
			
		||||
		// TODO: 需要留意 progress seeking 相关
 | 
			
		||||
		if (loopingMode.value === 'single') playProgress.value = 0
 | 
			
		||||
		else currentPlaying.value = currentPlaying.value + 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 回报播放进度
 | 
			
		||||
	const reportPlayProgress = (progress: number) => {
 | 
			
		||||
		debugStore(`进度更新回报: ${progress}`)
 | 
			
		||||
		playProgress.value = progress
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/************
 | 
			
		||||
	 * 播放模式相关
 | 
			
		||||
	 **********/
 | 
			
		||||
| 
						 | 
				
			
			@ -81,7 +112,7 @@ export const usePlayQueueStore = defineStore('queue', () => {
 | 
			
		|||
 | 
			
		||||
		if (newShuffleState === isShuffle.value) return // 状态未改变
 | 
			
		||||
 | 
			
		||||
		// TODO: 进行洗牌
 | 
			
		||||
		// TODO: 进行洗牌(以下代码是 AI 写的,需要人工复查)
 | 
			
		||||
		/* if (newShuffleState) {
 | 
			
		||||
			// 开启随机播放:保存当前顺序并打乱
 | 
			
		||||
			const originalOrder = [...queueOrder.value]
 | 
			
		||||
| 
						 | 
				
			
			@ -149,11 +180,16 @@ export const usePlayQueueStore = defineStore('queue', () => {
 | 
			
		|||
		currentTrack,
 | 
			
		||||
		currentIndex: currentPlaying,
 | 
			
		||||
		isPlaying: playingState,
 | 
			
		||||
		playProgress: playProgressState,
 | 
			
		||||
 | 
			
		||||
		// 修改方法
 | 
			
		||||
		replaceQueue,
 | 
			
		||||
		toggleShuffle,
 | 
			
		||||
		toggleLoop,
 | 
			
		||||
		togglePlay
 | 
			
		||||
		togglePlay,
 | 
			
		||||
		toggleQueuePlay,
 | 
			
		||||
		skipToNext,
 | 
			
		||||
		continueToNext,
 | 
			
		||||
		reportPlayProgress
 | 
			
		||||
	}
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user