further progress on wishlist rebuild

This commit is contained in:
Broque Thomas 2025-11-23 19:14:13 -08:00
parent 0b2322f225
commit 4804a995f7
4 changed files with 182 additions and 41 deletions

View file

@ -191,7 +191,8 @@ class PersonalizedPlaylistsService:
album_cover_url,
duration_ms,
popularity,
release_date
release_date,
track_data_json
FROM discovery_pool
WHERE release_date IS NOT NULL
AND CAST(SUBSTR(release_date, 1, 4) AS INTEGER) BETWEEN ? AND ?
@ -200,7 +201,17 @@ class PersonalizedPlaylistsService:
""", (start_year, end_year, limit * 10))
rows = cursor.fetchall()
all_tracks = [dict(row) for row in rows]
all_tracks = []
for row in rows:
track_dict = dict(row)
# Parse track_data_json if available
if track_dict.get('track_data_json'):
try:
import json
track_dict['track_data_json'] = json.loads(track_dict['track_data_json'])
except:
pass
all_tracks.append(track_dict)
if not all_tracks:
logger.warning(f"No tracks found for {decade}s")
@ -336,7 +347,8 @@ class PersonalizedPlaylistsService:
album_cover_url,
duration_ms,
popularity,
artist_genres
artist_genres,
track_data_json
FROM discovery_pool
WHERE artist_genres IS NOT NULL
""")
@ -386,6 +398,12 @@ class PersonalizedPlaylistsService:
'duration_ms': row[5],
'popularity': row[6]
}
# Parse track_data_json if available
if row[8]: # track_data_json column
try:
track_dict['track_data_json'] = json.loads(row[8])
except:
pass
matching_tracks.append(track_dict)
except Exception as e:
logger.debug(f"Error parsing genres for track: {e}")
@ -470,7 +488,8 @@ class PersonalizedPlaylistsService:
album_name,
album_cover_url,
duration_ms,
popularity
popularity,
track_data_json
FROM discovery_pool
WHERE popularity >= 60
ORDER BY popularity DESC, RANDOM()
@ -478,7 +497,17 @@ class PersonalizedPlaylistsService:
""", (limit * 3,)) # Get 3x more for diversity filtering
rows = cursor.fetchall()
all_tracks = [dict(row) for row in rows]
all_tracks = []
for row in rows:
track_dict = dict(row)
# Parse track_data_json if available
if track_dict.get('track_data_json'):
try:
import json
track_dict['track_data_json'] = json.loads(track_dict['track_data_json'])
except:
pass
all_tracks.append(track_dict)
# Apply diversity constraint: max 2 tracks per album, max 3 per artist
tracks_by_album = {}
@ -523,7 +552,8 @@ class PersonalizedPlaylistsService:
album_name,
album_cover_url,
duration_ms,
popularity
popularity,
track_data_json
FROM discovery_pool
WHERE popularity < 40
ORDER BY RANDOM()
@ -531,7 +561,18 @@ class PersonalizedPlaylistsService:
""", (limit,))
rows = cursor.fetchall()
return [dict(row) for row in rows]
tracks = []
for row in rows:
track_dict = dict(row)
# Parse track_data_json if available
if track_dict.get('track_data_json'):
try:
import json
track_dict['track_data_json'] = json.loads(track_dict['track_data_json'])
except:
pass
tracks.append(track_dict)
return tracks
except Exception as e:
logger.error(f"Error getting hidden gems: {e}")
@ -555,14 +596,26 @@ class PersonalizedPlaylistsService:
album_name,
album_cover_url,
duration_ms,
popularity
popularity,
track_data_json
FROM discovery_pool
ORDER BY RANDOM()
LIMIT ?
""", (limit,))
rows = cursor.fetchall()
return [dict(row) for row in rows]
tracks = []
for row in rows:
track_dict = dict(row)
# Parse track_data_json if available
if track_dict.get('track_data_json'):
try:
import json
track_dict['track_data_json'] = json.loads(track_dict['track_data_json'])
except:
pass
tracks.append(track_dict)
return tracks
except Exception as e:
logger.error(f"Error getting discovery shuffle: {e}")
@ -728,7 +781,8 @@ class PersonalizedPlaylistsService:
album_name,
album_cover_url,
duration_ms,
popularity
popularity,
track_data_json
FROM discovery_pool
WHERE artist_name LIKE ? OR track_name LIKE ?
ORDER BY RANDOM()
@ -736,7 +790,18 @@ class PersonalizedPlaylistsService:
""", (f'%{category}%', f'%{category}%', limit))
rows = cursor.fetchall()
return [dict(row) for row in rows]
tracks = []
for row in rows:
track_dict = dict(row)
# Parse track_data_json if available
if track_dict.get('track_data_json'):
try:
import json
track_dict['track_data_json'] = json.loads(track_dict['track_data_json'])
except:
pass
tracks.append(track_dict)
return tracks
except Exception as e:
logger.error(f"Error getting discovery tracks by category: {e}")

