blanchon's picture
Point viewer at opencs2_dataset layout
10d4db6 verified
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);
}