Commit Graph
96 Commits
Author SHA1 Message Date
RXWatcher ace830d9e2 Merge remote-tracking branch 'origin/main' into feat/audiobooks
# Conflicts:
#	go.sum
2026-05-27 16:27:25 +02:00
RXWatcherandClaude Opus 4.7 f07ad643d5 style(audiobooks): apply prettier to ChaptersSection and NarratorPicker
These two files came in with this branch but were missed by prettier
formatting passes — long single-prop lines were wrapped where they
would have fit on one line under the project's 100-col width.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 16:24:08 +02:00
QuickandGitHub 1b8a38a7cc Merge pull request #13 from Silo-Server/t3code/2f7d918d
feat(episode-carousel): highlight currently viewed episode
2026-05-26 21:29:37 -04:00
QuickandGitHub 90b04db4a9 Merge pull request #12 from Silo-Server/t3code/5155230b
feat(ui): shared PageBack component for consistent back navigation
2026-05-26 21:29:27 -04:00
Quick 19d9b5c75d feat(episode-carousel): highlight currently viewed episode
- Add "Now Viewing" badge with pulsing indicator on current episode
- Replace border with primary-color ring for current episode card
- Set aria-current="page" on links to the current episode
2026-05-26 21:29:11 -04:00
Quick 602fc5b413 fix(ui): make PageBack destinations deterministic 2026-05-26 21:18:39 -04:00
Quick a8ef653c11 feat(ui): add floating variant to PageBack for sticky nav
- Add `floating` prop to pin PageBack to viewport on lg+ screens
- Switch styling from glass-subtle to glass with shadow for better contrast
- Use floating variant on SettingsLayout
2026-05-26 20:51:52 -04:00
QuickandClaude Opus 4.7 f7cae960c4 feat(ui): add shared PageBack component for consistent back navigation
Replace the eight inconsistent back affordances across user-facing pages
with a single absolute-positioned chevron pill, so the control lives in
the same screen position regardless of title length or hero content.

