Wave 2B: reliable cross-sport headshots via ESPN athlete index
The NBA/WNBA espnId was captured only from espnStatsAdapter (the offline-
Python fallback), unreliable in prod. Add espnAthleteIndex — a pure,
defensive harvester that builds { nameKey -> {espnId, headshotHref} } from
the ESPN schedule->summary/boxscore/leaders/injuries/roster feeds the
pipeline already calls (free, bounded mapLimit, cached, MLB->{}).
snapshotService now fills any player the primary stats-resolve left without
an espnId from this index, and stores a DIRECT headshotHref as headshotUrl
on the enriched grade (the exact URL, never 404s on a constructed path).
Threaded headshotUrl through slateAdapter.buildPlayerStripsFromProps ->
GameCard -> StatStrip -> PlayerAvatar/getHeadshotUrl (direct href wins over
the constructed one). MLB's MLBAM path is untouched. Soccer resolves only
via a direct href; absent -> honest monogram (API_FOOTBALL_KEY remains the
reliable soccer path, unwired).
getGameSummary now also passes through ESPN `rosters` (pre-game lineups
carry id + headshot). Everything graceful: any miss -> absent -> monogram.
Tests: tests/unit/espnHeadshotIndex.test.js (11) — fixture->index, snapshot
merge fallback, direct-href-wins, soccer honest monogram, malformed/cyclic
parse never throws. Full suite 3080 green; web next build exit 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -121,6 +121,17 @@ Built by `slateAdapter.mapScheduleToGameCards` (`groupPropsByPlayer` +
|
||||
`mapPitchers`). The card prefers `playerStrips` (name once, horizontal) over
|
||||
legacy per-prop rows. Book chips use `web/src/lib/books.js` brand colors.
|
||||
|
||||
**Headshots (Wave 2A/2B):** each enriched grade + player strip may carry
|
||||
`playerId` (MLBAM), `espnId` (ESPN athlete id), and `headshotUrl` (a RESOLVED
|
||||
absolute ESPN href). `PlayerAvatar` prefers `headshotUrl` (exact URL, never
|
||||
404s) → else constructs from `(sport, playerId|espnId)` → else a team-colored
|
||||
monogram. `espnId`/`headshotUrl` come from `snapshotService`: primarily the
|
||||
per-player stats resolve, and — when that misses (the flaky NBA/WNBA fallback)
|
||||
— from `espnAthleteIndex.buildEspnAthleteIndex(sport)`, which harvests ESPN
|
||||
schedule→summary/boxscore/leaders/injuries/roster feeds (free, cached, MLB→{}).
|
||||
Soccer resolves ONLY via a direct `headshotUrl` (no constructed URL); absent →
|
||||
honest monogram (a free `API_FOOTBALL_KEY` is the reliable soccer path, unwired).
|
||||
|
||||
---
|
||||
|
||||
## 5. Stat Strip (`web/src/components/vyndr/StatStrip.tsx`)
|
||||
|
||||
Reference in New Issue
Block a user