This commit is contained in:
Broque Thomas 2025-07-24 18:09:31 -07:00
parent 6358de554f
commit 8e0e77f4d2

View file

@ -250,9 +250,8 @@ class PlexClient:
def search_tracks(self, title: str, artist: str, limit: int = 15) -> List[PlexTrackInfo]: 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 for tracks using a robust, multi-stage fallback strategy to find
searches within that artist's scope. If the artist is empty or not found, the best possible candidates for matching.
it falls back to a library-wide search for the title.
""" """
if not self.music_library: if not self.music_library:
logger.warning("Plex music library not found. Cannot perform search.") logger.warning("Plex music library not found. Cannot perform search.")
@ -260,41 +259,56 @@ class PlexClient:
try: try:
candidate_tracks = [] candidate_tracks = []
# Use a set to keep track of found track keys to avoid duplicates
# If an artist is provided, perform a targeted search. 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: if artist:
logger.debug(f"Stage 1: Searching for artist '{artist}'")
artist_results = self.music_library.searchArtists(title=artist, limit=1) artist_results = self.music_library.searchArtists(title=artist, limit=1)
if artist_results: if artist_results:
plex_artist = artist_results[0] plex_artist = artist_results[0]
all_artist_tracks = plex_artist.tracks() all_artist_tracks = plex_artist.tracks()
lower_title = title.lower() lower_title = title.lower()
for track in all_artist_tracks: # Find all tracks by this artist that contain the title text
if lower_title in track.title.lower(): stage1_results = [track for track in all_artist_tracks if lower_title in track.title.lower()]
candidate_tracks.append(track) add_candidates(stage1_results)
else: logger.debug(f"Stage 1 found {len(stage1_results)} candidates.")
# 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)
# --- 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]] tracks = [PlexTrackInfo.from_plex_track(track) for track in candidate_tracks[:limit]]
if tracks: 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 return tracks
except Exception as e: 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 import traceback
traceback.print_exc() traceback.print_exc()
return [] return []
def get_library_stats(self) -> Dict[str, int]: def get_library_stats(self) -> Dict[str, int]:
if not self.music_library: if not self.music_library:
return {} return {}