further progress on wishlist rebuild
This commit is contained in:
parent
0b2322f225
commit
4804a995f7
4 changed files with 182 additions and 41 deletions
|
|
@ -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}")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
104
web_server.py
104
web_server.py
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue