Skip to content

feat(badges): multiple badge overlays, rank badges, corner positions + pill corner fix - #161

Merged
sethwv merged 4 commits into
sethwv:devfrom
lpukatch:feat/multi-badge-overlays
Oct 1, 2026
Merged

sethwv merged 4 commits into
sethwv:devfrom
lpukatch:feat/multi-badge-overlays

Conversation

@lpukatch

@lpukatch lpukatch commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Target branch: Please open PRs against dev, not main. PRs targeting main directly will be asked to retarget.

Description

Extends the badge query parameter to support up to 4 badge overlays per request, motivated by showing AP/coaches poll rankings on matchup thumbnails:

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

renders #10 over Miami's half (top-left) and #6 over Ole Miss' half (top-right).

  • List syntax: comma-separated values or repeated badge params (?badge=4K,HD). Invalid entries are dropped silently (existing single-badge behavior); a request with no valid entries renders badge-free as before.
  • Rank badges: 1–2 digit values (6, 10) or URL-encoded hashes (%236) render as #6/#10. These are valid without ALLOW_CUSTOM_BADGES so rankings work on hosted instances — bare digits are the friendly form since a literal # starts the URL fragment.
  • Positioning: a single badge keeps the historic top-right corner. Multiple unpinned badges spread across corners in reading order (TL → TR → BL → BR), so badge order matches team order (first badge over the first team's half). An optional suffix pins a badge: @tl/@tr/@bl/@br (long forms accepted), e.g. ?badge=4K@br,10,6 keeps the quality badge clear of the rankings. Badges sharing a corner are laid out side by side.
  • Bug fix (pre-existing): the pill's bottom-right corner passed the tangent-start point to arcTo instead of the corner vertex, leaving a degenerate first tangent — node-canvas rendered a diagonal slash across the pill bottom (visible on light team-color backgrounds). Fixed to the canonical vertex form; verified via row-by-row pixel extents (bottom edge now mirrors the top). Pill cache key versioned (_v2) so instances that skip the startup badges-cache clear don't serve pre-fix pills.

Works on thumb, cover, and logo routes (logo keeps its badgeScale 0.18). Base-image caching is preserved: the un-badged render is cached under the badge-stripped URL and reused across badge combinations; the stripper now handles repeated badge params and ?badge=X&a=1 correctly.

Type of Change

  • New feature
  • Bug fix

Changes Made

  • src/helpers/image/badge.js: rank-badge validation, parseBadges() list/position parser, renderBadgePill() extraction (per-pill fsCache retained, versioned), addBadgesOverlay() multi-badge compositor (single-pass, corner grouping), BR-corner arcTo fix
  • src/helpers/routeUtils.js: applyBadgeWithCaching now drives the parsed badge list; robust stripBadgeParam
  • src/helpers/imageUtils.js: re-export updates
  • docs/api-reference/{thumb,cover,logo}.md: param docs + new Badge Overlays section with examples
  • test/endpoints.test.js: single-badge regression, bare-digit ranks, encoded %23 ranks, pinned position, four badges, invalid-entry cases

Related Issue

Closes #

Testing

  • Tested locally
  • Added/updated tests
  • Verified endpoints work as expected

Test Details:

  • Docker image built from this branch, run in production mode without ALLOW_CUSTOM_BADGES (exercises the real whitelist path) and IMAGE_CACHE_HOURS=0
  • Full endpoint suite: 45/45 passed (39 images generated), including the 6 new badge cases
  • Byte-level invariants verified: badge=10,6 ≡ badge=%2310,%236; badge= ≡ badge=100 (3-digit, invalid → no overlay); badge=BOGUS,4K ≡ badge=4K (invalid dropped, base-image cache reused)
  • Pill corner fix verified by row-by-row pixel extents: bottom extreme row 158px wide (was 17px collapsing to the left), mirroring the top edge (160px)

Screenshots/Examples

GET /ncaa/miamihurricanes/olemiss/thumb?style=1&logo=true&fallback=true&badge=10,6
GET /ncaa/miamihurricanes/olemiss/thumb?style=1&logo=true&fallback=true&badge=%2310,%236
GET /nfl/chiefs/49ers/thumb?badge=4K@br,1,2
GET /nba/lakers/celtics/thumb?badge=PLAYOFFS,10,6,4K

Checklist

  • My code follows the project's coding style
  • I have updated documentation if needed
  • My changes generate no new warnings or errors
  • I have tested my changes thoroughly

Additional Notes

Backward compatible: every existing single-badge URL produces pixel-identical output to dev (verified by md5 across the old/new overlay paths on identical inputs) except the corrected bottom-right pill corner, which is the intended fix.

lpukatch and others added 4 commits September 30, 2026 22:34
…ner positions

- `badge` now accepts a comma-separated list (or repeated params) of up to
  4 values: '?badge=4K,HD'
- Rank badges: 1-2 digit values (6, 12) or URL-encoded hashes (%236) render
  as 'sethwv#6'/'sethwv#12' and are valid without ALLOW_CUSTOM_BADGES, enabling AP/coaches
  poll rankings on hosted instances ('#'' starts a URL fragment, so bare
  digits are the friendly form)
- Optional per-badge position suffix pins a corner: 4K@br / HD@top-left
  (tl/tr/bl/br or long forms); unpinned badges spread across corners in
  reading order (TL, TR, BL, BR) so '?badge=6,12' puts each team's rank over
  its own half; a lone badge keeps the historic top-right placement; badges
  sharing a corner are laid out side by side
- Invalid entries are dropped silently, matching existing single-badge
  behavior; base-image caching now strips repeated badge params cleanly
- Fix pre-existing pill rendering bug: the bottom-right corner arcTo passed
  the tangent-start point instead of the corner vertex, leaving a degenerate
  first tangent and drawing a diagonal slash across the pill bottom
- Pill cache key versioned (_v2) so instances without the startup cache
  clear don't serve pre-fix pills
- Docs updated for thumb/cover/logo; endpoint tests added for single,
  ranked, encoded, pinned, four-badge, and invalid-entry cases
Limit multi-badge overlay documentation to matchup logo routes, which are the paths that apply the overlay.
Show rendered NCAA rank-badge examples in the thumbnail and matchup logo API references.
@sethwv
sethwv merged commit b0eb995 into sethwv:dev Oct 1, 2026
21 checks passed
github-actions Bot pushed a commit that referenced this pull request Oct 1, 2026
…+ pill corner fix (#161)

Extends the `badge` query parameter to support **up to 4 badge overlays
per request**, motivated by showing AP/coaches poll rankings on matchup
thumbnails:
github-actions Bot pushed a commit that referenced this pull request Oct 1, 2026
…+ pill corner fix (#161)

Extends the `badge` query parameter to support **up to 4 badge overlays
per request**, motivated by showing AP/coaches poll rankings on matchup
thumbnails:
github-actions Bot pushed a commit that referenced this pull request Oct 1, 2026
…+ pill corner fix (#161)

Extends the `badge` query parameter to support **up to 4 badge overlays
per request**, motivated by showing AP/coaches poll rankings on matchup
thumbnails:
github-actions Bot pushed a commit that referenced this pull request Oct 1, 2026
…+ pill corner fix (#161)

Extends the `badge` query parameter to support **up to 4 badge overlays
per request**, motivated by showing AP/coaches poll rankings on matchup
thumbnails:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants