fix(logos): one hardened logo download path; shared, real HTTP headers (#612)

* fix(logos): harden the plugin logo download and share core HTTP headers

download_missing_logo / LogoDownloader.download_logo, the path the
scoreboard plugins use, read response.content with no size cap and wrote
straight to the final path, so a failed or corrupt download could be left
in place and cached as the logo. It now goes through fetch_logo: streamed
with a 10 MB cap, image/* only, decoded by Pillow, converted to RGBA once,
and moved into place atomically. A failure leaves no partial or temp file
and keeps any logo already on disk. LogoHelper._download_logo delegates to
the same code. Public signatures and return values are unchanged; saved
files are pixel-identical to before (RGBA, palette+tRNS, L+tRNS, LA, JPEG).

download_missing_logo reuses one downloader per thread instead of a new
Session per logo. Per thread rather than behind a lock: Session is not
documented thread-safe, and a lock would serialise every plugin's
downloads behind the slowest one.

Placeholders are written atomically, without the test_write.tmp probe.

The logo downloader and background data service now send the real
ChuckBuilds User-Agent from src.common.api_helper (USER_AGENT,
DEFAULT_HTTP_HEADERS) instead of a yourusername/contact@example.com
placeholder, and no longer hand-set Accept-Encoding: br (brotli is not
installed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(http): drop APIHelper's hand-set brotli encoding; LogoHelper sends the real UA

APIHelper advertised `br` though brotli isn't installed, so a server that
honoured it would send a body requests can't decode. LogoHelper sent a bare
`LEDMatrix-Common/1.0`, the kind of User-Agent ESPN has been rejecting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-23 12:58:11 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent f813ea2117
commit 3f8edf5113
8 changed files with 622 additions and 153 deletions
+29
View File
@@ -60,6 +60,35 @@ core, the monorepo or the registry's third-party plugins calls them:
instead of handing `config.json` to root, and an install path with
"secrets" in a directory name no longer makes `config.json` mode 0640.
New names in existing modules (no new modules; a plugin importing these must
floor on the release that ships them):
- `src.common.api_helper`: `USER_AGENT`, `DEFAULT_HTTP_HEADERS` (read-only).
- `src.logo_downloader`: `fetch_logo`, `save_png_atomically`,
`shared_downloader`.
### Logo downloads
- `download_missing_logo` / `LogoDownloader.download_logo` (the path the
scoreboard plugins use) now stream the logo with a 10 MB cap, accept only an
`image/*` response that Pillow can decode, and move the finished RGBA PNG
into place atomically. A failed, oversized or non-image download no longer
leaves a partial file behind, and no longer replaces a logo already on disk.
`LogoHelper._download_logo` goes through the same code. Signatures and return
values are unchanged; saved files are pixel-identical to before.
- `download_missing_logo` reuses one downloader (one `requests.Session`) per
thread instead of building a new one for every logo.
- Placeholder logos are written atomically, without the `test_write.tmp`
probe file.
### HTTP headers
- The logo downloader and the background data service send the real
`LEDMatrix/1.0 (+https://github.com/ChuckBuilds/LEDMatrix)` User-Agent
instead of a `yourusername` / `contact@example.com` placeholder, and no
longer set `Accept-Encoding: ... br` by hand (brotli is not installed, so a
`br` response could not be decoded); requests picks the encodings.
## 3.5.0
New modules a plugin may import via `src.*` (floor on 3.5.0):