soulsync/core/video
BoulderBadgeDad 3af3a1cd24 video downloads Phase C: auto-retry + alternate-query requery
When a grabbed release fails (transfer error / peer-cancel / never lands), the engine
now retries instead of giving up — the music-style depth:
- Grab stores the OTHER accepted results as a retry pool + the search context (schema
  v16: candidates / search_ctx / tried_queries / tried_files / attempts).
- core/video/retry.py (pure, tested): plan_retry() → try the next-best candidate; when
  the pool is dry, next_query() generates an ALTERNATE query (movie: drop the year; TV:
  numbering variants) to re-search; budget MAX_ATTEMPTS=6. merge_candidates dedupes
  against already-tried releases.
- Monitor: on failure, _fail_or_retry hops to the next candidate inline; if none, flips
  the row to a new 'searching' state and a background requery thread re-searches the
  alternate query, evaluates against the profile, and starts the best fresh hit — or
  marks failed once truly exhausted. 'searching' rows are owned by their thread.
- Page: 'Searching' status (Trying another release…) + a 'Nx' attempt badge.
16 tests (retry engine + candidate-retry transition); ruff clean on touched files.
2026-06-19 17:12:44 -07:00
..
enrichment video backfill: log the title (was 'None') — read item['title'] not ['name'] 2026-06-19 15:12:02 -07:00
__init__.py video side: library scanner (server = source of truth) + scan API 2026-06-13 23:13:50 -07:00
download_config.py video downloads: reword download_config docstring (a wrapped line started with 'from the music side' — tripped the core/video no-music-import guard, a test false positive) 2026-06-19 00:25:57 -07:00
download_monitor.py video downloads Phase C: auto-retry + alternate-query requery 2026-06-19 17:12:44 -07:00
download_pipeline.py video downloads pipeline: data + pure logic foundation 2026-06-19 14:50:27 -07:00
mock_search.py video search: each source returns its own results (was identical) 2026-06-19 13:56:04 -07:00
quality_eval.py video search: evaluate/rank pipeline + mock indexer + /downloads/search 2026-06-19 13:39:09 -07:00
quality_profile.py video quality profile: loose resolution cutoff + movie/episode size split 2026-06-19 10:40:53 -07:00
release_parse.py video search: scene/p2p release-title parser (Sonarr-style) 2026-06-19 13:28:37 -07:00
retry.py video downloads Phase C: auto-retry + alternate-query requery 2026-06-19 17:12:44 -07:00
scanner.py video: pause enrichment workers during library scans, resume after 2026-06-14 13:55:59 -07:00
slskd_download.py video downloads Phase A: cancelled state, cancel/retry, monitor robustness 2026-06-19 15:31:51 -07:00
slskd_search.py video search: poll the FULL slskd timeout (~60s) + explain audio-only results 2026-06-19 16:55:53 -07:00
sources.py Video side: own server connection (Plex/Jellyfin), music-style picker, isolated 2026-06-15 16:42:22 -07:00
youtube.py Playlist/channel extraction: stop pinning a static user_agent for yt-dlp 2026-06-17 22:21:12 -07:00
youtube_enrichment.py Remember each channel: cache the video catalog + metadata (instant re-open) 2026-06-17 18:08:04 -07:00
youtube_quality.py video downloads: separate YouTube quality profile (yt-dlp) 2026-06-19 10:45:38 -07:00