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:
2026-07-23 23:25:06 +02:00
parent 5130584d0a
commit 2a7f49c981
9 changed files with 307 additions and 79 deletions
+47 -38
View File
@@ -7,6 +7,7 @@ never synced. Videos in a playlist that aren't in our shared catalog yet are fet
ingested (with a stub channel) so the mirror is faithful."""
import hashlib
import logging
from collections.abc import Callable
from datetime import datetime, timezone
from sqlalchemy import delete, select
@@ -271,8 +272,16 @@ def plan_push(db: Session, user: User, pl: Playlist, *, live=None) -> dict:
_MAX_CONSECUTIVE_FAILURES = 5
#: Returned by `_Breaker.run` when the write did not happen (refused or failed).
FAILED = object()
class _Breaker:
"""Consecutive-failure circuit breaker for a single push run. Any success re-arms it."""
"""Consecutive-failure circuit breaker for a single push run. Any success re-arms it.
Drive it with `run()` — it owns the whole open-check / call / record-outcome sequence, so a
write site can't accidentally implement two thirds of the protocol (forgetting to record a
failure would silently disable the breaker for that operation)."""
def __init__(self, limit: int = _MAX_CONSECUTIVE_FAILURES) -> None:
self.limit = limit
@@ -296,6 +305,22 @@ class _Breaker:
self.limit,
)
def run(self, op: Callable[[], object], on_fail: Callable[[], None]) -> object:
"""Attempt one YouTube write. Returns the op's result, or `FAILED` when the breaker is
already tripped (op not called) or the op raised YouTubeError. `on_fail` records the item
in the caller's failure list either way."""
if self.tripped:
on_fail()
return FAILED
try:
out = op()
except YouTubeError:
on_fail()
self.hit(False)
return FAILED
self.hit(True)
return out
def push_playlist(db: Session, user: User, pl: Playlist, *, live=None) -> dict:
"""Push a local playlist to YouTube: create it if needed, then reconcile membership
@@ -313,16 +338,11 @@ def push_playlist(db: Session, user: User, pl: Playlist, *, live=None) -> dict:
created = 1
# Brand-new playlist: just append every video in order.
for vid in desired:
if not breaker.open():
failures.append(vid)
continue
try:
yt.add_playlist_item(pl.yt_playlist_id, vid)
if breaker.run(
lambda v=vid: yt.add_playlist_item(pl.yt_playlist_id, v),
lambda v=vid: failures.append(v),
) is not FAILED:
inserted += 1
breaker.hit(True)
except YouTubeError:
failures.append(vid)
breaker.hit(False)
if not failures:
pl.synced_fingerprint = fingerprint(pl.name, desired)
pl.dirty = False
@@ -354,30 +374,22 @@ def push_playlist(db: Session, user: User, pl: Playlist, *, live=None) -> dict:
# Delete extras (local is authoritative).
for it in current:
if it["video_id"] not in desired_set:
if not breaker.open():
failures.append(it["video_id"])
continue
try:
yt.delete_playlist_item(it["item_id"])
if breaker.run(
lambda i=it: yt.delete_playlist_item(i["item_id"]),
lambda i=it: failures.append(i["video_id"]),
) is not FAILED:
deleted += 1
breaker.hit(True)
except YouTubeError:
failures.append(it["video_id"])
breaker.hit(False)
# Insert missing (append; the reorder pass below fixes positions).
cur_set = set(cur_vids)
for vid in desired:
if vid not in cur_set:
if not breaker.open():
failures.append(vid)
continue
try:
item_id_by_vid[vid] = yt.add_playlist_item(pl.yt_playlist_id, vid)
item_id = breaker.run(
lambda v=vid: yt.add_playlist_item(pl.yt_playlist_id, v),
lambda v=vid: failures.append(v),
)
if item_id is not FAILED:
item_id_by_vid[vid] = item_id
inserted += 1
breaker.hit(True)
except YouTubeError:
failures.append(vid)
breaker.hit(False)
# Reorder to match `desired` via insertion-sort moves over a local model.
model = [v for v in cur_vids if v in desired_set] + [
v for v in desired if v not in cur_set and v in item_id_by_vid
@@ -387,17 +399,14 @@ def push_playlist(db: Session, user: User, pl: Playlist, *, live=None) -> dict:
continue # failed insert; skip
if i < len(model) and model[i] == want:
continue
if not breaker.open():
failures.append(want)
continue
try:
yt.move_playlist_item(item_id_by_vid[want], pl.yt_playlist_id, want, i)
reordered += 1
breaker.hit(True)
except YouTubeError:
failures.append(want)
breaker.hit(False)
if breaker.run(
lambda w=want, pos=i: yt.move_playlist_item(
item_id_by_vid[w], pl.yt_playlist_id, w, pos
),
lambda w=want: failures.append(w),
) is FAILED:
continue
reordered += 1
j = model.index(want)
model.pop(j)
model.insert(i, want)