From cdcaa245d1a4797eee99e2c369205daf77a999b8 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Fri, 6 Mar 2026 23:15:30 -0800 Subject: [PATCH] Add post-scan phase progress to watchlist automation card --- core/watchlist_scanner.py | 22 +++++++++++++++++++- web_server.py | 43 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/core/watchlist_scanner.py b/core/watchlist_scanner.py index e5b9b53d..4a1e119d 100644 --- a/core/watchlist_scanner.py +++ b/core/watchlist_scanner.py @@ -1508,7 +1508,7 @@ class WatchlistScanner: logger.error(f"Error fetching similar artists for {watchlist_artist.artist_name}: {e}") return False - def populate_discovery_pool(self, top_artists_limit: int = 50, albums_per_artist: int = 10, profile_id: int = 1): + def populate_discovery_pool(self, top_artists_limit: int = 50, albums_per_artist: int = 10, profile_id: int = 1, progress_callback=None): """ Populate discovery pool with tracks from top similar artists. Called after watchlist scan completes. @@ -1529,8 +1529,14 @@ class WatchlistScanner: if skip_pool_population: logger.info("Discovery pool was populated recently (< 24 hours ago). Skipping pool population.") logger.info("But still refreshing recent albums cache and curated playlists...") + if progress_callback: + progress_callback('skip', 'Discovery pool recently updated, skipping') # Still run these even when skipping main pool population + if progress_callback: + progress_callback('phase', 'Caching recent albums...') self.cache_discovery_recent_albums(profile_id=profile_id) + if progress_callback: + progress_callback('phase', 'Curating playlists...') self.curate_discovery_playlists(profile_id=profile_id) return @@ -1556,8 +1562,14 @@ class WatchlistScanner: if not similar_artists: logger.info("No similar artists found to populate discovery pool from similar artists") logger.info("But still caching recent albums from watchlist artists and curating playlists...") + if progress_callback: + progress_callback('skip', 'No similar artists found') # Still run these even without similar artists - they use watchlist artists + if progress_callback: + progress_callback('phase', 'Caching recent albums...') self.cache_discovery_recent_albums(profile_id=profile_id) + if progress_callback: + progress_callback('phase', 'Curating playlists...') self.curate_discovery_playlists(profile_id=profile_id) return @@ -1568,6 +1580,8 @@ class WatchlistScanner: for artist_idx, similar_artist in enumerate(similar_artists, 1): try: logger.info(f"[{artist_idx}/{len(similar_artists)}] Processing {similar_artist.similar_artist_name} (occurrence: {similar_artist.occurrence_count})") + if progress_callback: + progress_callback('artist', f'{similar_artist.similar_artist_name} ({artist_idx}/{len(similar_artists)})') # Build list of sources to process for this artist # iTunes is ALWAYS processed (baseline), Spotify is added if authenticated @@ -1766,6 +1780,8 @@ class WatchlistScanner: continue logger.info(f"Discovery pool from similar artists complete: {total_tracks_added} tracks added") + if progress_callback: + progress_callback('success', f'Discovery pool: {total_tracks_added} tracks from {len(similar_artists)} artists') # Note: Watchlist artist albums are already in discovery pool from the watchlist scan itself # No need to re-fetch them here to avoid duplicate API calls @@ -1927,10 +1943,14 @@ class WatchlistScanner: # Cache recent albums for discovery page logger.info("Caching recent albums for discovery page...") + if progress_callback: + progress_callback('phase', 'Caching recent albums...') self.cache_discovery_recent_albums(profile_id=profile_id) # Curate playlists for consistent daily experience logger.info("Curating discovery playlists...") + if progress_callback: + progress_callback('phase', 'Curating playlists...') self.curate_discovery_playlists(profile_id=profile_id) except Exception as e: diff --git a/web_server.py b/web_server.py index 16ac5201..56b16401 100644 --- a/web_server.py +++ b/web_server.py @@ -24850,35 +24850,66 @@ def _process_watchlist_scan_automatically(automation_id=None): # Populate discovery pool from similar artists (per-profile) print("đŸŽĩ Starting discovery pool population...") watchlist_scan_state['current_phase'] = 'populating_discovery_pool' + _update_automation_progress(automation_id, progress=96, phase='Populating discovery pool', + log_line='Building discovery pool from similar artists...', log_type='info') try: + def _discovery_progress(event_type, message): + if event_type == 'artist': + _update_automation_progress(automation_id, phase=f'Discovery pool: {message}', + log_line=message, log_type='info', + current_item=message) + elif event_type == 'phase': + _update_automation_progress(automation_id, phase=message, + log_line=message, log_type='info') + elif event_type == 'success': + _update_automation_progress(automation_id, + log_line=message, log_type='success') + elif event_type == 'skip': + _update_automation_progress(automation_id, + log_line=message, log_type='info') + for p in all_profiles: - scanner.populate_discovery_pool(profile_id=p['id']) + scanner.populate_discovery_pool(profile_id=p['id'], progress_callback=_discovery_progress) print("✅ Discovery pool population complete") except Exception as discovery_error: print(f"âš ī¸ Error populating discovery pool: {discovery_error}") import traceback traceback.print_exc() + _update_automation_progress(automation_id, + log_line=f'Discovery pool error: {discovery_error}', log_type='error') # Update ListenBrainz playlists cache print("🧠 Starting ListenBrainz playlists update...") watchlist_scan_state['current_phase'] = 'updating_listenbrainz' + _update_automation_progress(automation_id, progress=97, phase='Updating ListenBrainz', + log_line='Fetching ListenBrainz playlists...', log_type='info') try: from core.listenbrainz_manager import ListenBrainzManager lb_manager = ListenBrainzManager(str(get_database().database_path)) lb_result = lb_manager.update_all_playlists() if lb_result.get('success'): summary = lb_result.get('summary', {}) + updated = summary.get('updated', 0) + total_lb = summary.get('total', 0) print(f"✅ ListenBrainz update complete: {summary}") + _update_automation_progress(automation_id, + log_line=f'ListenBrainz: {updated}/{total_lb} playlists updated', log_type='success') else: print(f"âš ī¸ ListenBrainz update had issues: {lb_result.get('error', 'Unknown error')}") + _update_automation_progress(automation_id, + log_line=f'ListenBrainz: {lb_result.get("error", "Unknown error")}', log_type='error') except Exception as lb_error: print(f"âš ī¸ Error updating ListenBrainz: {lb_error}") import traceback traceback.print_exc() + _update_automation_progress(automation_id, + log_line=f'ListenBrainz error: {lb_error}', log_type='error') # Update current seasonal playlist (weekly refresh) print("🎃 Starting seasonal content update...") watchlist_scan_state['current_phase'] = 'updating_seasonal' + _update_automation_progress(automation_id, progress=98, phase='Updating seasonal content', + log_line='Checking seasonal playlists...', log_type='info') try: from core.seasonal_discovery import get_seasonal_discovery_service seasonal_service = get_seasonal_discovery_service(spotify_client, database) @@ -24888,17 +24919,27 @@ def _process_watchlist_scan_automatically(automation_id=None): if current_season: if seasonal_service.should_populate_seasonal_content(current_season, days_threshold=7): print(f"🎃 Updating {current_season} seasonal content...") + _update_automation_progress(automation_id, + log_line=f'Updating {current_season} seasonal content...', log_type='info') seasonal_service.populate_seasonal_content(current_season) seasonal_service.curate_seasonal_playlist(current_season) print(f"✅ {current_season.capitalize()} seasonal content updated") + _update_automation_progress(automation_id, + log_line=f'{current_season.capitalize()} seasonal content updated', log_type='success') else: print(f"â­ī¸ {current_season.capitalize()} seasonal content recently updated, skipping") + _update_automation_progress(automation_id, + log_line=f'{current_season.capitalize()} seasonal content up to date', log_type='info') else: print("â„šī¸ No active season at this time") + _update_automation_progress(automation_id, + log_line='No active season', log_type='info') except Exception as seasonal_error: print(f"âš ī¸ Error updating seasonal content: {seasonal_error}") import traceback traceback.print_exc() + _update_automation_progress(automation_id, + log_line=f'Seasonal error: {seasonal_error}', log_type='error') # Add activity for watchlist scan completion if total_added_to_wishlist > 0: