generated from VLADIMIR/template_frontend
81 lines
2.6 KiB
TypeScript
81 lines
2.6 KiB
TypeScript
import { computed, onScopeDispose, type Ref, shallowReactive, watch } from 'vue'
|
|
|
|
import type { Playlist } from '@/api/generated/crabs/evening_detective_server'
|
|
import { getURL } from '@/api/generated/crabs/evening_detective_server/client'
|
|
import { playlistDurationLabel } from '@/utils/playlistDuration'
|
|
|
|
export function usePlaylistDurations(playlist: Ref<Playlist | undefined>) {
|
|
const durations = shallowReactive(new Map<string, number | null>())
|
|
const files = computed(() =>
|
|
JSON.stringify(
|
|
[...new Set(playlist.value?.tracks?.map((track) => track.filename ?? '') ?? [])].sort(),
|
|
),
|
|
)
|
|
const cancelLoads = new Set<() => void>()
|
|
let version = 0
|
|
|
|
function cancel() {
|
|
version++
|
|
for (const stop of cancelLoads) stop()
|
|
}
|
|
|
|
function load(filename: string): Promise<number | null | undefined> {
|
|
return new Promise((resolve) => {
|
|
const audio = new Audio()
|
|
const finish = (duration: number | null | undefined) => {
|
|
clearTimeout(timeout)
|
|
audio.onloadedmetadata = null
|
|
audio.onerror = null
|
|
audio.removeAttribute('src')
|
|
audio.load()
|
|
cancelLoads.delete(stop)
|
|
resolve(duration)
|
|
}
|
|
const stop = () => finish(undefined)
|
|
const timeout = setTimeout(() => finish(null), 15000)
|
|
cancelLoads.add(stop)
|
|
audio.preload = 'metadata'
|
|
audio.onloadedmetadata = () =>
|
|
finish(Number.isFinite(audio.duration) && audio.duration > 0 ? audio.duration : null)
|
|
audio.onerror = () => finish(null)
|
|
audio.src = `${getURL()}/api/files/${encodeURIComponent(filename)}`
|
|
})
|
|
}
|
|
|
|
watch(
|
|
files,
|
|
() => {
|
|
cancel()
|
|
const currentVersion = version
|
|
const pending = [
|
|
...new Set(playlist.value?.tracks?.map((track) => track.filename ?? '') ?? []),
|
|
].filter((filename) => !durations.has(filename))
|
|
async function worker() {
|
|
while (currentVersion === version && pending.length) {
|
|
const filename = pending.shift()!
|
|
if (!filename) {
|
|
durations.set(filename, null)
|
|
continue
|
|
}
|
|
const duration = await load(filename)
|
|
if (currentVersion !== version) return
|
|
if (duration !== undefined) durations.set(filename, duration)
|
|
}
|
|
}
|
|
for (let index = 0; index < 3; index++) void worker()
|
|
},
|
|
{ immediate: true },
|
|
)
|
|
onScopeDispose(cancel)
|
|
|
|
function groupDuration(groupID: number) {
|
|
return playlistDurationLabel(
|
|
(playlist.value?.tracks ?? [])
|
|
.filter((track) => (track.groupId ?? 0) === groupID)
|
|
.map((track) => durations.get(track.filename ?? '')),
|
|
)
|
|
}
|
|
|
|
return { groupDuration }
|
|
}
|