Spaces:
Running
Running
File size: 5,540 Bytes
91677d6 5a67aa1 91677d6 5a67aa1 91677d6 95e3d2a 91677d6 e66c992 91677d6 95e3d2a 91677d6 e66c992 91677d6 e66c992 91677d6 5a67aa1 91677d6 e66c992 91677d6 e66c992 91677d6 95e3d2a e66c992 91677d6 | 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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | import type { Match, Round } from '$lib/types';
const TICK_RATE = 64;
const PLAYERS_PER_ROUND = 10;
export type MapRow = Match & {
duration_s: number;
};
export type PovRow = {
match_id: number;
map_name: string;
round: number;
player: number;
duration_s: number;
event: string;
team1: string;
team2: string;
score1: number;
score2: number;
winner: Match['winner'];
format: string;
match_date: string;
uploaded_at: string;
rounds_played: number;
// When this row is materialized from an SQL filter, the timestamp inside
// the POV's video to seek to on open. Otherwise undefined → opens at 0.
start_t?: number;
};
export type RoundRow = {
match_id: number;
map_name: string;
round: number;
demo_round: number;
duration_s: number;
event: string;
team1: string;
team2: string;
score1: number;
score2: number;
winner: Match['winner'];
format: string;
match_date: string;
uploaded_at: string;
rounds_played: number;
};
export type MatchRow = {
match_id: number;
event: string;
team1: string;
team2: string;
score1: number;
score2: number;
winner: Match['winner'] | '';
format: string;
match_date: string;
uploaded_at: string;
maps: string[];
maps_played: number;
first_map: string;
rounds_played: number;
duration_s: number;
};
export function buildMapRows(matches: Match[], rounds: Round[]): MapRow[] {
const durationByMap = new Map<string, number>();
for (const r of rounds) {
const k = `${r.match_id}|${r.map_name}`;
durationByMap.set(k, (durationByMap.get(k) ?? 0) + (r.round_duration_ticks || 0) / TICK_RATE);
}
return matches.map((m) => ({
...m,
duration_s: durationByMap.get(`${m.match_id}|${m.map_name}`) ?? 0
}));
}
export function buildRoundRows(matches: Match[], rounds: Round[]): RoundRow[] {
const matchByMap = new Map<string, Match>();
for (const m of matches) matchByMap.set(`${m.match_id}|${m.map_name}`, m);
const out: RoundRow[] = [];
for (const r of rounds) {
const m = matchByMap.get(`${r.match_id}|${r.map_name}`);
if (!m) continue;
out.push({
match_id: r.match_id,
map_name: r.map_name,
round: r.round,
demo_round: r.demo_round,
duration_s: (r.round_duration_ticks || 0) / TICK_RATE,
event: m.event,
team1: m.team1,
team2: m.team2,
score1: m.score1,
score2: m.score2,
winner: m.winner,
format: m.format,
match_date: m.match_date,
uploaded_at: r.uploaded_at,
rounds_played: m.rounds_played
});
}
return out;
}
/**
* Synthesize 10 rows per round (one per player slot). No per-POV metadata
* is loaded eagerly — side/weapon would require fetching per-(match, map)
* chunks-preview.parquet for every visible match, which doesn't pay back on
* the home page. Per-POV detail surfaces inside the match viewer.
*/
export function buildPovRows(matches: Match[], rounds: Round[]): PovRow[] {
const matchByMap = new Map<string, Match>();
for (const m of matches) matchByMap.set(`${m.match_id}|${m.map_name}`, m);
const out: PovRow[] = [];
for (const r of rounds) {
const m = matchByMap.get(`${r.match_id}|${r.map_name}`);
if (!m) continue;
const duration_s = (r.round_duration_ticks || 0) / TICK_RATE;
for (let player = 0; player < PLAYERS_PER_ROUND; player++) {
out.push({
match_id: r.match_id,
map_name: r.map_name,
round: r.round,
player,
duration_s,
event: m.event,
team1: m.team1,
team2: m.team2,
score1: m.score1,
score2: m.score2,
winner: m.winner,
format: m.format,
match_date: m.match_date,
uploaded_at: r.uploaded_at,
rounds_played: m.rounds_played
});
}
}
return out;
}
export function buildMatchRows(matches: Match[], rounds: Round[]): MatchRow[] {
const durationByMap = new Map<string, number>();
for (const r of rounds) {
const k = `${r.match_id}|${r.map_name}`;
durationByMap.set(k, (durationByMap.get(k) ?? 0) + (r.round_duration_ticks || 0) / TICK_RATE);
}
const grouped = new Map<number, Match[]>();
for (const m of matches) {
const arr = grouped.get(m.match_id);
if (arr) arr.push(m);
else grouped.set(m.match_id, [m]);
}
const rows: MatchRow[] = [];
for (const [matchId, mapsForMatch] of grouped) {
const sorted = [...mapsForMatch].sort((a, b) => (a.map_index ?? 0) - (b.map_index ?? 0));
const head = sorted[0];
const team1MapWins = sorted.filter((m) => m.winner === 'team1').length;
const team2MapWins = sorted.filter((m) => m.winner === 'team2').length;
const totalRounds = sorted.reduce((acc, m) => acc + m.rounds_played, 0);
const totalDuration = sorted.reduce(
(acc, m) => acc + (durationByMap.get(`${m.match_id}|${m.map_name}`) ?? 0),
0
);
// Use the latest upload across the match's maps so the row reflects when
// the whole series finished landing in the dataset.
const latestUpload = sorted.reduce(
(acc, m) => (m.uploaded_at > acc ? m.uploaded_at : acc),
head.uploaded_at
);
rows.push({
match_id: matchId,
event: head.event,
team1: head.team1,
team2: head.team2,
score1: team1MapWins,
score2: team2MapWins,
winner: team1MapWins > team2MapWins ? 'team1' : team2MapWins > team1MapWins ? 'team2' : '',
format: head.format,
match_date: head.match_date,
uploaded_at: latestUpload,
maps: sorted.map((m) => m.map_name),
maps_played: sorted.length,
first_map: head.map_name,
rounds_played: totalRounds,
duration_s: totalDuration
});
}
rows.sort(
(a, b) =>
new Date(b.uploaded_at).getTime() - new Date(a.uploaded_at).getTime() ||
b.match_id - a.match_id
);
return rows;
}
|