feat: 跳转至下一首功能
This commit is contained in:
parent
024a84b2eb
commit
61a99975b2
|
@ -7,6 +7,7 @@ import apis from '../apis'
|
||||||
const playQueue = usePlayQueueStore()
|
const playQueue = usePlayQueueStore()
|
||||||
|
|
||||||
const resourcesUrl = ref<{ [key: string]: string }>({})
|
const resourcesUrl = ref<{ [key: string]: string }>({})
|
||||||
|
const audioRefs = ref<{ [key: string]: HTMLAudioElement }>({}) // audio 元素的引用
|
||||||
|
|
||||||
// 监听播放列表变化
|
// 监听播放列表变化
|
||||||
watch(() => playQueue.queue, async () => {
|
watch(() => playQueue.queue, async () => {
|
||||||
|
@ -20,9 +21,10 @@ watch(() => playQueue.queue, async () => {
|
||||||
resourcesUrl.value = newResourcesUrl
|
resourcesUrl.value = newResourcesUrl
|
||||||
})
|
})
|
||||||
|
|
||||||
// 在播放曲目变动时,将元数据更新到浏览器和操作系统中
|
watch(() => playQueue.currentTrack, async (newTrack, oldTrack) => {
|
||||||
watch(() => playQueue.currentTrack, async () => {
|
|
||||||
if (!playQueue.currentTrack) return
|
if (!playQueue.currentTrack) return
|
||||||
|
|
||||||
|
// 更新元数据
|
||||||
navigator.mediaSession.metadata = new MediaMetadata({
|
navigator.mediaSession.metadata = new MediaMetadata({
|
||||||
title: playQueue.currentTrack.song.name,
|
title: playQueue.currentTrack.song.name,
|
||||||
artist: artistsOrganize(playQueue.currentTrack.song.artists ?? []),
|
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
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -74,9 +126,12 @@ function isAutoPlay(cid: string) {
|
||||||
v-if="resourcesUrl[track.song.cid]"
|
v-if="resourcesUrl[track.song.cid]"
|
||||||
:src="resourcesUrl[track.song.cid]"
|
:src="resourcesUrl[track.song.cid]"
|
||||||
preload="auto"
|
preload="auto"
|
||||||
:ref="`audio-${track.song.cid}`"
|
:ref="el => setAudioRef(track.song.cid, el as HTMLAudioElement)"
|
||||||
:autoplay="isAutoPlay(track.song.cid)"
|
:autoplay="isAutoPlay(track.song.cid)"
|
||||||
|
@ended="endOfPlay"
|
||||||
|
@timeupdate=""
|
||||||
/>
|
/>
|
||||||
|
{{track.song.cid}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
|
import { debugStore } from '../utils/debug'
|
||||||
|
|
||||||
export const usePlayQueueStore = defineStore('queue', () => {
|
export const usePlayQueueStore = defineStore('queue', () => {
|
||||||
// 内部状态
|
// 内部状态
|
||||||
|
@ -10,6 +11,7 @@ export const usePlayQueueStore = defineStore('queue', () => {
|
||||||
const currentPlaying = ref(0) // 当前播放指针,指针在 queueOrder 中寻址(无论是否开启了随机播放)
|
const currentPlaying = ref(0) // 当前播放指针,指针在 queueOrder 中寻址(无论是否开启了随机播放)
|
||||||
const queueOrder = ref<number[]>([]) // 播放队列顺序
|
const queueOrder = ref<number[]>([]) // 播放队列顺序
|
||||||
const isPlaying = ref(false)
|
const isPlaying = ref(false)
|
||||||
|
const playProgress = ref(0) // 当前曲目的播放时间指针
|
||||||
|
|
||||||
// 暴露给外部的响应式只读引用
|
// 暴露给外部的响应式只读引用
|
||||||
const queueState = computed(() =>
|
const queueState = computed(() =>
|
||||||
|
@ -27,6 +29,9 @@ export const usePlayQueueStore = defineStore('queue', () => {
|
||||||
return queue.value[actualIndex] || null
|
return queue.value[actualIndex] || null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 获取当前播放时间
|
||||||
|
const playProgressState = computed(() => playProgress.value)
|
||||||
|
|
||||||
// 获取当前是否正在播放
|
// 获取当前是否正在播放
|
||||||
const playingState = computed(() => isPlaying.value)
|
const playingState = computed(() => isPlaying.value)
|
||||||
|
|
||||||
|
@ -62,7 +67,6 @@ export const usePlayQueueStore = defineStore('queue', () => {
|
||||||
|
|
||||||
/***********
|
/***********
|
||||||
* 播放控制相关
|
* 播放控制相关
|
||||||
*
|
|
||||||
**********/
|
**********/
|
||||||
// 控制播放
|
// 控制播放
|
||||||
const togglePlay = (turnTo?: boolean) => {
|
const togglePlay = (turnTo?: boolean) => {
|
||||||
|
@ -71,6 +75,33 @@ export const usePlayQueueStore = defineStore('queue', () => {
|
||||||
isPlaying.value = newPlayState
|
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 // 状态未改变
|
if (newShuffleState === isShuffle.value) return // 状态未改变
|
||||||
|
|
||||||
// TODO: 进行洗牌
|
// TODO: 进行洗牌(以下代码是 AI 写的,需要人工复查)
|
||||||
/* if (newShuffleState) {
|
/* if (newShuffleState) {
|
||||||
// 开启随机播放:保存当前顺序并打乱
|
// 开启随机播放:保存当前顺序并打乱
|
||||||
const originalOrder = [...queueOrder.value]
|
const originalOrder = [...queueOrder.value]
|
||||||
|
@ -149,11 +180,16 @@ export const usePlayQueueStore = defineStore('queue', () => {
|
||||||
currentTrack,
|
currentTrack,
|
||||||
currentIndex: currentPlaying,
|
currentIndex: currentPlaying,
|
||||||
isPlaying: playingState,
|
isPlaying: playingState,
|
||||||
|
playProgress: playProgressState,
|
||||||
|
|
||||||
// 修改方法
|
// 修改方法
|
||||||
replaceQueue,
|
replaceQueue,
|
||||||
toggleShuffle,
|
toggleShuffle,
|
||||||
toggleLoop,
|
toggleLoop,
|
||||||
togglePlay
|
togglePlay,
|
||||||
|
toggleQueuePlay,
|
||||||
|
skipToNext,
|
||||||
|
continueToNext,
|
||||||
|
reportPlayProgress
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue
Block a user