From 3c7a8c7a14aad037f025677e2de75c2a80a9b2ee Mon Sep 17 00:00:00 2001 From: npeter83 Date: Sat, 11 Jul 2026 21:53:24 +0200 Subject: [PATCH] fix(plex): honor the admin "max concurrent transcodes" setting (config drift) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit stream.py enforced a hardcoded _MAX_SESSIONS = 4 and never read the DB-overridable plex_max_transcodes ConfigSpec, so the Configuration → Plex → "Max concurrent transcodes" knob was dead (same env-vs-DB drift class as the Downloads/Admin fixes). _enforce_cap(cap) now takes the cap from sysconfig.get_int(db, "plex_max_transcodes") (>=1 so playback can't be capped to zero). Bumped the config default 1→4 so the effective default matches the old hardcoded cap (no behavior change for non-overriders; the knob now works). --- backend/app/config.py | 2 +- backend/app/plex/stream.py | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/backend/app/config.py b/backend/app/config.py index c83f0f7..3f75965 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -211,7 +211,7 @@ class Settings(BaseSettings): plex_watch_reconcile_interval_min: int = 1440 # Max concurrent transcodes for the P3 fallback. Low by default — CPU-only transcode is # expensive; direct-serve (browser-compatible files) has no such limit. - plex_max_transcodes: int = 1 + plex_max_transcodes: int = 4 # Where on-the-fly HLS remux segments are written (transient, reaped). Empty → a `.plex-hls` # dir under download_root. On localdev the download_root is a slow Windows bind-mount, so point # this at a fast container-local path (e.g. /var/tmp/plex-hls); prod's download_root is fast diff --git a/backend/app/plex/stream.py b/backend/app/plex/stream.py index 129e050..5d1f5c2 100644 --- a/backend/app/plex/stream.py +++ b/backend/app/plex/stream.py @@ -23,6 +23,7 @@ from pathlib import Path from sqlalchemy.orm import Session as DbSession +from app import sysconfig from app.config import settings from app.models import PlexItem from app.plex import paths @@ -31,7 +32,6 @@ log = logging.getLogger("siftlode.plex") _HLS_ROOT = Path(settings.plex_hls_dir) if settings.plex_hls_dir else Path(settings.download_root) / ".plex-hls" _SEG_SECONDS = 6 -_MAX_SESSIONS = 4 # concurrent remux sessions (safety valve for the CPU-only host) _SESSION_IDLE_S = 600 # reap a session with no access for this long _lock = threading.Lock() @@ -168,11 +168,11 @@ def _kill(s: HlsSession) -> None: shutil.rmtree(s.dir, ignore_errors=True) -def _enforce_cap() -> None: +def _enforce_cap(cap: int) -> None: # Caller holds _lock. Drop the least-recently-accessed sessions over the cap. - if len(_sessions) <= _MAX_SESSIONS: + if len(_sessions) <= cap: return - for key, s in sorted(_sessions.items(), key=lambda kv: kv[1].last_access)[: len(_sessions) - _MAX_SESSIONS]: + for key, s in sorted(_sessions.items(), key=lambda kv: kv[1].last_access)[: len(_sessions) - cap]: _kill(s) _sessions.pop(key, None) @@ -211,7 +211,9 @@ def start_session( ) s = HlsSession(key, directory, proc, start_s, "master.m3u8" if is_multi else "index.m3u8") _sessions[key] = s - _enforce_cap() + # Admin-configurable concurrency cap (Configuration → Plex → Max concurrent transcodes); + # at least 1 so playback can't be capped to zero. + _enforce_cap(max(1, sysconfig.get_int(db, "plex_max_transcodes"))) # Learn the REAL start offset K from the first VIDEO segment (outside the lock — this blocks on # ffmpeg producing it, and we must not hold up other sessions). The client reads `media_start_s`, so # its absolute clock + subtitle shift key off the true keyframe, not the requested offset. On