Fetch only new queue tracks from indexedDB
This commit is contained in:
parent
9f36c4b3a8
commit
f51007b807
|
@ -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,
|
||||||
|
|
|
@ -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')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -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'
|
||||||
})
|
})
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue