Add post-scan phase progress to watchlist automation card

This commit is contained in:
Broque Thomas 2026-03-06 23:15:30 -08:00
parent 5b507d897e
commit cdcaa245d1
2 changed files with 63 additions and 2 deletions

View file

@ -1508,7 +1508,7 @@ class WatchlistScanner:
logger.error(f"Error fetching similar artists for {watchlist_artist.artist_name}: {e}") logger.error(f"Error fetching similar artists for {watchlist_artist.artist_name}: {e}")
return False 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. Populate discovery pool with tracks from top similar artists.
Called after watchlist scan completes. Called after watchlist scan completes.
@ -1529,8 +1529,14 @@ class WatchlistScanner:
if skip_pool_population: if skip_pool_population:
logger.info("Discovery pool was populated recently (< 24 hours ago). Skipping 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...") 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 # 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) 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) self.curate_discovery_playlists(profile_id=profile_id)
return return
@ -1556,8 +1562,14 @@ class WatchlistScanner:
if not similar_artists: if not similar_artists:
logger.info("No similar artists found to populate discovery pool from 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...") 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 # 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) 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) self.curate_discovery_playlists(profile_id=profile_id)
return return
@ -1568,6 +1580,8 @@ class WatchlistScanner:
for artist_idx, similar_artist in enumerate(similar_artists, 1): for artist_idx, similar_artist in enumerate(similar_artists, 1):
try: try:
logger.info(f"[{artist_idx}/{len(similar_artists)}] Processing {similar_artist.similar_artist_name} (occurrence: {similar_artist.occurrence_count})") 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 # Build list of sources to process for this artist
# iTunes is ALWAYS processed (baseline), Spotify is added if authenticated # iTunes is ALWAYS processed (baseline), Spotify is added if authenticated
@ -1766,6 +1780,8 @@ class WatchlistScanner:
continue continue
logger.info(f"Discovery pool from similar artists complete: {total_tracks_added} tracks added") 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 # 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 # No need to re-fetch them here to avoid duplicate API calls
@ -1927,10 +1943,14 @@ class WatchlistScanner:
# Cache recent albums for discovery page # Cache recent albums for discovery page
logger.info("Caching 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) self.cache_discovery_recent_albums(profile_id=profile_id)
# Curate playlists for consistent daily experience # Curate playlists for consistent daily experience
logger.info("Curating discovery playlists...") logger.info("Curating discovery playlists...")
if progress_callback:
progress_callback('phase', 'Curating playlists...')
self.curate_discovery_playlists(profile_id=profile_id) self.curate_discovery_playlists(profile_id=profile_id)
except Exception as e: except Exception as e:

View file

@ -24850,35 +24850,66 @@ def _process_watchlist_scan_automatically(automation_id=None):
# Populate discovery pool from similar artists (per-profile) # Populate discovery pool from similar artists (per-profile)
print("🎵 Starting discovery pool population...") print("🎵 Starting discovery pool population...")
watchlist_scan_state['current_phase'] = 'populating_discovery_pool' 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: 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: 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") print("✅ Discovery pool population complete")
except Exception as discovery_error: except Exception as discovery_error:
print(f"⚠️ Error populating discovery pool: {discovery_error}") print(f"⚠️ Error populating discovery pool: {discovery_error}")
import traceback import traceback
traceback.print_exc() traceback.print_exc()
_update_automation_progress(automation_id,
log_line=f'Discovery pool error: {discovery_error}', log_type='error')
# Update ListenBrainz playlists cache # Update ListenBrainz playlists cache
print("🧠 Starting ListenBrainz playlists update...") print("🧠 Starting ListenBrainz playlists update...")
watchlist_scan_state['current_phase'] = 'updating_listenbrainz' 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: try:
from core.listenbrainz_manager import ListenBrainzManager from core.listenbrainz_manager import ListenBrainzManager
lb_manager = ListenBrainzManager(str(get_database().database_path)) lb_manager = ListenBrainzManager(str(get_database().database_path))
lb_result = lb_manager.update_all_playlists() lb_result = lb_manager.update_all_playlists()
if lb_result.get('success'): if lb_result.get('success'):
summary = lb_result.get('summary', {}) summary = lb_result.get('summary', {})
updated = summary.get('updated', 0)
total_lb = summary.get('total', 0)
print(f"✅ ListenBrainz update complete: {summary}") print(f"✅ ListenBrainz update complete: {summary}")
_update_automation_progress(automation_id,
log_line=f'ListenBrainz: {updated}/{total_lb} playlists updated', log_type='success')
else: else:
print(f"⚠️ ListenBrainz update had issues: {lb_result.get('error', 'Unknown error')}") 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: except Exception as lb_error:
print(f"⚠️ Error updating ListenBrainz: {lb_error}") print(f"⚠️ Error updating ListenBrainz: {lb_error}")
import traceback import traceback
traceback.print_exc() traceback.print_exc()
_update_automation_progress(automation_id,
log_line=f'ListenBrainz error: {lb_error}', log_type='error')
# Update current seasonal playlist (weekly refresh) # Update current seasonal playlist (weekly refresh)
print("🎃 Starting seasonal content update...") print("🎃 Starting seasonal content update...")
watchlist_scan_state['current_phase'] = 'updating_seasonal' 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: try:
from core.seasonal_discovery import get_seasonal_discovery_service from core.seasonal_discovery import get_seasonal_discovery_service
seasonal_service = get_seasonal_discovery_service(spotify_client, database) 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 current_season:
if seasonal_service.should_populate_seasonal_content(current_season, days_threshold=7): if seasonal_service.should_populate_seasonal_content(current_season, days_threshold=7):
print(f"🎃 Updating {current_season} seasonal content...") 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.populate_seasonal_content(current_season)
seasonal_service.curate_seasonal_playlist(current_season) seasonal_service.curate_seasonal_playlist(current_season)
print(f"{current_season.capitalize()} seasonal content updated") 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: else:
print(f"⏭️ {current_season.capitalize()} seasonal content recently updated, skipping") 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: else:
print(" No active season at this time") 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: except Exception as seasonal_error:
print(f"⚠️ Error updating seasonal content: {seasonal_error}") print(f"⚠️ Error updating seasonal content: {seasonal_error}")
import traceback import traceback
traceback.print_exc() traceback.print_exc()
_update_automation_progress(automation_id,
log_line=f'Seasonal error: {seasonal_error}', log_type='error')
# Add activity for watchlist scan completion # Add activity for watchlist scan completion
if total_added_to_wishlist > 0: if total_added_to_wishlist > 0: