Commit graph

55 commits

Author SHA1 Message Date
npeter83
6dece18ae8 fix(plex): image proxy no longer exhausts the DB connection pool
A collections page fires 100+ concurrent poster requests. Both image proxies
(/image and /person-image) authenticated via current_user + Depends(get_db),
holding a pooled DB connection for the whole request — including the slow Plex/
CDN fetch. Under the burst, the 15-connection pool was exhausted → QueuePool
checkout timeouts → 502s → slow, partially-loaded grids on prod.

Fix: authenticate these two high-fan-out endpoints with a signed-session check
(no DB user-load), serve disk-cache hits with zero DB access, and on a cold miss
open a short session only to resolve the image key + Plex config, releasing it
BEFORE the fetch (image_bytes uses no DB). Also raise the pool (20 + 30 overflow)
as headroom above the sync-endpoint threadpool.
2026-07-06 07:14:28 +02:00
npeter83
f037a4563c feat(plex): info-page polish — per-source strip toggles, glassy look, scroll restore
From UAT feedback on the collections info page:
- Per-source show/hide toggles in the customize menu, one per collection-strip
  type present on the item (Collections/IMDb/TMDb/…), persisted per account.
- Customize menu closes on Escape / outside click (reuses useDismiss).
- Preserve the info-page scroll position when returning from "Browse collection".
- Glassy refresh: content floats as .glass/.glass-card panels over a faint FIXED
  art backdrop on the <main> scroller (HTPC-style, toggleable); frosted menu;
  wider ~90% layout. Uses the existing glass surface system.
2026-07-06 06:50:03 +02:00
npeter83
418a874851 feat(plex): collections — sync, browse, filter, and info-page strips (Phase 1, read)
Backend (migration 0047_plex_collections): a plex_collections table mirrors every Plex
collection (card metadata + smart flag + an editable flag reserved for Phase 2); membership
is stored as GIN-indexed collection_keys on member movies (plex_items) and shows (plex_shows).
The background sync (plex_sync) now fetches each library's collections + their children and
rebuilds membership — so ALL reads are local (Plex's dual-language collection queries are slow;
this trades a ~4-min background sync for instant reads). New /api/plex/collections endpoint;
/browse gains a combinable  filter; item_detail returns the movie's collection
'strips' (sibling titles as playable cards, smallest/most-specific collection first) — all pure
local lookups.

Frontend: PlexSidebar gains a searchable Collection picker + an active-collection chip;
PlexInfo renders the collection strips (playable posters + 'Browse collection' → sets the
filter); the collection is part of PlexFilters (persisted). i18n en/hu/de.