View file

@ -332,6 +332,15 @@ class SeasonalDiscoveryService:
rows = cursor.fetchall()
for row in rows:
import json
# Parse track_data_json if it's a string
track_data_json = row['track_data_json']
if isinstance(track_data_json, str):
try:
track_data_json = json.loads(track_data_json)
except:
track_data_json = {}
seasonal_tracks.append({
'spotify_track_id': row['spotify_track_id'],
'track_name': row['track_name'],
@ -340,7 +349,7 @@ class SeasonalDiscoveryService:
'album_cover_url': row['album_cover_url'],
'duration_ms': row['duration_ms'],
'popularity': row['popularity'],
'track_data_json': row['track_data_json']
'track_data_json': track_data_json # Now parsed as dict
})
return seasonal_tracks
@ -643,6 +652,19 @@ class SeasonalDiscoveryService:
# Use track's actual artist, not album artist
track_artist = track['artists'][0]['name'] if track.get('artists') else album['artist_name']
# Enhance track object with full album data (including total_tracks)
enhanced_track = {
**track,
'album': {
'id': album_data['id'],
'name': album_data.get('name', 'Unknown Album'),
'images': album_data.get('images', []),
'release_date': album_data.get('release_date', ''),
'album_type': album_data.get('album_type', 'album'),
'total_tracks': album_data.get('total_tracks', 0)
}
}
track_data = {
'spotify_track_id': track['id'],
'track_name': track['name'],
@ -650,7 +672,8 @@ class SeasonalDiscoveryService:
'album_name': album['album_name'],
'popularity': album.get('popularity', 50),
'album_cover_url': album.get('album_cover_url'),
'duration_ms': track.get('duration_ms', 0)
'duration_ms': track.get('duration_ms', 0),
'track_data_json': enhanced_track # Add full track data with album info
}
all_tracks.append(track_data)

View file

@ -523,12 +523,14 @@ class WatchlistScanner:
album_release_date = album.get('release_date', '')
album_images = album.get('images', [])
album_type = album.get('album_type', 'album') # 'album', 'single', or 'ep'
total_tracks = album.get('total_tracks', 0)
else:
album_name = album.name
album_id = album.id
album_release_date = album.release_date
album_images = album.images if hasattr(album, 'images') else []
album_type = album.album_type if hasattr(album, 'album_type') else 'album'
total_tracks = album.total_tracks if hasattr(album, 'total_tracks') else 0
# Create Spotify track data structure
spotify_track_data = {
@ -540,7 +542,8 @@ class WatchlistScanner:
'id': album_id,
'release_date': album_release_date,
'images': album_images,
'album_type': album_type # Store album type for category filtering
'album_type': album_type, # Store album type for category filtering
'total_tracks': total_tracks # Store track count for accurate categorization
},
'duration_ms': track_duration,
'explicit': track_explicit,

View file

