Decouple wishlist processing and watchlist scanning — allow concurrent execution

Removed cross-guards that blocked wishlist downloads while watchlist scans
ran (and vice versa). Downloads use bandwidth, scans use API calls —
different resources. The per-call rate limiter handles any API contention.

- Automation engine: each handler now has self-only guard (no cross-block)
- _process_wishlist_automatically: removed watchlist scanning check
- Pipeline Phase 4: removed watchlist scanning check
- Manual watchlist scan endpoint: removed wishlist processing check

Users with large watchlists (6+ hour scans) will now see downloads
starting immediately instead of waiting for the scan to finish.
This commit is contained in:
Broque Thomas 2026-04-05 13:25:44 -07:00
parent 8ba1585646
commit 8344cf2c7a

View file

@ -530,9 +530,14 @@ def _register_automation_handlers():
finally:
_scan_library_automation_id = None
cross_guard = lambda: is_wishlist_actually_processing() or is_watchlist_actually_scanning()
automation_engine.register_action_handler('process_wishlist', _auto_process_wishlist, cross_guard)
automation_engine.register_action_handler('scan_watchlist', _auto_scan_watchlist, cross_guard)
# Self-guards only — prevent duplicate runs of the same operation,
# but allow wishlist processing and watchlist scanning to run concurrently.
# Downloads use bandwidth (Soulseek/Tidal/etc), scans use API calls — different resources.
# The per-call rate limiter handles any API contention during post-processing.
automation_engine.register_action_handler('process_wishlist', _auto_process_wishlist,
guard_fn=lambda: is_wishlist_actually_processing())
automation_engine.register_action_handler('scan_watchlist', _auto_scan_watchlist,
guard_fn=lambda: is_watchlist_actually_scanning())
automation_engine.register_action_handler('scan_library', _auto_scan_library,
lambda: _scan_library_automation_id is not None)
@ -1156,14 +1161,14 @@ def _register_automation_handlers():
log_line='Phase 4: Wishlist', log_type='info')
try:
if not is_wishlist_actually_processing() and not is_watchlist_actually_scanning():
if not is_wishlist_actually_processing():
_process_wishlist_automatically(automation_id=None)
_update_automation_progress(automation_id,
log_line='Wishlist processing triggered', log_type='success')
wishlist_queued = 1
else:
_update_automation_progress(automation_id,
log_line='Wishlist/watchlist already running — skipped', log_type='skip')
log_line='Wishlist already running — skipped', log_type='skip')
except Exception as e:
_update_automation_progress(automation_id,
log_line=f'Wishlist error: {e}', log_type='warning')
@ -22279,7 +22284,6 @@ def _process_wishlist_automatically(automation_id=None):
# Check conditions and set flag
should_skip_already_running = False
should_skip_watchlist_conflict = False
with wishlist_timer_lock:
# Re-check inside lock to handle race conditions
@ -22287,11 +22291,6 @@ def _process_wishlist_automatically(automation_id=None):
print("⚠️ [Auto-Wishlist] Already processing (race condition check), skipping.")
should_skip_already_running = True
# Check if watchlist scan is currently running (using smart detection)
elif is_watchlist_actually_scanning():
print("👁️ Watchlist scan in progress, skipping automatic wishlist processing to avoid conflicts.")
should_skip_watchlist_conflict = True
else:
# Set flag and timestamp
import time
@ -22299,7 +22298,7 @@ def _process_wishlist_automatically(automation_id=None):
wishlist_auto_processing_timestamp = time.time()
print(f"🔒 [Auto-Wishlist] Flag set at timestamp {wishlist_auto_processing_timestamp}")
if should_skip_already_running or should_skip_watchlist_conflict:
if should_skip_already_running:
return
# Use app context for database operations
@ -37778,10 +37777,6 @@ def start_watchlist_scan():
logger.info(f"Starting watchlist scan with {active_provider} provider")
# Check if wishlist auto-processing is currently running (using smart detection)
if is_wishlist_actually_processing():
return jsonify({"success": False, "error": "Wishlist auto-processing is currently running. Please wait for it to complete before starting a watchlist scan."}), 409
# Check if watchlist is already scanning
if is_watchlist_actually_scanning():
return jsonify({"success": False, "error": "Watchlist scan is already in progress."}), 409