Repository navigation
feat(catalog): list an item's collections on its detail page - #239
Conversation
Add the additive `collections` member to the v2 item detail: the visible server collections a movie or series belongs to, each with id, title, poster_url, and item_count. A dedicated `getItemCollectionsCapability` document reports whether the row is wired. The lookup reuses `LibraryCollectionRepository.ListContainingItem` through a narrow `ItemCollectionIndex` seam on the existing collection handler, so the item detail never widens `LibraryCollectionService` for every fake. It filters by `CanAccessLibraryCollection` and resolves posters through the same viewer-aware path as the library Collections tab, so a hidden or out-of-scope collection never leaks. An episode or season answers its parent series' collections, because only movies and series can be members. The row is best-effort, mirroring the theme lookup: an unwired index or a lookup failure leaves the detail response a success with an empty row. Contract artifacts are regenerated, not hand-edited. New fixture cases append at the end of the positional list so committed request ids stay stable.
The v2 item detail now carries a `collections` array — the visible collections a movie or series belongs to, each with id, title, poster_url and item_count. Render it as a Collections row on the movie and series detail pages: one poster chip per collection (poster, title, item count) linking to that collection's browse view, using the shared MediaCarousel so the row scrolls and sizes like the neighboring poster rows. The membership list arrives on the detail payload itself, so the row has no fetch of its own: the page's existing skeleton covers loading and its error state covers a failed read, and a title in no collections renders no row at all. The chip links through buildLibraryCollectionCatalogHref, the same href the sidebar and Collections tab use for a library collection. Regenerate the committed v2 web bindings so the new detail member and the item-collections capability are typed, and pin the mapper with a test: catalogItemDetailFromV2 builds ItemDetail field by field, so a member it forgets to copy would drop silently and the row would never render.
The #230 item-collections capability route was added to the v2 router without regenerating internal/api/testdata/media_routes.txt, so TestMediaRouteManifest failed in CI. Regenerate it: the only change is the new GET /api/v2/capabilities/item-collections entry, once for each fixture router.
Review — COMMENT (no auth bypass found, 1 medium to settle)Main GET → mapper → movie/series UI path looks sound. Verified at 1. Medium — metadata-update response reports no memberships
2. Low — document the narrower semanticsStored server-collection memberships only (no smart/live-query, no personal collections), 3. Low — test wording overstates coverageMember-of-2 / none / inaccessible-excluded cases exist, but on stub indexes not SQL; the HTTP fake discards the access filter; the Before merge: settle #1, clarify #2 in docs, correct the validation wording, add the |
The metadata editor's PATCH response built its detail through the shared mapper, whose `collections` member defaults to empty, so editing an item that belongs to a collection returned `"collections":[]` until the client refetched the read detail. Populate the row on that path with the access scope the permission gate resolved, the same lookup the read detail uses. Correct the documented and commented semantics: the field reports stored server-collection memberships only (a smart collection derives its members from its query and stores no rows; personal collections are a separate surface), and `item_count` is the collection's total, not the viewer-visible count. Document `poster_thumbhash` and the metadata-save row. Strengthen the tests where the prior coverage was thin: assert the resolved access filter reaches the reverse lookup, cover season and series mapping, and cover the lookup-failure fallback. Add a Postgres-backed handler test for the real SQL that skips without SILO_TEST_DATABASE_URL.
|
Review addressed and pushed ( |
Re-check at
|
|
Re-check items closed: full CI green on this head (all 8 checks), and the PR body refreshed (poster_thumbhash + PATCH-save row documented, new save-response/DB tests listed, Related issue + Validation tasks lines present, native-client deferral recorded). Ready for merge decision. |
Final: APPROVE — ready to mergeHead |
|
Synced with current main (includes #242 CI and prior merges): one digest-fixture conflict resolved by regeneration; CI workflow files identical to main. No code changes in this sync. Awaiting review — no merge until approved. |
Problem
Related issue: #230
Validation tasks: none found covering collection membership on detail pages.
Movie/series detail pages show no collection membership — collections are only reachable from library browse, disconnected from their items.
Approach
Server: reverse lookup on the existing item↔collection mapping, exposed as an additive
collectionsarray (id, title, poster_url, poster_thumbhash, item_count) on the v2 item detail response, with existing collection authorization mirrored. Metadata-save responses populate the same row. Web: Collections row of poster chips on the title page (renders only when non-empty), linking to each collection's browse view, following neighboring row conventions.Stored server-collection memberships only (no smart/live-query, no personal collections);
item_countis the global total; lookup failure yields[].Validation
Risks
Additive API surface only; web row hidden when empty. Android/Apple: field ships regardless; native clients defer UI (no native work in this PR).
Checklist
AI Disclosure