File size: 2,676 Bytes
3f5bd49
 
95e3d2a
31d3580
 
 
 
 
 
 
3f5bd49
 
 
10d4db6
31d3580
 
 
3f5bd49
31d3580
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3f5bd49
 
 
 
 
 
31d3580
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10d4db6
3f5bd49
 
 
 
 
 
 
10d4db6
3f5bd49
 
 
 
 
 
 
 
31d3580
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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);
}