feat: 处理暂停

This commit is contained in:
Astrian Zheng 2025-08-22 18:55:39 +10:00
parent c173f83301
commit d89dd55a51
Signed by: Astrian
SSH Key Fingerprint: SHA256:rVnhx3DAKjujCwWE13aDl7uV6+9U1MvydLkNRXJrBiA
3 changed files with 77 additions and 49 deletions

View File

@ -90,7 +90,7 @@ class WebAudioPlayer {
})
navigator.mediaSession.setActionHandler('stop', () => {
console.log('Media session: stop requested')
this.stop()
})
}
}
@ -111,29 +111,20 @@ class WebAudioPlayer {
this.nextSource?.stop()
}
// buffer
const loadBuffer = async (track: QueueItem) => {
if (this.audioBuffer[track.song.cid]) return //
const response = await fetch(track.sourceUrl ?? "")
const arrayBuffer = await response.arrayBuffer()
const audioBuffer = await this.context.decodeAudioData(arrayBuffer)
this.audioBuffer[track.song.cid] = audioBuffer
}
if (playQueue.currentTrack) {
await loadBuffer(playQueue.currentTrack)
await this.loadBuffer(playQueue.currentTrack)
this.play()
}
if (playQueue.nextTrack) {
await loadBuffer(playQueue.nextTrack)
await this.loadBuffer(playQueue.nextTrack)
if (playState.isPlaying) this.scheduleNextTrack()
} else {
this.nextSource = null
}
if (playQueue.previousTrack)
await loadBuffer(playQueue.previousTrack)
await this.loadBuffer(playQueue.previousTrack)
debugPlayer("缓存完成")
} catch (error) {
@ -141,25 +132,56 @@ class WebAudioPlayer {
}
}
// buffer
loadBuffer = async (track: QueueItem) => {
if (this.audioBuffer[track.song.cid]) return //
const response = await fetch(track.sourceUrl ?? "")
const arrayBuffer = await response.arrayBuffer()
const audioBuffer = await this.context.decodeAudioData(arrayBuffer)
this.audioBuffer[track.song.cid] = audioBuffer
}
//
play() {
if (!playQueue.currentTrack) return
debugPlayer("开始播放")
if (playState.playProgress !== 0) debugPlayer(`已经有所进度!${playState.playProgress}`)
this.currentSource = this.context.createBufferSource()
this.currentSource.buffer = this.audioBuffer[playQueue.currentTrack.song.cid]
this.currentSource.connect(this.context.destination)
this.currentSource.start(this.context.currentTime, playState.playProgress)
playState.reportActualPlaying(true)
this.reportProgress()
if (!playState.actualPlaying) {
//
debugPlayer("开始播放")
if (playState.playProgress !== 0) debugPlayer(`已经有所进度!${playState.playProgress}`)
this.currentSource = this.context.createBufferSource()
this.currentSource.buffer = this.audioBuffer[playQueue.currentTrack.song.cid]
this.currentSource.connect(this.context.destination)
this.currentSource.start(this.context.currentTime, playState.playProgress)
playState.reportActualPlaying(true)
this.reportProgress()
}
//
//
this.currentTrackStartTime = this.context.currentTime - playState.playProgress
if (this.audioBuffer[playQueue.nextTrack.song.cid]) this.scheduleNextTrack()
if (playQueue.nextTrack && this.audioBuffer[playQueue.nextTrack.song.cid]) this.scheduleNextTrack()
//
if (this.currentSource) this.currentSource.onended = () => {
debugPlayer("当前歌曲播放结束")
if (!!this.reportInterval) {
//
debugPlayer("歌曲自然结束")
this.onTrackEnded()
} else {
debugPlayer("用户暂停")
}
}
}
//
scheduleNextTrack() {
if (this.nextSource !== null) {
debugPlayer("下一首已经调度,跳过重复调度")
return
}
// TODO:
if (!playQueue.nextTrack) return
this.nextSource = null
const nextTrackStartTime = this.currentTrackStartTime
@ -184,7 +206,9 @@ class WebAudioPlayer {
//
stopReportProgress() {
if (this.reportInterval) clearInterval(this.reportInterval)
this.reportInterval = null
debugPlayer(this.reportInterval)
}
pause() {
@ -192,33 +216,43 @@ class WebAudioPlayer {
debugPlayer(this.currentSource)
this.currentSource?.stop()
this.nextSource?.stop()
this.nextSource = null
playState.reportActualPlaying(false)
this.stopReportProgress()
}
stop() {
}
async onTrackEnded() {
// 1.
this.stopReportProgress()
playState.reportPlayProgress(0)
togglePlay() {
}
// 2.
if (!this.nextSource) {
//
playState.reportActualPlaying(false)
playState.togglePlay(false)
return
}
getCurrentTime() {
// 3.
playQueue.continueToNext()
this.currentSource = this.nextSource
this.nextSource = null
}
// 4.
this.currentTrackStartTime = this.context.currentTime
this.reportProgress()
updateMediaSessionState() {
}
//
startPositionUpdates() {
}
//
destroy() {
// 5.
if (playQueue.nextTrack) {
debugPlayer("处理下下一首歌")
if (this.audioBuffer[playQueue.nextTrack.song.cid]) {
this.scheduleNextTrack()
} else {
await this.loadBuffer(playQueue.nextTrack)
if (playState.actualPlaying) this.scheduleNextTrack()
}
}
}
}

View File

@ -31,5 +31,6 @@ async function pauseOrResume() {
<button class="bg-white/20 px-2 py-1" @click="playTheList">开始播放</button>
<div>当前播放队列里有 {{ playQueue.queue.length }} 首歌</div>
<button class="bg-white/20 px-2 py-1" @click="pauseOrResume">播放/暂停</button>
<div>播放进度{{ Math.floor(playState.playProgress) }} / {{ Math.floor(playState.trackDuration) }}</div>
</div>
</template>

View File

@ -102,16 +102,10 @@ export const usePlayQueueStore = defineStore('queue', () => {
currentPlaying.value = turnTo
}
// 跳至下一首(通常为用户点按下一首按钮)
const skipToNext = () => {
currentPlaying.value = currentPlaying.value + 1
}
// 继续播放接下来的曲目
// 通常为当前曲目播放完毕,需要通过循环模式判断应该重置进度或队列指针 +1
const continueToNext = () => {
debugStore(loopingMode.value)
// 注意:单曲循环时的进度重置需要在播放状态管理中处理
if (loopingMode.value !== 'single') {
currentPlaying.value = currentPlaying.value + 1
}
@ -202,7 +196,6 @@ export const usePlayQueueStore = defineStore('queue', () => {
toggleShuffle,
toggleLoop,
toggleQueuePlay,
skipToNext,
continueToNext,
}
})