diff --git a/core/imports/resolution.py b/core/imports/resolution.py index 40e4149c..be55c1f9 100644 --- a/core/imports/resolution.py +++ b/core/imports/resolution.py @@ -119,7 +119,7 @@ def _pick_best_track_match(search_results: List[Any], title: str, artist: str = return search_results[0] -def _search_tracks_for_source(source: str, client: Any, query: str, limit: int = 1) -> List[Any]: +def search_tracks_for_source(source: str, client: Any, query: str, limit: int = 1) -> List[Any]: if not client or not hasattr(client, 'search_tracks'): return [] @@ -366,11 +366,11 @@ def get_single_track_import_context( if not search_query: continue - search_results = _search_tracks_for_source(source, client, search_query, limit=5) + search_results = search_tracks_for_source(source, client, search_query, limit=5) if not search_results and search_query != title: - search_results = _search_tracks_for_source(source, client, title, limit=5) + search_results = search_tracks_for_source(source, client, title, limit=5) if not search_results and artist and search_query != artist: - search_results = _search_tracks_for_source(source, client, artist, limit=5) + search_results = search_tracks_for_source(source, client, artist, limit=5) if not search_results: continue diff --git a/core/imports/side_effects.py b/core/imports/side_effects.py index dbc5bbf7..b44bfe61 100644 --- a/core/imports/side_effects.py +++ b/core/imports/side_effects.py @@ -484,10 +484,10 @@ def record_retag_download(context: Dict[str, Any], artist_context: Dict[str, Any ) file_format = os.path.splitext(str(final_path))[1].lstrip(".").lower() - spotify_track_id = None + source_track_id = None itunes_track_id = None if source == "spotify": - spotify_track_id = source_ids.get("track_id", "") or None + source_track_id = source_ids.get("track_id", "") or None elif source == "itunes": itunes_track_id = source_ids.get("track_id", "") or None @@ -499,7 +499,7 @@ def record_retag_download(context: Dict[str, Any], artist_context: Dict[str, Any title=title, file_path=str(final_path), file_format=file_format, - spotify_track_id=spotify_track_id, + spotify_track_id=source_track_id, itunes_track_id=itunes_track_id, ) logger.info("[Retag] Recorded track for retag: '%s' in '%s'", title, album_name) @@ -513,31 +513,38 @@ def check_and_remove_from_wishlist(context: Dict[str, Any]) -> None: """Check whether a successful download should be removed from the wishlist.""" try: wishlist_service = get_wishlist_service() - spotify_track_id = None - + source = get_import_source(context) + source_ids = get_import_source_ids(context) + source_label = { + "spotify": "Spotify", + "itunes": "iTunes", + "deezer": "Deezer", + "discogs": "Discogs", + "hydrabase": "Hydrabase", + }.get(source, "Source") track_info = context.get("track_info", {}) - if track_info.get("id"): - spotify_track_id = track_info["id"] - logger.info("[Wishlist] Found Spotify ID from track_info: %s", spotify_track_id) - elif context.get("original_search_result", {}).get("id"): - spotify_track_id = context["original_search_result"]["id"] - logger.info("[Wishlist] Found Spotify ID from original_search_result: %s", spotify_track_id) + track_id = None + + if source == "spotify": + track_id = source_ids.get("track_id") or None + if track_id: + logger.info("[Wishlist] Found %s track ID from source_ids: %s", source_label, track_id) elif "wishlist_id" in track_info: wishlist_id = track_info["wishlist_id"] logger.info("[Wishlist] Found wishlist_id in context: %s", wishlist_id) wishlist_tracks = _all_profile_wishlist_tracks(wishlist_service) for wishlist_track in wishlist_tracks: if wishlist_track.get("wishlist_id") == wishlist_id: - spotify_track_id = wishlist_track.get("spotify_track_id") or wishlist_track.get("id") - logger.info("[Wishlist] Found Spotify ID from wishlist entry: %s", spotify_track_id) + track_id = wishlist_track.get("spotify_track_id") or wishlist_track.get("id") + logger.info("[Wishlist] Found track ID from wishlist entry: %s", track_id) break - if not spotify_track_id: + if not track_id: track_name = track_info.get("name") or context.get("original_search_result", {}).get("title", "") artist_name = _primary_track_artist_name(track_info) or _primary_track_artist_name(context.get("original_search_result", {})) if track_name and artist_name: - logger.warning("[Wishlist] No Spotify ID found, checking for fuzzy match: '%s' by '%s'", track_name, artist_name) + logger.warning("[Wishlist] No track ID found, checking for fuzzy match: '%s' by '%s'", track_name, artist_name) wishlist_tracks = _all_profile_wishlist_tracks(wishlist_service) for wishlist_track in wishlist_tracks: @@ -550,18 +557,18 @@ def check_and_remove_from_wishlist(context: Dict[str, Any]) -> None: else: wl_artist_name = str(wl_artists[0]).lower() if wl_name == track_name.lower() and wl_artist_name == artist_name.lower(): - spotify_track_id = wishlist_track.get("spotify_track_id") or wishlist_track.get("id") - logger.info("[Wishlist] Found fuzzy match - Spotify ID: %s", spotify_track_id) + track_id = wishlist_track.get("spotify_track_id") or wishlist_track.get("id") + logger.info("[Wishlist] Found fuzzy match - track ID: %s", track_id) break - if spotify_track_id: - logger.info("[Wishlist] Attempting to remove track from wishlist: %s", spotify_track_id) - removed = wishlist_service.mark_track_download_result(spotify_track_id, success=True) + if track_id: + logger.info("[Wishlist] Attempting to remove track from wishlist: %s", track_id) + removed = wishlist_service.mark_track_download_result(track_id, success=True) if removed: - logger.info("[Wishlist] Successfully removed track from wishlist: %s", spotify_track_id) + logger.info("[Wishlist] Successfully removed track from wishlist: %s", track_id) else: - logger.warning("ℹ️ [Wishlist] Track not found in wishlist or already removed: %s", spotify_track_id) + logger.warning("ℹ️ [Wishlist] Track not found in wishlist or already removed: %s", track_id) else: - logger.warning("ℹ️ [Wishlist] No Spotify track ID found for wishlist removal check") + logger.warning("ℹ️ [Wishlist] No track ID found for wishlist removal check") except Exception as exc: logger.error("[Wishlist] Error in wishlist removal check: %s", exc) diff --git a/core/imports/staging.py b/core/imports/staging.py index 44edb0bf..5843d144 100644 --- a/core/imports/staging.py +++ b/core/imports/staging.py @@ -137,9 +137,9 @@ def _search_albums_for_source(source: str, client: Any, query: str, limit: int = def _search_tracks_for_source(source: str, client: Any, query: str, limit: int = 5): - from core.metadata_service import _search_tracks_for_source as _metadata_search_tracks_for_source + from core.imports.resolution import search_tracks_for_source - return _metadata_search_tracks_for_source(source, client, query, limit=limit) + return search_tracks_for_source(source, client, query, limit=limit) def _extract_value(value: Any, *names: str, default: Any = None) -> Any: diff --git a/web_server.py b/web_server.py index 1e036ef6..c2880dfb 100644 --- a/web_server.py +++ b/web_server.py @@ -18625,32 +18625,43 @@ def _check_and_remove_from_wishlist(context): """ try: from core.wishlist_service import get_wishlist_service + from core.imports.context import get_import_source, get_import_source_ids wishlist_service = get_wishlist_service() - # Try to extract Spotify track ID from various sources in the context + # Try to extract a source-aware track ID from the context spotify_track_id = None # Populated lazily by Method 3 or Method 4. Initialized here so Method 4's # `if not wishlist_tracks` guard doesn't UnboundLocalError when Methods 1/2 # found nothing and Method 3 never ran (no wishlist_id in track_info). wishlist_tracks = [] - # Method 1: Direct track_info with id + # Method 1: Source-specific track lookup from track_info / source_ids track_info = context.get('track_info', {}) - if track_info.get('id'): - spotify_track_id = track_info['id'] - logger.info(f"[Wishlist] Found Spotify ID from track_info: {spotify_track_id}") - - # Method 2: From original search result - elif context.get('original_search_result', {}).get('id'): + source = get_import_source(context) + source_ids = get_import_source_ids(context) + source_label = { + 'spotify': 'Spotify', + 'itunes': 'iTunes', + 'deezer': 'Deezer', + 'discogs': 'Discogs', + 'hydrabase': 'Hydrabase', + }.get(source, 'Source') + + if source == 'spotify' and source_ids.get('track_id'): + spotify_track_id = source_ids['track_id'] + logger.info(f"[Wishlist] Found {source_label} track ID from source_ids: {spotify_track_id}") + + # Method 2: Fallback to the original search result for source-specific IDs + elif source == 'spotify' and context.get('original_search_result', {}).get('id'): spotify_track_id = context['original_search_result']['id'] - logger.info(f"[Wishlist] Found Spotify ID from original_search_result: {spotify_track_id}") + logger.info(f"[Wishlist] Found {source_label} track ID from original_search_result: {spotify_track_id}") # Method 3: Check if this is a wishlist download (context has wishlist_id) elif 'wishlist_id' in track_info: wishlist_id = track_info['wishlist_id'] logger.info(f"[Wishlist] Found wishlist_id in context: {wishlist_id}") - # Get the Spotify track ID from the wishlist entry (search all profiles) + # Get the track ID from the wishlist entry (search all profiles) database = get_database() all_profiles = database.get_all_profiles() wishlist_tracks = [] @@ -18659,16 +18670,16 @@ def _check_and_remove_from_wishlist(context): for wl_track in wishlist_tracks: if wl_track.get('wishlist_id') == wishlist_id: spotify_track_id = wl_track.get('spotify_track_id') or wl_track.get('id') - logger.info(f"[Wishlist] Found Spotify ID from wishlist entry: {spotify_track_id}") + logger.info(f"[Wishlist] Found track ID from wishlist entry: {spotify_track_id}") break - # Method 4: Try to construct ID from track metadata for fuzzy matching + # Method 4: Try to construct a track ID from metadata for fuzzy matching if not spotify_track_id: track_name = track_info.get('name') or context.get('original_search_result', {}).get('title', '') artist_name = _get_track_artist_name(track_info) or _get_track_artist_name(context.get('original_search_result', {})) if track_name and artist_name: - logger.warning(f"[Wishlist] No Spotify ID found, checking for fuzzy match: '{track_name}' by '{artist_name}'") + logger.warning(f"[Wishlist] No track ID found, checking for fuzzy match: '{track_name}' by '{artist_name}'") # Get all wishlist tracks and find potential matches (search all profiles) if not wishlist_tracks: @@ -18692,10 +18703,10 @@ def _check_and_remove_from_wishlist(context): # Simple fuzzy matching if (wl_name == track_name.lower() and wl_artist_name == artist_name.lower()): spotify_track_id = wl_track.get('spotify_track_id') or wl_track.get('id') - logger.info(f"[Wishlist] Found fuzzy match - Spotify ID: {spotify_track_id}") + logger.info(f"[Wishlist] Found fuzzy match - track ID: {spotify_track_id}") break - # If we found a Spotify track ID, remove it from wishlist + # If we found a track ID, remove it from wishlist if spotify_track_id: logger.info(f"[Wishlist] Attempting to remove track from wishlist: {spotify_track_id}") removed = wishlist_service.mark_track_download_result(spotify_track_id, success=True) @@ -18704,7 +18715,7 @@ def _check_and_remove_from_wishlist(context): else: logger.warning(f"ℹ️ [Wishlist] Track not found in wishlist or already removed: {spotify_track_id}") else: - logger.warning("ℹ️ [Wishlist] No Spotify track ID found for wishlist removal check") + logger.warning("ℹ️ [Wishlist] No track ID found for wishlist removal check") except Exception as e: logger.error(f"[Wishlist] Error in wishlist removal check: {e}") @@ -48259,7 +48270,7 @@ def import_singles_process(): title = file_info.get('title', '') artist = file_info.get('artist', '') - manual_match = file_info.get('manual_match') + manual_match = file_info.get('manual_match') or file_info.get('spotify_override') if manual_match is not None and not isinstance(manual_match, dict): manual_match = None diff --git a/webui/static/stats-automations.js b/webui/static/stats-automations.js index 7b48c34c..fe6d1a98 100644 --- a/webui/static/stats-automations.js +++ b/webui/static/stats-automations.js @@ -1214,6 +1214,9 @@ function importPageRenderMatchList() { // Build rows let matchedCount = 0; const rows = data.matches.map((m, idx) => { + const track = m.track || m.spotify_track || {}; + const trackNumber = track.track_number ?? track.trackNumber; + const displayTrackNumber = trackNumber ? trackNumber : (idx + 1); let file = null; let confidence = m.confidence; let isOverride = false; @@ -1253,8 +1256,8 @@ function importPageRenderMatchList() {