Commit Graph
105 Commits
Author SHA1 Message Date
peter 175e49b361 chore(release): 0.56.0 — R7 frontend API layer 2026-07-28 00:18:39 +02:00
peter c86a15c252 chore(release): 0.55.0 — R6 API contract & transaction ownership 2026-07-26 21:25:36 +02:00
peter 51193105a7 fix(player): first-class maximise/fullscreen view (UAT round)
UAT feedback on the F-maximise view — three fixes:
- Own control bar in the big views (maximise + our fullscreen): previous/next +
  N/M, auto-advance, loop, add-to-playlist, download, mark-watched, exit, and a
  true-fullscreen toggle. Auto-hides on idle. Add/download are hidden in true
  fullscreen (their popovers portal to <body>, outside the fullscreen element).
- Wheel volume works in the big views again: the centre interaction overlay is
  now armed in maximise/fullscreen (it only yields to YouTube's own settings
  menu), so the wheel bubbles to our handler instead of the bare iframe. F also
  offers true fullscreen that WE own (Fullscreen API on the stage), so wheel/
  arrow volume and our controls work inside it too — YouTube's own fullscreen
  button, which we never surface, is not involved.
- Back (mouse/browser) steps out one level at a time via nested history layers
  (useBackToExit): fullscreen -> maximise -> the modal -> closed, instead of
  closing the whole player at once.

