Spaces:
Running
Running
| 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<Match[]> | null = null; | |
| let roundsPromise: Promise<Round[]> | null = null; | |
| const matchPreviewsCache = new Map<string, Promise<PreviewChunk[]>>(); | |
| 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<Match[]> { | |
| if (matchesPromise) return matchesPromise; | |
| matchesPromise = fetchParquetRows<Match>(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<Round[]> { | |
| if (roundsPromise) return roundsPromise; | |
| roundsPromise = fetchParquetRows<Round>(resolveUrl(WEB_ROUNDS), opts).catch((err) => { | |
| roundsPromise = null; | |
| throw err; | |
| }); | |
| return roundsPromise; | |
| } | |
| export async function listRounds( | |
| matchId: number, | |
| mapName: string, | |
| opts: FetchOptions = {} | |
| ): Promise<Round[]> { | |
| 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<PreviewChunk[]> { | |
| 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<PreviewChunk>( | |
| 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<PreviewChunk[]> { | |
| 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); | |
| } | |