diff --git a/web_server.py b/web_server.py index 30a67259..dadee398 100644 --- a/web_server.py +++ b/web_server.py @@ -22253,10 +22253,51 @@ def update_tidal_playlist_phase(playlist_id): _playlist_discovery_cancelled = set() # Set of automation_ids that have been cancelled +def _pause_enrichment_workers(label='discovery'): + """Pause enrichment workers during discovery to reduce API contention. + Returns tuple of (spotify_was_running, itunes_was_running) for resume.""" + s_was = False + i_was = False + try: + if spotify_enrichment_worker and not spotify_enrichment_worker.paused: + spotify_enrichment_worker.pause() + s_was = True + print(f"⏸️ Paused Spotify enrichment worker during {label}") + except Exception: + pass + try: + if itunes_enrichment_worker and not itunes_enrichment_worker.paused: + itunes_enrichment_worker.pause() + i_was = True + print(f"⏸️ Paused iTunes enrichment worker during {label}") + except Exception: + pass + return s_was, i_was + + +def _resume_enrichment_workers(was_running, label='discovery'): + """Resume enrichment workers that were paused by _pause_enrichment_workers.""" + s_was, i_was = was_running + try: + if s_was and spotify_enrichment_worker: + spotify_enrichment_worker.resume() + print(f"▶️ Resumed Spotify enrichment worker after {label}") + except Exception: + pass + try: + if i_was and itunes_enrichment_worker: + itunes_enrichment_worker.resume() + print(f"▶️ Resumed iTunes enrichment worker after {label}") + except Exception: + pass + + def _run_playlist_discovery_worker(playlists, automation_id=None): """Background worker that discovers Spotify/iTunes metadata for undiscovered mirrored playlist tracks. Stores results in extra_data for use by sync.""" + _ew_state = (False, False) try: + _ew_state = _pause_enrichment_workers('mirrored playlist discovery') use_spotify = spotify_client and spotify_client.is_spotify_authenticated() discovery_source = 'spotify' if use_spotify else 'itunes' @@ -22512,6 +22553,8 @@ def _run_playlist_discovery_worker(playlists, automation_id=None): _update_automation_progress(automation_id, status='error', progress=100, phase='Error', log_line=f'Error: {str(e)}', log_type='error') + finally: + _resume_enrichment_workers(_ew_state, 'mirrored playlist discovery') def _extract_artist_name(artist): @@ -22660,7 +22703,9 @@ def _discovery_score_candidates(source_title, source_artist, source_duration_ms, def _run_tidal_discovery_worker(playlist_id): """Background worker for Tidal discovery process (Spotify preferred, iTunes fallback)""" + _ew_state = (False, False) try: + _ew_state = _pause_enrichment_workers('Tidal discovery') state = tidal_discovery_states[playlist_id] playlist = state['playlist'] @@ -22824,6 +22869,8 @@ def _run_tidal_discovery_worker(playlist_id): print(f"❌ Error in Tidal discovery worker: {e}") state['phase'] = 'error' state['status'] = f'error: {str(e)}' + finally: + _resume_enrichment_workers(_ew_state, 'Tidal discovery') def _search_spotify_for_tidal_track(tidal_track, use_spotify=True, itunes_client=None): @@ -23385,7 +23432,9 @@ def update_youtube_discovery_match(): def _run_youtube_discovery_worker(url_hash): """Background worker for YouTube music discovery process (Spotify preferred, iTunes fallback)""" + _ew_state = (False, False) try: + _ew_state = _pause_enrichment_workers('YouTube discovery') state = youtube_playlist_states[url_hash] playlist = state['playlist'] tracks = playlist['tracks'] @@ -23689,10 +23738,14 @@ def _run_youtube_discovery_worker(url_hash): print(f"❌ Error in YouTube discovery worker: {e}") state['status'] = 'error' state['phase'] = 'fresh' + finally: + _resume_enrichment_workers(_ew_state, 'YouTube discovery') def _run_listenbrainz_discovery_worker(playlist_mbid): """Background worker for ListenBrainz music discovery process (Spotify preferred, iTunes fallback)""" + _ew_state = (False, False) try: + _ew_state = _pause_enrichment_workers('ListenBrainz discovery') state = listenbrainz_playlist_states[playlist_mbid] playlist = state['playlist'] tracks = playlist['tracks'] @@ -23956,6 +24009,8 @@ def _run_listenbrainz_discovery_worker(playlist_mbid): print(f"❌ Error in ListenBrainz discovery worker: {e}") state['status'] = 'error' state['phase'] = 'fresh' + finally: + _resume_enrichment_workers(_ew_state, 'ListenBrainz discovery') def _calculate_similarity(str1, str2): """Calculate string similarity using simple character overlap""" @@ -25855,8 +25910,7 @@ def start_watchlist_scan(): # Start the scan in a background thread scan_profile_id = get_current_profile_id() def run_scan(): - _enrichment_was_running = False - _itunes_enrichment_was_running = False + _ew_state = (False, False) try: global watchlist_scan_state, watchlist_auto_scanning, watchlist_auto_scanning_timestamp from core.watchlist_scanner import WatchlistScanner @@ -25925,14 +25979,7 @@ def start_watchlist_scan(): scan_results = [] # Pause enrichment workers during scan to reduce API contention - if spotify_enrichment_worker and not spotify_enrichment_worker.paused: - spotify_enrichment_worker.pause() - _enrichment_was_running = True - print("⏸️ Paused Spotify enrichment worker during watchlist scan") - if itunes_enrichment_worker and not itunes_enrichment_worker.paused: - itunes_enrichment_worker.pause() - _itunes_enrichment_was_running = True - print("⏸️ Paused iTunes enrichment worker during watchlist scan") + _ew_state = _pause_enrichment_workers('watchlist scan') # Dynamic delay calculation based on scan scope lookback_period = scanner._get_lookback_period_setting() @@ -26225,12 +26272,7 @@ def start_watchlist_scan(): finally: # Resume enrichment workers if we paused them - if _enrichment_was_running and spotify_enrichment_worker: - spotify_enrichment_worker.resume() - print("▶️ Resumed Spotify enrichment worker after watchlist scan") - if _itunes_enrichment_was_running and itunes_enrichment_worker: - itunes_enrichment_worker.resume() - print("▶️ Resumed iTunes enrichment worker after watchlist scan") + _resume_enrichment_workers(_ew_state, 'watchlist scan') # Always reset flag when scan completes (success or error) with watchlist_timer_lock: @@ -26699,8 +26741,7 @@ def _process_watchlist_scan_automatically(automation_id=None): print("🤖 [Auto-Watchlist] Timer triggered - starting automatic watchlist scan...") - _enrichment_was_running = False - _itunes_enrichment_was_running = False + _ew_state = (False, False) try: # CRITICAL FIX: Use smart stuck detection BEFORE acquiring lock @@ -26785,14 +26826,7 @@ def _process_watchlist_scan_automatically(automation_id=None): scan_results = [] # Pause enrichment workers during scan to reduce API contention - if spotify_enrichment_worker and not spotify_enrichment_worker.paused: - spotify_enrichment_worker.pause() - _enrichment_was_running = True - print("⏸️ [Auto-Watchlist] Paused Spotify enrichment worker during scan") - if itunes_enrichment_worker and not itunes_enrichment_worker.paused: - itunes_enrichment_worker.pause() - _itunes_enrichment_was_running = True - print("⏸️ [Auto-Watchlist] Paused iTunes enrichment worker during scan") + _ew_state = _pause_enrichment_workers('auto-watchlist scan') # Dynamic delay calculation based on scan scope lookback_period = scanner._get_lookback_period_setting() @@ -27155,12 +27189,7 @@ def _process_watchlist_scan_automatically(automation_id=None): finally: # Resume enrichment workers if we paused them - if _enrichment_was_running and spotify_enrichment_worker: - spotify_enrichment_worker.resume() - print("▶️ [Auto-Watchlist] Resumed Spotify enrichment worker after scan") - if _itunes_enrichment_was_running and itunes_enrichment_worker: - itunes_enrichment_worker.resume() - print("▶️ [Auto-Watchlist] Resumed iTunes enrichment worker after scan") + _resume_enrichment_workers(_ew_state, 'auto-watchlist scan') # Always reset flag with watchlist_timer_lock: @@ -30815,7 +30844,9 @@ def clean_beatport_text(text): def _run_beatport_discovery_worker(url_hash): """Background worker for Beatport discovery process (Spotify preferred, iTunes fallback)""" + _ew_state = (False, False) try: + _ew_state = _pause_enrichment_workers('Beatport discovery') state = beatport_chart_states[url_hash] chart = state['chart'] tracks = chart['tracks'] @@ -31111,6 +31142,8 @@ def _run_beatport_discovery_worker(url_hash): if url_hash in beatport_chart_states: beatport_chart_states[url_hash]['status'] = 'error' beatport_chart_states[url_hash]['phase'] = 'fresh' + finally: + _resume_enrichment_workers(_ew_state, 'Beatport discovery') @app.route('/api/beatport/sync/start/', methods=['POST']) def start_beatport_sync(url_hash):