feat: 暂停与续播

This commit is contained in:
Astrian Zheng 2025-08-22 17:41:33 +10:00
parent dae6210239
commit f42ba2662b
Signed by: Astrian
SSH Key Fingerprint: SHA256:rVnhx3DAKjujCwWE13aDl7uV6+9U1MvydLkNRXJrBiA
5 changed files with 99 additions and 17 deletions

View File

@ -126,6 +126,7 @@ watch(
album.value = undefined // Reset album when cid changes
try {
let res = await apis.getAlbum(props.albumCid)
debugUI(res.cid)
for (const track in res.songs) {
res.songs[parseInt(track)] = await apis.getSong(
res.songs[parseInt(track)].cid,

View File

@ -16,6 +16,7 @@ class WebAudioPlayer {
currentTrackStartTime: number
currentSource: AudioBufferSourceNode | null
nextSource: AudioBufferSourceNode | null
reportInterval: ReturnType<typeof setTimeout> | null
constructor() {
this.context = new window.AudioContext()
@ -23,6 +24,7 @@ class WebAudioPlayer {
this.currentTrackStartTime = 0
this.currentSource = null
this.nextSource = null
this.reportInterval = null
// HTML Audio
this.dummyAudio = new Audio()
@ -93,7 +95,7 @@ class WebAudioPlayer {
}
}
//
//
async loadResourceAndPlay() {
try {
debugPlayer("从播放器实例内部获取播放项目:")
@ -103,6 +105,9 @@ class WebAudioPlayer {
if (playQueue.queue.length === 0) {
// TODO:
playState.reportPlayProgress(0)
playState.reportActualPlaying(false)
this.currentSource?.stop()
}
// buffer
@ -121,7 +126,7 @@ class WebAudioPlayer {
if (playQueue.nextTrack) {
await loadBuffer(playQueue.nextTrack)
this.preloadNextTrack()
if (playState.isPlaying) this.scheduleNextTrack()
} else {
this.nextSource = null
}
@ -135,23 +140,28 @@ class WebAudioPlayer {
}
}
//
//
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()
if (!playQueue.nextTrack) return
this.currentSource.start(this.context.currentTime, playState.playProgress)
this.reportProgress()
//
//
this.currentTrackStartTime = this.context.currentTime
this.currentTrackStartTime = this.context.currentTime - playState.playProgress
if (this.audioBuffer[playQueue.nextTrack.song.cid]) this.scheduleNextTrack()
}
//
preloadNextTrack() {
//
scheduleNextTrack() {
if (!playQueue.nextTrack) return
this.nextSource = null
const nextTrackStartTime = this.currentTrackStartTime + this.audioBuffer[playQueue.currentTrack.song.cid].duration
const nextTrackStartTime = this.currentTrackStartTime
+ this.audioBuffer[playQueue.currentTrack.song.cid].duration
debugPlayer(`下一首歌将在 ${nextTrackStartTime} 时间点接入`)
this.nextSource = this.context.createBufferSource()
@ -161,9 +171,26 @@ class WebAudioPlayer {
this.nextSource.start(nextTrackStartTime)
}
pause() {}
//
reportProgress() {
this.reportInterval = setInterval(() => {
const progress = this.context.currentTime - this.currentTrackStartTime
playState.reportPlayProgress(progress)
playState.reportCurrentTrackDuration(this.audioBuffer[playQueue.currentTrack.song.cid].duration)
}, 100)
}
resume() {}
//
stopReportProgress() {
this.reportInterval = null
}
pause() {
debugPlayer("尝试暂停播放")
debugPlayer(this.currentSource)
this.currentSource?.stop()
this.nextSource?.stop()
}
stop() {
@ -201,6 +228,16 @@ watch(() => playQueue.currentTrack, () => {
debugPlayer(`检测到当前播放曲目更新`)
playerInstance.value?.loadResourceAndPlay()
})
watch(() => playState.isPlaying, () => {
if (!playState.isPlaying) {
//
playerInstance.value?.pause()
} else {
//
playerInstance.value?.play()
}
})
</script>
<template>

View File

@ -10,12 +10,14 @@ import HomePage from './pages/Home.vue'
import AlbumDetailView from './pages/AlbumDetail.vue'
import Playroom from './pages/Playroom.vue'
import Library from './pages/Library.vue'
import Debug from './pages/Debug.vue'
const routes = [
{ path: '/', component: HomePage },
{ path: '/albums/:albumId', component: AlbumDetailView },
{ path: '/playroom', component: Playroom },
{ path: '/library', component: Library },
{ path: '/debug', component: Debug}
]
const router = createRouter({

35
src/pages/Debug.vue Normal file
View File

@ -0,0 +1,35 @@
<script lang="ts" setup>
import apis from '../apis'
import { debugUI } from '../utils/debug'
import { usePlayQueueStore } from '../stores/usePlayQueueStore'
import { usePlayState } from '../stores/usePlayState'
const playQueue = usePlayQueueStore()
const playState = usePlayState()
async function playTheList() {
debugUI("开始播放")
const res = await apis.getAlbum("8936")
let newQueue: QueueItem[] = []
for (const track of res.songs ?? []) {
newQueue[newQueue.length] = {
song: track,
album: res
}
}
playQueue.replaceQueue(newQueue)
playState.togglePlay(true)
}
async function pauseOrResume() {
playState.togglePlay()
}
</script>
<template>
<div class="text-white flex justify-center items-center min-h-screen flex-col">
<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>
</template>

View File

@ -5,16 +5,18 @@ import artistsOrganize from '../utils/artistsOrganize'
export const usePlayState = defineStore('playState', () => {
// 播放状态
const isPlaying = ref(false)
const isPlaying = ref(false) // 用户控制的播放与暂停
const playProgress = ref(0) // 播放进度
const currentTrackDuration = ref(0) // 曲目总时长
const currentTrack = ref<QueueItem | null>(null) // 当前播放的曲目
const mediaSessionInitialized = ref(false)
const actualPlaying = ref(false) // 实际音频的播放与暂停
// 外显播放状态方法
const playingState = computed(() => isPlaying.value)
const playProgressState = computed(() => playProgress.value)
const trackDurationState = computed(() => currentTrackDuration.value)
const actualPlayingState = computed(() => actualPlaying.value)
// 回报目前播放进度百分比
const playProgressPercent = computed(() => {
@ -40,13 +42,11 @@ export const usePlayState = defineStore('playState', () => {
// 回报播放位置
const reportPlayProgress = (progress: number) => {
debugStore(`播放位置回报: ${progress}`)
playProgress.value = progress
}
// 回报曲目进度
const setCurrentTrackDuration = (duration: number) => {
debugStore(`曲目进度回报: ${duration}`)
// 回报曲目长度
const reportCurrentTrackDuration = (duration: number) => {
currentTrackDuration.value = duration
}
@ -63,6 +63,11 @@ export const usePlayState = defineStore('playState', () => {
playProgress.value = clampedTime
}
// 回报 Web Audio API 正在播放
const reportActualPlaying = (playing: boolean) => {
actualPlaying.value = playing
}
/***********
*
**********/
@ -184,13 +189,15 @@ export const usePlayState = defineStore('playState', () => {
playProgressPercent,
remainingTime,
currentTrack: computed(() => currentTrack.value),
actualPlaying: actualPlayingState,
// 修改方法
togglePlay,
reportPlayProgress,
setCurrentTrackDuration,
reportCurrentTrackDuration,
resetProgress,
seekTo,
reportActualPlaying,
// 媒体会话方法
setCurrentTrack,