migration to new feed source

This commit is contained in:
2026-07-01 16:34:01 -05:00
parent cfb778776e
commit 4e4c72da7f
58 changed files with 444 additions and 48 deletions

50
scripts/download-flags.ts Normal file
View File

@@ -0,0 +1,50 @@
// download-flags.ts — Lädt circle-flags SVGs für alle Teams aus TLA_TO_ISO2.
// Ausführung: npx tsx scripts/download-flags.ts
import { TLA_TO_ISO2 } from "../lib/flags";
import * as fs from "fs";
import * as path from "path";
const BASE = "https://hatscripts.github.io/circle-flags/flags";
const OUT = path.join(__dirname, "..", "public", "flags");
async function main() {
const codes = Object.values(TLA_TO_ISO2);
const unique = [...new Set(codes)];
console.log(`Lade ${unique.length} Flaggen (${codes.length} Team-Codes)...`);
if (!fs.existsSync(OUT)) fs.mkdirSync(OUT, { recursive: true });
let ok = 0;
let failed = 0;
for (const iso2 of unique) {
const url = `${BASE}/${iso2}.svg`;
try {
const res = await fetch(url, {
headers: { "User-Agent": "circle-flags-download/1.0" },
});
if (!res.ok) {
console.warn(`${iso2}: HTTP ${res.status}`);
failed++;
continue;
}
const svg = await res.text();
// Prüfen: keine leere/Fehler-SVG (<10 Bytes = kaputt)
if (svg.trim().length < 10) {
console.warn(`${iso2}: leere SVG (${svg.length} Bytes)`);
failed++;
continue;
}
fs.writeFileSync(path.join(OUT, `${iso2}.svg`), svg);
ok++;
} catch (err) {
console.warn(`${iso2}: ${err instanceof Error ? err.message : err}`);
failed++;
}
}
console.log(`\n✅ ${ok} OK, ❌ ${failed} fehlgeschlagen`);
if (failed > 0) process.exit(1);
}
main();

119
scripts/fifa-eval.ts Normal file
View File

