Fix batch completion detection for post-processing race condition

This commit is contained in:
Broque Thomas 2026-03-06 20:53:06 -08:00
parent 156c37d907
commit 4fa66b8981

View file

@ -16180,15 +16180,22 @@ def _on_download_completed(batch_id, task_id, success=True):
# Guard against double-calling: track which tasks have already been completed # Guard against double-calling: track which tasks have already been completed
# This prevents active_count from being decremented multiple times for the same task # This prevents active_count from being decremented multiple times for the same task
# (e.g. monitor detects completion AND post-processing calls this again) # (e.g. monitor detects completion AND post-processing calls this again)
# NOTE: On duplicate calls, we skip decrement/tracking but STILL check batch completion.
# This is critical because the first call may see the task in 'post_processing' (not finished),
# and the second call (from post-processing worker) arrives after the task is truly 'completed'.
# Without the fallthrough, batch_complete would never be emitted.
completed_tasks = download_batches[batch_id].setdefault('_completed_task_ids', set()) completed_tasks = download_batches[batch_id].setdefault('_completed_task_ids', set())
if task_id in completed_tasks: _is_duplicate_completion = task_id in completed_tasks
print(f"⚠️ [Batch Manager] Task {task_id} already completed — skipping duplicate _on_download_completed call") if _is_duplicate_completion:
print(f"⚠️ [Batch Manager] Task {task_id} already completed — skipping decrement, still checking batch completion")
# Set terminal status so the monitor loop stops re-processing this task # Set terminal status so the monitor loop stops re-processing this task
if task_id in download_tasks and download_tasks[task_id].get('status') in ('downloading', 'queued'): if task_id in download_tasks and download_tasks[task_id].get('status') in ('downloading', 'queued'):
download_tasks[task_id]['status'] = 'completed' download_tasks[task_id]['status'] = 'completed'
return # Fall through to batch completion check below (don't return)
else:
completed_tasks.add(task_id) completed_tasks.add(task_id)
if not _is_duplicate_completion:
# Track failed/cancelled tasks in batch state (replicating sync.py) # Track failed/cancelled tasks in batch state (replicating sync.py)
if not success and task_id in download_tasks: if not success and task_id in download_tasks:
task = download_tasks[task_id] task = download_tasks[task_id]
@ -16274,7 +16281,7 @@ def _on_download_completed(batch_id, task_id, success=True):
print(f"🔄 [Batch Manager] Task {task_id} completed ({'success' if success else 'failed/cancelled'}). Active workers: {old_active}{new_active}/{download_batches[batch_id]['max_concurrent']}") print(f"🔄 [Batch Manager] Task {task_id} completed ({'success' if success else 'failed/cancelled'}). Active workers: {old_active}{new_active}/{download_batches[batch_id]['max_concurrent']}")
# ENHANCED: Always check batch completion after any task completes # ENHANCED: Always check batch completion after any task completes (including duplicate calls)
# This ensures completion is detected even when mixing normal downloads with cancelled tasks # This ensures completion is detected even when mixing normal downloads with cancelled tasks
print(f"🔍 [Batch Manager] Checking batch completion after task {task_id} completed") print(f"🔍 [Batch Manager] Checking batch completion after task {task_id} completed")
@ -16392,6 +16399,9 @@ def _on_download_completed(batch_id, task_id, success=True):
else: else:
# For manual batches, use standard wishlist processing # For manual batches, use standard wishlist processing
missing_download_executor.submit(_process_failed_tracks_to_wishlist_exact, batch_id) missing_download_executor.submit(_process_failed_tracks_to_wishlist_exact, batch_id)
else:
print(f"✅ [Batch Manager] Batch {batch_id} already marked complete - skipping duplicate processing")
return # Don't start next batch if we're done return # Don't start next batch if we're done
# Start next downloads in queue # Start next downloads in queue
@ -18680,6 +18690,24 @@ def _check_batch_completion_v2(batch_id):
# Mark batch as complete and set completion timestamp for auto-cleanup # Mark batch as complete and set completion timestamp for auto-cleanup
batch['phase'] = 'complete' batch['phase'] = 'complete'
batch['completion_time'] = time.time() # Track when batch completed batch['completion_time'] = time.time() # Track when batch completed
# Add activity for batch completion
playlist_name = batch.get('playlist_name', 'Unknown Playlist')
failed_count = len(batch.get('permanently_failed_tracks', []))
successful_downloads = finished_count - failed_count
add_activity_item("", "Download Batch Complete", f"'{playlist_name}' - {successful_downloads} tracks downloaded", "Now")
# Emit batch_complete event for automation engine
try:
if automation_engine:
automation_engine.emit('batch_complete', {
'playlist_name': playlist_name,
'total_tracks': str(len(queue)),
'completed_tracks': str(successful_downloads),
'failed_tracks': str(failed_count),
})
except Exception:
pass
else: else:
print(f"✅ [Completion Check V2] Batch {batch_id} already marked complete - skipping duplicate processing") print(f"✅ [Completion Check V2] Batch {batch_id} already marked complete - skipping duplicate processing")
return True # Already complete return True # Already complete