Phase 2 (create/edit collections with write-back to Plex) is separate.
2026-07-06 02:25:48 +02:00
npeter83
280c62dda8 feat(plex): search cast & crew names + clickable person cards
Backend (migration 0046_plex_people_search): a new people_text column (cast +
director names, de-duped) is folded into the generated search_vector at weight B,
so the top search box finds titles by an actor/director name and ranks them above
summary-only mentions. New /api/plex/people endpoint returns the cast/crew matching
the term (name prefix or word-start) with a film count + a headshot (pulled from one
representative film's live metadata; image bytes disk-cached).

Frontend: PlexBrowse shows matching people as virtual cards above the grid; clicking
one adds them to the actor/director filter (multi-value, from the C1 work) and clears
the search box so you land on exactly that person's films. Answers the 'why does
"drew" match Rambo?' confusion — it was matching the word in the synopsis; now names
are searchable. i18n en/hu/de.

⚠️ Prod needs a Plex re-sync after deploy to populate people_text (search_vector
regenerates automatically once the column has data).
2026-07-06 00:56:01 +02:00
npeter83
1982cfa7b9 feat(plex): sort direction, genre any/all, multi-person filters; fix back-nav + player stop
UAT follow-ups on the Plex filter epic:
- Sort now has an asc/desc toggle (sort_dir), applied to any sort field.
- Genre multi-select gains an Any/All mode (genre_mode: OR vs AND containment).
- Director/actor/studio become multi-value: people AND (titles featuring all selected),
  studios OR; clicking them on the info page stacks (unions) instead of replacing, and
  the sidebar shows each as a removable 'Active' chip.
- fix(history): clicking a metadata filter on the info page now pushes a fresh grid
  entry instead of history.back(), so browser Back returns to the info page rather than
  leaving the Plex module.
- fix(player): fully tear down the <video> + hls on unmount and guard late play() calls,
  so backing out of a just-started video no longer leaves audio playing in the background.
i18n en/hu/de (match any/all, sort direction).
2026-07-06 00:15:31 +02:00
npeter83
eefd7e3abd feat(plex): expanded metadata filters, clickable info page, image disk cache
Backend (migration 0045_plex_filter_meta): plex_items gains rating (audienceRating
~IMDb), content_rating, studio, originally_available_at, and GIN-indexed genres /
directors / cast_names — all mirrored from the cheap section listing (no per-item API
calls; they also seed a future watch-habit recommender). /browse gains genre / content-
rating / year / rating / duration / added-within / director / actor / studio filters
(@> containment, GIN) + sort by year|rating|duration|release; new /facets endpoint
returns available genres+ratings (with counts) and the year/rating/duration bounds. A
thin on-disk image cache (.plex-img-cache) serves posters/art/cast photos from local
disk after first fetch (~7-14x faster repeat loads).

Frontend: PlexSidebar grows the full filter set (facet-driven genre/age chips, rating
steps, year range inputs, duration buckets, added-within, active people/studio chips,
clear-all); filters persist per-account as one JSON blob. PlexInfo metadata (year,
genre, director, cast, studio, IMDb score) is clickable → sets the matching filter and
returns to the filtered grid (page variant only; the in-player overlay stays read-only
so a stray click can't stop playback). i18n en/hu/de.
2026-07-05 23:45:55 +02:00
npeter83
690e17611c feat(plex): rich media info — info page, in-player info overlay, IMDb + cast photos
Backend (no migration): item_detail now also returns IMDb score + id/url (from the
Plex Rating/Guid arrays), content rating, genres, director(s), studio, tagline, and
cast as {name, role, photo}. New host-whitelisted /person-image proxy serves cast
photos from Plex's public metadata CDN (keeps third-party requests off the browser).

Frontend: reusable PlexInfo component in two forms — a full info page opened from a
card's 'i' button (history subview, art backdrop, Play/Resume + watch controls) and a
lean overlay in the player toggled with 'I' (video keeps playing). Two per-user view
prefs (faint backdrop, cast row) persisted in preferences. Mark-unwatched / clear-resume
cover the watched-reset gap. i18n en/hu/de.
2026-07-05 22:57:18 +02:00
npeter83
38c4bee869 feat(plex): player HTPC polish — Stop button, keyboard help, time-remaining + clock, scroll-restore
- Explicit Stop button (stops playback + returns to the library).
- Keyboard cheat-sheet overlay toggled with 'H' (movie hides the episode-nav row).
- Backspace = stop & back to feed (HTPC-remote convention); closes the help first.
- Time-remaining readout beside the seek bar + a live wall clock in the top bar
  (both fade with the controls).
- PlexBrowse restores the grid scroll position when returning from the player, so
  Back lands on the same card instead of the top of the library.
- New plex.player i18n keys (stop/help/keys.*) in en/hu/de.
2026-07-05 21:27:02 +02:00
npeter83
c84cb22e5a fix(player): freeze the launch queue so Loop All + Next/Previous use the original list
The player's queue came live from the feed query, which refetches under a
watch-state filter as each video is auto-marked watched — steadily shrinking
the list mid-session. Loop "All" could then never wrap back to the first
video (earlier items had dropped out), and prev/next drifted. Snapshot the
queue once at mount (useState lazy init) and step through that frozen list for
the whole session; the live prop is ignored thereafter. Supersedes the earlier
active-item pin with a list-level freeze that also fixes Loop All.
2026-07-05 20:50:05 +02:00
npeter83
0fff5ad35d fix(player): keep playing the current video when it leaves the live feed queue
Auto-marking a video watched near its end invalidates the feed query; the
refetched/reordered queue can no longer contain the playing item, so
findIndex returned -1 and the active item fell back to queue[0] — silently
jumping playback to an unrelated video and defeating Loop "One" (and
auto-advance:off). Pin the last resolved video in a ref and keep playing it
when it drops out of the queue instead of snapping to queue[0].
2026-07-05 19:49:39 +02:00
npeter83
2ee4d563f6 release: v0.23.1 — clearer Plex playback errors + admin media-mount check 2026-07-05 18:58:43 +02:00
npeter83
3e1460759c release: v0.23.0 — Plex integration (browse/search + rich player) 2026-07-05 06:28:15 +02:00
npeter83
401fe90256 docs(release): note player auto-advance + loop in v0.22.3 2026-07-05 00:21:15 +02:00
npeter83
597cec6517 docs(release): note the saved-view persistence fix in v0.22.3 2026-07-04 23:42:40 +02:00
npeter83
7ea91ae42c docs(release): note player stepping + native controls in v0.22.3 2026-07-04 22:26:22 +02:00
npeter83
5d56fbed99 chore(release): v0.22.3 — source URL reference + adaptive watch player 2026-07-04 21:26:08 +02:00
npeter83
1c5a58b4de docs(release): note code-splitting in v0.22.2 2026-07-04 19:44:19 +02:00
npeter83
e612e09c4e docs(release): note a11y improvements in v0.22.2 2026-07-04 19:13:35 +02:00
npeter83
3911ab48f3 chore(release): v0.22.2 — landing perf + SEO polish 2026-07-04 18:19:07 +02:00
npeter83
95984a7ce4 chore(release): v0.22.1 — iOS/Safari-compatible downloads 2026-07-04 17:30:02 +02:00
npeter83
abedca5b8c chore(release): v0.22.0 — Download Center worker/sidecar in all composes + docs
Prep the Download Center epic (Phase 1 + editor + share) for prod/self-host:
- Dockerfile: create /downloads owned by appuser so a named-volume mount is writable (prod Linux).
- docker-compose.{home,selfhost,yml}: add the 'worker' (yt-dlp/ffmpeg job loop) + 'bgutil-pot'
  (PO-token) services + a downloads mount (DOWNLOAD_ROOT, WORKER_ENABLED). Media defaults to a
  named volume; DOWNLOAD_HOST_PATH points it at a host dir (e.g. a Plex-readable folder).
- README / docs/self-hosting.md / .env.example / install.{sh,ps1}: document the Download Center,
  the two extra containers, and DOWNLOAD_HOST_PATH.
- VERSION 0.22.0 + releaseNotes entry.
2026-07-04 06:31:31 +02:00
npeter83
9ca90f515c chore(release): 0.21.0 2026-07-02 02:19:25 +02:00
npeter83
b7c79cbe53 chore(release): 0.20.2 2026-07-01 14:30:50 +02:00
npeter83
381794d9ae chore(release): 0.20.1 2026-07-01 12:07:58 +02:00
npeter83
b8e1c9386e chore(release): 0.20.0 2026-07-01 11:28:56 +02:00
npeter83
669994cc85 chore(release): 0.19.0 2026-07-01 02:37:07 +02:00
npeter83
a4abfa402f chore(release): 0.18.0 2026-06-29 23:41:53 +02:00
npeter83
b1ed706cab chore(release): 0.17.0 — maintenance epic + messaging account-switch fix 2026-06-29 01:12:22 +02:00
npeter83
7a97fef33c chore(release): 0.16.2 — access-requests UX + back-navigation fixes 2026-06-26 01:42:12 +02:00
npeter83
6286dcc3c2 chore(release): 0.16.1 — notification context + YouTube API egress proxy 2026-06-26 00:36:46 +02:00
npeter83
557f2cef9c chore(release): 0.16.0 — end-to-end encrypted real-time messaging 2026-06-26 00:01:54 +02:00
npeter83
c84f5d5fe5 chore(release): 0.15.0 — feed virtualization + keyset pagination 2026-06-25 20:04:25 +02:00
npeter83
231e0c708b chore: release 0.14.1 — finish subfeed→siftlode rename
Bump VERSION and add a release-notes entry for the rename cleanup so the
prod image is published under a fresh tag rather than overwriting 0.14.0.
2026-06-21 07:07:56 +02:00
npeter83
0f124dc4d3 chore(release): 0.14.0 2026-06-21 03:23:23 +02:00
npeter83
2d1d5c80ac chore(release): 0.13.0 2026-06-19 19:53:31 +02:00
npeter83
4fc7e1e7df chore(release): 0.12.0
Cut a version marker for the accumulated work staged on dev (epics 1-4): reusable
Banner; Discover total-videos column + subscribe confirm; Stats consolidation +
quota-action taxonomy; DB-backed system_config + admin Configuration page. NOT
deployed to prod yet — dev/localdev report 0.12.0, prod stays 0.11.2 until a ship.
Alembic head at this marker: 0021.
2026-06-19 13:23:33 +02:00
npeter83
262ed06b3c release: 0.11.2 2026-06-19 04:27:22 +02:00
npeter83
403a5608df release: 0.11.1 2026-06-19 04:16:23 +02:00
npeter83
d254cf4353 release: 0.11.0 2026-06-19 04:02:38 +02:00
npeter83
b5141ab112 docs(release): correct 0.10.0 note — reload isn't guarded
The note claimed 'leaving the page with unsaved changes asks first', but the
beforeunload prompt was deliberately removed (native dialog; we use in-app
ones). Only in-app section switching is guarded — reword to say exactly that.
2026-06-19 00:20:38 +02:00
npeter83
1e41a624ff release: 0.10.0
Explicit Save/Discard for Settings; self-resolving connection-lost notice +
automatic retry of transient gateway 502s; state-change notices gated on
server confirmation; Settings panel height tidy-up.
2026-06-19 00:07:25 +02:00
npeter83
d5e4f5353e docs(release): reflect unified notification center in v0.9.0 notes 2026-06-18 04:37:51 +02:00
npeter83
264a3c74af docs(release): note admin scheduler run-now + live progress under v0.9.0 2026-06-18 04:02:11 +02:00
npeter83
3a0789ebe7 chore(release): v0.9.0 — notification inbox + automatic video maintenance
Bump VERSION to 0.9.0 and add the user-facing release notes for the durable
notification inbox (P1) and the maintenance/validation job that retires videos
that can no longer be played.
2026-06-18 03:22:22 +02:00
npeter83
a11a8db278 chore(release): v0.8.0 — channel columns, tag workflow, video reset, error dialog 2026-06-18 01:18:41 +02:00
npeter83
a7d087c6d3 chore(release): v0.7.0 — data-table channel manager, sidebar reorg, fixes 2026-06-17 19:17:35 +02:00
npeter83
79249104d4 chore(release): v0.6.0 — admin Background Scheduler dashboard 2026-06-16 17:46:15 +02:00
npeter83
e6c53b3f96 chore(release): v0.5.1 — graceful fallback for non-embeddable videos 2026-06-16 12:02:38 +02:00
npeter83
a76bbb5825 chore(release): v0.5.0 — demo account, clearer live videos 2026-06-16 10:23:29 +02:00
npeter83
af84cc0404 chore(release): v0.4.1 — feed toolbar + clearer counts/dates
Bump VERSION to 0.4.1 and add the 0.4.1 release-notes entry (feed view/content-type chips
above the videos, key+direction sort, exact view count + upload date on cards/player).
2026-06-16 03:18:58 +02:00