@@ -0,0 +1,119 @@
// fifa-eval.ts — Analyse-Skript: IdGroup, MatchNumber, Attendance, MatchStatus
// Ausführung: npx ts-node --compiler-options '{"module":"commonjs","target":"es2020"}' scripts/fifa-eval.ts
// Oder mit Deno, Bun: bun run scripts/fifa-eval.ts
const FIFA_BASE = "https://api.fifa.com/api/v3";
const FIFA_SEASON = "285023";
interface RawMatch {
MatchNumber: number;
MatchStatus: number;
IdGroup: string | null;
IdStage: string;
StageName: Array<{ Locale: string; Description: string }>;
GroupName?: Array<{ Locale: string; Description: string }>;
Home: { IdTeam: string; Abbreviation: string; TeamName?: Array<{ Locale: string; Description: string }> } | null;
Away: { IdTeam: string; Abbreviation: string; TeamName?: Array<{ Locale: string; Description: string }> } | null;
HomeTeamScore: number | null;
AwayTeamScore: number | null;
Attendance: string | null;
Date: string;
LocalDate: string;
}
async function main() {
console.log("📡 Rufe FIFA Calendar-Endpoint ab...");
const res = await fetch(
`${FIFA_BASE}/calendar/matches?language=de&count=500&idSeason=${FIFA_SEASON}`,
{ headers: { "User-Agent": "fifa-eval/1.0" } },
);
if (!res.ok) { console.error("Fehler:", res.status, res.statusText); process.exit(1); }
const data = (await res.json()) as { Results: RawMatch[] };
const matches = data.Results ?? [];
console.log(`${matches.length} Matches geladen\n`);
// ── A) IdGroup → GroupName Mapping ──
console.log("═══ A) IdGroup → Gruppe (A-L) ═══");
const groupMap = new Map<string, string>();
for (const m of matches) {
if (!m.IdGroup || !m.GroupName) continue;
const desc = m.GroupName.find(g => g.Locale === "de-DE")?.Description
?? m.GroupName[0]?.Description
?? `ID:${m.IdGroup}`;
if (!groupMap.has(m.IdGroup)) groupMap.set(m.IdGroup, desc);
}
console.log(`Anzahl distinct IdGroup-Werte: ${groupMap.size}`);
const sorted = [...groupMap.entries()].sort((a, b) => parseInt(a[0]) - parseInt(b[0]));
for (const [id, name] of sorted) {
console.log(` ${id} → "${name}"`);
}
// Als kopierbare Konstante
console.log("\n// Kopierbare Konstante:");
console.log("const FIFA_IDGROUP_TO_GROUP: Record<string, string> = {");
for (const [id, name] of sorted) {
const letter = name.replace(/^Gruppe\s+/, "");
console.log(` "${id}": "${letter}", // ${name}`);
}
console.log("};");
// ── B) MatchNumber-Konsistenz ──
console.log("\n═══ B) MatchNumber-Konsistenz ═══");
const groupMatches = matches.filter(m => m.IdGroup != null).sort((a, b) => a.MatchNumber - b.MatchNumber);
const koMatches = matches.filter(m => m.IdGroup == null).sort((a, b) => a.MatchNumber - b.MatchNumber);
console.log(`Gruppenspiele: ${groupMatches.length} (MatchNumber ${groupMatches[0]?.MatchNumber}${groupMatches[groupMatches.length-1]?.MatchNumber})`);
console.log(`K.o.-Spiele: ${koMatches.length} (MatchNumber ${koMatches[0]?.MatchNumber}${koMatches[koMatches.length-1]?.MatchNumber})`);
// Stichproben für K.o.-Slots
const checkNums = [73, 74, 76, 89, 104];
for (const n of checkNums) {
const m = matches.find(x => x.MatchNumber === n);
if (m) {
const home = m.Home?.Abbreviation ?? "?";
const away = m.Away?.Abbreviation ?? "?";
console.log(` #${n}: ${home} vs ${away} | Stage=${m.StageName?.[0]?.Description ?? "?"} | Status=${m.MatchStatus}`);
} else {
console.log(` #${n}: NICHT GEFUNDEN`);
}
}
// ── C) Attendance + Scheduled ──
console.log("\n═══ C) Attendance + Scheduled-Matches ═══");
const withAttendance = matches.filter(m => m.Attendance != null);
console.log(`Matches mit Attendance: ${withAttendance.length}`);
if (withAttendance.length > 0) {
const ex = withAttendance[0];
console.log(` Beispiel: #${ex.MatchNumber}${ex.Attendance}`);
}
const scheduled = matches.filter(m => m.MatchStatus === 1);
console.log(`Scheduled-Matches (Status=1): ${scheduled.length}`);
if (scheduled.length > 0) {
const ex = scheduled[0];
console.log(` Beispiel: #${ex.MatchNumber} ${ex.Home?.Abbreviation ?? "?"} vs ${ex.Away?.Abbreviation ?? "?"} | Date=${ex.Date}`);
}
// ── D) MatchStatus-Werte ──
console.log("\n═══ D) Distinct MatchStatus-Werte ═══");
const statuses = new Map<number, number>();
for (const m of matches) {
statuses.set(m.MatchStatus, (statuses.get(m.MatchStatus) ?? 0) + 1);
}
const statusLabels: Record<number, string> = { 0: "finished", 1: "scheduled", 10: "postponed", 3: "live?" };
for (const [s, c] of [...statuses.entries()].sort((a, b) => a[0] - b[0])) {
console.log(` MatchStatus ${s}: ${c} Matches ${statusLabels[s] ? `(${statusLabels[s]})` : ""}`);
}
// ── E) Stage-Werte ──
console.log("\n═══ E) Distinct Stages ═══");
const stages = new Map<string, number>();
for (const m of matches) {
const sn = m.StageName?.[0]?.Description ?? String(m.IdStage);
stages.set(sn, (stages.get(sn) ?? 0) + 1);
}
for (const [s, c] of stages.entries()) {
console.log(` "${s}" (IdStage=${matches.find(m => (m.StageName?.[0]?.Description ?? "") === s)?.IdStage}): ${c}`);
}
console.log("\n✅ Analyse abgeschlossen.");
}
main().catch(console.error);