DetailBreadcrumb keeps its textual hierarchy path but no longer owns the
back chevron; PageBack does. DetailHero gains a topNav slot consumed by
Movie/Series/Season/Episode/Request detail pages. Non-hero pages drop
their bespoke back buttons and add PageBack inside a relative wrapper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 20:18:05 -04:00
Quick a7f62020fd fix(auth): gate media file paths on metadata curation permission
- Allow curators (not just admins) to view media file paths and locations
- Apply library access filter to file-level access checks
2026-05-26 20:04:54 -04:00
Quick 21e318dc16 fix(auth): address metadata curation review issues 2026-05-26 19:27:56 -04:00
Quick c6bccd358e feat(web): show metadata tools to curators 2026-05-26 18:24:39 -04:00
Quick afaac04edf fix(web): keep device profile hooks unconditional 2026-05-26 18:24:13 -04:00
Quick 96d90b6d06 feat(web): assign metadata curation permission 2026-05-26 18:21:16 -04:00
Quick 99c2bfd1ce feat(web): add permission helpers 2026-05-26 18:18:55 -04:00
Quick 6f783a4a6e feat(auth): expose user permissions 2026-05-26 18:13:32 -04:00
Quick 28196232c9 feat(subtitles): restore upload management 2026-05-26 17:57:26 -04:00
QuickandGitHub 4861aee170 Merge pull request #9 from Silo-Server/t3code/search-tmdb-fallback-results
feat(search): add TMDB-backed Request to Add section to search surfaces
2026-05-26 15:17:34 -04:00
Quick cd42610c95 fix(search): address request search review feedback 2026-05-26 15:11:10 -04:00
Quick 50ac193a36 refactor(admin): show per-section loading and error states
- Replace page-level loading gate with skeletons per section on dashboard and stats
- Surface query errors inline instead of blocking the whole page
- Disable "Scan All Libraries" when no libraries are configured
2026-05-26 14:33:25 -04:00
Silo Server Migration 0a66a3dd62 fix(webhooksync): skip events for unmapped external users
- Require explicit profile mapping instead of falling back to the default profile
- Update settings UI copy to reflect that unmapped users are ignored
2026-05-26 08:34:17 -04:00
Silo Server Migration 17a2cb0844 commit message
{"subject":"fix(search): prevent empty-state flash before TMDB fallback renders","body":"- Add isResolving to useCanRequest and gate empty states on it across GlobalSearch and Catalog\n- Debounce TMDB query in Catalog and hide ItemGrid when the request section may rescue an empty library\n- Track per-card submit state in RequestToAddSection grid so concurrent requests don't trample each other\n- Suppress anonymous TMDB request-search fetches to avoid cross-viewer cache leakage"}
2026-05-25 22:27:46 -04:00
Silo Server Migration e1bf9f5666 test(web): avoid unsupported Array.at in search request tests 2026-05-25 18:22:27 -04:00
Silo Server Migration 640df7254a chore(web): format request search section 2026-05-25 18:20:39 -04:00
Silo Server Migration a64f41da45 feat(catalog): render RequestToAddSection grid with empty-state suppression 2026-05-25 18:19:42 -04:00
Silo Server Migration b78b43e958 feat(search): render RequestToAddSection in the Cmd+K dialog with empty-state suppression 2026-05-25 18:17:31 -04:00
Silo Server Migration f7f56c9ded feat(search): add RequestToAddSection grid variant for Catalog page 2026-05-25 18:15:52 -04:00
Silo Server Migration b56bd5bff3 feat(search): add RequestToAddSection dialog variant 2026-05-25 18:15:10 -04:00
Silo Server Migration 09148cac46 feat(request-card): make onRequest optional on discover variant 2026-05-25 18:13:31 -04:00
Silo Server Migration c2ea1966f5 test(requests): document viewer-keyed cache isolation and invalidation cascade 2026-05-25 18:12:45 -04:00
Silo Server Migration a2a3495361 feat(requests): key useRequestSearch by viewer, forward signal, raise staleTime 2026-05-25 18:12:19 -04:00
Silo Server Migration 70bb7a3e2b refactor(keys): add viewerKey to requestKeys.search 2026-05-25 18:11:11 -04:00
Silo Server Migration d923051513 feat(hooks): add useCanRequest gating hook for discovery eligibility 2026-05-25 18:10:58 -04:00
Silo Server Migration 06e3424097 test(api): pin AbortSignal forwarding contract on api() 2026-05-25 18:10:06 -04:00
RXWatcher 71174db04e Merge remote-tracking branch 'origin/main' into feat/audiobooks
# Conflicts:
#	AGENTS.md
#	Makefile.local.example
#	internal/metadata/tmdb/client.go
#	web/src/components/AppSidebar.tsx
2026-05-25 19:50:59 +02:00
RXWatcherandClaude Opus 4.7 14a05bba9f feat(audiobooks): comprehensive UX, scanner, and collections work
Detail page:
- collapse Chapters section behind header toggle (73-chapter pages no
  longer push content below the fold)
- square cover frames throughout (audiobook covers are Audible-style 1:1,
  not 2:3 book portrait)
- narrator picker dropdown when multiple narrations of the same book exist
- clickable genre badges (route to /audiobooks?genre=X)
- reordered so credits/rails sit above the chapter list
- Play-from-Start button forces remount via playToken counter (was a
  no-op when player was already at position 0)
- regression test for the Play-from-Start fix

Mini bar / Now Listening:
- mini bar respects --app-sidebar-offset so it stops getting covered by
  the desktop sidebar
- Now Listening adds overflow scroll + a labeled "Back to player" button
  so controls are never inaccessible on short viewports

Library page:
- infinite scroll (replaces Previous/Next pagination)
- genre filter chip with X-to-clear

Scanner:
- 8-worker parallel reconcile (env SILO_AUDIOBOOK_SCAN_WORKERS to override)
- file-path-first dedup so cleaned titles don't collapse separate
  narrations
- title cleanup at write time (strips "Read by X" / "(unabridged)"
  suffixes; original tag preserved in original_title)
- audiobook_series upsert from tag-derived series_name/series_position
- secondary dedup check (author + narrator + year + duration ±0.5% +
  title-prefix) so two folders of the same book attach to one row

Backend detail handler:
- new fetchAlsoByAuthor, fetchInSeries (with series row > 1 entry guard),
  fetchSimilar (embedding-first with shared-genre fallback),
  fetchOtherNarrations (regex-strips narrator suffix to group siblings)
- audiobookDetailResponse gained also_by_author, in_series,
  similar_audiobooks, other_narrations fields
- list endpoint accepts a genre query param

Embeddings:
- BuildEmbeddingText branches on item.Type == "audiobook" to use
  author/narrator credits instead of cast/director/writer
- mediaTypeLabel helper centralizes movie / "TV series" / audiobook
- ListEmbeddingTextCandidates SQL mirrors the Go branching exactly
- ItemsNeedingEmbedding and TotalMediaItemCount loosen status='matched'
  gate to also include audiobooks (which don't go through TMDB match)
- FindSimilar gains a mediaType filter so cross-type results never appear
- callers in similar.go and personal.go pass the source item's type

Collections:
- MediaAudiobook MediaKind + audiobook(s) case in templateEligibleForLibrary
  (stops offering broken movie/TV templates to audiobook libraries)
- useAddItemToCollection hook (user + admin/library endpoints)
- AddToCollectionDialog wired into audiobook detail and movie/series
  ActionBar overflow menu
- ManualCollectionItemsEditor gained a search-and-add panel with
  debounced live results
- QueryDefinition.media_scope, QuerySortRelevanceScope, ALL_MEDIA_SCOPES
  extended to include "audiobook"
- CatalogFilterBar gained an Audiobooks media scope option
- parseCatalogMediaScope (backend) accepts "audiobook"

Migrations:
- 145_audiobook_series: per-book series_name/series_index with a
  best-effort title-pattern backfill for the existing corpus
- 146_audiobook_title_cleanup: strips narrator suffix / (unabridged)
  noise from existing titles, preserving raw in original_title

Scripts:
- scripts/dedup_audiobooks.py: one-shot merge for "Title" vs
  "Title: Subtitle" duplicates, file-path-stable, dry-run by default

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 19:47:54 +02:00
Silo Server Migration 98ea57ead8 chore: add planning docs and requests updates
- Add plans for date-named episodes and Jellyfin autoscan compat
- Update requests handlers, service, and UI hooks
- Remove Makefile.local.example
2026-05-25 12:07:50 -04:00
Silo Server Migration c157be571f feat(requests): tighten browse grid with more columns
- Increase poster density across breakpoints and use fluid cards
2026-05-25 10:55:33 -04:00
Silo Server Migration 1ea3e078d3 feat(requests): sync search and tab state with URL params
- Persist tab, query, media type, and page in the URL so requests views are shareable and survive reloads
- Rename the "mine" tab to "yours" with backward-compatible normalization
- Require at least 2 characters before submitting a search
2026-05-25 10:48:48 -04:00
Silo Server Migration ee8aefb311 fix(web): remove padding on request detail routes 2026-05-25 02:09:28 -04:00
Silo Server Migration cc09e14154 feat(tmdb): cache discover and external ID responses
- Add TTL caches with singleflight for DiscoverSection, DiscoverPage, and GetExternalIDs to reduce upstream TMDB calls
- Reuse CastCarousel with a fullBleed variant on RequestDetail and drop the bespoke RequestCastRow
2026-05-25 02:02:33 -04:00
Silo Server Migration 9cf5062bf0 chore: restore tmdb project api key fallback and move requests nav
- tmdb client falls back to bundled project API key when none configured
- move Requests link next to Recommendations in the sidebar
2026-05-25 01:12:47 -04:00
Silo Server MigrationandClaude Opus 4.7 64765126b8 feat(web): replace decline prompt with dialog and polish list query
window.prompt for the admin decline reason was inaccessible (screen
reader announcements are inconsistent), unstylable, and blocked in
hardened environments. Replace with a controlled Dialog and a small
textarea so the decline UX matches the rest of the admin pages.

buildListQuery used a truthiness check on params.offset/params.limit
that silently dropped explicit zero values, leaving the URL out of
sync with the query key. Compare against null and positive numbers
instead.

Poster <img> elements had alt="" with no surrounding aria-label, so
screen readers reaching the cards via image-mode browsing got no
title. Use the poster's own title as the alt text.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 00:27:21 -04:00
Silo Server Migration 98bebd4e5e fix(requests): harden integration submission and queue handling
- Batch integration upserts in a single transaction
- Treat radarr/sonarr lookup results as arrays and require exact matches
- Prefer queue failures over downloading state when evaluating arr queues
- Allow retrying queued/downloading requests and block declines once fulfillment started
- Fall back to pending when auto-approval integration check fails
- Rename requests query hooks file and fix discover card request affordance
2026-05-24 21:40:34 -04:00
Silo Server Migration f023c4a6f9 feat(requests): support combined movie and series search
- Add `media_type=all` to request search, backed by TMDB `/search/multi` filtered to movies and series
- Default the Requests page filter to All and refresh search results grid styling
- Refine RequestPosterCard with status accent bar, richer fallback poster, and fluid grid layout
2026-05-24 21:30:02 -04:00
Silo Server MigrationandCursor 509a6c84ba feat(requests): add discover studios, networks, and genres
Wire curated TMDB-backed studios/networks/genres discovery into the requests service and UI, replacing on-demand logo fetches with fixed duotone logos and adding browse routes plus tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 19:53:21 -04:00
RXWatcherandClaude Opus 4.7 32ba81d395 fix(audiobooks): satisfy noUncheckedIndexedAccess in audiobook code
tsc -b runs with noUncheckedIndexedAccess on, so indexed reads must
guard for undefined even when the loop bounds make it provably safe.
Bind the indexed value to a local in findChapterAt and ChaptersSection,
and use non-null assertions in tests where the test data shape makes
the indexed element guaranteed. No runtime change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 01:51:39 +02:00
RXWatcherandClaude Opus 4.7 67fb38f762 style(audiobooks): collapse ChaptersSection signature per prettier
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 01:49:45 +02:00
RXWatcherandClaude Opus 4.7 958db9911c feat(audiobooks): add embedding-based Similar audiobooks rail
Third feature-detected rail on the detail page. When the backend
returns similar_audiobooks (server-side embedding similarity, ranked
top-N), the rail mounts above Also-by-author and In-series, with the
subtitle 'Based on listening patterns' to give the recommendation
source a little provenance. Frontend has no embedding logic — purely a
display path that lights up the moment the API exposes the field.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 01:43:55 +02:00
RXWatcherandClaude Opus 4.7 eeb22e47d3 feat(audiobooks): render Also-by-author / In-series rails when API exposes them
Frontend gains a feature-detected display path for two relationship
rails. Detail responses may now carry also_by_author and in_series.
When present, the detail page mounts Also-by-author followed by
In-series, with the current book highlighted inside the series strip.
When the backend omits them — which is the case today — the rails are
not rendered, so this lands as zero visible change until the backend
ships the data.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 01:42:53 +02:00
RXWatcherandClaude Opus 4.7 07da12f8f4 feat(audiobooks): add narrator card to detail page
Surfaces the narrator as a first-class card with a circular initials
avatar. Rendered only when data.narrator is present, so audiobooks
without narrator metadata don't introduce empty space.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 01:41:11 +02:00