From 8e0e77f4d25f152373d9f32f9e24e01d5fbdeb37 Mon Sep 17 00:00:00 2001 From: Broque Thomas Date: Thu, 24 Jul 2025 18:09:31 -0700 Subject: [PATCH] good --- core/plex_client.py | 56 ++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/core/plex_client.py b/core/plex_client.py index 1a8b5bb7..d3430f92 100644 --- a/core/plex_client.py +++ b/core/plex_client.py @@ -250,9 +250,8 @@ class PlexClient: def search_tracks(self, title: str, artist: str, limit: int = 15) -> List[PlexTrackInfo]: """ - Searches for tracks in the Plex music library. If an artist is provided, it - searches within that artist's scope. If the artist is empty or not found, - it falls back to a library-wide search for the title. + Searches for tracks using a robust, multi-stage fallback strategy to find + the best possible candidates for matching. """ if not self.music_library: logger.warning("Plex music library not found. Cannot perform search.") @@ -260,41 +259,56 @@ class PlexClient: try: candidate_tracks = [] - - # If an artist is provided, perform a targeted search. + # Use a set to keep track of found track keys to avoid duplicates + found_track_keys = set() + + def add_candidates(tracks): + """Helper function to add unique tracks to the main candidate list.""" + for track in tracks: + if track.ratingKey not in found_track_keys: + candidate_tracks.append(track) + found_track_keys.add(track.ratingKey) + + # --- Stage 1: High-Precision Search (Artist -> then filter by Title) --- if artist: + logger.debug(f"Stage 1: Searching for artist '{artist}'") artist_results = self.music_library.searchArtists(title=artist, limit=1) if artist_results: plex_artist = artist_results[0] all_artist_tracks = plex_artist.tracks() lower_title = title.lower() - for track in all_artist_tracks: - if lower_title in track.title.lower(): - candidate_tracks.append(track) - else: - # If artist not found, still fall back to a general title search - logger.debug(f"Artist '{artist}' not found. Falling back to title search for '{title}'.") - candidate_tracks = self.music_library.searchTracks(title=title, limit=limit) - - # If no artist is provided, perform a library-wide title search directly. - else: - logger.debug(f"Performing title-only search for '{title}'.") - candidate_tracks = self.music_library.searchTracks(title=title, limit=limit) + # Find all tracks by this artist that contain the title text + stage1_results = [track for track in all_artist_tracks if lower_title in track.title.lower()] + add_candidates(stage1_results) + logger.debug(f"Stage 1 found {len(stage1_results)} candidates.") + # --- Stage 2: Flexible Keyword Search (Artist + Title combined) --- + # This is good for artist variations like "The Dare" vs "Dare" + search_query = f"{artist} {title}".strip() + logger.debug(f"Stage 2: Performing keyword search for '{search_query}'") + stage2_results = self.music_library.search(title=search_query, libtype='track', limit=limit) + add_candidates(stage2_results) + + # --- Stage 3: Title-Only Fallback Search --- + # This is the broadest search for cases where artist metadata is wrong or missing. + logger.debug(f"Stage 3: Performing title-only search for '{title}'") + stage3_results = self.music_library.searchTracks(title=title, limit=limit) + add_candidates(stage3_results) + + # Convert the raw Plex track objects to our simplified PlexTrackInfo dataclass tracks = [PlexTrackInfo.from_plex_track(track) for track in candidate_tracks[:limit]] - + if tracks: - logger.debug(f"Plex search for title='{title}', artist='{artist or 'N/A'}' found {len(tracks)} potential matches.") + logger.info(f"Found {len(tracks)} total potential matches for '{title}' by '{artist}' after all stages.") return tracks except Exception as e: - logger.error(f"Error searching Plex tracks for title='{title}', artist='{artist}': {e}") + logger.error(f"Error during multi-stage search for title='{title}', artist='{artist}': {e}") import traceback traceback.print_exc() return [] - def get_library_stats(self) -> Dict[str, int]: if not self.music_library: return {}