diff --git a/core/watchlist_scanner.py b/core/watchlist_scanner.py index e868fe9a..697dc443 100644 --- a/core/watchlist_scanner.py +++ b/core/watchlist_scanner.py @@ -240,10 +240,32 @@ class WatchlistScanner: """ try: logger.info(f"Scanning artist: {watchlist_artist.artist_name}") - + + # Update artist image from Spotify (cached for performance) + try: + artist_data = self.spotify_client.get_artist(watchlist_artist.spotify_artist_id) + if artist_data and 'images' in artist_data and artist_data['images']: + # Get medium-sized image (usually the second one, or first if only one) + image_url = None + if len(artist_data['images']) > 1: + image_url = artist_data['images'][1]['url'] + else: + image_url = artist_data['images'][0]['url'] + + # Update in database + if image_url: + self.database.update_watchlist_artist_image(watchlist_artist.spotify_artist_id, image_url) + logger.info(f"Updated artist image for {watchlist_artist.artist_name}") + else: + logger.warning(f"No image URL found for {watchlist_artist.artist_name}") + else: + logger.warning(f"No images in Spotify data for {watchlist_artist.artist_name}") + except Exception as img_error: + logger.warning(f"Could not update artist image for {watchlist_artist.artist_name}: {img_error}") + # Get artist discography from Spotify albums = self.get_artist_discography(watchlist_artist.spotify_artist_id, watchlist_artist.last_scan_timestamp) - + if albums is None: return ScanResult( artist_name=watchlist_artist.artist_name, diff --git a/database/music_database.py b/database/music_database.py index 3c35fb4d..0267f3be 100644 --- a/database/music_database.py +++ b/database/music_database.py @@ -85,6 +85,7 @@ class WatchlistArtist: last_scan_timestamp: Optional[datetime] = None created_at: Optional[datetime] = None updated_at: Optional[datetime] = None + image_url: Optional[str] = None @dataclass class SimilarArtist: @@ -258,6 +259,9 @@ class MusicDatabase: # Add discovery feature tables (migration) self._add_discovery_tables(cursor) + # Add image_url column to watchlist_artists (migration) + self._add_watchlist_artist_image_column(cursor) + conn.commit() logger.info("Database initialized successfully") @@ -573,6 +577,20 @@ class MusicDatabase: logger.error(f"Error creating discovery tables: {e}") # Don't raise - this is a migration, database can still function + def _add_watchlist_artist_image_column(self, cursor): + """Add image_url column to watchlist_artists table""" + try: + cursor.execute("PRAGMA table_info(watchlist_artists)") + columns = [column[1] for column in cursor.fetchall()] + + if 'image_url' not in columns: + cursor.execute("ALTER TABLE watchlist_artists ADD COLUMN image_url TEXT") + logger.info("Added image_url column to watchlist_artists table") + + except Exception as e: + logger.error(f"Error adding image_url column to watchlist_artists: {e}") + # Don't raise - this is a migration, database can still function + def close(self): """Close database connection (no-op since we create connections per operation)""" # Each operation creates and closes its own connection, so nothing to do here @@ -2544,9 +2562,9 @@ class MusicDatabase: cursor = conn.cursor() cursor.execute(""" - SELECT id, spotify_artist_id, artist_name, date_added, - last_scan_timestamp, created_at, updated_at - FROM watchlist_artists + SELECT id, spotify_artist_id, artist_name, date_added, + last_scan_timestamp, created_at, updated_at, image_url + FROM watchlist_artists ORDER BY date_added DESC """) @@ -2554,6 +2572,12 @@ class MusicDatabase: watchlist_artists = [] for row in rows: + # Try to get image_url, fallback to None if column doesn't exist yet (migration) + try: + image_url = row['image_url'] + except (KeyError, IndexError): + image_url = None + watchlist_artists.append(WatchlistArtist( id=row['id'], spotify_artist_id=row['spotify_artist_id'], @@ -2561,7 +2585,8 @@ class MusicDatabase: date_added=datetime.fromisoformat(row['date_added']), last_scan_timestamp=datetime.fromisoformat(row['last_scan_timestamp']) if row['last_scan_timestamp'] else None, created_at=datetime.fromisoformat(row['created_at']) if row['created_at'] else None, - updated_at=datetime.fromisoformat(row['updated_at']) if row['updated_at'] else None + updated_at=datetime.fromisoformat(row['updated_at']) if row['updated_at'] else None, + image_url=image_url )) return watchlist_artists @@ -2585,6 +2610,25 @@ class MusicDatabase: logger.error(f"Error getting watchlist count: {e}") return 0 + def update_watchlist_artist_image(self, spotify_artist_id: str, image_url: str) -> bool: + """Update the image URL for a watchlist artist""" + try: + with self._get_connection() as conn: + cursor = conn.cursor() + + cursor.execute(""" + UPDATE watchlist_artists + SET image_url = ?, updated_at = CURRENT_TIMESTAMP + WHERE spotify_artist_id = ? + """, (image_url, spotify_artist_id)) + + conn.commit() + return cursor.rowcount > 0 + + except Exception as e: + logger.error(f"Error updating watchlist artist image: {e}") + return False + # === Discovery Feature Methods === def add_or_update_similar_artist(self, source_artist_id: str, similar_artist_spotify_id: str, diff --git a/web_server.py b/web_server.py index ab94454e..a33e0597 100644 --- a/web_server.py +++ b/web_server.py @@ -14358,12 +14358,12 @@ def get_watchlist_count(): @app.route('/api/watchlist/artists', methods=['GET']) def get_watchlist_artists(): - """Get all artists in the watchlist""" + """Get all artists in the watchlist with cached images""" try: database = get_database() watchlist_artists = database.get_watchlist_artists() - - # Convert to JSON serializable format + + # Convert to JSON serializable format (images are cached from watchlist scans) artists_data = [] for artist in watchlist_artists: artists_data.append({ @@ -14373,9 +14373,10 @@ def get_watchlist_artists(): "date_added": artist.date_added.isoformat() if artist.date_added else None, "last_scan_timestamp": artist.last_scan_timestamp.isoformat() if artist.last_scan_timestamp else None, "created_at": artist.created_at.isoformat() if artist.created_at else None, - "updated_at": artist.updated_at.isoformat() if artist.updated_at else None + "updated_at": artist.updated_at.isoformat() if artist.updated_at else None, + "image_url": artist.image_url # Cached during watchlist scans }) - + return jsonify({"success": True, "artists": artists_data}) except Exception as e: print(f"Error getting watchlist artists: {e}") diff --git a/webui/static/script.js b/webui/static/script.js index 925bce8b..73cc485f 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -19748,10 +19748,29 @@ async function showWatchlistModal() { Update Similar Artists - -