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>({}) const failed = new Set() 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(`/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 } }