Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .beads/interactions.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,5 @@
{"id":"int-da8f1e13ed4fa8bf71891741adc8771d","kind":"field_change","created_at":"2026-09-03T14:38:47.731805Z","actor":"Seth","issue_id":"game-thumbs-j98","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Confirmed the URL omits required + separators; the corrected URL returns 200."}}
{"id":"int-0b81d4e6d297dd14d17a256a0124b054","kind":"field_change","created_at":"2026-09-03T15:18:39.688871Z","actor":"Seth","issue_id":"game-thumbs-ot7","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Added support and coverage for official football-club suffixes used by AFL team identifiers."}}
{"id":"int-dfee9d02f58917cce99d13206fff9a9f","kind":"field_change","created_at":"2026-09-03T15:32:38.05281Z","actor":"Seth","issue_id":"game-thumbs-0aj","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Successful assertion-only tests no longer fail CI summaries."}}
{"id":"int-b2e9f2c6cecf21c497c48b1c0de73718","kind":"field_change","created_at":"2026-09-13T21:35:44.947226Z","actor":"Seth","issue_id":"game-thumbs-2wi","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Implemented strict league-as-team resolution with branding, endpoint coverage, and documentation"}}
{"id":"int-1a4362037e07d913700a6e177c37efde","kind":"field_change","created_at":"2026-09-15T14:52:50.314815Z","actor":"Seth","issue_id":"game-thumbs-xat","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Configured a black WNBA league-team surface and added regression coverage"}}
2 changes: 2 additions & 0 deletions .beads/issues.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
{"_type":"issue","id":"game-thumbs-1l3","title":"Migrate provider HTTP calls to route through Bullpen proxy","description":"Replace direct calls to ESPN, MLB Stats, TheSportsDB, HockeyTech, FlagCDN, and NCAA upstream hosts with calls through the Bullpen proxy (X-Bullpen-Key header, /v1/{target}/{path} shape). Full cutover per docs/bullpen-translation-guide: add BULLPEN_BASE_URL/BULLPEN_API_KEY, remove THESPORTSDB_API_KEY/HOCKEYTECH_API_KEY, add credential-leak-safe header injection for image fetches. See plan at /Users/seth/.claude/plans/bullpen-translation-guide-jiggly-feather.md","notes":"Implementation complete: added Bullpen helpers to requestConfig.js (bullpenUrl, getBullpenHeaders with credential-leak safeguard), extended BaseProvider.handleHttpError for 401/Retry-After, wired header injection into 5 shared image-fetch paths + 8 provider files, removed THESPORTSDB_API_KEY/HOCKEYTECH_API_KEY, dropped hockeytech 'key' query param and PROXY_FEED toggle, fixed ESPN $ref-following bypass in ESPNAthleteProvider, updated docs/docker.md and docs/technical-details.md. All files syntax-checked and server boots cleanly with expected startup warning when Bullpen creds are unset. Blocked on live test verification pending real BULLPEN_API_KEY/BULLPEN_BASE_URL from user (placeholder .env created).","status":"in_progress","priority":1,"issue_type":"task","assignee":"Seth","owner":"seth@swvn.io","created_at":"2026-07-15T14:37:02Z","created_by":"Seth","updated_at":"2026-07-15T14:46:14Z","started_at":"2026-07-15T14:37:05Z","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"game-thumbs-3ll","title":"Fix SVG logo support for MiLB and other SVG league logos","description":"downloadImageWithSvgSupport was self-defeating: it detected .svg URLs and intended to rasterize them, but called downloadImage internally which also detected SVG content and threw before returning bytes. Additionally, several league logo fetch call sites in logoResolution.js and route handlers called downloadImage directly, bypassing SVG handling entirely. Fixed by adding allowSvg option to downloadImage and switching all external logo fetch sites to downloadImageWithSvgSupport.","status":"closed","priority":1,"issue_type":"bug","owner":"seth@swvn.io","created_at":"2026-06-24T22:59:40Z","created_by":"Seth","updated_at":"2026-06-24T22:59:44Z","closed_at":"2026-06-24T22:59:44Z","close_reason":"Implemented: added allowSvg option to downloadImage; switched all league/team logo fetch sites to downloadImageWithSvgSupport","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"game-thumbs-ujd","title":"HockeyTech SOCKS5 proxy for config extraction","description":"Opt-in SOCKS5/HTTP proxy support for HockeyTech config extraction. Branch test/hockeyTechProxy, commits 8f83b02 (expand proxy support) + 49ed73e (add opt-in SOCKS5 proxy). Not yet merged to main/dev.","status":"closed","priority":1,"issue_type":"feature","assignee":"Seth Van Niekerk","owner":"seth@swvn.io","created_at":"2026-06-19T19:49:39Z","created_by":"Seth Van Niekerk","updated_at":"2026-06-20T14:20:22Z","started_at":"2026-06-20T14:12:16Z","closed_at":"2026-06-20T14:20:22Z","close_reason":"Re-implemented HockeyTech SOCKS5 proxy support on dev from scratch (orphaned branch test/hockeyTechProxy had unmergeable conflicts). Same params/env toggles. Tests: 34/36 pass; the 2 WHL failures are pre-existing on clean dev.","external_ref":"branch-test/hockeyTechProxy","labels":["in-progress"],"dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"game-thumbs-xat","title":"Fix league logo badge contrast","description":"League identifiers used as matchup teams currently use the logo's dominant foreground color as the panel background, causing orange-on-orange WNBA rendering. Assign a contrast-safe primary surface and cover it with a unit test.","status":"closed","priority":2,"issue_type":"bug","assignee":"Seth","owner":"seth@swvn.io","created_at":"2026-09-15T14:50:09Z","created_by":"Seth","updated_at":"2026-09-15T14:52:50Z","started_at":"2026-09-15T14:50:13Z","closed_at":"2026-09-15T14:52:50Z","close_reason":"Configured a black WNBA league-team surface and added regression coverage","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"game-thumbs-2wi","title":"Add league branding as strict synthetic teams","description":"Allow each configured league to resolve as a team only when the identifier exactly matches its key, short code, ESPN slug, or full name. Use the league logo and derived colors for all team-rendering endpoints, and cover the behavior with tests and documentation.","status":"closed","priority":2,"issue_type":"feature","assignee":"Seth","owner":"seth@swvn.io","created_at":"2026-09-13T21:30:51Z","created_by":"Seth","updated_at":"2026-09-13T21:35:45Z","started_at":"2026-09-13T21:30:55Z","closed_at":"2026-09-13T21:35:45Z","close_reason":"Implemented strict league-as-team resolution with branding, endpoint coverage, and documentation","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"game-thumbs-0aj","title":"Allow assertion-only tests in CI summaries","description":"The automated test workflow currently fails any successful test that does not write a JSON result file. Update summary handling so assertion-only tests pass while failures remain reported.","status":"closed","priority":2,"issue_type":"bug","assignee":"Seth","owner":"seth@swvn.io","created_at":"2026-09-03T15:31:58Z","created_by":"Seth","updated_at":"2026-09-03T15:32:38Z","started_at":"2026-09-03T15:32:05Z","closed_at":"2026-09-03T15:32:38Z","close_reason":"Successful assertion-only tests no longer fail CI summaries.","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"game-thumbs-ot7","title":"Resolve AFL club-name identifiers","description":"Fix AFL matchup requests using full club names such as carlton-football-club and geelong-football-club, which fail despite ESPN providing the teams.","status":"closed","priority":2,"issue_type":"bug","assignee":"Seth","owner":"seth@swvn.io","created_at":"2026-09-03T15:18:01Z","created_by":"Seth","updated_at":"2026-09-03T15:18:40Z","started_at":"2026-09-03T15:18:10Z","closed_at":"2026-09-03T15:18:40Z","close_reason":"Added support and coverage for official football-club suffixes used by AFL team identifiers.","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"game-thumbs-j98","title":"Fix WTA doubles logo request parsing","description":"Investigate and correct the 400 response for /wta/WangXiyuXuYifan/ClaireLiuJulietaPareja/logo.","status":"closed","priority":2,"issue_type":"bug","assignee":"Seth","owner":"seth@swvn.io","created_at":"2026-09-03T14:38:21Z","created_by":"Seth","updated_at":"2026-09-03T14:38:48Z","started_at":"2026-09-03T14:38:26Z","closed_at":"2026-09-03T14:38:48Z","close_reason":"Confirmed the URL omits required + separators; the corrected URL returns 200.","dependency_count":0,"dependent_count":0,"comment_count":0}
Expand Down
3 changes: 2 additions & 1 deletion docs/api-reference/cover.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ nav_order: 8
| `aspect` | string | `3-4` | Aspect ratio: `3-4` (1080x1440), `9-16` (1080x1920), or `1-1` (1080x1080) |
| `style` | integer | 1 | Visual style 1-6 (matchup only); community styles 98 and 99 also available |
| `logo` | boolean | true | Include league logo (matchup only) |
| `badge` | string | - | Add quality badge overlay: `ALT`, `4K`, `HD`, `FHD`, or `UHD` (matchup only) |
| `badge` | string | - | Badge overlay(s), comma-separated (up to 4): keywords (`4K`, `HD`, `ALT`, network, language), rank numbers (`10` or `%2310` renders `#10`), optional `@tl`/`@tr`/`@bl`/`@br` position suffix. See [Badge Overlays](thumb.html#badge-overlays) |
| `winner` | string | - | Winning team identifier - displays losing team in greyscale (matchup only) |
| `fallback` | boolean | false | **Single team:** Return league cover. **Matchup:** Use greyscale league logo for missing teams |
| `mode` | string | league config | Override the league's configured `mode` (see [Customization](../customization.html#mode-name-render-team-names-instead-of-logos)) for this request: `name` renders team names as text instead of logos, `default` forces logos even if the league is configured with `mode: "name"`. Single team and matchup only. |
Expand Down Expand Up @@ -96,6 +96,7 @@ GET /mlb/bluejays/orioles/cover?logo=false
GET /nhl/oilers/jets/cover?style=99
GET /mls/toronto-fc/montreal/cover?badge=UHD
GET /nhl/canucks/flames/cover?badge=4K&style=2
GET /ncaa/miamihurricanes/olemiss/cover?fallback=true&badge=10,6
GET /nhl/senators/sabres/cover?style=5
GET /mls/vancouver/seattle/cover?style=6
```
Expand Down
6 changes: 5 additions & 1 deletion docs/api-reference/logo.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ nav_order: 6
- `team` / `team1` / `team2` - Team identifier (name, city, or abbreviation)
- **Athlete Sports (Tennis, MMA):** Use athlete names (e.g., `djokovic`, `serena-williams`)
- **Doubles/Teams:** Use `+` to combine multiple athletes (e.g., `djokovic+federer`)
- **League teams:** Use the selected league's short code, ESPN slug, or full name to render its logo and colors as a team. For example, `/nhl/nhl/tor/logo` renders the NHL against Toronto.

---

Expand All @@ -35,7 +36,7 @@ nav_order: 6
| `size` | integer | 1024 | Output size: 256, 512, 1024, or 2048 (matchup, or league/team with `style=1`) |
| `logo` | boolean | false | Include league logo (matchup only) |
| `trim` | boolean | true | Trim whitespace (matchup, or league/team with `style=1`) |
| `badge` | string | - | Add quality badge overlay: `ALT`, `4K`, `HD`, `FHD`, or `UHD` (matchup only) |
| `badge` | string | - | Badge overlay(s), comma-separated (up to 4): keywords (`4K`, `HD`, `ALT`, network, language), rank numbers (`10` or `%2310` renders `#10`), optional `@tl`/`@tr`/`@bl`/`@br` position suffix (matchup only). See [Badge Overlays](thumb.html#badge-overlays) |
| `winner` | string | - | Winning team identifier - displays losing team in greyscale (matchup only) |
| `fallback` | boolean | false | **Single team:** Return league logo. **Matchup:** Use greyscale league logo for missing teams (or skipLogos mode for configured leagues) |
| `mode` | string | league config | Override the league's configured `mode` (see [Customization](../customization.html#mode-name-render-team-names-instead-of-logos)) for this request: `name` renders a team name text card instead of the real logo asset, `default` forces the real logo even if the league is configured with `mode: "name"`. Single team and matchup only. |
Expand Down Expand Up @@ -93,8 +94,11 @@ GET /nba/raptors/lakers/logo?style=3
GET /mlb/bluejays/redsox/logo?size=2048
GET /nhl/oilers/flames/logo?badge=4K
GET /mls/toronto-fc/montreal/logo?badge=ALT&style=2
GET /ncaa/miamihurricanes/olemiss/logo?fallback=true&badge=10,6
```

**Live badge example:** [![NCAA matchup logo with rank badges](https://game-thumbs.swvn.io/ncaa/miamihurricanes/olemiss/logo?fallback=true&badge=10,6)](https://game-thumbs.swvn.io/ncaa/miamihurricanes/olemiss/logo?fallback=true&badge=10,6)

### Fallback Behavior

When `fallback=true` is set, the API gracefully handles missing data instead of returning errors.
Expand Down
5 changes: 3 additions & 2 deletions docs/api-reference/raw-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ nav_order: 9

**Endpoint:** `/:league/:team/raw`

Returns raw JSON data for a team from the provider.
Returns raw JSON data for a team or a synthetic league team.

---

## Parameters

- `league` - Sport league code (see [Supported Leagues](supported-leagues.html))
- `team` - Team identifier (name, city, or abbreviation)
- `team` - Team identifier (name, city, or abbreviation). A league's own short code, ESPN slug, or full name also resolves to a synthetic league team using that league's logo and colors.

---

Expand All @@ -26,6 +26,7 @@ Returns raw JSON data for a team from the provider.
curl http://localhost:3000/nba/lakers/raw
curl http://localhost:3000/nfl/chiefs/raw
curl http://localhost:3000/ncaaf/alabama/raw
curl http://localhost:3000/nhl/nhl/raw
```

---
Expand Down
45 changes: 44 additions & 1 deletion docs/api-reference/thumb.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ nav_order: 7
- `/:league/:team/thumb[.png]` - Team thumbnail (team color gradient with logo)
- `/:league/:team1/:team2/thumb[.png]` - Matchup thumbnail (split design, 1440x1080)

A league can also be used as a team by supplying its short code, ESPN slug, or full name. For example, `/nhl/nhl/thumb` generates an NHL-branded team thumbnail.

---

## Parameters
Expand All @@ -33,7 +35,7 @@ nav_order: 7
| `aspect` | string | `4-3` | Aspect ratio: `4-3` (1440x1080), `16-9` (1920x1080), or `1-1` (1080x1080) |
| `style` | integer | 1 | Visual style 1-6 (matchup only); community styles 98 and 99 also available |
| `logo` | boolean | true | Include league logo (matchup only) |
| `badge` | string | - | Add quality badge overlay: `ALT`, `4K`, `HD`, `FHD`, or `UHD` (matchup only) |
| `badge` | string | - | Badge overlay(s), comma-separated (up to 4): keywords (`4K`, `HD`, `ALT`, network, language), rank numbers (`10` or `%2310` renders `#10`), optional `@tl`/`@tr`/`@bl`/`@br` position suffix. See [Badge Overlays](#badge-overlays) |
| `winner` | string | - | Winning team identifier - displays losing team in greyscale (matchup only) |
| `fallback` | boolean | false | **Single team:** Return league thumb. **Matchup:** Use greyscale league logo for missing teams |
| `mode` | string | league config | Override the league's configured `mode` (see [Customization](../customization.html#mode-name-render-team-names-instead-of-logos)) for this request: `name` renders team names as text instead of logos, `default` forces logos even if the league is configured with `mode: "name"`. Single team and matchup only. |
Expand All @@ -43,6 +45,46 @@ nav_order: 7

---

## Badge Overlays

**Applies to:** matchup, team, and league thumbnails

Add up to **4** badge overlays with the `badge` query parameter, comma-separated:

```
GET /ncaa/miamihurricanes/olemiss/thumb?style=1&logo=true&fallback=true&badge=10,6
```

**Badge types:**

| Type | Examples | Renders | Notes |
|------|----------|---------|-------|
| Quality | `4K`, `HD`, `FHD`, `UHD` | as given | |
| Alternate feed | `ALT`, `MANNINGCAST`, `PRIMEVISION` | as given | |
| Event | `PLAYOFFS`, `PRESEASON` | as given | |
| Language | `EN`, `ES`, `FR`, `DE`, `IT` (+ longer forms) | as given | |
| Network | `ESPN`, `FOX`, `NBC`, `CBS`, `ABC`, `NFLN`, `MLBN`, `NBA TV`, `CW`, `PEACOCK` | as given | |
| **Rank** | `10`, `6`, or `%2310` (`#` must be URL-encoded) | `#10`, `#6` | 1-2 digits, e.g. AP poll standings |

{: .note }
> A bare `#` starts the URL fragment, so pass ranks as plain digits (`badge=10,6`) or URL-encode the hash (`badge=%2310,%236`) — both render as `#10` / `#6`.

**Positioning:** a single badge renders in the top-right corner (existing behavior). Multiple badges are distributed across corners in reading order — top-left, top-right, bottom-left, bottom-right — so `badge=10,6` places each team's rank over its own half of the thumbnail. Pin any badge to a corner with a position suffix: `@tl`, `@tr`, `@bl`, or `@br` (long forms like `@top-left` also work). Badges sharing a corner are laid out side by side.

```
GET /nfl/chiefs/49ers/thumb?badge=4K -> 4K in the top-right
GET /nba/lakers/celtics/thumb?badge=10,6 -> #10 top-left, #6 top-right
GET /nba/lakers/celtics/thumb?badge=%2310,%236 -> same as above
GET /nfl/chiefs/49ers/thumb?badge=4K@br,10,6 -> ranks top-left/top-right, 4K bottom-right
GET /nba/lakers/celtics/thumb?badge=PLAYOFFS,10,6,4K -> one badge per corner
```

**Live example:** [![NCAA matchup with rank badges](https://game-thumbs.swvn.io/ncaa/miamihurricanes/olemiss/thumb?style=1&logo=true&fallback=true&badge=10,6)](https://game-thumbs.swvn.io/ncaa/miamihurricanes/olemiss/thumb?style=1&logo=true&fallback=true&badge=10,6)

Invalid badge values are ignored (the thumbnail renders without them), matching the single-badge behavior. Custom badge text outside the keywords above requires `ALLOW_CUSTOM_BADGES=true` on the server.

---

## Matchup Styles

**Applies to:** `/:league/:team1/:team2/thumb` only
Expand Down Expand Up @@ -96,6 +138,7 @@ GET /mlb/bluejays/yankees/thumb?logo=false
GET /nhl/oilers/flames/thumb?style=99
GET /mls/toronto-fc/lafc/thumb?badge=4K
GET /nhl/canucks/bruins/thumb?badge=HD&style=3
GET /ncaa/miamihurricanes/olemiss/thumb?fallback=true&badge=10,6
GET /nhl/jets/blackhawks/thumb?style=5
GET /nhl/senators/rangers/thumb?style=6
```
Expand Down
1 change: 1 addition & 0 deletions leagues.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
},
"wnba": {
"name": "Women's National Basketball Association",
"leagueTeamColor": "#222222",
"providers": [
{
"espn": {
Expand Down
Loading
Loading