From 63c6e782c8f65b5c4eda2ab4ab6e2d0f3e0eef77 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Sun, 5 Jul 2026 02:20:23 +0200 Subject: [PATCH] =?UTF-8?q?feat(plex):=20P1=20backend=20=E2=80=94=20catalo?= =?UTF-8?q?g=20sync=20+=20browse/search/show/image=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - app/plex/sync.py: full reconcile of enabled movie/show sections into plex_* (upsert by rating_key). Reuses Plex's own codec info to classify browser playability (direct/remux/transcode) — no ffprobe. Movies + episodes become playable leaves; shows/seasons mirrored for the drill-down. - routes/plex.py: GET /libraries, /browse (FTS search + sort + paging, movie leaves or show cards), /show/{rk} (seasons+episodes, per-user state), /image (proxies Plex art, no duplication); admin POST /sync. All auth'd, demo-allowed. - scheduler: plex_sync job (interval settings.plex_sync_interval_min). - Verified against the real server: 2 libs, 3206 movies, 260 shows/961 seasons/ 14768 episodes synced in 13s; FTS search + drill-down work. Playability: ~1% direct, 89% remux, 6% transcode (informs P2/P3). --- backend/app/plex/sync.py | 236 ++++++++++++++++++++++++++++++++++ backend/app/routes/plex.py | 252 ++++++++++++++++++++++++++++++++++--- backend/app/scheduler.py | 9 ++ backend/app/sysconfig.py | 2 +- 4 files changed, 483 insertions(+), 16 deletions(-) create mode 100644 backend/app/plex/sync.py diff --git a/backend/app/plex/sync.py b/backend/app/plex/sync.py new file mode 100644 index 0000000..ea1b1c1 --- /dev/null +++ b/backend/app/plex/sync.py @@ -0,0 +1,236 @@ +"""Mirror a locally-reachable Plex library into Siftlode's own catalog (plex_* tables). + +Playback is from the local physical file, so this only mirrors METADATA + the physical-media facts +(codecs/container/path) needed to browse, search, and later play. It reuses Plex's own codec info +(no ffprobe needed) to classify each item's browser playability. Movies + episodes become playable +LEAF rows (plex_items); shows/seasons are mirrored for the drill-down + episode ordering. + +The sync is a full reconcile of the enabled sections (upsert by rating_key). Pruning of items that +disappeared from Plex is intentionally left for later (a follow-up can diff rating_keys per library). +Cast + intro/credit markers are fetched lazily on the item-detail/player path (P2), not here. +""" +import logging +from datetime import datetime, timezone + +from sqlalchemy.orm import Session + +from app import sysconfig +from app.models import PlexItem, PlexLibrary, PlexSeason, PlexShow +from app.plex.client import PlexClient, PlexError, PlexNotConfigured + +log = logging.getLogger("siftlode.plex") + +# Codecs/containers a browser can play in a native