fix(r5): address the /code-review high round — 10 findings
- HLS sweep only deletes directories matching the session-name shape; the root is admin-configurable and may be a shared path - playlist unwrap picks the first entry that actually downloaded, not entries[0] - concat quoting keeps backslashes literal (ffmpeg treats them so inside '') - ArrowUp/Down defer while the focused element can still scroll that way - the staging fallback skips yt-dlp working files (.part-Frag/.ytdl/.temp/.fNNN) - the boot HLS sweep runs off the event loop - volume persistence is debounced (was a setItem per wheel notch) and a spin no longer loses notches to the iframe's lagging getVolume - _Breaker.run owns the whole open/call/record protocol; four call sites shrink - should_prune extracted and unit-tested, plus RSS error-vs-empty tests - the worker exits non-zero when the schema never lands
This commit is contained in:
@@ -15,6 +15,7 @@ segments. The remux is CPU-light (video copy + a tiny audio transcode), so it ru
|
||||
CPU-only prod host; only P3's full transcode is CPU-heavy.
|
||||
"""
|
||||
import logging
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import threading
|
||||
@@ -33,6 +34,11 @@ 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
|
||||
_SESSION_IDLE_S = 600 # reap a session with no access for this long
|
||||
# A session directory is named `{rating_key}_{int(start_s)}_{tag}_{aoff:+.2f}` (see start_session).
|
||||
# `sweep_orphans` deletes ONLY names matching this shape: `_HLS_ROOT` is admin-configurable
|
||||
# (PLEX_HLS_DIR), so it may legitimately point at a shared scratch path we do not own — a blanket
|
||||
# "delete every directory under the root" would take the neighbours with it.
|
||||
_SESSION_DIR_RE = re.compile(r"^.+_\d+_[^_]+_[+-]\d+\.\d{2}$")
|
||||
|
||||
_lock = threading.Lock()
|
||||
_sessions: dict[str, "HlsSession"] = {}
|
||||
@@ -255,12 +261,16 @@ def wait_for(path: Path, timeout: float = 20.0) -> bool:
|
||||
|
||||
|
||||
def sweep_orphans() -> int:
|
||||
"""Delete segment directories no live session owns.
|
||||
"""Delete OUR segment directories that no live session owns.
|
||||
|
||||
Sessions live only in THIS process, so at startup every directory under `_HLS_ROOT` is a
|
||||
leftover from a crashed or killed run — and nothing else ever reaps them (the download GC
|
||||
Sessions live only in THIS process, so at startup every session directory under `_HLS_ROOT` is
|
||||
a leftover from a crashed or killed run — and nothing else ever reaps them (the download GC
|
||||
deliberately skips `.plex-hls` as a system tree), so a few restarts mid-playback leave
|
||||
gigabytes of `.ts` segments behind forever."""
|
||||
gigabytes of `.ts` segments behind forever.
|
||||
|
||||
Only entries whose name matches `_SESSION_DIR_RE` are touched: the root is admin-configurable,
|
||||
so anything else under it belongs to someone else and is left alone. Blocking (rmtree) — call
|
||||
it off the event loop."""
|
||||
with _lock:
|
||||
live = {s.dir for s in _sessions.values()}
|
||||
dropped = 0
|
||||
@@ -269,7 +279,7 @@ def sweep_orphans() -> int:
|
||||
except OSError: # the root doesn't exist yet — nothing to sweep
|
||||
return 0
|
||||
for p in entries:
|
||||
if p.is_dir() and p not in live:
|
||||
if p.is_dir() and p not in live and _SESSION_DIR_RE.match(p.name):
|
||||
shutil.rmtree(p, ignore_errors=True)
|
||||
dropped += 1
|
||||
return dropped
|
||||
|
||||
Reference in New Issue
Block a user