From 7e1fc13e52e2da72e85be698380a0e23c1f04f87 Mon Sep 17 00:00:00 2001 From: Antti Kettunen Date: Fri, 17 Apr 2026 09:03:38 +0300 Subject: [PATCH] Make watchlist update_discovery_pool_incremental use provider priority Continuation on recent changes --- core/watchlist_scanner.py | 88 +++++++++++++++++++++------- tests/test_watchlist_scanner_scan.py | 68 +++++++++++++++++++++ 2 files changed, 136 insertions(+), 20 deletions(-) diff --git a/core/watchlist_scanner.py b/core/watchlist_scanner.py index 1d3f461f..93a1df7b 100644 --- a/core/watchlist_scanner.py +++ b/core/watchlist_scanner.py @@ -2841,6 +2841,11 @@ class WatchlistScanner: logger.info("No watchlist artists to check for incremental update") return + discovery_sources = self._discovery_source_priority() + if not discovery_sources: + logger.warning("No discovery sources available for incremental update") + return + cutoff_date = datetime.now() - timedelta(days=7) # Only last week's releases total_tracks_added = 0 @@ -2848,26 +2853,56 @@ class WatchlistScanner: try: logger.info(f"[{artist_idx}/{len(watchlist_artists)}] Checking {artist.artist_name} for new releases...") - # Only fetch latest 5 releases (much faster than full scan) - recent_releases = self.spotify_client.get_artist_albums( - artist.spotify_artist_id, - album_type='album,single,ep', - limit=5, - skip_cache=True, - max_pages=1, - ) + selected_source = None + selected_artist_id = None + recent_releases = [] + artist_genres: List[str] = [] - if not recent_releases: - continue + for source in discovery_sources: + source_attr = self._artist_id_attribute_for_source(source) + stored_id = getattr(artist, source_attr, None) if source_attr else None - # Fetch artist genres once for all tracks of this artist - artist_genres = [] - try: - artist_data = self.spotify_client.get_artist(artist.spotify_artist_id) - if artist_data and 'genres' in artist_data: - artist_genres = artist_data['genres'] - except Exception as e: - logger.debug(f"Could not fetch genres for {artist.artist_name}: {e}") + cache_callback = None + if source == 'spotify': + cache_callback = lambda found_id, watchlist_id=artist.id: self._cache_watchlist_artist_source_id(artist, 'spotify', found_id) + elif source == 'itunes': + cache_callback = lambda found_id, watchlist_id=artist.id: self._cache_watchlist_artist_source_id(artist, 'itunes', found_id) + elif source == 'deezer': + cache_callback = lambda found_id, watchlist_id=artist.id: self._cache_watchlist_artist_source_id(artist, 'deezer', found_id) + + artist_id = self._resolve_artist_id_for_source( + source, + artist.artist_name, + stored_id=stored_id, + cache_callback=cache_callback, + ) + if not artist_id: + continue + + recent_releases = self._get_artist_albums_for_source( + source, + artist_id, + album_type='album,single,ep', + limit=5, + skip_cache=True, + max_pages=1, + ) + if not recent_releases: + continue + + try: + artist_data = self._get_artist_data_for_source(source, artist_id) + if artist_data and 'genres' in artist_data: + artist_genres = artist_data['genres'] + except Exception as e: + logger.debug(f"Could not fetch genres for {artist.artist_name} on {source}: {e}") + + selected_source = source + selected_artist_id = artist_id + break + + if not recent_releases or not selected_source or not selected_artist_id: + continue for release in recent_releases: try: @@ -2876,7 +2911,7 @@ class WatchlistScanner: continue # Skip older releases # Get full album data with tracks - album_data = self.spotify_client.get_album(release.id) + album_data = self._get_album_data_for_source(selected_source, release.id, album_name=release.name) if not album_data or 'tracks' not in album_data: continue @@ -2926,7 +2961,20 @@ class WatchlistScanner: 'artist_genres': artist_genres } - if self.database.add_to_discovery_pool(track_data, profile_id=profile_id): + if selected_source == 'spotify': + track_data['spotify_track_id'] = track['id'] + track_data['spotify_album_id'] = album_data['id'] + track_data['spotify_artist_id'] = selected_artist_id + elif selected_source == 'deezer': + track_data['deezer_track_id'] = track['id'] + track_data['deezer_album_id'] = album_data['id'] + track_data['deezer_artist_id'] = selected_artist_id + else: + track_data['itunes_track_id'] = track['id'] + track_data['itunes_album_id'] = album_data['id'] + track_data['itunes_artist_id'] = selected_artist_id + + if self.database.add_to_discovery_pool(track_data, source=selected_source, profile_id=profile_id): total_tracks_added += 1 except Exception as track_error: diff --git a/tests/test_watchlist_scanner_scan.py b/tests/test_watchlist_scanner_scan.py index a1fad3bd..d7c307f1 100644 --- a/tests/test_watchlist_scanner_scan.py +++ b/tests/test_watchlist_scanner_scan.py @@ -827,6 +827,74 @@ def test_cache_discovery_recent_albums_falls_back_to_spotify_when_primary_has_no assert spotify_client.album_calls +def test_update_discovery_pool_incremental_uses_source_priority(monkeypatch): + monkeypatch.setattr(watchlist_scanner_module, "DELAY_BETWEEN_ARTISTS", 0) + monkeypatch.setattr(watchlist_scanner_module, "time", types.SimpleNamespace(sleep=lambda *_args, **_kwargs: None)) + monkeypatch.setattr(watchlist_scanner_module, "get_primary_source", lambda: "deezer") + monkeypatch.setattr(watchlist_scanner_module, "get_source_priority", lambda primary: [primary, "spotify", "itunes"]) + + artist = _build_artist("Incremental Artist") + artist.spotify_artist_id = None + artist.deezer_artist_id = None + + release = types.SimpleNamespace( + id="dz-release-1", + name="Incremental Release", + release_date="2026-04-16", + album_type="album", + image_url="https://example.com/deezer-release.jpg", + ) + + deezer_client = _FakeSourceClient( + artist_id="dz-artist", + albums=[release], + image_url="https://example.com/deezer-artist.jpg", + album_payload={ + "id": "dz-release-1", + "name": "Incremental Release", + "images": [{"url": "https://example.com/deezer-release.jpg"}], + "release_date": "2026-04-16", + "popularity": 10, + "tracks": {"items": [{"id": "dz-track-1", "name": "Incremental Track", "artists": [{"name": "Incremental Artist"}], "duration_ms": 180000}]}, + "artists": [{"id": "dz-artist"}], + }, + ) + spotify_client = _FakeSourceClient( + artist_id="sp-artist", + albums=[], + image_url="https://example.com/spotify-artist.jpg", + album_payload={ + "id": "sp-release-1", + "name": "Spotify Incremental Release", + "images": [{"url": "https://example.com/spotify-release.jpg"}], + "release_date": "2026-04-16", + "popularity": 50, + "tracks": {"items": [{"id": "sp-track-1", "name": "Spotify Incremental Track", "artists": [{"name": "Incremental Artist"}], "duration_ms": 180000}]}, + "artists": [{"id": "sp-artist"}], + }, + ) + + def fake_get_client_for_source(source): + return { + "deezer": deezer_client, + "spotify": spotify_client, + }.get(source) + + monkeypatch.setattr(watchlist_scanner_module, "get_client_for_source", fake_get_client_for_source) + + scanner = _build_scanner({"tracks": {"items": []}}, [artist]) + scanner.database.should_populate_discovery_pool = lambda hours_threshold=6, profile_id=1: True + + scanner.update_discovery_pool_incremental(profile_id=1) + + assert scanner.database.discovery_pool_calls + assert scanner.database.discovery_pool_calls[0][1] == "deezer" + assert deezer_client.search_calls == [("Incremental Artist", 1, {})] + assert deezer_client.album_calls + assert spotify_client.search_calls == [] + assert spotify_client.album_calls == [] + + def test_curate_discovery_playlists_uses_source_priority_for_recent_albums(monkeypatch): monkeypatch.setattr(watchlist_scanner_module, "DELAY_BETWEEN_ARTISTS", 0) monkeypatch.setattr(watchlist_scanner_module, "get_primary_source", lambda: "deezer")