fix(sync): an empty answer the remote's own count contradicts is not believed, one level down
E32 review round 1 (B1, B2, B3, B8). - Plex watch reconcile: a section the server SAYS is empty (totalSize 0, nothing listed) is a complete listing. It read as "cut short" forever, so one empty library stopped un-watches from ever syncing. A listing with no total at all stays incomplete (the client now returns None for it instead of 0), and a whole scan that listed nothing still clears nothing. Tests now cover completeness across two sections, both orders. - Plex collections: no members from a collection whose listing counts some (childCount) keeps the membership we hold, in the full sync and in the targeted resync after a write. - YouTube playlists: no items for a playlist whose listing counts some (itemCount) keeps the mirror in the bulk pull, and is refused as retryable in "reset to YouTube" and in the push plan/merge, where it read as "YouTube deleted them all". The rule sits beside vanished(). - A playlist deleted on YouTube answered "reconnect your account"; it now says it is gone (errors.youtube.gone, HU+EN). - The pure 3-way merge moved to sync/playlist_merge.py; playlists.py is back under 800 lines and off the size list.
This commit is contained in:
@@ -75,9 +75,9 @@ def _scan_plex_states(
|
||||
) -> tuple[dict[int, tuple[str, int, datetime | None, datetime | None]], int, bool]:
|
||||
"""Scan the enabled movie/show sections and return ``{item_id: (status, pos, watched_at,
|
||||
prog_at)}`` for every mirrored leaf Plex holds a watch signal for, the scanned-leaf count, and
|
||||
whether EVERY section's walk reached the server's total. Shared by the one-time import and the
|
||||
full reconcile (both need Plex's whole current picture). The last one matters to the reconcile
|
||||
alone: it reads "absent from the scan" as "Plex un-watched it", which a walk cut short by a
|
||||
whether EVERY section's walk reached the server's total (or the server said it holds none).
|
||||
Shared by the one-time import and the full reconcile (both need Plex's whole current picture).
|
||||
The last one matters to the reconcile alone: it reads "absent from the scan" as "Plex un-watched it", which a walk cut short by a
|
||||
Plex hiccup does not mean (see `_paginate`)."""
|
||||
item_id_by_rk = _rk_to_id(db)
|
||||
wanted = _enabled_section_keys(db)
|
||||
@@ -93,6 +93,7 @@ def _scan_plex_states(
|
||||
continue
|
||||
item_type = _TYPE_MOVIE if stype == "movie" else _TYPE_EPISODE
|
||||
progress: dict = {}
|
||||
before = scanned
|
||||
for meta in _paginate(plex, key, item_type, progress):
|
||||
scanned += 1
|
||||
item_id = item_id_by_rk.get(str(meta.get("ratingKey") or ""))
|
||||
@@ -103,7 +104,10 @@ def _scan_plex_states(
|
||||
)
|
||||
if res is not None:
|
||||
out[item_id] = res
|
||||
complete = complete and bool(progress.get("complete"))
|
||||
# A section the server SAYS is empty (total 0, nothing listed) is complete too: without this
|
||||
# one genuinely empty library kept every run "incomplete" and un-watches never synced again.
|
||||
said_empty = progress.get("total") == 0 and scanned == before
|
||||
complete = complete and (bool(progress.get("complete")) or said_empty)
|
||||
return out, scanned, complete
|
||||
|
||||
|
||||
@@ -637,12 +641,14 @@ def run_plex_watch_reconcile(db: Session) -> dict:
|
||||
if ch:
|
||||
total["applied"] += 1
|
||||
|
||||
if complete:
|
||||
# A whole scan that listed nothing at all is not "Plex un-watched everything" either:
|
||||
# every section can say 0 only when the server lost its libraries, never as news.
|
||||
if complete and scanned:
|
||||
total["cleared"] += _clear_unwatched(db, link.user_id, plex_states)
|
||||
else:
|
||||
total["incomplete"] = total.get("incomplete", 0) + 1
|
||||
log.warning(
|
||||
"the Plex scan ended before the server's total; nothing is cleared this run",
|
||||
"the Plex scan was cut short or listed nothing; nothing is cleared this run",
|
||||
extra=logs.event("plex.watch.reconcile_incomplete", user_id=link.user_id),
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user