pull/2/head
Broque Thomas 10 months ago
parent c62d36a338
commit 1e280f9211

File diff suppressed because it is too large Load Diff

@ -320,7 +320,7 @@ class SpotifyMatchingModal(QDialog):
# Temporarily force thread approach to debug UI issue
print(f"🔄 [DEBUG] FORCING thread approach for debugging")
self.suggestion_thread = ArtistSuggestionThread(self.track_result, self.spotify_client, self.matching_engine)
self.suggestion_thread = ArtistSuggestionThread(self.track_result, self.spotify_client, self.matching_engine, self.is_album, self.album_result)
self.suggestion_thread.suggestions_ready.connect(self.display_auto_suggestions)
self.suggestion_thread.start()
except Exception as e:
@ -332,7 +332,7 @@ class SpotifyMatchingModal(QDialog):
"""Worker function for generating suggestions in thread pool"""
try:
# Create suggestion thread instance for logic reuse
thread = ArtistSuggestionThread(self.track_result, self.spotify_client, self.matching_engine)
thread = ArtistSuggestionThread(self.track_result, self.spotify_client, self.matching_engine, self.is_album, self.album_result)
suggestions = thread.generate_artist_suggestions()
return suggestions
except Exception as e:
@ -754,11 +754,13 @@ class ArtistSuggestionThread(QThread):
suggestions_ready = pyqtSignal(list)
def __init__(self, track_result: TrackResult, spotify_client: SpotifyClient, matching_engine: MusicMatchingEngine):
def __init__(self, track_result: TrackResult, spotify_client: SpotifyClient, matching_engine: MusicMatchingEngine, is_album=False, album_result=None):
super().__init__()
self.track_result = track_result
self.spotify_client = spotify_client
self.matching_engine = matching_engine
self.is_album = is_album
self.album_result = album_result
def run(self):
"""Generate artist suggestions"""
@ -787,7 +789,125 @@ class ArtistSuggestionThread(QThread):
print(f" spotify_client.is_authenticated(): {self.spotify_client.is_authenticated()}")
print(f" track_result attributes: {[attr for attr in dir(self.track_result) if not attr.startswith('_')]}")
# Try to get artist name from different sources
# Special handling for albums - use album title to find artist instead of track data
if self.is_album and self.album_result and self.album_result.album_title:
print(f"🎵 [DEBUG] Album mode detected - using album title for artist search")
print(f" album_title: '{self.album_result.album_title}'")
print(f" album_artist: '{getattr(self.album_result, 'artist', 'NOT_FOUND')}'")
# Clean album title for searching (remove year prefixes like "(2005)")
album_title = self.album_result.album_title
import re
clean_album_title = re.sub(r'^\(\d{4}\)\s*', '', album_title).strip()
print(f" clean_album_title: '{clean_album_title}'")
# Strategy: Search tracks using album title to find the artist
print(f"🔍 Album Strategy: Searching tracks for album '{clean_album_title}'")
tracks = self.spotify_client.search_tracks(clean_album_title, limit=20)
print(f"📊 Found {len(tracks)} tracks from album search")
# Collect unique artist names and their associated tracks/albums first
unique_artists = {} # artist_name -> list of (track, album) tuples
for track in tracks:
for artist_name in track.artists:
if artist_name not in unique_artists:
unique_artists[artist_name] = []
unique_artists[artist_name].append((track, track.album))
print(f"🚀 [PERF] Found {len(unique_artists)} unique artists to lookup (down from {sum(len(track.artists) for track in tracks)} total)")
# Batch fetch artist objects using concurrent futures for speed
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
start_time = time.time()
artist_objects = {} # artist_name -> Artist object
def fetch_artist(artist_name):
"""Fetch single artist with error handling"""
try:
matches = self.spotify_client.search_artists(artist_name, limit=1)
if matches:
return artist_name, matches[0]
except Exception as e:
print(f"⚠️ Error fetching artist '{artist_name}': {e}")
return artist_name, None
# Use limited concurrency to respect rate limits while improving speed
with ThreadPoolExecutor(max_workers=3) as executor:
future_to_artist = {executor.submit(fetch_artist, name): name for name in unique_artists.keys()}
for future in as_completed(future_to_artist):
artist_name, artist_obj = future.result()
if artist_obj:
artist_objects[artist_name] = artist_obj
fetch_time = time.time() - start_time
print(f"⚡ [PERF] Fetched {len(artist_objects)} artists in {fetch_time:.2f}s using concurrent API calls")
# Now calculate confidence scores for each artist
artist_scores = {}
for artist_name, track_album_pairs in unique_artists.items():
if artist_name not in artist_objects:
continue
artist = artist_objects[artist_name]
best_confidence = 0
best_album_match = ""
# Find the best confidence score across all albums for this artist
for track, album in track_album_pairs:
# Calculate confidence based on album title match
confidence = self.matching_engine.similarity_score(
self.matching_engine.normalize_string(clean_album_title),
self.matching_engine.normalize_string(album)
)
# Boost confidence if album artist matches
if hasattr(self.album_result, 'artist') and self.album_result.artist:
artist_confidence = self.matching_engine.similarity_score(
self.matching_engine.normalize_string(self.album_result.artist),
self.matching_engine.normalize_string(artist.name)
)
confidence = max(confidence, artist_confidence)
# Keep highest confidence for this artist
if confidence > best_confidence:
best_confidence = confidence
best_album_match = album
# Store the artist with their best confidence
artist_scores[artist.id] = {
'artist': artist,
'confidence': best_confidence,
'album_match': best_album_match
}
# Add high-confidence album artists to suggestions
for artist_data in artist_scores.values():
if artist_data['confidence'] >= 0.6: # Higher threshold for album matches
print(f"✅ Added album artist match: {artist_data['artist'].name} ({artist_data['confidence']:.2f}) via '{artist_data['album_match']}'")
suggestions.append(ArtistMatch(
artist=artist_data['artist'],
confidence=artist_data['confidence'],
match_reason=f"Album match via '{artist_data['album_match']}'"
))
print(f"🎯 [DEBUG] Album strategy generated {len(suggestions)} suggestions")
# If we found good album matches, return them (don't try track-based strategies)
if suggestions:
# Remove duplicates and sort by confidence
unique_suggestions = {}
for suggestion in suggestions:
if suggestion.artist.id not in unique_suggestions or unique_suggestions[suggestion.artist.id].confidence < suggestion.confidence:
unique_suggestions[suggestion.artist.id] = suggestion
final_suggestions = sorted(unique_suggestions.values(), key=lambda x: x.confidence, reverse=True)
print(f"🎯 [DEBUG] Returning {len(final_suggestions)} album-based suggestions")
return final_suggestions[:5]
# Try to get artist name from different sources (for singles or fallback)
artist_name = None
if self.track_result.artist and self.track_result.artist != "Unknown Artist":
artist_name = self.track_result.artist

Loading…
Cancel
Save