better
This commit is contained in:
parent
c62d36a338
commit
1e280f9211
4 changed files with 1971 additions and 4 deletions
1847
logs/app.log
1847
logs/app.log
File diff suppressed because it is too large
Load diff
Binary file not shown.
Binary file not shown.
|
|
@ -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…
Reference in a new issue