Downloads: version-mismatch fallback also fires when retry search finds no candidates
The existing fallback (pipeline.py:1084) only ran inside post_process_matched_download_with_verification — i.e. when a file *was* downloaded and AcoustID retries were fully exhausted. If the retry *search* itself found zero valid candidates (source returned nothing, or all failed HiFi validation), the task was marked not_found and the fallback was never reached, even though the quarantine already held N version-mismatch entries. Fix: add try_version_mismatch_fallback to TaskWorkerDeps; in the "no valid candidates" path of task_worker, invoke it before marking not_found when is_quarantine_retry. Wired in _build_task_worker_deps via a new helper (_try_version_mismatch_fallback_for_worker) that calls try_accept_version_mismatch_fallback directly with the track's title and artist and a reprocess lambda over _post_process_matched_download_with_verification. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
69a37e96b7
commit
9b1445b3b5
2 changed files with 58 additions and 0 deletions
|
|
@ -151,6 +151,7 @@ class TaskWorkerDeps:
|
|||
attempt_download_with_candidates: Callable # (task_id, candidates, track, batch_id) -> bool
|
||||
on_download_completed: Callable # (batch_id, task_id, success) -> None
|
||||
recover_worker_slot: Callable # (batch_id, task_id) -> None
|
||||
try_version_mismatch_fallback: Optional[Callable] = None # (title, artist, task_id, batch_id) -> bool
|
||||
|
||||
|
||||
def download_track_worker(task_id: str, batch_id: Optional[str], deps: TaskWorkerDeps) -> None:
|
||||
|
|
@ -589,6 +590,15 @@ def download_track_worker(task_id: str, batch_id: Optional[str], deps: TaskWorke
|
|||
|
||||
# If we get here, all search queries and hybrid fallbacks failed
|
||||
logger.warning(f"[Modal Worker] No valid candidates found for '{track.name}' after trying all {len(search_queries)} queries.")
|
||||
|
||||
# Last-resort: quarantine retry with no new candidates — the retry search
|
||||
# exhausted all sources. If the setting is enabled, accept the best
|
||||
# already-quarantined candidate rather than leaving the track missing.
|
||||
if is_quarantine_retry and deps.try_version_mismatch_fallback:
|
||||
_fallback_artist = track.artists[0] if track.artists else ''
|
||||
if deps.try_version_mismatch_fallback(track.name, _fallback_artist, task_id, batch_id):
|
||||
return # fallback re-dispatched; batch completion handled by reprocess thread
|
||||
|
||||
with tasks_lock:
|
||||
if task_id in download_tasks:
|
||||
download_tasks[task_id]['status'] = 'not_found'
|
||||
|
|
|
|||
|
|
@ -16896,6 +16896,53 @@ def _run_post_processing_worker(task_id, batch_id):
|
|||
from core.downloads import task_worker as _downloads_task_worker
|
||||
|
||||
|
||||
def _try_version_mismatch_fallback_for_worker(expected_title, expected_artist, task_id, batch_id):
|
||||
"""Called by the download worker when a quarantine-retry search finds no
|
||||
candidates. Delegates to version_mismatch_fallback so the best already-
|
||||
quarantined version-mismatch candidate is accepted rather than giving up."""
|
||||
import threading
|
||||
import time
|
||||
from core.imports.version_mismatch_fallback import try_accept_version_mismatch_fallback
|
||||
from core.imports.quarantine import approve_quarantine_entry, list_quarantine_entries
|
||||
from config.settings import config_manager
|
||||
from core.imports.paths import docker_resolve_path
|
||||
import os
|
||||
try:
|
||||
download_path = docker_resolve_path(
|
||||
config_manager.get('soulseek.download_path', './downloads')
|
||||
)
|
||||
quarantine_dir = os.path.join(download_path, 'ss_quarantine')
|
||||
restore_dir = os.path.join(download_path, 'Transfer')
|
||||
|
||||
def _reprocess(restored_path, ctx, tid, bid):
|
||||
new_key = f"vmfallback_{tid}_{int(time.time())}"
|
||||
threading.Thread(
|
||||
target=lambda: _post_process_matched_download_with_verification(
|
||||
new_key, ctx, restored_path, tid, bid
|
||||
),
|
||||
daemon=True,
|
||||
).start()
|
||||
|
||||
return try_accept_version_mismatch_fallback(
|
||||
quarantine_dir=quarantine_dir,
|
||||
restore_dir=restore_dir,
|
||||
expected_title=expected_title,
|
||||
expected_artist=expected_artist,
|
||||
task_id=task_id,
|
||||
batch_id=batch_id,
|
||||
config_get=config_manager.get,
|
||||
list_entries=list_quarantine_entries,
|
||||
approve_entry=approve_quarantine_entry,
|
||||
reprocess=_reprocess,
|
||||
)
|
||||
except Exception as exc:
|
||||
import logging
|
||||
logging.getLogger(__name__).debug(
|
||||
"[Version-Mismatch Fallback] worker-path skipped due to error: %s", exc
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
def _build_task_worker_deps():
|
||||
"""Build TaskWorkerDeps bundle from web_server.py globals on each call."""
|
||||
return _downloads_task_worker.TaskWorkerDeps(
|
||||
|
|
@ -16909,6 +16956,7 @@ def _build_task_worker_deps():
|
|||
attempt_download_with_candidates=_attempt_download_with_candidates,
|
||||
on_download_completed=lambda b, t, success: _on_download_completed(b, t, success=success),
|
||||
recover_worker_slot=_recover_worker_slot,
|
||||
try_version_mismatch_fallback=_try_version_mismatch_fallback_for_worker,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue