diff --git a/core/watchlist_scanner.py b/core/watchlist_scanner.py index 2cdc9a03..bba90c66 100644 --- a/core/watchlist_scanner.py +++ b/core/watchlist_scanner.py @@ -865,7 +865,14 @@ class WatchlistScanner: Returns: WatchlistDiscographyResult or None on error """ - for source in self._watchlist_source_priority(): + # Per-artist metadata source override — if set, use that source first with fallback + preferred = getattr(watchlist_artist, 'preferred_metadata_source', None) + if preferred and preferred in ('spotify', 'deezer', 'itunes', 'discogs'): + source_priority = list(get_source_priority(preferred)) + else: + source_priority = self._watchlist_source_priority() + + for source in source_priority: result = self._resolve_watchlist_discography_for_source(watchlist_artist, source, last_scan_timestamp) if result: return result diff --git a/database/music_database.py b/database/music_database.py index d7ad5e64..0690d1ab 100644 --- a/database/music_database.py +++ b/database/music_database.py @@ -102,6 +102,7 @@ class WatchlistArtist: include_compilations: bool = False include_instrumentals: bool = False lookback_days: Optional[int] = None # Per-artist override; None = use global setting + preferred_metadata_source: Optional[str] = None # Per-artist override; None = use global setting profile_id: int = 1 @dataclass @@ -378,6 +379,9 @@ class MusicDatabase: # Add iTunes artist ID column to watchlist_artists (migration) self._add_watchlist_itunes_id_column(cursor) + # Add per-artist preferred_metadata_source column (migration) + self._add_watchlist_preferred_metadata_source_column(cursor) + # Make spotify_artist_id nullable for iTunes-only artists (migration) self._fix_watchlist_spotify_id_nullable(cursor) @@ -1529,6 +1533,17 @@ class MusicDatabase: logger.error(f"Error adding itunes_artist_id column to watchlist_artists: {e}") # Don't raise - this is a migration, database can still function + def _add_watchlist_preferred_metadata_source_column(self, cursor): + """Add per-artist preferred_metadata_source column to watchlist_artists table""" + try: + cursor.execute("PRAGMA table_info(watchlist_artists)") + columns = [column[1] for column in cursor.fetchall()] + if 'preferred_metadata_source' not in columns: + cursor.execute("ALTER TABLE watchlist_artists ADD COLUMN preferred_metadata_source TEXT DEFAULT NULL") + logger.info("Added preferred_metadata_source column to watchlist_artists table") + except Exception as e: + logger.error(f"Error adding preferred_metadata_source column to watchlist_artists: {e}") + def _add_similar_artists_last_featured_column(self, cursor): """Add last_featured column to similar_artists for hero slider cycling""" try: @@ -6941,7 +6956,7 @@ class MusicDatabase: 'last_scan_timestamp', 'created_at', 'updated_at'] optional_columns = ['image_url', 'itunes_artist_id', 'deezer_artist_id', 'discogs_artist_id', 'include_albums', 'include_eps', 'include_singles', 'include_live', 'include_remixes', 'include_acoustic', 'include_compilations', - 'include_instrumentals', 'lookback_days'] + 'include_instrumentals', 'lookback_days', 'preferred_metadata_source'] columns_to_select = base_columns + [col for col in optional_columns if col in existing_columns] @@ -6977,6 +6992,7 @@ class MusicDatabase: include_compilations = bool(row['include_compilations']) if 'include_compilations' in existing_columns else False include_instrumentals = bool(row['include_instrumentals']) if 'include_instrumentals' in existing_columns else False lookback_days = row['lookback_days'] if 'lookback_days' in existing_columns else None + preferred_metadata_source = row['preferred_metadata_source'] if 'preferred_metadata_source' in existing_columns else None watchlist_artists.append(WatchlistArtist( id=row['id'], @@ -6999,6 +7015,7 @@ class MusicDatabase: include_compilations=include_compilations, include_instrumentals=include_instrumentals, lookback_days=lookback_days, + preferred_metadata_source=preferred_metadata_source, profile_id=profile_id )) diff --git a/web_server.py b/web_server.py index 2135ee10..240a2c1d 100644 --- a/web_server.py +++ b/web_server.py @@ -40957,7 +40957,7 @@ def watchlist_artist_config(artist_id): include_live, include_remixes, include_acoustic, include_compilations, artist_name, image_url, spotify_artist_id, itunes_artist_id, last_scan_timestamp, date_added, include_instrumentals, deezer_artist_id, - lookback_days, discogs_artist_id + lookback_days, discogs_artist_id, preferred_metadata_source FROM watchlist_artists WHERE spotify_artist_id = ? OR itunes_artist_id = ? OR deezer_artist_id = ? OR discogs_artist_id = ? """, (artist_id, artist_id, artist_id, artist_id)) @@ -41063,8 +41063,10 @@ def watchlist_artist_config(artist_id): 'last_scan_timestamp': result[11], 'date_added': result[12], 'lookback_days': result[15] if len(result) > 15 else None, + 'preferred_metadata_source': result[17] if len(result) > 17 else None, } + from core.metadata_service import get_primary_source return jsonify({ "success": True, "config": config, @@ -41075,6 +41077,7 @@ def watchlist_artist_config(artist_id): "deezer_artist_id": deezer_id, "discogs_artist_id": discogs_id, "watchlist_name": result[7], # Original stored watchlist artist name + "global_metadata_source": get_primary_source(), }) else: # POST @@ -41094,6 +41097,10 @@ def watchlist_artist_config(artist_id): # Validate lookback_days if provided if lookback_days is not None: lookback_days = int(lookback_days) if lookback_days != '' else None + preferred_metadata_source = data.get('preferred_metadata_source', None) + # Validate — only accept known sources, empty string means clear override + if preferred_metadata_source == '' or preferred_metadata_source not in ('spotify', 'deezer', 'itunes', 'discogs'): + preferred_metadata_source = None # Validate at least one release type is selected if not (include_albums or include_eps or include_singles): @@ -41116,13 +41123,13 @@ def watchlist_artist_config(artist_id): UPDATE watchlist_artists SET include_albums = ?, include_eps = ?, include_singles = ?, include_live = ?, include_remixes = ?, include_acoustic = ?, include_compilations = ?, - include_instrumentals = ?, lookback_days = ?, + include_instrumentals = ?, lookback_days = ?, preferred_metadata_source = ?, last_scan_timestamp = CASE WHEN ? THEN NULL ELSE last_scan_timestamp END, updated_at = CURRENT_TIMESTAMP WHERE spotify_artist_id = ? OR itunes_artist_id = ? OR deezer_artist_id = ? OR discogs_artist_id = ? """, (int(include_albums), int(include_eps), int(include_singles), int(include_live), int(include_remixes), int(include_acoustic), int(include_compilations), - int(include_instrumentals), lookback_days, lookback_changed, + int(include_instrumentals), lookback_days, preferred_metadata_source, lookback_changed, artist_id, artist_id, artist_id, artist_id)) conn.commit() diff --git a/webui/index.html b/webui/index.html index b065c029..39384333 100644 --- a/webui/index.html +++ b/webui/index.html @@ -7390,6 +7390,15 @@ + +
+

Scan Source

+

Override which metadata provider is used when scanning this artist for new releases

+
+ +
+
+