Resolve race condition
This commit is contained in:
parent
0118fe0c63
commit
8c62d516b0
|
@ -1,5 +1,5 @@
|
||||||
import { createGlobalState, tryOnMounted, useIntervalFn, useRafFn, useStorage, useTimeoutFn, whenever } from '@vueuse/core'
|
import { createGlobalState, tryOnMounted, useIntervalFn, useRafFn, useStorage, useTimeoutFn, whenever } from '@vueuse/core'
|
||||||
import { computed, nextTick, ref, watch, watchEffect, type Ref } from 'vue'
|
import { computed, ref, watch, watchEffect, type Ref } from 'vue'
|
||||||
import { useTracks } from '~/composables/audio/tracks'
|
import { useTracks } from '~/composables/audio/tracks'
|
||||||
import { setGain } from './audio-api'
|
import { setGain } from './audio-api'
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ export const isPlaying = ref(false)
|
||||||
|
|
||||||
// Use Player
|
// Use Player
|
||||||
export const usePlayer = createGlobalState(() => {
|
export const usePlayer = createGlobalState(() => {
|
||||||
const { currentSound, createTrack } = useTracks()
|
const { currentSound } = useTracks()
|
||||||
const { playNext } = useQueue()
|
const { playNext } = useQueue()
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
|
@ -43,12 +43,6 @@ export const usePlayer = createGlobalState(() => {
|
||||||
sound.pause()
|
sound.pause()
|
||||||
})
|
})
|
||||||
|
|
||||||
const stop = async () => {
|
|
||||||
isPlaying.value = false
|
|
||||||
seekTo(0)
|
|
||||||
return nextTick()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create first track when we initalize the page
|
// Create first track when we initalize the page
|
||||||
// NOTE: We want to have it called only once, hence we're using createGlobalState
|
// NOTE: We want to have it called only once, hence we're using createGlobalState
|
||||||
const initializeFirstTrack = createGlobalState(() => tryOnMounted(() => {
|
const initializeFirstTrack = createGlobalState(() => tryOnMounted(() => {
|
||||||
|
@ -59,8 +53,6 @@ export const usePlayer = createGlobalState(() => {
|
||||||
trackRadioPopulating()
|
trackRadioPopulating()
|
||||||
|
|
||||||
trackListenSubmissions()
|
trackListenSubmissions()
|
||||||
|
|
||||||
createTrack(currentIndex.value)
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Volume
|
// Volume
|
||||||
|
@ -208,7 +200,6 @@ export const usePlayer = createGlobalState(() => {
|
||||||
return {
|
return {
|
||||||
initializeFirstTrack,
|
initializeFirstTrack,
|
||||||
isPlaying,
|
isPlaying,
|
||||||
stop,
|
|
||||||
volume,
|
volume,
|
||||||
mute,
|
mute,
|
||||||
looping,
|
looping,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { computed, ref, shallowReactive, watchEffect } from 'vue'
|
||||||
import { getMany, setMany } from 'idb-keyval'
|
import { getMany, setMany } from 'idb-keyval'
|
||||||
import { useClamp } from '@vueuse/math'
|
import { useClamp } from '@vueuse/math'
|
||||||
|
|
||||||
import { looping, LoopingMode, isPlaying, usePlayer } from '~/composables/audio/player'
|
import { looping, LoopingMode, isPlaying } from '~/composables/audio/player'
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
|
|
||||||
import { useTracks } from '~/composables/audio/tracks'
|
import { useTracks } from '~/composables/audio/tracks'
|
||||||
|
@ -166,13 +166,17 @@ export const useQueue = createGlobalState(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Play track
|
// Play track
|
||||||
const playTrack = async (trackIndex: number, force = false) => {
|
const playTrack = async (trackIndex: number, forceRestartIfCurrent = false) => {
|
||||||
if (isPlaying.value) currentSound.value?.pause()
|
if (isPlaying.value) currentSound.value?.pause()
|
||||||
|
if (currentIndex.value !== trackIndex) currentSound.value?.seekTo(0)
|
||||||
|
|
||||||
if (force && currentIndex.value === trackIndex) {
|
const shouldRestart = forceRestartIfCurrent && currentIndex.value === trackIndex
|
||||||
|
const nextTrackIsTheSame = queue.value[trackIndex].id === currentTrack.value.id
|
||||||
|
|
||||||
|
if (shouldRestart || nextTrackIsTheSame) {
|
||||||
currentSound.value?.seekTo(0)
|
currentSound.value?.seekTo(0)
|
||||||
if (isPlaying.value) currentSound.value?.play()
|
if (isPlaying.value) currentSound.value?.play()
|
||||||
return
|
if (shouldRestart) return
|
||||||
}
|
}
|
||||||
|
|
||||||
currentIndex.value = trackIndex
|
currentIndex.value = trackIndex
|
||||||
|
@ -270,8 +274,9 @@ export const useQueue = createGlobalState(() => {
|
||||||
// Clear
|
// Clear
|
||||||
const clearRadio = ref(false)
|
const clearRadio = ref(false)
|
||||||
const clear = async () => {
|
const clear = async () => {
|
||||||
const { stop } = usePlayer()
|
currentSound.value?.pause()
|
||||||
await stop()
|
currentSound.value?.seekTo(0)
|
||||||
|
currentSound.value?.dispose()
|
||||||
clearRadio.value = true
|
clearRadio.value = true
|
||||||
tracks.value.length = 0
|
tracks.value.length = 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { QueueTrack, QueueTrackSource } from '~/composables/audio/queue'
|
import type { QueueTrack, QueueTrackSource } from '~/composables/audio/queue'
|
||||||
import type { Sound } from '~/api/player'
|
import type { Sound } from '~/api/player'
|
||||||
|
|
||||||
import { createGlobalState, syncRef, whenever } from '@vueuse/core'
|
import { createGlobalState, syncRef, useTimeoutFn, whenever } from '@vueuse/core'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
import { connectAudioSource } from '~/composables/audio/audio-api'
|
import { connectAudioSource } from '~/composables/audio/audio-api'
|
||||||
|
@ -55,30 +55,40 @@ export const useTracks = createGlobalState(() => {
|
||||||
|
|
||||||
const createSoundPromise = async () => {
|
const createSoundPromise = async () => {
|
||||||
const sources = getTrackSources(track)
|
const sources = getTrackSources(track)
|
||||||
const { playNext, currentIndex } = useQueue()
|
const { playTrack, currentIndex } = useQueue()
|
||||||
|
|
||||||
const SoundImplementation = soundImplementation.value
|
const SoundImplementation = soundImplementation.value
|
||||||
const sound = new SoundImplementation(sources)
|
const sound = new SoundImplementation(sources)
|
||||||
sound.onSoundEnd(() => {
|
sound.onSoundEnd(() => {
|
||||||
console.log('TRACK ENDED, PLAYING NEXT')
|
console.log('TRACK ENDED, PLAYING NEXT')
|
||||||
createTrack(currentIndex.value + 1)
|
|
||||||
|
|
||||||
// NOTE: We push it to the end of the job queue
|
// NOTE: We push it to the end of the job queue
|
||||||
setTimeout(playNext, 0)
|
setTimeout(() => {
|
||||||
|
playTrack(currentIndex.value + 1)
|
||||||
|
}, 0)
|
||||||
})
|
})
|
||||||
soundCache.set(track.id, sound)
|
soundCache.set(track.id, sound)
|
||||||
soundPromises.delete(track.id)
|
soundPromises.delete(track.id)
|
||||||
return sound
|
return sound
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('NO TRACK IN CACHE, CREATING')
|
console.log('NO TRACK IN CACHE, CREATING', track)
|
||||||
const soundPromise = createSoundPromise()
|
const soundPromise = createSoundPromise()
|
||||||
soundPromises.set(track.id, soundPromise)
|
soundPromises.set(track.id, soundPromise)
|
||||||
return soundPromise
|
return soundPromise
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Preload next track
|
||||||
|
const { start: startPreloadTimeout, stop: stopPreloadTimeout } = useTimeoutFn(async (index) => {
|
||||||
|
const { queue } = useQueue()
|
||||||
|
const sound = await createSound(queue.value[index as number])
|
||||||
|
await sound.preload()
|
||||||
|
}, 100, { immediate: false })
|
||||||
|
|
||||||
// Create track from queue
|
// Create track from queue
|
||||||
const createTrack = async (index: number) => {
|
const createTrack = async (index: number) => {
|
||||||
|
stopPreloadTimeout()
|
||||||
|
|
||||||
const { queue, currentIndex } = useQueue()
|
const { queue, currentIndex } = useQueue()
|
||||||
if (queue.value.length <= index || index === -1) return
|
if (queue.value.length <= index || index === -1) return
|
||||||
console.log('LOADING TRACK', index)
|
console.log('LOADING TRACK', index)
|
||||||
|
@ -97,10 +107,8 @@ export const useTracks = createGlobalState(() => {
|
||||||
|
|
||||||
// NOTE: Preload next track
|
// NOTE: Preload next track
|
||||||
if (index === currentIndex.value && index + 1 < queue.value.length) {
|
if (index === currentIndex.value && index + 1 < queue.value.length) {
|
||||||
setTimeout(async () => {
|
// @ts-expect-error vueuse is wrongly typed?
|
||||||
const sound = await createSound(queue.value[index + 1])
|
startPreloadTimeout(index + 1)
|
||||||
await sound.preload()
|
|
||||||
}, 100)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +118,9 @@ export const useTracks = createGlobalState(() => {
|
||||||
const initialize = createGlobalState(() => {
|
const initialize = createGlobalState(() => {
|
||||||
const { currentIndex, currentTrack: track } = useQueue()
|
const { currentIndex, currentTrack: track } = useQueue()
|
||||||
|
|
||||||
whenever(track, () => createTrack(currentIndex.value))
|
whenever(track, () => {
|
||||||
|
createTrack(currentIndex.value)
|
||||||
|
}, { immediate: true })
|
||||||
syncRef(track, currentTrack, {
|
syncRef(track, currentTrack, {
|
||||||
direction: 'ltr'
|
direction: 'ltr'
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue