55 lines
1.4 KiB
TypeScript
55 lines
1.4 KiB
TypeScript
import { ref } from 'vue'
|
|
import { useApi } from '@/composables/useApi'
|
|
import type { MediaItem } from '@/composables/useMedia'
|
|
|
|
export const MEDIA_LOOKUP_CONCURRENCY = 4
|
|
export const MEDIA_FAILURE_CACHE_LIMIT = 50
|
|
|
|
export function useMediaNames() {
|
|
const { request } = useApi()
|
|
const items = ref<Record<number, MediaItem>>({})
|
|
const failed = new Set<number>()
|
|
|
|
function remember(item: MediaItem) {
|
|
items.value = { ...items.value, [item.id]: item }
|
|
}
|
|
|
|
function rememberFailure(id: number) {
|
|
failed.delete(id)
|
|
failed.add(id)
|
|
if (failed.size > MEDIA_FAILURE_CACHE_LIMIT) {
|
|
failed.delete(failed.values().next().value as number)
|
|
}
|
|
}
|
|
|
|
async function lookUp(id: number) {
|
|
try {
|
|
remember(await request<MediaItem>(`/v1/media/${id}`))
|
|
} catch {
|
|
rememberFailure(id)
|
|
}
|
|
}
|
|
|
|
async function lookUpAll(queue: number[]) {
|
|
const next = queue.values()
|
|
const worker = async () => {
|
|
for (const id of next) {
|
|
await lookUp(id)
|
|
}
|
|
}
|
|
await Promise.all(Array.from({ length: MEDIA_LOOKUP_CONCURRENCY }, worker))
|
|
}
|
|
|
|
async function resolve(ids: (number | null | undefined)[]) {
|
|
const wanted = ids.filter((id): id is number => typeof id === 'number')
|
|
const missing = [...new Set(wanted)].filter((id) => !(id in items.value) && !failed.has(id))
|
|
await lookUpAll(missing)
|
|
}
|
|
|
|
function itemOf(id: number | null | undefined) {
|
|
return typeof id === 'number' ? (items.value[id] ?? null) : null
|
|
}
|
|
|
|
return { itemOf, remember, resolve }
|
|
}
|