Fetch only new queue tracks from indexedDB

This commit is contained in:
wvffle 2022-10-28 12:29:58 +00:00 committed by Georg Krause
parent 9f36c4b3a8
commit f51007b807
No known key found for this signature in database
GPG Key ID: 2970D504B2183D22
4 changed files with 74 additions and 34 deletions

View File

@ -1,6 +1,6 @@
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 { useTracks } from '~/composables/audio/tracks' import { useTracks } from '~/composables/audio/tracks'
import { computed, ref, watch, watchEffect, type Ref } from 'vue'
import { setGain } from './audio-api' import { setGain } from './audio-api'
import { useQueue, currentIndex, currentTrack } from './queue' import { useQueue, currentIndex, currentTrack } from './queue'
@ -43,6 +43,12 @@ 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(() => {
@ -195,13 +201,14 @@ export const usePlayer = createGlobalState(() => {
return sound.isErrored.value return sound.isErrored.value
}) })
const { start, stop } = useTimeoutFn(() => playNext(), 3000, { immediate: false }) const { start: startErrorTimeout, stop: stopErrorTimeout } = useTimeoutFn(() => playNext(), 3000, { immediate: false })
watch(currentIndex, stop) watch(currentIndex, stopErrorTimeout)
whenever(errored, start) whenever(errored, startErrorTimeout)
return { return {
initializeFirstTrack, initializeFirstTrack,
isPlaying, isPlaying,
stop,
volume, volume,
mute, mute,
looping, looping,

View File

@ -1,12 +1,12 @@
import type { Track, Upload } from '~/types' import type { Track, Upload } from '~/types'
import { computedAsync, createGlobalState, useNow, useStorage, useTimeAgo } from '@vueuse/core' import { createGlobalState, useNow, useStorage, useTimeAgo, whenever } from '@vueuse/core'
import { shuffle as shuffleArray, sum, uniq } from 'lodash-es' import { shuffle as shuffleArray, sum } from 'lodash-es'
import { computed, ref, shallowReactive, watchEffect } from 'vue'
import { getMany, setMany } from 'idb-keyval' import { getMany, setMany } from 'idb-keyval'
import { computed, watchEffect } from 'vue'
import { useClamp } from '@vueuse/math' import { useClamp } from '@vueuse/math'
import { looping, LoopingMode, isPlaying } from '~/composables/audio/player' import { looping, LoopingMode, isPlaying, usePlayer } from '~/composables/audio/player'
import { useStore } from '~/store' import { useStore } from '~/store'
import axios from 'axios' import axios from 'axios'
@ -44,22 +44,45 @@ const shuffledIds = useStorage('queue:tracks:shuffled', [] as number[])
const isShuffled = computed(() => shuffledIds.value.length !== 0) const isShuffled = computed(() => shuffledIds.value.length !== 0)
const tracksById = computedAsync(async () => { const tracksById = shallowReactive(new Map<number, QueueTrack>())
const trackObjects = await getMany(uniq(tracks.value)) const fetchingTracks = ref(false)
return trackObjects.reduce((acc, track) => { watchEffect(async () => {
acc[track.id] = track if (fetchingTracks.value) return
return acc
}, {}) as Record<number, QueueTrack>
}, {})
const queue = computed(() => { const allTracks = new Set(tracks.value)
const indexedTracks = tracksById.value const addedIds = new Set(allTracks)
if (isShuffled.value) { for (const id of tracksById.keys()) {
return shuffledIds.value.map(id => indexedTracks[id]).filter(i => i) if (allTracks.has(id)) {
// Track in queue, so remove it from the new ids set
addedIds.delete(id)
} else {
// Track removed from queue, so remove it from the object
tracksById.delete(id)
}
} }
return tracks.value.map(id => indexedTracks[id]).filter(i => i) if (addedIds.size > 0) {
fetchingTracks.value = true
try {
const trackInfos: QueueTrack[] = await getMany([...addedIds])
for (const track of trackInfos) {
tracksById.set(track.id, track)
}
} catch (error) {
console.error(error)
} finally {
fetchingTracks.value = false
}
}
})
const queue = computed<QueueTrack[]>(() => {
const ids = isShuffled.value
? shuffledIds.value
: tracks.value
return ids.map(id => tracksById.get(id)).filter((i): i is QueueTrack => !!i)
}) })
// Current Index // Current Index
@ -235,7 +258,11 @@ export const useQueue = createGlobalState(() => {
})) }))
// Clear // Clear
const clearRadio = ref(false)
const clear = async () => { const clear = async () => {
const { stop } = usePlayer()
await stop()
clearRadio.value = true
tracks.value.length = 0 tracks.value.length = 0
} }
@ -249,8 +276,9 @@ export const useQueue = createGlobalState(() => {
} }
}) })
watchEffect(() => { whenever(clearRadio, () => {
if (tracks.value.length === 0) { clearRadio.value = false
if (store.state.radios.running) {
return store.dispatch('radios/stop') return store.dispatch('radios/stop')
} }
}) })

View File

@ -1,13 +1,13 @@
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 { soundImplementation } from '~/api/player' import { createGlobalState, syncRef, whenever } from '@vueuse/core'
import { createGlobalState, syncRef } from '@vueuse/core' import { computed, ref } from 'vue'
import { computed, ref, watch } from 'vue'
import { useQueue } from '~/composables/audio/queue'
import { connectAudioSource } from '~/composables/audio/audio-api' import { connectAudioSource } from '~/composables/audio/audio-api'
import { usePlayer } from '~/composables/audio/player' import { usePlayer } from '~/composables/audio/player'
import { useQueue } from '~/composables/audio/queue'
import { soundImplementation } from '~/api/player'
import useLRUCache from '~/composables/useLRUCache' import useLRUCache from '~/composables/useLRUCache'
import store from '~/store' import store from '~/store'
@ -112,8 +112,9 @@ export const useTracks = createGlobalState(() => {
// 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 initialize = createGlobalState(() => { const initialize = createGlobalState(() => {
const { currentTrack: track, currentIndex } = useQueue() const { currentIndex, currentTrack: track } = useQueue()
watch(currentIndex, (index) => createTrack(index))
whenever(track, () => createTrack(currentIndex.value))
syncRef(track, currentTrack, { syncRef(track, currentTrack, {
direction: 'ltr' direction: 'ltr'
}) })

View File

@ -164,21 +164,25 @@ export default (props: PlayOptionsProps) => {
const tracksToPlay = await getPlayableTracks() const tracksToPlay = await getPlayableTracks()
await addToQueue(...tracksToPlay) await addToQueue(...tracksToPlay)
const trackIndex = props.tracks?.findIndex(track => track.id === props.track?.id && track.position === props.track?.position) ?? 0 if (props.track && props.tracks?.length) {
await playTrack(trackIndex) const trackIndex = props.tracks?.findIndex(track => track.id === props.track?.id && track.position === props.track?.position) ?? 0
await playTrack(trackIndex)
isPlaying.value = true
} else {
await playTrack(0, true)
isPlaying.value = true
}
isPlaying.value = true
playTrack(0, true)
addMessage(tracksToPlay) addMessage(tracksToPlay)
} }
const activateTrack = (track: Track, index: number) => { const activateTrack = async (track: Track, index: number) => {
// TODO (wvffle): Check if position checking did not break anything // TODO (wvffle): Check if position checking did not break anything
if (track.id === currentTrack.value?.id && track.position === currentTrack.value?.position) { if (track.id === currentTrack.value?.id && track.position === currentTrack.value?.position) {
isPlaying.value = true isPlaying.value = true
} }
replacePlay() return replacePlay()
} }
return { return {