improve autoplay
This commit is contained in:
@@ -9,7 +9,7 @@ import PauseIcon from '@/icons/PauseIcon.vue';
|
||||
import PlayIcon from '@/icons/PlayIcon.vue';
|
||||
import StopIcon from '@/icons/StopIcon.vue';
|
||||
import { useAudioPlayerStore, useConfigStore, useFlagStore } from '@/stores';
|
||||
import { computed, ref, useTemplateRef, watchEffect } from 'vue';
|
||||
import { computed, ref, useTemplateRef, watch } from 'vue';
|
||||
|
||||
const config = useConfigStore()
|
||||
const flags = useFlagStore()
|
||||
@@ -18,12 +18,13 @@ const player = useAudioPlayerStore()
|
||||
const audio = useTemplateRef('audio')
|
||||
|
||||
const file = ref<dto.File>()
|
||||
const next = ref<dto.File>()
|
||||
const playing = ref(false)
|
||||
const duration = ref(0)
|
||||
const progress = ref(0)
|
||||
|
||||
const src = computed(() => `${config.value!.serverUrl}/file/${player.file?.id}`)
|
||||
const src = computed(() =>
|
||||
file.value ? `${config.value!.serverUrl}/file/${file.value.id}` : undefined,
|
||||
)
|
||||
|
||||
function play() {
|
||||
if (audio.value) {
|
||||
@@ -65,33 +66,35 @@ function timeupdate() {
|
||||
progress.value = audio.value?.currentTime || 0
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
// When a new file is pushed to the player store, queue it up
|
||||
if (player.file && player.file !== file.value) {
|
||||
next.value = player.file
|
||||
}
|
||||
// Mirror the app-wide selection into the local ref. Pre-flush so the <audio>
|
||||
// element and its :src are in the DOM before the playback watch runs.
|
||||
watch(() => player.file, (selected) => {
|
||||
file.value = selected
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
// When a new file is queued, substitute it for the current file
|
||||
// This two-stage approach enables unmounting the audio element to prevent playback issues
|
||||
if (next.value) {
|
||||
if (!flags.has('autoplay')) {
|
||||
stop()
|
||||
}
|
||||
}
|
||||
// Load and (optionally) start the new track. `flush: 'post'` runs this right
|
||||
// after the DOM updates but still in the microtask chain of the click that
|
||||
// selected the file, so play() counts as user-initiated on iOS/Safari. The
|
||||
// single <audio> element is never unmounted, so once iOS has seen one
|
||||
// gesture-driven play() it stays unlocked for every later track.
|
||||
watch(file, (selected) => {
|
||||
const el = audio.value
|
||||
if (!el) return
|
||||
|
||||
file.value = next.value
|
||||
next.value = undefined
|
||||
})
|
||||
el.load()
|
||||
if (selected && flags.has('autoplay')) {
|
||||
el.play().catch(() => {})
|
||||
} else {
|
||||
stop()
|
||||
}
|
||||
}, { flush: 'post' })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="audio-player">
|
||||
<audio v-if="file && !next"
|
||||
<audio v-if="file"
|
||||
ref="audio"
|
||||
preload="metadata"
|
||||
:autoplay="flags.value.includes('autoplay')"
|
||||
:loop="flags.value.includes('loop')"
|
||||
:src="src"
|
||||
v-on:loadedmetadata="onloadedmetadata"
|
||||
|
||||
Reference in New Issue
Block a user