Skip to content

feat(baseball-scoreboard): team-color grid, clearer at-bat labels, player card (v1.19.0)#180

Merged
ChuckBuilds merged 6 commits into
mainfrom
claude/baseball-scoreboard-enhancements-4im01k
Jul 10, 2026
Merged

feat(baseball-scoreboard): team-color grid, clearer at-bat labels, player card (v1.19.0)#180
ChuckBuilds merged 6 commits into
mainfrom
claude/baseball-scoreboard-enhancements-4im01k

Conversation

@ChuckBuilds

@ChuckBuilds ChuckBuilds commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Pull Request

Summary

Three aesthetic/usability enhancements to the baseball plugin: a subtle team-color tint on the traditional grid scoreboard, spelled-out "Pitcher:" / "Batter:" labels (so "B" is no longer ambiguous with the grid's Balls indicator), and a new masters-style player card with headshot, jersey number, position, bat/throw, and season stats for the current batter/pitcher.

Type of change

  • Bug fix in an existing plugin
  • New plugin (also fill out the SUBMISSION.md checklist below)
  • New feature for an existing plugin
  • Documentation only
  • Repo-wide change (registry script, hook, top-level docs)

Plugin(s) affected

baseball-scoreboard

Related issues

N/A

Test plan

  • Loaded the plugin in LEDMatrix on real hardware
  • Loaded the plugin in LEDMatrix emulator mode (EMULATOR=true python3 run.py)
  • Rendered the plugin in the dev preview server (scripts/dev_server.py)
  • Verified the web UI configuration form against the schema
  • N/A — repo-wide / docs-only change

Details:

  • Added test_player_card.py (17 tests): roster info-map join, current-batter/pitcher ID scan, ESPN bio parsing (_parse_player_details), stat selection (hitter vs pitcher vs generic fallback), team-color resolution, gatekeeper behavior (MiLB skip, skip-when-no-bio, favorites-only), and a render smoke test across all four panel sizes (64×32, 128×32, 128×64, 256×32) asserting no margin overflow and headshot-hidden on the tiny tier.
  • Added two grid-tint tests to test_traditional_scoreboard.py (tint present when enabled, absent when disabled while solid team colors still render).
  • Full existing baseball suite re-run: passing (the few failures present — test_baseball_plugin, one test_config_reload assertion, test_test_mode_live_games) are pre-existing and reproduce identically on main; they require the core src/fonts tree.
  • config_schema.json and manifest.json validate as JSON; update_registry.py synced plugins.json to 1.19.0; scripts/check_module_collisions.py passes (no new bare-name modules).

Required for plugin changes

  • Bumped version in plugins/<id>/manifest.json (1.18.0 → 1.19.0)
  • class_name in manifest.json matches the actual class in manager.py exactly
  • entry_point matches the real file (or is omitted to use the manager.py default)
  • Updated the plugin's README.md (documented the grid toggle, label change, and new Player Card screen + options)
  • config_schema.json is the source of truth for the web UI form — new options added with default, description, and constraints (show_team_color_backgrounds, per-league show_player_card, and a player_card customization block)
  • Pre-commit hook ran successfully (auto-syncs plugins.json) — hook not installed in this environment; ran update_registry.py manually instead and committed the synced plugins.json

Checklist

  • My commits follow the message convention in CONTRIBUTING.md
  • I read CONTRIBUTING.md and CODE_OF_CONDUCT.md
  • I've not committed any secrets

Notes for reviewer

  • No new modules. All logic lives in existing files (baseball.py, data_sources.py, logo_manager.py) to avoid the cross-plugin bare-name collision rule in CLAUDE.md.
  • Data reuse. The grid tint reuses the already-fetched away_team_color/home_team_color. Jersey/position/headshot-URL come free from the ESPN game-summary rosters; only the season stats need the extra athletes/{id} + /overview fetch (mirrors the masters plugin), cached in memory and on disk (assets/headshots/, gitignored).
  • Graceful degradation: MiLB (no ESPN player IDs) skips the card entirely; a missing headshot falls back to a text-only card; missing/sparse stats (NCAA) show name/jersey/position only; the card is skipped rather than shown blank when no bio is resolved yet.
  • Three rotating screens (at-bat / player card / traditional) share the live frame with distinct default intervals (25s / 40s / 30s) and each self-throttles, so they interleave without double-drawing.
  • The three rotating screens depend on core fonts to render, so the render-path tests fall back to a default bitmap font when run outside the core tree (same pattern as the existing traditional-scoreboard tests); the safety harness exercises real font metrics.

🤖 Generated with Claude Code

https://claude.ai/code/session_01R1z28MrGx56x6eiJvibDg9


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Added optional full-screen Player Card screen for MLB and NCAA live games with rotating headshot/player details and season stats (favorites-only and pitcher/batter controls).
    • Added team-color background tinting and left accent strips on the traditional scoreboard (configurable, tied to team colors).
  • Improvements
    • Updated at-bat labels from “P:/B:” to “Pitcher:” and “Batter:”.
    • Improved graceful fallback behavior when headshots or stats aren’t available.
  • Documentation
    • Updated README and configuration options for Player Card and team-color backgrounds (v1.19.0).

…ayer card (v1.19.0)

Three enhancements to the baseball plugin:

1. Traditional scoreboard: subtle team-color row tint (~12% brightness
   wash) plus a solid left-edge accent strip per team, reusing the
   already-fetched away/home ESPN colors. Gated by a new
   show_team_color_backgrounds toggle (default on; requires
   use_team_colors). Text stays legible via its black outline.

2. Pitcher/Batter screen: labels spelled out ("Pitcher:" / "Batter:")
   so "B" is no longer ambiguous with the grid's Balls indicator.

3. New Player Card screen (show_player_card, MLB & NCAA): rotates in a
   masters-style card for the current batter/pitcher with headshot,
   jersey number, position, bat/throw, and season stats (AVG/HR/RBI for
   hitters, ERA/W-L/K for pitchers). Bio + headshot fetched from ESPN's
   athlete API and cached (memory + disk); the headshot hides on tiny
   panels and the card degrades to text-only when unavailable. Skipped
   for MiLB (no ESPN player data).

All new code lives in existing files (no new bare-name modules, per the
cross-plugin collision rule). Adds config schema (grid toggle, per-league
show_player_card, player_card customization block), manager plumbing,
docs, and tests (test_player_card.py + grid-tint tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R1z28MrGx56x6eiJvibDg9
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a full-screen Player Card display with ESPN player bios, stats, and cached headshots. It also adds team-color row tinting, configuration and manager wiring, roster enrichment, regression tests, documentation, and version metadata updates.

Changes

Baseball Scoreboard Player Card and Team-Color Tint

Layer / File(s) Summary
ESPN athlete data and headshot caching
plugins/baseball-scoreboard/data_sources.py, plugins/baseball-scoreboard/logo_manager.py, plugins/baseball-scoreboard/.gitignore
Fetches and parses ESPN athlete details, downloads and caches allowlisted headshots, and ignores runtime headshot assets.
Player-card and scoreboard configuration
plugins/baseball-scoreboard/config_schema.json, plugins/baseball-scoreboard/manager.py
Adds player-card display and customization options, team-color background settings, and manager configuration wiring.
Roster enrichment and player-card rendering
plugins/baseball-scoreboard/baseball.py
Adds roster athlete mapping, pitcher/batter ID extraction, bio caching, player-card rotation and rendering, test fixtures, and expanded labels.
Traditional scoreboard team-color tint
plugins/baseball-scoreboard/baseball.py
Renders dimmed team-color row backgrounds and left accent strips under the configured gates.
Player-card and tint regression coverage
plugins/baseball-scoreboard/test_player_card.py, plugins/baseball-scoreboard/test_traditional_scoreboard.py
Tests parsing, caching safeguards, gating, stat formatting, rendering, team-color selection, and scoreboard tint behavior.
Documentation and release metadata
plugins/baseball-scoreboard/README.md, plugins/baseball-scoreboard/CHANGELOG.md, plugins/baseball-scoreboard/manifest.json, plugins.json
Documents the new options and records the 1.19.0 release dated 2026-07-09.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BaseballLive
  participant ESPNDataSource
  participant BaseballLogoManager
  participant Display
  BaseballLive->>BaseballLive: extract pitcher and batter IDs
  BaseballLive->>ESPNDataSource: fetch player bio
  ESPNDataSource-->>BaseballLive: return normalized bio
  BaseballLive->>BaseballLogoManager: load headshot
  BaseballLogoManager-->>BaseballLive: return headshot or None
  BaseballLive->>Display: draw player card
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main baseball-scoreboard changes: team-color grid styling, clearer at-bat labels, and the new player card release.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/baseball-scoreboard-enhancements-4im01k

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codacy-production

codacy-production Bot commented Jul 9, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 309 complexity

Metric Results
Complexity 309

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

claude added 2 commits July 9, 2026 23:21
Address a Codacy medium security finding on the new player-card headshot
loader:
- Sanitize the ESPN-provided player_id and league before using them as
  filesystem path segments (_safe_filename strips to [A-Za-z0-9_-]) so an
  unexpected id can't escape the headshot cache directory via '..' or path
  separators.
- Only fetch headshots over http(s), refusing file://, ftp://, etc.

Adds a unit test for the filename sanitization.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R1z28MrGx56x6eiJvibDg9
…nt pass

Resolve the Codacy/Bandit B110 (try/except/pass) medium finding in
_fetch_player_bio: the two cache_manager get/set guards swallowed
exceptions with a bare `pass`. Log them at debug level instead so
cache issues are diagnosable, matching how the rest of the plugin
handles non-fatal cache errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R1z28MrGx56x6eiJvibDg9

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugins/baseball-scoreboard/logo_manager.py`:
- Around line 241-260: The headshot download in the URL-fetching block lacks
host validation and a response-size limit. Before requests.get, parse the URL
and allow only expected ESPN CDN hostnames, rejecting all others; then enforce a
bounded response size using Content-Length and/or limited resp.iter_content
accumulation before Image.open. Preserve the existing timeout, caching, logging,
and _crop_square behavior in the surrounding headshot-loading logic.
- Around line 212-262: Move headshot HTTP fetching out of the synchronous render
path: update BaseballLive.update() to prefetch/cache player headshots using the
existing throttled background-thread and queue pattern from _fetch_player_bio,
with a bounded wait and failure handling. Change logo_manager.load_headshot() to
serve only memory/disk-cached images during rendering, and ensure
_draw_player_card_screen never invokes requests.get indirectly on a cache miss.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 02f95740-78d4-4f41-8a64-801aca6b0afd

📥 Commits

Reviewing files that changed from the base of the PR and between 4e5de3c and a488501.

📒 Files selected for processing (12)
  • plugins.json
  • plugins/baseball-scoreboard/.gitignore
  • plugins/baseball-scoreboard/CHANGELOG.md
  • plugins/baseball-scoreboard/README.md
  • plugins/baseball-scoreboard/baseball.py
  • plugins/baseball-scoreboard/config_schema.json
  • plugins/baseball-scoreboard/data_sources.py
  • plugins/baseball-scoreboard/logo_manager.py
  • plugins/baseball-scoreboard/manager.py
  • plugins/baseball-scoreboard/manifest.json
  • plugins/baseball-scoreboard/test_player_card.py
  • plugins/baseball-scoreboard/test_traditional_scoreboard.py

Comment thread plugins/baseball-scoreboard/logo_manager.py
Comment thread plugins/baseball-scoreboard/logo_manager.py Outdated
claude and others added 3 commits July 10, 2026 00:29
…t review)

Address two CodeRabbit review findings on the player-card headshot loader:

1. SSRF + unbounded response (security): the ESPN-supplied headshot URL was
   fetched with only a scheme check. Now restrict the host to ESPN domains
   (*.espncdn.com / *.espn.com) via an allowlist, and stream the download
   with a hard 5 MB size cap (Content-Length check + bounded iter_content)
   to prevent memory exhaustion.

2. Render-path blocking (stability): load_headshot() previously did a
   synchronous requests.get on a cache miss straight from
   _draw_player_card_screen, freezing the display up to 5s when a new
   batter/pitcher rotated in. The render path is now cache-only
   (allow_download=False); BaseballLive.update() prefetches headshot bytes
   off-thread (daemon thread, throttled) to warm the disk cache, mirroring
   the existing _fetch_player_bio pattern. A cold cache degrades to a
   text-only card until the prefetch completes.

Adds tests for the host allowlist (incl. metadata-IP and look-alike-host
rejection) and for the render path never downloading.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R1z28MrGx56x6eiJvibDg9
Resolve plugins.json conflict (last_updated timestamp only) by taking
main's later date and re-running update_registry.py to confirm the
whole file is consistent with all manifests post-merge.
Same recurring issue as PRs #175/#177/#178: the core's store_manager.py
validator flags ledmatrix_min as deprecated and expects
ledmatrix_min_version. This PR's new 1.19.0 changelog entry used the
deprecated name; corrected to match the non-deprecated field (older
entries below 1.6.2 already have this same pre-existing issue but are
untouched by this PR, so left as-is -- out of scope here).
@ChuckBuilds ChuckBuilds merged commit 5c84118 into main Jul 10, 2026
3 checks passed
@ChuckBuilds ChuckBuilds deleted the claude/baseball-scoreboard-enhancements-4im01k branch July 10, 2026 18:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
plugins/baseball-scoreboard/test_player_card.py (1)

205-229: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add a test for the 5 MB download size cap.

The PR enforces a 5 MB response limit in _download_headshot_image, but no test verifies that oversized responses are rejected. The existing tests cover URL allowlisting and render-path no-download behavior, but the size cap is a security control that should be validated. A mock-based test patching lm.requests.get to return a response exceeding 5 MB would close this gap without network calls.

♻️ Suggested test for the 5 MB download cap
+def test_download_headshot_rejects_oversized_response():
+    import logging
+    import logo_manager as lm
+
+    class FakeResponse:
+        def __init__(self, data):
+            self._data = data
+            self.headers = {"content-length": str(len(data))}
+        def iter_content(self, chunk_size=8192):
            yield self._data
+        def close(self):
+            pass
+
+    mgr = lm.BaseballLogoManager(None, logging.getLogger("t"))
+    orig_get = lm.requests.get
+    oversized = b"\x00" * (5 * 1024 * 1024 + 1)
+
+    def fake_get(*a, **k):
+        return FakeResponse(oversized)
+
+    lm.requests.get = fake_get
+    try:
+        result = mgr.load_headshot(
+            "oversized_id", "https://a.espncdn.com/x.png",
+            league="mlb", max_size=32, allow_download=True,
+        )
+        assert result is None, "Oversized headshot should be rejected"
+    finally:
+        lm.requests.get = orig_get
+    print("test_download_headshot_rejects_oversized_response: PASS")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/baseball-scoreboard/test_player_card.py` around lines 205 - 229, Add
a mock-based test alongside test_load_headshot_render_path_never_downloads that
patches lm.requests.get with an oversized response exceeding 5 MB, invokes
BaseballLogoManager.load_headshot with an allowed ESPN URL and
allow_download=True, and asserts the result is None. Restore the original
requests.get in a finally block and verify the mocked response is used without
real network access.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@plugins/baseball-scoreboard/test_player_card.py`:
- Around line 205-229: Add a mock-based test alongside
test_load_headshot_render_path_never_downloads that patches lm.requests.get with
an oversized response exceeding 5 MB, invokes BaseballLogoManager.load_headshot
with an allowed ESPN URL and allow_download=True, and asserts the result is
None. Restore the original requests.get in a finally block and verify the mocked
response is used without real network access.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6b1faf1c-6868-4dca-8b0d-db317386cce6

📥 Commits

Reviewing files that changed from the base of the PR and between a488501 and 2104810.

📒 Files selected for processing (5)
  • plugins.json
  • plugins/baseball-scoreboard/baseball.py
  • plugins/baseball-scoreboard/logo_manager.py
  • plugins/baseball-scoreboard/manifest.json
  • plugins/baseball-scoreboard/test_player_card.py
✅ Files skipped from review due to trivial changes (2)
  • plugins.json
  • plugins/baseball-scoreboard/manifest.json

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