update discovery modal to collect album info
This commit is contained in:
parent
178bb7696d
commit
3be1dc9ace
2 changed files with 142 additions and 43 deletions
|
|
@ -12610,12 +12610,30 @@ def _run_tidal_discovery_worker(playlist_id):
|
||||||
'status': 'not_found'
|
'status': 'not_found'
|
||||||
}
|
}
|
||||||
|
|
||||||
if spotify_track:
|
if isinstance(spotify_track, tuple):
|
||||||
|
# Function now returns (Track, raw_data)
|
||||||
|
track_obj, raw_track_data = spotify_track
|
||||||
|
# Use full album object from raw API response
|
||||||
|
album_obj = raw_track_data.get('album', {}) if raw_track_data else {}
|
||||||
|
|
||||||
|
result['spotify_data'] = {
|
||||||
|
'id': track_obj.id,
|
||||||
|
'name': track_obj.name,
|
||||||
|
'artists': track_obj.artists, # Already a list of strings
|
||||||
|
'album': album_obj, # Full album object with images
|
||||||
|
'duration_ms': track_obj.duration_ms,
|
||||||
|
'external_urls': track_obj.external_urls
|
||||||
|
}
|
||||||
|
result['status'] = 'found'
|
||||||
|
successful_discoveries += 1
|
||||||
|
state['spotify_matches'] = successful_discoveries
|
||||||
|
elif spotify_track:
|
||||||
|
# Fallback for old format (shouldn't happen after update)
|
||||||
result['spotify_data'] = {
|
result['spotify_data'] = {
|
||||||
'id': spotify_track.id,
|
'id': spotify_track.id,
|
||||||
'name': spotify_track.name,
|
'name': spotify_track.name,
|
||||||
'artists': spotify_track.artists, # Already a list of strings
|
'artists': spotify_track.artists,
|
||||||
'album': spotify_track.album, # Already a string
|
'album': {'name': spotify_track.album, 'album_type': 'album', 'images': []},
|
||||||
'duration_ms': spotify_track.duration_ms,
|
'duration_ms': spotify_track.duration_ms,
|
||||||
'external_urls': spotify_track.external_urls
|
'external_urls': spotify_track.external_urls
|
||||||
}
|
}
|
||||||
|
|
@ -12698,19 +12716,28 @@ def _search_spotify_for_tidal_track(tidal_track):
|
||||||
|
|
||||||
# Find best match using confidence scoring
|
# Find best match using confidence scoring
|
||||||
best_match = None
|
best_match = None
|
||||||
|
best_match_raw = None # Store raw Spotify API data for full album info
|
||||||
best_confidence = 0.0
|
best_confidence = 0.0
|
||||||
min_confidence = 0.7 # Higher threshold for Tidal since data is cleaner
|
min_confidence = 0.7 # Higher threshold for Tidal since data is cleaner
|
||||||
|
|
||||||
for query_idx, search_query in enumerate(search_queries):
|
for query_idx, search_query in enumerate(search_queries):
|
||||||
try:
|
try:
|
||||||
print(f"🔍 Tidal query {query_idx + 1}/{len(search_queries)}: {search_query}")
|
print(f"🔍 Tidal query {query_idx + 1}/{len(search_queries)}: {search_query}")
|
||||||
|
|
||||||
|
# Get raw Spotify API response to access full album object with images
|
||||||
|
raw_results = spotify_client.sp.search(q=search_query, type='track', limit=5)
|
||||||
|
if not raw_results or 'tracks' not in raw_results or not raw_results['tracks']['items']:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Also get Track objects for matching logic
|
||||||
results = spotify_client.search_tracks(search_query, limit=5)
|
results = spotify_client.search_tracks(search_query, limit=5)
|
||||||
|
|
||||||
if not results:
|
if not results:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Score each result using matching engine
|
# Score each result using matching engine
|
||||||
for result in results:
|
for idx, result in enumerate(results):
|
||||||
|
raw_track = raw_results['tracks']['items'][idx] if idx < len(raw_results['tracks']['items']) else None
|
||||||
try:
|
try:
|
||||||
# Calculate confidence using matching engine's similarity scoring (with fallback)
|
# Calculate confidence using matching engine's similarity scoring (with fallback)
|
||||||
try:
|
try:
|
||||||
|
|
@ -12745,6 +12772,7 @@ def _search_spotify_for_tidal_track(tidal_track):
|
||||||
if combined_confidence > best_confidence and combined_confidence >= min_confidence:
|
if combined_confidence > best_confidence and combined_confidence >= min_confidence:
|
||||||
best_confidence = combined_confidence
|
best_confidence = combined_confidence
|
||||||
best_match = result
|
best_match = result
|
||||||
|
best_match_raw = raw_track # Store raw data with full album object
|
||||||
print(f"✅ New best Tidal match: {result.artists[0]} - {result.name} (confidence: {combined_confidence:.3f})")
|
print(f"✅ New best Tidal match: {result.artists[0]} - {result.name} (confidence: {combined_confidence:.3f})")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -12762,10 +12790,10 @@ def _search_spotify_for_tidal_track(tidal_track):
|
||||||
|
|
||||||
if best_match:
|
if best_match:
|
||||||
print(f"✅ Final Tidal match: {best_match.artists[0]} - {best_match.name} (confidence: {best_confidence:.3f})")
|
print(f"✅ Final Tidal match: {best_match.artists[0]} - {best_match.name} (confidence: {best_confidence:.3f})")
|
||||||
|
return (best_match, best_match_raw) # Return both Track object and raw data
|
||||||
else:
|
else:
|
||||||
print(f"❌ No suitable Tidal match found (best confidence was {best_confidence:.3f}, required {min_confidence:.3f})")
|
print(f"❌ No suitable Tidal match found (best confidence was {best_confidence:.3f}, required {min_confidence:.3f})")
|
||||||
|
return None
|
||||||
return best_match
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ Error searching Spotify for Tidal track: {e}")
|
print(f"❌ Error searching Spotify for Tidal track: {e}")
|
||||||
|
|
@ -13179,16 +13207,26 @@ def _run_youtube_discovery_worker(url_hash):
|
||||||
# Fallback to original simple query
|
# Fallback to original simple query
|
||||||
search_queries = [f"artist:{cleaned_artist} track:{cleaned_title}"]
|
search_queries = [f"artist:{cleaned_artist} track:{cleaned_title}"]
|
||||||
|
|
||||||
|
# Store raw Spotify data for best match
|
||||||
|
best_raw_track = None
|
||||||
|
|
||||||
for query_idx, search_query in enumerate(search_queries):
|
for query_idx, search_query in enumerate(search_queries):
|
||||||
try:
|
try:
|
||||||
print(f"🔍 YouTube query {query_idx + 1}/{len(search_queries)}: {search_query}")
|
print(f"🔍 YouTube query {query_idx + 1}/{len(search_queries)}: {search_query}")
|
||||||
|
|
||||||
|
# Get raw Spotify API response to access full album object with images
|
||||||
|
raw_results = spotify_client.sp.search(q=search_query, type='track', limit=5)
|
||||||
|
if not raw_results or 'tracks' not in raw_results or not raw_results['tracks']['items']:
|
||||||
|
continue
|
||||||
|
|
||||||
spotify_results = spotify_client.search_tracks(search_query, limit=5)
|
spotify_results = spotify_client.search_tracks(search_query, limit=5)
|
||||||
|
|
||||||
if not spotify_results:
|
if not spotify_results:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Score each result using matching engine
|
# Score each result using matching engine
|
||||||
for spotify_result in spotify_results:
|
for result_idx, spotify_result in enumerate(spotify_results):
|
||||||
|
raw_track = raw_results['tracks']['items'][result_idx] if result_idx < len(raw_results['tracks']['items']) else None
|
||||||
try:
|
try:
|
||||||
# Calculate confidence using matching engine's similarity scoring (with fallback)
|
# Calculate confidence using matching engine's similarity scoring (with fallback)
|
||||||
try:
|
try:
|
||||||
|
|
@ -13238,6 +13276,7 @@ def _run_youtube_discovery_worker(url_hash):
|
||||||
if combined_confidence > best_confidence and combined_confidence >= min_confidence:
|
if combined_confidence > best_confidence and combined_confidence >= min_confidence:
|
||||||
best_confidence = combined_confidence
|
best_confidence = combined_confidence
|
||||||
spotify_track = spotify_result
|
spotify_track = spotify_result
|
||||||
|
best_raw_track = raw_track # Store raw data with full album object
|
||||||
print(f"✅ New best YouTube match: {spotify_result.artists[0]} - {spotify_result.name} (confidence: {combined_confidence:.3f})")
|
print(f"✅ New best YouTube match: {spotify_result.artists[0]} - {spotify_result.name} (confidence: {combined_confidence:.3f})")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -13291,11 +13330,17 @@ def _run_youtube_discovery_worker(url_hash):
|
||||||
|
|
||||||
if spotify_track:
|
if spotify_track:
|
||||||
state['spotify_matches'] += 1
|
state['spotify_matches'] += 1
|
||||||
|
# Use full album object from raw Spotify data if available
|
||||||
|
album_data = best_raw_track.get('album', {}) if best_raw_track else {}
|
||||||
|
if not album_data:
|
||||||
|
# Fallback to string album name
|
||||||
|
album_data = {'name': spotify_track.album, 'album_type': 'album', 'images': []}
|
||||||
|
|
||||||
result['spotify_data'] = {
|
result['spotify_data'] = {
|
||||||
'id': spotify_track.id,
|
'id': spotify_track.id,
|
||||||
'name': spotify_track.name,
|
'name': spotify_track.name,
|
||||||
'artists': spotify_track.artists,
|
'artists': spotify_track.artists,
|
||||||
'album': spotify_track.album,
|
'album': album_data, # Full album object with images
|
||||||
'duration_ms': spotify_track.duration_ms
|
'duration_ms': spotify_track.duration_ms
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17868,19 +17913,27 @@ def _run_beatport_discovery_worker(url_hash):
|
||||||
|
|
||||||
# Try each search query until we find a good match
|
# Try each search query until we find a good match
|
||||||
best_match = None
|
best_match = None
|
||||||
|
best_raw_track = None # Store raw Spotify data for full album info
|
||||||
best_confidence = 0.0
|
best_confidence = 0.0
|
||||||
min_confidence = 0.75 # Increased threshold to avoid bad matches like "Dolce" for "Fancy $hit"
|
min_confidence = 0.75 # Increased threshold to avoid bad matches like "Dolce" for "Fancy $hit"
|
||||||
|
|
||||||
for query_idx, search_query in enumerate(search_queries):
|
for query_idx, search_query in enumerate(search_queries):
|
||||||
try:
|
try:
|
||||||
print(f"🔍 Query {query_idx + 1}/{len(search_queries)}: {search_query}")
|
print(f"🔍 Query {query_idx + 1}/{len(search_queries)}: {search_query}")
|
||||||
|
|
||||||
|
# Get raw Spotify API response to access full album object with images
|
||||||
|
raw_results = spotify_client.sp.search(q=search_query, type='track', limit=10)
|
||||||
|
if not raw_results or 'tracks' not in raw_results or not raw_results['tracks']['items']:
|
||||||
|
continue
|
||||||
|
|
||||||
search_results = spotify_client.search_tracks(search_query, limit=10)
|
search_results = spotify_client.search_tracks(search_query, limit=10)
|
||||||
|
|
||||||
if not search_results:
|
if not search_results:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Use matching engine to find the best match from search results
|
# Use matching engine to find the best match from search results
|
||||||
for result in search_results:
|
for result_idx, result in enumerate(search_results):
|
||||||
|
raw_track = raw_results['tracks']['items'][result_idx] if result_idx < len(raw_results['tracks']['items']) else None
|
||||||
try:
|
try:
|
||||||
# Calculate confidence using matching engine's similarity scoring (with fallback)
|
# Calculate confidence using matching engine's similarity scoring (with fallback)
|
||||||
try:
|
try:
|
||||||
|
|
@ -17938,6 +17991,7 @@ def _run_beatport_discovery_worker(url_hash):
|
||||||
core_title_confidence >= min_core_title_confidence):
|
core_title_confidence >= min_core_title_confidence):
|
||||||
best_confidence = combined_confidence
|
best_confidence = combined_confidence
|
||||||
best_match = result
|
best_match = result
|
||||||
|
best_raw_track = raw_track # Store raw data with full album object
|
||||||
print(f"✅ New best match: {result.artists[0]} - {result.name} (confidence: {combined_confidence:.3f})")
|
print(f"✅ New best match: {result.artists[0]} - {result.name} (confidence: {combined_confidence:.3f})")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -17988,10 +18042,16 @@ def _run_beatport_discovery_worker(url_hash):
|
||||||
# Single artist case
|
# Single artist case
|
||||||
formatted_artists = [{'name': str(spotify_track.artists)}]
|
formatted_artists = [{'name': str(spotify_track.artists)}]
|
||||||
|
|
||||||
|
# Use full album object from raw Spotify data if available
|
||||||
|
album_data = best_raw_track.get('album', {}) if best_raw_track else {}
|
||||||
|
if not album_data:
|
||||||
|
# Fallback to string album name
|
||||||
|
album_data = {'name': spotify_track.album, 'album_type': 'album', 'images': []}
|
||||||
|
|
||||||
result_entry['spotify_data'] = {
|
result_entry['spotify_data'] = {
|
||||||
'name': spotify_track.name,
|
'name': spotify_track.name,
|
||||||
'artists': formatted_artists, # Now formatted as list of objects with 'name' property
|
'artists': formatted_artists, # Now formatted as list of objects with 'name' property
|
||||||
'album': spotify_track.album, # Already a string
|
'album': album_data, # Full album object with images
|
||||||
'id': spotify_track.id
|
'id': spotify_track.id
|
||||||
# Remove uri for now since it's causing errors
|
# Remove uri for now since it's causing errors
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8807,38 +8807,35 @@ async function addModalTracksToWishlist(playlistId) {
|
||||||
artists: formattedArtists
|
artists: formattedArtists
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use track's own album data if available (has correct album_type)
|
// Use track's own album data if available
|
||||||
// If missing, fetch from Spotify using track ID
|
// Convert string album names to objects if needed (no Spotify fetch!)
|
||||||
let trackAlbum = track.album;
|
let trackAlbum = track.album;
|
||||||
let trackAlbumType = track.album?.album_type || 'album';
|
let trackAlbumType = 'album';
|
||||||
|
|
||||||
if (!trackAlbum || !trackAlbum.name) {
|
// Handle both object and string album formats
|
||||||
// Try to fetch track data from Spotify to get album info
|
if (typeof trackAlbum === 'string') {
|
||||||
if (track.spotify_track_id || track.id) {
|
// Album is just a string - convert to minimal object
|
||||||
try {
|
trackAlbum = {
|
||||||
const spotifyTrackId = track.spotify_track_id || track.id;
|
name: trackAlbum,
|
||||||
const trackInfoResponse = await fetch(`/api/spotify/track/${spotifyTrackId}`);
|
album_type: 'album',
|
||||||
const trackInfo = await trackInfoResponse.json();
|
images: []
|
||||||
|
};
|
||||||
if (trackInfo && trackInfo.album) {
|
trackAlbumType = 'album';
|
||||||
trackAlbum = trackInfo.album;
|
} else if (trackAlbum && typeof trackAlbum === 'object') {
|
||||||
trackAlbumType = trackInfo.album.album_type || 'album';
|
// Album is already an object - extract album_type
|
||||||
console.log(`✅ Fetched album data for "${track.name}"`);
|
trackAlbumType = trackAlbum.album_type || 'album';
|
||||||
} else {
|
// Ensure it has a name
|
||||||
console.warn(`⚠️ Skipping track "${track.name}" - could not fetch album data`);
|
if (!trackAlbum.name) {
|
||||||
errorCount++;
|
trackAlbum.name = 'Unknown Album';
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} catch (fetchError) {
|
|
||||||
console.error(`❌ Error fetching track data for "${track.name}":`, fetchError);
|
|
||||||
errorCount++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.warn(`⚠️ Skipping track "${track.name}" - missing album data and track ID`);
|
|
||||||
errorCount++;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// No album data at all - create minimal object
|
||||||
|
trackAlbum = {
|
||||||
|
name: 'Unknown Album',
|
||||||
|
album_type: 'album',
|
||||||
|
images: []
|
||||||
|
};
|
||||||
|
trackAlbumType = 'album';
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch('/api/add-album-to-wishlist', {
|
const response = await fetch('/api/add-album-to-wishlist', {
|
||||||
|
|
@ -11848,11 +11845,22 @@ async function startTidalDownloadMissing(urlHash) {
|
||||||
spotifyTracks.push(result.spotify_data);
|
spotifyTracks.push(result.spotify_data);
|
||||||
} else if (result.spotify_track && result.status_class === 'found') {
|
} else if (result.spotify_track && result.status_class === 'found') {
|
||||||
// Build from individual fields (automatic discovery format)
|
// Build from individual fields (automatic discovery format)
|
||||||
|
// Convert album to proper object format for wishlist compatibility
|
||||||
|
const albumData = result.spotify_album || 'Unknown Album';
|
||||||
|
const albumObject = typeof albumData === 'object' && albumData !== null
|
||||||
|
? albumData
|
||||||
|
: {
|
||||||
|
name: typeof albumData === 'string' ? albumData : 'Unknown Album',
|
||||||
|
album_type: 'album',
|
||||||
|
images: []
|
||||||
|
};
|
||||||
|
|
||||||
spotifyTracks.push({
|
spotifyTracks.push({
|
||||||
id: result.spotify_id || 'unknown',
|
id: result.spotify_id || 'unknown',
|
||||||
name: result.spotify_track || 'Unknown Track',
|
name: result.spotify_track || 'Unknown Track',
|
||||||
artists: result.spotify_artist ? [result.spotify_artist] : ['Unknown Artist'],
|
artists: result.spotify_artist ? [result.spotify_artist] : ['Unknown Artist'],
|
||||||
album: result.spotify_album || 'Unknown Album'
|
album: albumObject,
|
||||||
|
duration_ms: 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -13889,11 +13897,21 @@ async function startBeatportDownloadMissing(urlHash) {
|
||||||
track = result.spotify_data;
|
track = result.spotify_data;
|
||||||
} else {
|
} else {
|
||||||
// Build from individual fields (automatic discovery format)
|
// Build from individual fields (automatic discovery format)
|
||||||
|
// Convert album to proper object format for wishlist compatibility
|
||||||
|
const albumData = result.spotify_album || 'Unknown Album';
|
||||||
|
const albumObject = typeof albumData === 'object' && albumData !== null
|
||||||
|
? albumData
|
||||||
|
: {
|
||||||
|
name: typeof albumData === 'string' ? albumData : 'Unknown Album',
|
||||||
|
album_type: 'album',
|
||||||
|
images: []
|
||||||
|
};
|
||||||
|
|
||||||
track = {
|
track = {
|
||||||
id: result.spotify_id || 'unknown',
|
id: result.spotify_id || 'unknown',
|
||||||
name: result.spotify_track || 'Unknown Track',
|
name: result.spotify_track || 'Unknown Track',
|
||||||
artists: result.spotify_artist ? [result.spotify_artist] : ['Unknown Artist'],
|
artists: result.spotify_artist ? [result.spotify_artist] : ['Unknown Artist'],
|
||||||
album: result.spotify_album || 'Unknown Album',
|
album: albumObject,
|
||||||
duration_ms: 0
|
duration_ms: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -13908,11 +13926,21 @@ async function startBeatportDownloadMissing(urlHash) {
|
||||||
} else {
|
} else {
|
||||||
track.artists = ['Unknown Artist'];
|
track.artists = ['Unknown Artist'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure album is an object (in case it was converted back to string somehow)
|
||||||
|
const albumForReturn = typeof track.album === 'object' && track.album !== null
|
||||||
|
? track.album
|
||||||
|
: {
|
||||||
|
name: typeof track.album === 'string' ? track.album : 'Unknown Album',
|
||||||
|
album_type: 'album',
|
||||||
|
images: []
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: track.id,
|
id: track.id,
|
||||||
name: track.name,
|
name: track.name,
|
||||||
artists: track.artists,
|
artists: track.artists,
|
||||||
album: track.album || 'Unknown Album',
|
album: albumForReturn,
|
||||||
duration_ms: track.duration_ms || 0,
|
duration_ms: track.duration_ms || 0,
|
||||||
external_urls: track.external_urls || {}
|
external_urls: track.external_urls || {}
|
||||||
};
|
};
|
||||||
|
|
@ -16374,11 +16402,22 @@ async function startYouTubeDownloadMissing(urlHash) {
|
||||||
return result.spotify_data;
|
return result.spotify_data;
|
||||||
} else {
|
} else {
|
||||||
// Build from individual fields (automatic discovery format)
|
// Build from individual fields (automatic discovery format)
|
||||||
|
// Convert album to proper object format for wishlist compatibility
|
||||||
|
const albumData = result.spotify_album || 'Unknown Album';
|
||||||
|
const albumObject = typeof albumData === 'object' && albumData !== null
|
||||||
|
? albumData
|
||||||
|
: {
|
||||||
|
name: typeof albumData === 'string' ? albumData : 'Unknown Album',
|
||||||
|
album_type: 'album',
|
||||||
|
images: []
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: result.spotify_id || 'unknown',
|
id: result.spotify_id || 'unknown',
|
||||||
name: result.spotify_track || 'Unknown Track',
|
name: result.spotify_track || 'Unknown Track',
|
||||||
artists: result.spotify_artist ? [result.spotify_artist] : ['Unknown Artist'],
|
artists: result.spotify_artist ? [result.spotify_artist] : ['Unknown Artist'],
|
||||||
album: result.spotify_album || 'Unknown Album'
|
album: albumObject,
|
||||||
|
duration_ms: 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue