Commit Graph
549 Commits
Author SHA1 Message Date
zZebrahz 20da980cc8 fix(catalog): include user sort metrics 2026-05-30 02:55:05 +00:00
zZebrahz a2d8118978 fix(web): preserve date-only item card days 2026-05-30 02:51:17 +00:00
zZebrahz 0cdd6c543f fix(web): avoid library saved-state layout shift 2026-05-30 02:48:29 +00:00
QuickandClaude Opus 4.8 9123cfb1f4 fix(api): accept forwarded host in websocket origin check
The shared WebSocket upgrader rejected handshakes unless the browser's
Origin host exactly matched r.Host. Behind a TLS-terminating CDN/proxy
that rewrites Host to the internal origin (carrying the public host in
X-Forwarded-Host), this comparison always failed and every realtime
socket 403'd at the handshake — playback control, events, watch-together
rooms, and admin log streaming all share the upgrader.

checkWebSocketOrigin now also accepts an Origin matching X-Forwarded-Host,
keeping the same-origin CSRF guard intact while supporting proxied
deployments. Extract a shared forwardedHost helper (first hop of a
multi-proxy list) and reuse it from requestBaseURL, replacing the
duplicated inline parse. Also reject opaque (empty-host) origins
explicitly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 22:40:55 -04:00
zZebrahz 1bfbdd2826 fix(people): refresh stale person metadata on view 2026-05-30 02:19:11 +00:00
zZebrahz 449fcf394c fix(web): preserve active library query from sidebar 2026-05-30 02:07:37 +00:00
zZebrahz 9c56f40170 feat(web): remember library page state 2026-05-30 02:07:37 +00:00
zZebrahz 96256ecbf8 fix(catalog): avoid skeletons after short result pages 2026-05-30 02:07:37 +00:00
zZebrahz bdc46b8c6c feat(catalog): show sort metadata on item cards 2026-05-30 02:07:37 +00:00
QuickandGitHub cdb5b70a8d Merge pull request #25 from Silo-Server/feat/calendar-presets
feat(calendar): personalized presets (Following default) with watched overlay
2026-05-29 14:57:57 -04:00
QuickandGitHub 66cfb0750d Merge pull request #25 from Silo-Server/feat/calendar-presets
feat(calendar): personalized presets (Following default) with watched overlay
2026-05-29 14:57:57 -04:00
QuickandClaude Opus 4.8 4fea42488a refactor(calendar): simplify preset handler and reuse storage util
- Extract hardcoded trending snapshot source/window to named constants.
- Collapse the three identical personal-preset nil-checks into one case.
- Persist the selected preset through the shared storage util (try/catch
  wrapped) instead of raw localStorage with manual SSR guards.
- Derive KNOWN_FILTERS from PRESET_OPTIONS so the lists can't drift.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 14:56:16 -04:00
QuickandClaude Opus 4.8 de8b8189d7 refactor(calendar): simplify preset handler and reuse storage util
- Extract hardcoded trending snapshot source/window to named constants.
- Collapse the three identical personal-preset nil-checks into one case.
- Persist the selected preset through the shared storage util (try/catch
  wrapped) instead of raw localStorage with manual SSR guards.
- Derive KNOWN_FILTERS from PRESET_OPTIONS so the lists can't drift.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 14:56:16 -04:00
QuickandClaude Opus 4.8 dc194c4e54 feat(calendar): hide server-wide Popular preset in web UI for now
Popular reflects server-wide watch counts, which are sparse on a
low-traffic server. Hidden from the selector, URL allowlist, and
empty-state nudge; backend filter and the CalendarFilter type are
left intact so re-enabling is a one-line change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 14:24:18 -04:00
QuickandClaude Opus 4.8 4c3911b5e1 feat(calendar): hide server-wide Popular preset in web UI for now
Popular reflects server-wide watch counts, which are sparse on a
low-traffic server. Hidden from the selector, URL allowlist, and
empty-state nudge; backend filter and the CalendarFilter type are
left intact so re-enabling is a one-line change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 14:24:18 -04:00
QuickandClaude Opus 4.8 93b835e1fd feat(calendar): dim and check-mark already-watched event cards
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 14:13:51 -04:00
QuickandClaude Opus 4.8 c2d8dbd746 feat(calendar): dim and check-mark already-watched event cards
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 14:13:51 -04:00
QuickandClaude Opus 4.8 6cebbb3cd9 feat(calendar): preset selector with responsive pills, persistence, empty-state nudge
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 14:13:51 -04:00
QuickandClaude Opus 4.8 c32b5703ac feat(calendar): preset selector with responsive pills, persistence, empty-state nudge
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 14:13:51 -04:00
QuickandClaude Opus 4.8 f859cbfd7a feat(calendar): add watched field to CalendarEvent type
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 14:13:51 -04:00
QuickandClaude Opus 4.8 431341c235 feat(calendar): add watched field to CalendarEvent type
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 14:13:51 -04:00
QuickandClaude Opus 4.8 e9847a4424 feat(calendar): wire popular and trending sources into calendar handler
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:56:14 -04:00
QuickandClaude Opus 4.8 4dc0522cf2 feat(calendar): wire popular and trending sources into calendar handler
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:56:14 -04:00
QuickandClaude Opus 4.8 6560dfc470 feat(calendar): resolve presets to id-sets and overlay watched status
Also drops the now-unused Filter/UserID/ProfileID fields from the
blendUpcomingIntoDiscoverRows CalendarFilter literal in recommendations.go,
which only wants an unrestricted windowed query.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:55:39 -04:00
QuickandClaude Opus 4.8 2bd0edb264 feat(calendar): resolve presets to id-sets and overlay watched status
Also drops the now-unused Filter/UserID/ProfileID fields from the
blendUpcomingIntoDiscoverRows CalendarFilter literal in recommendations.go,
which only wants an unrestricted windowed query.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:55:39 -04:00
QuickandClaude Opus 4.8 134ebf399e feat(calendar): add per-profile followed/favorites/watchlist/watched resolvers
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:50:48 -04:00
QuickandClaude Opus 4.8 1ba76af353 feat(calendar): add per-profile followed/favorites/watchlist/watched resolvers
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:50:48 -04:00
QuickandClaude Opus 4.8 a119b60ca6 feat(calendar): generalize personal filter to an id-set restriction
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:49:34 -04:00
QuickandClaude Opus 4.8 62b3861da0 feat(calendar): generalize personal filter to an id-set restriction
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:49:34 -04:00
QuickandClaude Opus 4.8 1f729c4764 docs(calendar): add presets design spec and implementation plan
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:47:45 -04:00
QuickandClaude Opus 4.8 f113b32de0 docs(calendar): add presets design spec and implementation plan
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 13:47:45 -04:00
QuickandClaude Opus 4.8 cade7f37aa fix(calendar): order events by viewer-local wall-clock time
The local-airtime change re-sorted calendar events in Go using air_at,
the absolute UTC instant, which is nil whenever air_timezone is unset.
Since air_timezone is only inferred for a few networks/countries, most
events fell through to the alphabetical title tiebreak while still
displaying their raw air_time, so each day appeared scrambled.

Sort each local day by the wall-clock time the viewer actually sees,
mirroring the client: zoned events convert air_at into the viewer
timezone, unzoned events use the raw air_time, and date-only entries
(no air_time) sort last. The timezone reasoning lives in the new
catalog.CalendarEventLocalTime helper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 12:16:18 -04:00
QuickandClaude Opus 4.8 58fc4abca8 fix(calendar): order events by viewer-local wall-clock time
The local-airtime change re-sorted calendar events in Go using air_at,
the absolute UTC instant, which is nil whenever air_timezone is unset.
Since air_timezone is only inferred for a few networks/countries, most
events fell through to the alphabetical title tiebreak while still
displaying their raw air_time, so each day appeared scrambled.

Sort each local day by the wall-clock time the viewer actually sees,
mirroring the client: zoned events convert air_at into the viewer
timezone, unzoned events use the raw air_time, and date-only entries
(no air_time) sort last. The timezone reasoning lives in the new
catalog.CalendarEventLocalTime helper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 12:16:18 -04:00
QuickandGitHub 57bcae38e9 Merge pull request #24 from Silo-Server/claude/amazing-thompson-b2843d
fix(catalog): project air_timezone in episode catalog subquery
2026-05-29 11:55:56 -04:00
QuickandGitHub 3fe38c8c63 Merge pull request #24 from Silo-Server/claude/amazing-thompson-b2843d
fix(catalog): project air_timezone in episode catalog subquery
2026-05-29 11:55:56 -04:00
QuickandClaude Opus 4.8 aa1a0bd38b fix(catalog): project air_timezone in episode catalog subquery
episodeCatalogSelectBody is the derived "mi" relation that episode catalog
hydration and preview read qualifiedListItemColumns("mi") from. The local
episode airtimes feature (808f265f) added air_timezone to the shared column
lists but not to this hand-written subquery, so the outer projection
referenced mi.air_timezone, which the subquery never exposed.

Postgres returns SQLSTATE 42703 (undefined_column), which is not one of the
codes episodeCatalogEntriesUnavailable treats as "fast path unavailable" (it
only catches 42P01/42883), so episode catalog requests failed with HTTP 500
instead of degrading. movie and series scopes query media_items directly, so
the column is present there and only episode scope broke.