Verified in Chrome: wheel volume in maximise + fullscreen, the control bar, and
the full Back ladder (fullscreen -> maximise -> modal -> closed).
2026-07-26 02:48:46 +02:00
peter 475bd7fef8 chore(release): 0.54.0 — R5 data integrity (finalize version + release notes) 2026-07-26 01:40:43 +02:00
peter 58f836bcd8 fix(plex): stop /facets 500 on a metadata-poor library
A mirrored Shows library whose items have no genres stored `genres` as JSON
`null` — SQLAlchemy's JSONB persists a Python None as a JSON scalar, not SQL
NULL. /facets unnested it with jsonb_array_elements_text(), which raises
"cannot extract elements from a scalar", so a single bare library 500'd the
whole Plex module on open (the grid loads, the filter sidebar's facets call dies).

- Guard the /facets genre unnest with jsonb_typeof(genres)='array', mirroring
  the guard people.py already applies to the same columns.
- Persist the JSONB tag columns (genres/directors/cast_names/collection_keys on
  plex_items and plex_shows) with none_as_null=True, so an empty list round-trips
  as SQL NULL and self-heals on the next sync — closing the trap for any future
  unnest.

Verified against the real prod row (DumaTV show, genres = JSON null): the
unguarded query errors, the guarded one returns cleanly.
2026-07-25 18:22:07 +02:00
peter 08049d2aa5 release: 0.53.0 — R4 auth funnel & abuse hardening 2026-07-23 04:27:20 +02:00
peter e663247791 chore: prettier-format release notes 2026-07-22 04:52:37 +02:00
peter 745ba10f2e release: 0.52.0 — R3 honest states (loading/error across the app) 2026-07-22 04:51:32 +02:00
peter 56111ceadf release: 0.51.1 — shell-safe names, re-download title refresh, GC orphan sweep 2026-07-22 03:18:36 +02:00
peter bce6175983 release: 0.51.0 — downloads correctness & UX (iOS playability, 720p, re-download, ASCII names) 2026-07-22 02:52:01 +02:00
peter 51781819db release: 0.50.0 — R1 quick-win sweep
Ships R1 (S1 backend safety, S2 frontend interaction, S3 i18n/a11y) plus the
code-review follow-ups (filename byte-cap, demo deep_requested guard, release-notes
highlight fallback). Mixed epic, so a real user-facing note: download-overwrite +
long-title fixes, Escape layering, Plex light-theme menus, toast timer, unsubscribe
error, modal-close i18n, toast aria-live; keyboard + hardening under chores.
2026-07-22 00:31:47 +02:00
peter 3d5662df22 perf(frontend): keep the changelog out of the eager App chunk (C-3.19)
App.tsx imported CURRENT_VERSION from releaseNotes, which pulled its ~900-line
changelog into the eager App chunk and defeated the lazy ReleaseNotes split. App now
uses FRONTEND_VERSION (the built VITE_APP_VERSION, equal to RELEASE_NOTES[0].version
by release convention) from lib/version, and CURRENT_VERSION is removed. Verified in
the built bundle: the changelog text now lives only in the ReleaseNotes lazy chunk.
2026-07-21 21:47:45 +02:00
peter 9445e4d71a release: 0.49.0 — R2 guardrails & test net
Internal-only epic (gates, tests, compiler strictness); no user-facing change, so
the release note is chores-only. Bundles: the siftlode check/publish gate
(tsc/eslint/prettier/knip/vitest/pytest) with a hard pre-publish gate + e2e
freshness guard, ESLint flat config + one-time Prettier pass, the backend pytest
harness (isolated test image), frontend vitest growth, the e2e scroll-restore fix,
and noUncheckedIndexedAccess (+ the zero-fallout compiler flags).
2026-07-21 04:30:51 +02:00
peter e257e2aca0 style(frontend): Prettier config + one-time repo-wide format pass
Prettier 3 (pinned), printWidth 100, double quotes — chosen to match the existing
hand-formatting and minimise reflow. This commit is ONLY the mechanical format pass
(`prettier --write`) plus the config/ignore/scripts, isolated so it never pollutes a
feature diff. Verified behaviour-neutral: typecheck, eslint (0 errors), and vitest
all green before and after. See .git-blame-ignore-revs — `git blame` skips this sha.
2026-07-21 02:26:40 +02:00
peter 855ae0c995 release: 0.48.0 — playlist column headers, and sorting stops rewriting the list 2026-07-21 00:46:41 +02:00
peter 742cf2df73 release: 0.47.0 — relative timestamps fixed, and localized by the platform 2026-07-20 22:31:30 +02:00
peter 758e655642 release: 0.46.0 — channel manager overhaul (table/cards, About, shared sort, paging) 2026-07-20 01:31:15 +02:00
peter 97611be9c4 release: 0.45.1 — player & share fixes 2026-07-19 06:08:06 +02:00
peter 86a6d696b8 release: 0.45.0 — feed view modes + Plex cast-explore
Cuts the release for the unshipped stack since 0.44.0: E3 feed view modes, the
Platform refactor (S1–S6b, internal), and the Plex cast-explore epic. VERSION
0.44.0 -> 0.45.0; user-facing releaseNotes entry.
2026-07-19 04:58:36 +02:00
peter 303c896d87 chore(release): 0.44.0 — peek-open panels, active-filter chips, channel filter, sync chip 2026-07-16 01:21:55 +02:00
peter 9d9c6894f4 chore(release): 0.43.1 — sidebar no longer latches open after a portaled menu 2026-07-15 20:29:41 +02:00
peter 44926f1c23 chore(release): 0.43.0 — nav overlay sidebar, account chrome, opt-in glass tuner 2026-07-14 21:41:44 +02:00
peter 27b9e83af6 docs(release): expand v0.42.0 notes (header, side panels, i18n, notifications) 2026-07-13 05:13:52 +02:00
peter 88e6b6a2a0 chore(release): 0.42.0 — glass-over-image look + Starship/Matrix schemes 2026-07-12 22:28:50 +02:00
peter 08919199c2 chore(release): 0.41.0 — admin audit log + channel About enrichment/fixes 2026-07-12 17:57:39 +02:00
peter 2c4d4fa193 chore(release): 0.40.0 — deferred-bugs closeout 2026-07-12 06:54:49 +02:00
peter 216200eebf release: v0.39.1
OAuth scope-clobber fix (sign-out/in no longer drops YouTube access) + admin
access-request email accuracy/multi-admin + auth loose-ends (register/reset timing,
messages_ws dedup, revoke decrypt).
2026-07-12 04:57:06 +02:00
peter 9d3624d51b release: v0.39.0
Auth security round: SB3 (email tokens out of URL query → fragment), SA4 (server-side
session revocation + 'Log out other sessions'), SA3 (trusted-proxy X-Forwarded-For so
rate limits can't be bypassed via a forged header).
2026-07-12 03:49:55 +02:00
peter c7ef5a87b2 release: v0.38.0
Plex filter-sidebar collapse + infinite-scroll fix; the whole code-hygiene sweep
(Phases 1-4: dead-code/duplication cleanup, watch_sync/player fixes, guardrails).
2026-07-12 02:26:42 +02:00
peter 2bdccfb8c7 release: v0.37.0 — unified library, series view, adaptive filters, collections picker 2026-07-11 03:50:34 +02:00
peter 8279f259bb revert(player): drop the experimental HD-unlock fullscreen flash
User-tested on prod: the flash fires (fullscreen → back to small) but the source
stays 360p — YouTube doesn't switch quality within the flash window and we can't set
it via the (dead) API, so the unlock has no effect. Give up on forcing windowed HD.
Restore PlayerModal to its 0.36.2 state (native-menu-yield + scroll-anywhere volume
kept). Also prune the failed HD-quality attempts from the release notes (the flash
and the transform-revert notes, plus the hollow 'higher quality' claim in 0.36.0) so
there's no user-facing trace of an effort that yielded nothing.
2026-07-10 18:14:32 +02:00
peter aac03e2c2b chore(release): 0.36.3 2026-07-10 17:30:50 +02:00
peter 967364b938 chore(release): 0.36.2 2026-07-10 16:56:24 +02:00
peter 3a25f8341f chore(release): 0.36.1 2026-07-10 16:27:43 +02:00
peter bab1b3361a chore(release): 0.36.0 2026-07-10 15:52:34 +02:00
peter 1dd42f06a2 chore(release): 0.35.0 — two-way Plex watch sync (Phase B + C)
Completes the two-way Plex ↔ Siftlode watch-state sync: Phase B (Siftlode→Plex push, immediate)
and Phase C (incremental history/on-deck pull + daily full reconcile incl. un-watch). Also the
auto-advance watched-mark id-race fix.
2026-07-10 00:47:55 +02:00
peter 9ecb3e6e81 chore(release): 0.34.2 — watch-sync toggle UX polish 2026-07-09 15:34:52 +02:00
peter 980f228561 chore(release): 0.34.1 — Plex watch import concurrency fix 2026-07-09 15:19:17 +02:00
peter b67a14bd13 chore(release): 0.34.0 — Plex watch-state import (Phase A) 2026-07-09 14:43:22 +02:00
peter ccd5f21921 chore(release): 0.33.1 — channel page network-error fix 2026-07-09 09:43:08 +02:00
peter ad733c4422 chore(release): v0.33.0
Plex player (copyts playback, resume, personalization, skip intro/credits,
audio/subtitle switching, seek drag, projected finish time, +25% UI) and
Plex search improvements (own ephemeral state; empty-with-filters hint).
2026-07-09 02:32:46 +02:00
peter 1e9d58555a fix(share): let link-preview crawlers fetch /watch/ pages (robots.txt)
Shared links didn't unfurl in Messenger: robots.txt disallowed /watch/ for ALL
user-agents, so facebookexternalhit honoured it and got a blocked (403) fetch —
the OG tags were correct but never read. Add an allow group for the link-preview
bots (facebookexternalhit, Twitterbot, Discordbot, WhatsApp, Slack, Telegram,
LinkedIn, …) so they can fetch share pages; the wildcard still keeps /watch/ out
of search indexes. Release v0.32.1.
2026-07-07 22:56:38 +02:00
peter 61a721e55a fix(share): self-host og:image so link previews stay reliable
A remote thumbnail as og:image is unreliable for the crawler — Facebook's signed
CDN URL expires (so an old shared link's image later vanishes) and cross-origin
hotlinking is flaky. Record a self-hosted poster for EVERY download (ensure_poster
returns the existing <base>.jpg thumbnail sidecar, or cuts a frame) and point
og:image at our own /api/public/watch/{token}/poster.jpg. Backfill now fills
poster_path for all existing downloads, not just thumbnail-less ones.
2026-07-07 22:43:18 +02:00
peter 977c09c43d chore(release): v0.32.0 2026-07-07 22:29:05 +02:00
peter 6cd18dc17f fix(downloads): clip long titles to fit display_name (VARCHAR 255)
A download whose source title exceeded 255 chars (e.g. a recipe baked into a
Facebook video's title) failed with a StringDataRightTruncation: the worker
auto-fills an empty display_name from the title, which overflows the 255-char
column. Clip the title to 255 at every point it flows into display_name (worker
completion paths + enqueue); the full title is untouched on the asset. Pre-existing
latent bug, surfaced by an unusually long title.

Release v0.31.1.
2026-07-07 21:40:15 +02:00
peter d73cfb16a3 chore(release): v0.31.0 2026-07-07 20:19:39 +02:00
peter cb6fb60b08 feat(plex): Playlists Phase 1 — per-user ordered watch-lists (Siftlode-native)
Personal ordered lists of Plex items, kept in Siftlode's own DB (works for users
without a Plex account, like watch-state) — the "your own lists" counterpart to
shared collections. Plex-direction sync is a later phase (plex_rating_key
reserved).

Backend: migration 0048 (plex_playlists + plex_playlist_items with position),
PlexPlaylist/PlexPlaylistItem models, and per-user CRUD endpoints under
/api/plex/playlists (list [+?contains for the add dialog], create [seeded],
detail [ordered cards], rename, delete, add/remove item, reorder). _leaf_card
handles the movie/episode mix. Frontend: "Add to playlist" dialog from the movie
info page (all users), a Playlists section in PlexSidebar (list + create),
PlexPlaylistView (reorder up/down, remove, rename, delete, Play all), and
PlexPlayer gained an optional `queue` so play-through follows the list order
(prev/next + auto-advance). i18n en/hu/de. Verified end-to-end on localdev
(backend CRUD + the create→add→view→play-through UI flow).
2026-07-06 19:05:12 +02:00
peter cd2175ccc3 feat(plex): Collections Phase 2 — admin collection editing (write-back to Plex)
Admins can curate Plex collections from a movie's info page ("Collections"
button → PlexCollectionEditor dialog): create a collection (seeded with the
movie), add/remove the movie to/from editable collections, delete a collection,
and "take over" an existing plain Plex collection (mark it editable). All writes
go to Plex (POST/PUT/DELETE via new PlexClient methods) and are reflected on
every client; a targeted single-collection re-sync (sync.resync_collection /
delete_collection_local) updates the local mirror without the full ~4-min sync.

Gating (per the design decisions): editing is ADMIN-ONLY (collections are shared
library-wide); only plain manual collections are editable — smart + external
auto-lists (IMDb/TMDb/…) are always read-only (can_edit = editable && !smart &&
source=="collection"). New admin endpoints under /api/plex/collections
(create/items add+remove/rename/delete/editable). Verified end-to-end incl. the
live Plex write API (create/add/rename/remove/delete all 200, self-cleaned) and
the editor UI (create + delete with confirm) on localdev.
2026-07-06 17:33:16 +02:00
peter 8853b73dda fix(plex): play subtitles as WebVTT tracks (external sidecar subs no longer crash)
Selecting a subtitle restarted the HLS session with `-map 0:s:{ord}`, assuming an
EMBEDDED stream. Films whose subs are external sidecar .srt files (Plex reports
them, but they aren't in the mkv) matched no stream; the master-playlist's
declared subtitle group then made ffmpeg fail → "Playback couldn't start".

Subtitles now go through a new GET /api/plex/subtitle/{rk}/{ord} → text/vtt
(external subs fetched from Plex via the stream key + SRT→VTT; embedded text subs
extracted with ffmpeg; image subs → 415), served as native <video><track> that
the browser overlays. So choosing/switching a subtitle is instant with NO session
restart, and stream.py drops all subtitle muxing (`-sn`, no master playlist).
Image-based subs (PGS/VobSub) are marked text=false and hidden in the picker.
Verified on prod's Nymphomaniac Vol. II: HU sidecar → 1693 WebVTT cues, no crash.
2026-07-06 08:45:30 +02:00
peter c1a3848271 improvement(plex): serve right-sized poster/art images via Plex transcode
Posters/art were proxied at full resolution (many 1-7MB, some 9.5MB) into ≤176px
grid/info cells — heavy to fetch + decode, so fast scrolling lagged 1-2s even
with a warm cache (the cache removed the Plex round-trip, not the image weight).

Now PlexClient.image_bytes optionally hits Plex's /photo/:/transcode to resize
server-side; the /image endpoint requests thumb=400x600, art=1280x720 and keys
the cache by width (old full-size files orphaned, re-cached small). Measured:
a poster 5.5MB->50KB, art 8.1MB->137KB, at 400x600 (crisp on 2x DPR). Grid
scroll is near-instant and the disk cache shrinks ~8x.
2026-07-06 08:04:13 +02:00