better
This commit is contained in:
parent
39ca1e369a
commit
3f04d7f984
2 changed files with 71 additions and 24 deletions
|
|
@ -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…
Reference in a new issue