pull/2/head
Broque Thomas 10 months ago
parent 39ca1e369a
commit 3f04d7f984

@ -248,48 +248,52 @@ class PlexClient:
logger.error(f"Error searching for track '{title}' by '{artist}': {e}")
return None
def search_tracks(self, title: str, artist: str, limit: int = 10) -> List[PlexTrackInfo]:
def search_tracks(self, title: str, artist: str, limit: int = 15) -> List[PlexTrackInfo]:
"""
Searches for tracks in the Plex music library using a more robust, two-step method
that is more compatible with different Plex server versions.
"""
if not self.music_library:
logger.warning("Plex music library not found. Cannot perform search.")
return []
try:
# Step 1: Search for the artist first. This is generally reliable.
artist_results = self.music_library.searchArtists(title=artist, limit=1)
results = []
candidate_tracks = []
if artist_results:
# If artist is found, get all their tracks and filter by title in Python.
# This avoids the API filter issue entirely.
# This avoids potential API filter issues where special characters in the title
# might cause the search to fail.
plex_artist = artist_results[0]
all_artist_tracks = plex_artist.tracks()
# Use a case-insensitive substring match to find potential tracks.
# The matching engine will do the final, more precise comparison.
# The matching engine will do the final, more precise comparison later.
lower_title = title.lower()
for track in all_artist_tracks:
if lower_title in track.title.lower():
results.append(track)
candidate_tracks.append(track)
else:
# Fallback: If the artist wasn't found, search for the track title
# across the entire library. This is less precise but better than nothing.
logger.debug(f"Artist '{artist}' not found. Falling back to title search for '{title}'.")
results = self.music_library.searchTracks(title=title, limit=limit)
candidate_tracks = self.music_library.searchTracks(title=title, limit=limit)
tracks = []
for result in results[:limit]: # Apply limit after filtering
tracks.append(PlexTrackInfo.from_plex_track(result))
# Convert the raw Plex track objects to our simplified PlexTrackInfo dataclass.
# Apply the limit here to the final list of candidates.
tracks = [PlexTrackInfo.from_plex_track(track) for track in candidate_tracks[:limit]]
if tracks:
logger.debug(f"Plex search for '{title}' by '{artist}' found {len(tracks)} potential matches.")
logger.debug(f"Plex search for title='{title}' by artist='{artist}' found {len(tracks)} potential matches.")
return tracks
except Exception as e:
logger.error(f"Error searching Plex tracks for '{title}': {e}")
logger.error(f"Error searching Plex tracks for title='{title}', artist='{artist}': {e}")
import traceback
traceback.print_exc()
return []
def get_library_stats(self) -> Dict[str, int]:

@ -120,28 +120,71 @@ class PlaylistTrackAnalysisWorker(QRunnable):
def _check_track_in_plex(self, spotify_track):
"""
Check if a Spotify track exists in Plex by searching Plex and using the
MusicMatchingEngine to find the best match.
Check if a Spotify track exists in Plex by trying several search strategies
and using the MusicMatchingEngine to find the best match.
"""
try:
# Use the first artist for the primary search query
artist_name = spotify_track.artists[0] if spotify_track.artists else ""
original_title = spotify_track.name
# --- Generate a list of search queries, from most specific to most broad ---
search_queries = []
# Strategy 1: Original, unmodified title. Catches exact matches.
search_queries.append(original_title)
# Strategy 2: Title with content after a hyphen removed.
# e.g., "Song Title - Remaster" -> "Song Title"
if " - " in original_title:
title_before_hyphen = original_title.split(' - ')[0].strip()
if title_before_hyphen:
search_queries.append(title_before_hyphen)
# Strategy 3: Title with parenthetical/bracketed content removed.
# (Uses the simple cleaner from this file for an intermediate search)
cleaned_for_search = clean_track_name_for_search(original_title)
if cleaned_for_search.lower() != original_title.lower():
search_queries.append(cleaned_for_search)
# Use the cleaned track name to get a broader set of potential matches from Plex
search_title = clean_track_name_for_search(spotify_track.name)
# Strategy 4: A "base" title with all extra info removed (remixes, feats, etc.)
# using the more aggressive cleaning from the matching engine.
base_title = self.matching_engine.clean_title(original_title)
if base_title.lower() != cleaned_for_search.lower() and base_title.lower() != original_title.lower():
search_queries.append(base_title)
# Remove duplicate queries that might have resulted from the cleaning steps, preserving order.
unique_queries = list(dict.fromkeys(search_queries))
# Call the updated search_tracks with separate artist and title
potential_plex_matches = self.plex_client.search_tracks(
title=search_title,
artist=artist_name,
limit=10
)
print(f"🧠 Generated search queries for '{original_title}': {unique_queries}")
# --- Execute searches and collect all potential matches ---
all_potential_matches = []
found_match_ids = set()
for query_title in unique_queries:
if self._cancelled:
return None, 0.0
# Call the updated search_tracks with the query title and artist
potential_plex_matches = self.plex_client.search_tracks(
title=query_title,
artist=artist_name,
limit=15 # Increased limit to get more candidates
)
for track in potential_plex_matches:
if track.id not in found_match_ids:
all_potential_matches.append(track)
found_match_ids.add(track.id)
if not potential_plex_matches:
if not all_potential_matches:
print(f"❌ No Plex candidates found for '{original_title}' after trying all strategies.")
return None, 0.0
# Use the matching engine to find the best match among the candidates.
match_result = self.matching_engine.find_best_match(spotify_track, potential_plex_matches)
# --- Use the matching engine to find the best match among ALL candidates ---
print(f"✅ Found {len(all_potential_matches)} potential Plex matches for '{original_title}'. Scoring now...")
match_result = self.matching_engine.find_best_match(spotify_track, all_potential_matches)
# Return the best Plex track found and its confidence score.
return match_result.plex_track, match_result.confidence

Loading…
Cancel
Save