fix(deferred): close backend correctness/efficiency tails from the hygiene sweep

Verified each deferred per-subsystem item against current source, then fixed the
real ones (tradeoffs left as-is, see siftlode-ops/CODE-HYGIENE.md closeout):

- search BUG-3: don't finalise shorts_probed on un-enriched stubs (enrichment
  failure/omitted rows) — restores the scheduler's enriched_at guard so a real
  Short can't leak into the feed and never get reclassified.
- downloads GC: 4th pass reaps orphaned errored, fileless, unreferenced assets
  (they carry no TTL, so the expiry passes never cleared them).
- downloads B6: quota/edit errors now return a structured {code,reason,limit}
  the trilingual client localises, instead of hardcoded English + dot-decimal GB.
- channels BB1: reset-backfill re-arms deep sync on every subscriber (bulk update)
  instead of 404ing when the admin isn't personally subscribed.
- channels BB2: enrich the stub on BOTH the normal and "already exists" desync
  path (nest the insert try inside the client `with`).
- channels CB3: drop the redundant second _discovery_rows read (identity-mapped
  rows are already enriched; re-sort in Python for the title tie-break).
- playlists PC1: read the live playlist once in push() and share it with plan_push
  + push_playlist (halves read-quota, closes the second TOCTOU window).
- playlists PC2/PC5: batch the cover thumbnails in one window query (was N+1);
  rename combines count+duration into one aggregate + a single cover lookup.
- messages MB2: get_thread only resolves a messageable partner OR one we already
  share a thread with (closes the user-enumeration oracle).
- messages MB3: push a live unread-count to the user's other tabs after mark-read.
- messages MC4: list_conversations uses DISTINCT ON + grouped COUNT instead of
  loading the whole message history into memory.
- config AB3: per-spec allow_empty so smtp_user/youtube_api_proxy can be blanked
  to disable them rather than snapping back to the env default.
This commit is contained in:
npeter83 2026-07-12 05:59:03 +02:00
parent 182dddf5ed
commit 4e80e2b39b
8 changed files with 205 additions and 102 deletions

View file

@ -217,11 +217,12 @@ def _reorder_moves(current: list[str], desired: list[str]) -> int:
return moves
def plan_push(db: Session, user: User, pl: Playlist) -> dict:
def plan_push(db: Session, user: User, pl: Playlist, *, live=None) -> dict:
"""Compute (without writing) what pushing this local playlist to YouTube would do:
create vs update, how many inserts/deletes/reorders, the quota estimate, and whether
YouTube has diverged (items present there that local will remove). Costs read units
(1/page) for a linked playlist; nothing for a fresh export."""
(1/page) for a linked playlist; nothing for a fresh export. `live` = a pre-fetched
(current_items, snippet) tuple so the caller can share one read with push_playlist."""
desired = _local_video_ids(db, pl)
if not pl.yt_playlist_id:
ops = 1 + len(desired) # create the playlist + one insert per video
@ -234,9 +235,12 @@ def plan_push(db: Session, user: User, pl: Playlist) -> dict:
"yt_extra": 0,
"units_estimate": ops * WRITE_UNIT_COST,
}
with YouTubeClient(db, user) as yt:
current = list(yt.iter_playlist_items_with_ids(pl.yt_playlist_id))
snippet = yt.get_playlist_snippet(pl.yt_playlist_id)
if live is not None:
current, snippet = live
else:
with YouTubeClient(db, user) as yt:
current = list(yt.iter_playlist_items_with_ids(pl.yt_playlist_id))
snippet = yt.get_playlist_snippet(pl.yt_playlist_id)
rename = bool(snippet) and (snippet.get("title") or "") != pl.name
cur_vids = [it["video_id"] for it in current]
cur_set = set(cur_vids)
@ -259,10 +263,11 @@ def plan_push(db: Session, user: User, pl: Playlist) -> dict:
}
def push_playlist(db: Session, user: User, pl: Playlist) -> dict:
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
and order so YouTube matches local (local wins). Marks the playlist clean on success.
Caller must have checked the write scope and the quota budget."""
Caller must have checked the write scope and the quota budget. `live` = a pre-fetched
(current_items, snippet) tuple (from plan_push's read) to avoid a second live page-through."""
desired = _local_video_ids(db, pl)
inserted = deleted = reordered = created = 0
failures: list[str] = []
@ -291,14 +296,17 @@ def push_playlist(db: Session, user: User, pl: Playlist) -> dict:
"yt_playlist_id": pl.yt_playlist_id,
}
# Existing link: diff against the live YouTube state.
snippet = yt.get_playlist_snippet(pl.yt_playlist_id)
# Existing link: diff against the live YouTube state (reuse plan_push's read if given).
if live is not None:
current, snippet = live
else:
snippet = yt.get_playlist_snippet(pl.yt_playlist_id)
current = list(yt.iter_playlist_items_with_ids(pl.yt_playlist_id))
if snippet is not None and (snippet.get("title") or "") != pl.name:
try:
yt.update_playlist_title(pl.yt_playlist_id, pl.name)
except YouTubeError:
failures.append("title")
current = list(yt.iter_playlist_items_with_ids(pl.yt_playlist_id))
item_id_by_vid = {it["video_id"]: it["item_id"] for it in current}
cur_vids = [it["video_id"] for it in current]
desired_set = set(desired)