Add si.air_timezone to the subquery, and add a regression test asserting that
episodeCatalogSelectBody exposes every column qualifiedListItemColumns reads
off mi, so future additions to the shared column lists cannot silently drift
from the episode read model again.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 11:53:32 -04:00
QuickandClaude Opus 4.8 32302e7b17 fix(catalog): project air_timezone in episode catalog subquery
episodeCatalogSelectBody is the derived "mi" relation that episode catalog
hydration and preview read qualifiedListItemColumns("mi") from. The local
episode airtimes feature (26370616) added air_timezone to the shared column
lists but not to this hand-written subquery, so the outer projection
referenced mi.air_timezone, which the subquery never exposed.

Postgres returns SQLSTATE 42703 (undefined_column), which is not one of the
codes episodeCatalogEntriesUnavailable treats as "fast path unavailable" (it
only catches 42P01/42883), so episode catalog requests failed with HTTP 500
instead of degrading. movie and series scopes query media_items directly, so
the column is present there and only episode scope broke.

Add si.air_timezone to the subquery, and add a regression test asserting that
episodeCatalogSelectBody exposes every column qualifiedListItemColumns reads
off mi, so future additions to the shared column lists cannot silently drift
from the episode read model again.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 11:53:32 -04:00
QuickandGitHub 3c338e855a Merge pull request #23 from Silo-Server/feat/trending-section
feat(sections): persist trending_discover via background-refreshed snapshot
2026-05-29 11:35:05 -04:00
QuickandGitHub 98d2596fbf Merge pull request #23 from Silo-Server/feat/trending-section
feat(sections): persist trending_discover via background-refreshed snapshot
2026-05-29 11:35:05 -04:00
QuickandClaude Opus 4.8 f105361658 fix(sections): harden trending refresher per PR review
- Interleave Trakt movies/shows by rank so the mixed row shows both types
  instead of burying all series past the display limit.
- Treat any Trakt sub-fetch failure as fatal (errors.Join) so a partial
  result never overwrites the last-good snapshot with a media type missing.
- Skip non-title entries (TMDB trending/all returns media_type "person") in
  both ID batching and ordering so they can't match an unrelated library title.
- Guard the refresh task against a nil refresher.
- Tests: person skip, Trakt interleave, Trakt partial-failure preserves
  last-good, snapshot read error propagation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 11:28:59 -04:00
QuickandClaude Opus 4.8 943b5dd9f0 fix(sections): harden trending refresher per PR review
- Interleave Trakt movies/shows by rank so the mixed row shows both types
  instead of burying all series past the display limit.
- Treat any Trakt sub-fetch failure as fatal (errors.Join) so a partial
  result never overwrites the last-good snapshot with a media type missing.
- Skip non-title entries (TMDB trending/all returns media_type "person") in
  both ID batching and ordering so they can't match an unrelated library title.
- Guard the refresh task against a nil refresher.
- Tests: person skip, Trakt interleave, Trakt partial-failure preserves
  last-good, snapshot read error propagation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 11:28:59 -04:00
QuickandClaude Opus 4.8 c98f828c25 fix(migrations): renumber trending_discover_snapshots 166 -> 167
The shared dev DB already recorded version 166 (166_trending_blend_collection_type
from another branch), so the integer-version migration runner silently skipped our
166 and the table was never created — the trending section errored out empty.
167 is the next free version.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 10:57:57 -04:00
QuickandClaude Opus 4.8 ffef55283a fix(migrations): renumber trending_discover_snapshots 166 -> 167
The shared dev DB already recorded version 166 (166_trending_blend_collection_type
from another branch), so the integer-version migration runner silently skipped our
166 and the table was never created — the trending section errored out empty.
167 is the next free version.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 10:57:57 -04:00
Quick 4455c2cd40 chore(sections): satisfy lint (wrap trakt errors, lift source/window constants) 2026-05-29 10:51:54 -04:00
Quick 81d1644cd7 chore(sections): satisfy lint (wrap trakt errors, lift source/window constants) 2026-05-29 10:51:54 -04:00
Quick 9d03ad0b37 feat: wire trending refresh task and snapshot reader 2026-05-29 10:49:01 -04:00
Quick 59874c717d feat: wire trending refresh task and snapshot reader 2026-05-29 10:49:01 -04:00
Quick 38723c8b81 refactor(sections): read trending_discover from persisted snapshot 2026-05-29 10:46:01 -04:00
Quick d27f21b10f refactor(sections): read trending_discover from persisted snapshot 2026-05-29 10:46:01 -04:00
Quick 3c3ef82ae8 feat(tasks): add refresh_trending_discover task 2026-05-29 10:44:15 -04:00