* feat(jellycompat): expose library collections as a Collections library view
Surface server library collections as a top-level Jellyfin "Collections"
library (CollectionType "boxsets") so compat clients see them as the first
library in /UserViews and can browse them by ParentId. The BoxSet machinery
(list/detail/children) already existed; this adds the library wrapper.
- catalog: add LibraryCollectionRepository.AnyVisibleInLibraries, an
index-only EXISTS probe (no item join/aggregation) used to gate the view so
an empty Collections tab never shows. Mirrors collectionVisible semantics
(multi-library scope rows or the legacy single library_id fallback).
- jellycompat: add the synthetic Collections CollectionFolder (fixed Jellyfin
sentinel ID, stable across servers), prepend it to the user's views when a
visible collection exists, and route ParentId/Items-by-ID for that sentinel
to the existing BoxSet listing. ChildCount is left omitted (no per-call
count, no unwatched badge).
AI-use disclosure: implemented with assistance from Claude.
* fix(jellycompat): display collection posters and a generated Collections tile
Library collections surfaced as Jellyfin BoxSets showed blank cards: their
poster_url is frequently a bundled frontend template path
(/images/collection-templates/x.jpg), which the compat image route passed
through unchanged and then rejected in parseRemoteImageURL (no scheme/host),
returning BadGateway. Clients probed Images/Primary and got nothing.
- images: serve app-relative artwork (bundled template posters) straight from
the embedded frontend FS (new ImagesHandler.frontendFS), with content-type
and cache headers. Wired through jellycompat.Dependencies.FrontendFS.
- poster_gen: on-the-fly gradient poster generator (per-title hue, centered
white caption with black outline, gobold/opentype), memoized in a bounded
cache. Used for the synthetic Collections library tile and as a fallback for
collections without usable artwork, so cards are never blank.
- consolidate collection/view image routing in HandleItemImage, authorized by
the signed tag or an authenticated, visibility-checked session.
AI-use disclosure: implemented with assistance from Claude.
* fix(jellycompat): declare 2:3 PrimaryImageAspectRatio on BoxSets and Collections tile
Clients defaulted collection cards to a square and crop the 2:3 poster to fit.
Set PrimaryImageAspectRatio (portrait 2/3) on the BoxSet DTO and the synthetic
Collections library tile so the full poster is shown, matching Jellyfin.
AI-use disclosure: implemented with assistance from Claude.