* feat(sections): fix broken home section templates and add six new ones
Fixes templates that silently produced nothing:
- award_winners: hide from gallery (resolver is a stub until award data
exists); saved sections keep resolving
- seasonal_themed: christmas/st_patricks/thanksgiving get an interim
title-keyword resolver, and multi-theme selection skips themes without
an executable query so a data-less theme can no longer black out the
section during its own window (previously killed the section all of
December)
- taste_match: empty genre now auto-picks the profile's strongest taste
cluster (fallback: server top genre); the default preset was permanently
empty
- because_you_watched: honor the recipe's anchor_item_id key (fetcher only
read legacy source_item_id, so pinning an anchor did nothing)
- editorial_spotlight: reject subject_type=franchise (validated but could
never resolve); fix drawer misrepresenting pinned presets as auto-rotate
- admin_curated_list: add a catalog-search item picker so Editor's Picks
is actually addable; block saving an empty list; hide admin_only recipes
from profile-facing galleries
- discovery fetchers (hidden_gems, forgotten_favorites,
critically_acclaimed): honor single/multi library scope, intersected
with viewer access; implement hidden_gems max_play_count
New templates: returning_shows (new season of shows you've watched),
genre_roulette (rotating top-genre spotlight with title override),
anniversaries (milestone release anniversaries this month), short_watches
(well-rated movies under a runtime cap), family_movie_night seasonal
theme (Fri/Sat evenings), and a "New in 4K" format_showcase preset via a
new sort=recent param.
Adds a blanket test asserting every visible gallery preset's defaults
pass its own recipe validation — the gap that let taste_match and
Editor's Picks ship broken. New SQL shapes validated with EXPLAIN against
the dev database.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(sections): address PR #332 review findings
Codex review:
- returning_shows: the new-season file check now applies the effective
library scope (section scope ∩ viewer-allowed, minus disabled) to
media_files.media_folder_id, so an episode file that only exists in an
out-of-scope folder can no longer surface the series
- buildLibraryScope: replaced the media_item_libraries row join with
EXISTS / NOT EXISTS semi-joins. An item in several in-scope libraries
now yields exactly one row in the non-GROUP BY rails (short_watches,
anniversaries, seasonal keyword, format_showcase, new_to_library, ...),
and the disabled-library check is item-level, closing the join-row leak
where membership in an allowed library masked membership in a disabled
one. Deny-only mode keeps the positive-membership guard, mirroring
catalog's appendDiscoveryLibraryScope.
CodeRabbit review:
- recommendations reader: a taste cluster whose cached items are entirely
filtered out now falls through to the next cluster / global fallback
instead of returning an empty row
- genre_roulette: multi-library scopes get distinct rotation seeds
- returning_shows: reject negative lookback_days at validation
- shared oneOf() enum validator replaces per-recipe switch duplication
- SeasonalTitleOverride usable-filter contract covered by a direct test
- web NumberParamField: integer-only guard + step=1 (backend fields are
Go ints; fractional values failed unmarshalling at save)
- curated list picker: search failures show an error instead of a
misleading "No matches."; pre-existing item_ids hydrate display titles
via the watch-detail endpoint instead of rendering raw ids
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(audiobooks): redesign audiobook library around resume and series progression
Audiobook libraries previously reused the video-shaped library page: a
backdrop carousel hero (audiobooks have square covers and no backdrops),
movie-style default sections, and a browse grid whose primary audiobook
axes (author, narrator, series) were buried as filters.
Backend:
- New next_in_series section type: surfaces the next unstarted book, by
series_index, in series the profile has finished a book of, ordered by
most recent finish. Registered as a library-staple recipe.
- New GET /api/v1/catalog/audiobook-groups endpoint: grouped browse by
author/narrator/series with book count, total duration, per-profile
progress counts, and poster URLs for cover stacks.
- Audiobook library defaults: continue-listening is featured (renders as
the Now Listening hero) with next-in-series directly after it. A data
migration upgrades existing audiobook libraries, skipping layouts where
an admin already featured a section.
Frontend:
- NowListeningHero replaces HeroBanner for audiobook libraries: resume
deck with chapter position, hours left, ambient color from the cover,
and one-click resume; remaining in-progress books render as the
Continue Listening row.
- Library tab gains Books/Series/Authors/Narrators browse axes persisted
via the type param; selecting a group drops into the Books grid with
the matching filter applied.
- "Recommended" tab is labeled "Home" for audiobook libraries; audiobook
continue cards use square covers and hr/min time-left formatting.
- Shared audiobook chapter/file/duration helpers extracted to
web/src/lib/audiobooks (deduplicated from AudiobookContent).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(audiobooks): address review feedback on library redesign
- Push library scoping into the next-in-series candidate SQL so finished
series whose next book lives in another library can't consume the
candidate limit and starve a library-scoped section (Codex P2).
- Paginate the audiobook groups fetch until the server-reported total is
reached (500/page, 20-page bound) so client-side filtering sees the
complete author/narrator/series list (Codex P2, CodeRabbit).
- Make the redesign migration rollback-safe: rows the Up touches carry
config markers (featured_by_migration / seeded_by_migration) and the
Down reverts only marked rows, leaving admin-set featured state and
hand-created next_in_series sections alone (Codex P2, CodeRabbit).
- Gate NowListeningHero's detail-derived files/credits on the detail
matching the deck item, so Resume can't start the new book with the
previous book's files while keepPreviousData shows stale detail
(Codex P2).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
A library-agnostic home section that surfaces external global trending
(TMDB or Trakt, admin-selectable) mixing movies + series, matched to
titles in the viewer's enabled libraries. TMDB uses /trending/all/{window}
(natively mixed); Trakt merges trending movies + shows. Fetched live with
a 1h in-process cache, so no background job or stored collection — and no
per-library duplication.
Appears in the admin section gallery via its recipe presets (TMDB Trending
Today/This Week, Trakt Trending); featured -> hero via the existing flag.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>