This commit is contained in:
2026-07-04 10:17:41 -05:00
parent b727c110a2
commit 867d3c96b1

View File

@@ -670,18 +670,11 @@ function normMinuteStr(min: string | null | undefined): string {
return (min ?? "").replace(/'/g, "").replace(/\s+/g, "").trim(); return (min ?? "").replace(/'/g, "").replace(/\s+/g, "").trim();
} }
const RECENT_FINISHED_MS = 60 * 60 * 1000; // 60 Minuten async function fetchFifaGoals(
const MATCH_DURATION_ESTIMATE = 2.5 * 60 * 60 * 1000; // 2.5h Puffer für Spiel + Pause + Nachspielzeit idStage: string, idMatch: string, locale: string = "de",
ttlMs: number = 60_000,
function isRecentlyFinished(m: Match): boolean { ): Promise<FifaGoalRaw[]> {
if (m.status !== "FINISHED") return false; return cached(`fifa:detail:${locale}:${idMatch}`, ttlMs, async () => {
const kickoff = new Date(m.utcDate).getTime();
if (isNaN(kickoff)) return false;
return kickoff + MATCH_DURATION_ESTIMATE > Date.now() - RECENT_FINISHED_MS;
}
async function fetchFifaGoals(idStage: string, idMatch: string, locale: string = "de"): Promise<FifaGoalRaw[]> {
return cached(`fifa:detail:${locale}:${idMatch}`, 60_000, async () => {
const lang = locale === "en" ? "en" : "de"; const lang = locale === "en" ? "en" : "de";
const url = `${FIFA_BASE}/live/football/17/${FIFA_SEASON}/${idStage}/${idMatch}?language=${lang}`; const url = `${FIFA_BASE}/live/football/17/${FIFA_SEASON}/${idStage}/${idMatch}?language=${lang}`;
const res = await fetch(url, { const res = await fetch(url, {
@@ -724,16 +717,16 @@ async function fetchFifaGoals(idStage: string, idMatch: string, locale: string =
}); });
} }
// Lädt Tor-Details für laufende oder kürzlich beendete Spiele mit Toren. // Lädt Tor-Details für laufende und alle beendeten Spiele mit Toren.
// FINISHED-Matches werden nur innerhalb eines 60-Minuten-Fensters berücksichtigt, // Beendete Spiele werden über langlebiges Caching (24h) gespart, nicht über Ausschluss.
// um die Request-Zahl bei 79+ Spielen im Turnierverlauf drastisch zu reduzieren. // Tore eines beendeten Spiels ändern sich nie mehr → einmal fetchen genügt.
export async function attachFifaGoals( export async function attachFifaGoals(
matches: Match[], matches: Match[],
fifaData: { scores: Map<number, FifaScores>; fifaIdToAppCode: Map<string, string> }, fifaData: { scores: Map<number, FifaScores>; fifaIdToAppCode: Map<string, string> },
locale: string = "de", locale: string = "de",
): Promise<Match[]> { ): Promise<Match[]> {
const targets = matches.filter(m => const targets = matches.filter(m =>
((m.status === "LIVE" || m.status === "IN_PLAY" || m.status === "PAUSED") || isRecentlyFinished(m)) && (m.status === "LIVE" || m.status === "IN_PLAY" || m.status === "PAUSED" || m.status === "FINISHED") &&
((m.homeScore ?? 0) + (m.awayScore ?? 0)) > 0, ((m.homeScore ?? 0) + (m.awayScore ?? 0)) > 0,
); );
if (targets.length === 0) return matches; if (targets.length === 0) return matches;
@@ -742,7 +735,9 @@ export async function attachFifaGoals(
const results = await Promise.allSettled(targets.map(async (m) => { const results = await Promise.allSettled(targets.map(async (m) => {
const fs = fifaData.scores.get(m.matchNumber); const fs = fifaData.scores.get(m.matchNumber);
if (!fs?.idMatch || !fs?.idStage) return; if (!fs?.idMatch || !fs?.idStage) return;
const goals = await fetchFifaGoals(fs.idStage, fs.idMatch, locale); const isFinished = m.status === "FINISHED";
const ttl = isFinished ? 24 * 60 * 60 * 1000 : 60_000;
const goals = await fetchFifaGoals(fs.idStage, fs.idMatch, locale, ttl);
if (goals.length) goalsByMatchId.set(m.id, goals); if (goals.length) goalsByMatchId.set(m.id, goals);
})); }));
@@ -787,11 +782,14 @@ export async function attachFifaLiveData(
// --- Step 2: Scores auf Matches anwenden --- // --- Step 2: Scores auf Matches anwenden ---
const updated = applyFifaScores(matches, teams, fifaData); const updated = applyFifaScores(matches, teams, fifaData);
// --- Step 3: Goal-Details nur für live + kürzlich beendete Matches mit Toren --- // --- Step 3: Goal-Details für alle Spiele mit Toren (live + beendet) ---
// Requests werden durch differenzierte Cache-TTL gespart:
// beendete Spiele → 24h (Tore ändern sich nie)
// laufende Spiele → 60s (neue Tore erscheinen)
const targets = updated.filter(m => { const targets = updated.filter(m => {
const fs = fifaData.scores.get(m.matchNumber); const fs = fifaData.scores.get(m.matchNumber);
return ( return (
((m.status === "LIVE" || m.status === "IN_PLAY" || m.status === "PAUSED") || isRecentlyFinished(m)) && (m.status === "LIVE" || m.status === "IN_PLAY" || m.status === "PAUSED" || m.status === "FINISHED") &&
((m.homeScore ?? 0) + (m.awayScore ?? 0)) > 0 && ((m.homeScore ?? 0) + (m.awayScore ?? 0)) > 0 &&
fs?.idMatch != null && fs?.idMatch != null &&
fs?.idStage != null fs?.idStage != null
@@ -810,8 +808,10 @@ export async function attachFifaLiveData(
for (const m of targets) { for (const m of targets) {
const fs = fifaData.scores.get(m.matchNumber)!; const fs = fifaData.scores.get(m.matchNumber)!;
if (!fs.idMatch || !fs.idStage) continue; if (!fs.idMatch || !fs.idStage) continue;
const isFinished = m.status === "FINISHED";
const ttl = isFinished ? 24 * 60 * 60 * 1000 : 60_000; // beendet: 24h, live: 60s
try { try {
const goals = await fetchFifaGoals(fs.idStage, fs.idMatch, locale); const goals = await fetchFifaGoals(fs.idStage, fs.idMatch, locale, ttl);
fetched++; fetched++;
if (goals.length) goalsByMatchId.set(m.id, goals); if (goals.length) goalsByMatchId.set(m.id, goals);
} catch { } catch {