@ -7691,7 +7691,7 @@ def _process_wishlist_automatically():
import json
filtered_tracks = []
for track in wishlist_tracks:
# Extract album_type from spotify_data JSON (after sanitization)
# Extract track count from spotify_data JSON (after sanitization)
spotify_data = track.get('spotify_data', {})
if isinstance(spotify_data, str):
try:
@ -7700,14 +7700,27 @@ def _process_wishlist_automatically():
spotify_data = {}
album_data = spotify_data.get('album', {})
total_tracks = album_data.get('total_tracks')
album_type = album_data.get('album_type', 'album').lower()
if current_cycle == 'singles':
if album_type in ['single', 'ep']:
filtered_tracks.append(track)
elif current_cycle == 'albums':
if album_type == 'album':
filtered_tracks.append(track)
# Categorize by track count if available, otherwise use album_type
# Single: 1 track, EP: 2-5 tracks, Album: 6+ tracks
is_single_or_ep = False
is_album = False
if total_tracks is not None and total_tracks > 0:
# Use track count (most accurate)
is_single_or_ep = total_tracks < 6
is_album = total_tracks >= 6
else:
# Fall back to Spotify's album_type
is_single_or_ep = album_type in ['single', 'ep']
is_album = album_type == 'album'
if current_cycle == 'singles' and is_single_or_ep:
filtered_tracks.append(track)
elif current_cycle == 'albums' and is_album:
filtered_tracks.append(track)
print(f"🔄 [Auto-Wishlist] Current cycle: {current_cycle}")
print(f"📊 [Auto-Wishlist] Filtered {len(filtered_tracks)}/{len(wishlist_tracks)} tracks for '{current_cycle}' category")
@ -7911,22 +7924,30 @@ def get_wishlist_stats():
albums_count = 0
for track in raw_tracks:
# Extract album_type from spotify_data JSON
# Extract track count from spotify_data JSON
spotify_data = track.get('spotify_data', {})
if isinstance(spotify_data, str):
import json
spotify_data = json.loads(spotify_data)
album_data = spotify_data.get('album', {})
total_tracks = album_data.get('total_tracks')
album_type = album_data.get('album_type', 'album').lower()
if album_type in ['single', 'ep']:
singles_count += 1
elif album_type == 'album':
albums_count += 1
# Categorize by track count if available, otherwise use album_type
# Single: 1 track, EP: 2-5 tracks, Album: 6+ tracks
if total_tracks is not None and total_tracks > 0:
# Use track count (most accurate)
if total_tracks >= 6:
albums_count += 1
else:
singles_count += 1
else:
# Default unknown types to albums
albums_count += 1
# Fall back to Spotify's album_type
if album_type in ['single', 'ep']:
singles_count += 1
else:
albums_count += 1
total_count = singles_count + albums_count
@ -8127,7 +8148,7 @@ def get_wishlist_tracks():
import json
filtered_tracks = []
for track in sanitized_tracks:
# Extract album_type from spotify_data JSON (same as stats endpoint)
# Extract track count from spotify_data JSON (same as stats endpoint)
spotify_data = track.get('spotify_data', {})
if isinstance(spotify_data, str):
try:
@ -8136,16 +8157,27 @@ def get_wishlist_tracks():
spotify_data = {}
album_data = spotify_data.get('album', {})
total_tracks = album_data.get('total_tracks')
album_type = album_data.get('album_type', 'album').lower()
if category == 'singles':
# Singles category includes 'single' and 'ep'
if album_type in ['single', 'ep']:
filtered_tracks.append(track)
elif category == 'albums':
# Albums category includes only 'album'
if album_type == 'album':
filtered_tracks.append(track)
# Categorize by track count if available, otherwise use album_type
# Single: 1 track, EP: 2-5 tracks, Album: 6+ tracks
is_single_or_ep = False
is_album = False
if total_tracks is not None and total_tracks > 0:
# Use track count (most accurate)
is_single_or_ep = total_tracks < 6
is_album = total_tracks >= 6
else:
# Fall back to Spotify's album_type
is_single_or_ep = album_type in ['single', 'ep']
is_album = album_type == 'album'
if category == 'singles' and is_single_or_ep:
filtered_tracks.append(track)
elif category == 'albums' and is_album:
filtered_tracks.append(track)
print(f"📊 Wishlist filter: {len(filtered_tracks)}/{len(sanitized_tracks)} tracks in '{category}' category")
return jsonify({"tracks": filtered_tracks, "category": category})
@ -15251,7 +15283,8 @@ def get_seasonal_playlist(season_key):
album_name,
album_cover_url,
duration_ms,
popularity
popularity,
track_data_json
FROM seasonal_tracks
WHERE spotify_track_id = ?
""", (track_id,))
@ -15259,7 +15292,15 @@ def get_seasonal_playlist(season_key):
result = cursor.fetchone()
if result:
tracks.append(dict(result))
track_dict = dict(result)
# Parse track_data_json if available
if track_dict.get('track_data_json'):
try:
import json
track_dict['track_data_json'] = json.loads(track_dict['track_data_json'])
except:
pass
tracks.append(track_dict)
else:
# Try discovery_pool as fallback
cursor.execute("""
@ -15270,14 +15311,23 @@ def get_seasonal_playlist(season_key):
album_name,
album_cover_url,
duration_ms,
popularity
popularity,
track_data_json
FROM discovery_pool
WHERE spotify_track_id = ?
""", (track_id,))
result = cursor.fetchone()
if result:
tracks.append(dict(result))
track_dict = dict(result)
# Parse track_data_json if available
if track_dict.get('track_data_json'):
try:
import json
track_dict['track_data_json'] = json.loads(track_dict['track_data_json'])
except:
pass
tracks.append(track_dict)
config = SEASONAL_CONFIG[season_key]