import type { Match, PreviewChunk, Round } from '$lib/types'; import { resolveUrl, type FetchOpts } from '$lib/api/hub'; import { fetchParquetRows } from '$lib/api/parquet'; export type FetchOptions = FetchOpts; let matchesPromise: Promise | null = null; let roundsPromise: Promise | null = null; const matchPreviewsCache = new Map>(); const WEB_MAPS = 'index/web/maps.parquet'; const WEB_ROUNDS = 'index/web/rounds.parquet'; const matchPreviewsPath = (matchId: number, mapName: string) => `rounds/match_id=${matchId}/map_name=${mapName}/chunks-preview.parquet`; export function listMatches(opts: FetchOptions = {}): Promise { if (matchesPromise) return matchesPromise; matchesPromise = fetchParquetRows(resolveUrl(WEB_MAPS), opts) .then((rows) => { rows.sort( (a, b) => new Date(b.match_date).getTime() - new Date(a.match_date).getTime() || (a.map_index ?? 0) - (b.map_index ?? 0) ); return rows; }) .catch((err) => { matchesPromise = null; throw err; }); return matchesPromise; } export function listAllRounds(opts: FetchOptions = {}): Promise { if (roundsPromise) return roundsPromise; roundsPromise = fetchParquetRows(resolveUrl(WEB_ROUNDS), opts).catch((err) => { roundsPromise = null; throw err; }); return roundsPromise; } export async function listRounds( matchId: number, mapName: string, opts: FetchOptions = {} ): Promise { const all = await listAllRounds(opts); return all .filter((r) => r.match_id === matchId && r.map_name === mapName) .sort((a, b) => a.round - b.round); } async function loadMatchPreviews( matchId: number, mapName: string, opts: FetchOptions ): Promise { const key = `${matchId}/${mapName}`; const cached = matchPreviewsCache.get(key); if (cached) return cached; const baseDir = `rounds/match_id=${matchId}/map_name=${mapName}`; const promise = fetchParquetRows( resolveUrl(matchPreviewsPath(matchId, mapName)), opts ) .then((rows) => { for (const r of rows) { r.preview_video = { src: resolveUrl(`${baseDir}/${r.preview_path}`) }; } return rows; }) .catch((err) => { matchPreviewsCache.delete(key); throw err; }); matchPreviewsCache.set(key, promise); return promise; } export async function listRoundPreviews( matchId: number, mapName: string, round: number, opts: FetchOptions = {} ): Promise { const all = await loadMatchPreviews(matchId, mapName, opts); return all .filter((p) => p.round === round) .sort((a, b) => a.player - b.player || a.chunk_index - b.chunk_index); }