v2 Phase D: capture-first ingestion — FotMob, FIFA, weather, scorer props
Four new feeds, all verified against real tournament matches: - FotMob (unofficial, works from the VPS again): per-shot xG/xGOT with pitch coords, team xG, player ratings, POTM, momentum, attacking zones — trimmed payloads (~87KB/match) on a gentle 90s-live/30m-idle cadence. Capture-first: this source could lock again any day. - FIFA official v3: confirmed lineups with player IDs, typed XY event timelines (80 events for the opener), attendance + named referees, tactics — id-mapped via the calendar, hot while live. - Open-Meteo: kickoff-hour weather per venue from a build-time geocoded venues.json (with elevation — Azteca 2,240m); fetched in the enrich loop inside the 48h horizon. - DraftKings scorer props (first/last/anytime goalscorer per athlete) via ESPN's core propBets, insert-if-changed line history, athlete names resolved through a 30-day cache. Also fixes a real resilience gap found along the way: fixture results now persist in SQLite and restore at boot — previously a restart after ESPN's scoreboard window moved on would silently forget a match day (standings, model and scoreboard would all regress). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+10
-1
@@ -10,7 +10,7 @@ import { startScheduler } from './ingest/scheduler';
|
||||
import { ModelEngine } from './model';
|
||||
import { buildPreview, buildTeamProfile } from './preview';
|
||||
import { buildScoreboard, snapshotCheck } from './scoreboard';
|
||||
import { db, healthAll, oddsFor, latestOddsAll, dbCounts, inplayHistory, recordInplay, getMatchExt } from './db/db';
|
||||
import { db, healthAll, oddsFor, latestOddsAll, dbCounts, inplayHistory, recordInplay, getMatchExt, allFixtureResults, saveFixtureResult } from './db/db';
|
||||
import { inPlayProbs } from '../../src/lib/model/inplay';
|
||||
import { PushNotifier, pushEnabled, subscribe as pushSubscribe, unsubscribe as pushUnsubscribe, vapidPublicKey } from './push';
|
||||
import type { EspnSummary } from './ingest/espnNormalize';
|
||||
@@ -37,6 +37,14 @@ export function buildServer() {
|
||||
|
||||
db(); // open + migrate SQLite before anything reads it
|
||||
const state = new TournamentState(STATIC_DIR);
|
||||
// Restore persisted results over the seed — the in-memory snapshot forgets
|
||||
// them on restart once ESPN's scoreboard window has moved past a match day.
|
||||
let restored = 0;
|
||||
for (const r of allFixtureResults()) {
|
||||
if (r.status === 'scheduled') continue;
|
||||
if (state.applyScore(r.fixture_num, r.home_score, r.away_score, r.status as MatchStatus, null, 'seed')) restored++;
|
||||
}
|
||||
if (restored) console.log(`[state] restored ${restored} fixture result(s) from the DB`);
|
||||
const model = new ModelEngine(STATIC_DIR);
|
||||
model.refitTeamDc(state.allFixtures()); // current Team-DC from day one (covers restarts mid-tournament)
|
||||
model.recompute(state.allFixtures());
|
||||
@@ -184,6 +192,7 @@ export function buildServer() {
|
||||
'football-data',
|
||||
);
|
||||
if (!f) return reply.code(404).send({ error: 'no such fixture' });
|
||||
saveFixtureResult(f.num, f.homeScore, f.awayScore, f.status);
|
||||
snapshotCheck(state, model);
|
||||
recordInplayTick();
|
||||
broadcast();
|
||||
|
||||
Reference in New Issue
Block a user