feat(hub): downsample external cover art and dedupe cache variants (#145) - #260
Merged
Conversation
…loses #145) getCoverArt now normalizes every request to an effective size (default and max 1024px, min 16): the cache key always carries it, local/peer branches forward it to Navidrome, and the external fanart.tv/Last.fm branch — which previously cached full-resolution bytes once per requested size — downsamples hub-side via sharp before caching and transmitting. GIF/SVG pass through unresized; unparseable image bytes fall back to the original buffer. Default art-cache cap raised 100 MB -> 1 GB (still overridable via ART_CACHE_MAX_BYTES / admin API). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SwwPcrohddTUN7kmQVedQC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #145.
Problem
The art cache filled its 100 MB cap almost immediately. Two causes (see investigation on the issue):
getCoverArt(fanart.tv / Last.fm) ignored thesizeparam but keyed the cache by it — identical full-resolution ~1000×1000 bytes (100–500 KB) were cached once per requested size. The SPA requests five variants (48/80/300/400/unsized), so one cover could occupy ~1.5 MB across five slots.Changes
hub/src/routes/subsonic/art-size.ts): every request resolves to a clamped size in [16, 1024]; absent/invalid defaults to 1024. Cache key is always${id}:${effSize}, so unsized andsize=1024dedupe; old-format entries LRU-evict naturally (no migration).hub/src/services/art-resize.ts, newsharpdep): the external branch resizes fetched bytes to fit the effective size before caching and transmitting — a 48px PlayerBar thumbnail no longer downloads 300 KB. Local/peer art is untouched (already resized upstream by Navidrome, which now always receives the effective size). GIF/SVG pass through unresized; sharp failure on corrupt bytes falls back to the original buffer.ART_CACHE_MAX_BYTESenv and the admin cache API.hub-internals.md(env table, art-cache section),opensubsonic.md(getCoverArt size cap caveat).Behavior change note
sizevalues above 1024 are now clamped and unsized requests return at most 1024px (this caps the #200 lightbox at 1024 — deemed sufficient for a music player UI).Testing
pnpm verifygreen: hub 799 tests / frontend 145, typecheck clean, boundary lint clean.pnpm lintexits zero with no warnings.pnpm test:federationgreen (full py-sonic compat + eviction/re-admission cluster suite).effectiveArtSizeunit cases; external resize asserted viasharp().metadata()on the response body; dedupe viax-cache: HIT+ single upstream fetch; 4096→1024 clamp; corrupt-bytes fallback; GIF passthrough.🤖 Generated with Claude Code
https://claude.ai/code/session_01SwwPcrohddTUN7kmQVedQC