From 3ce0955b1cc5ff43b81aefcb95b30fcbf1b2cf8b Mon Sep 17 00:00:00 2001 From: Broque Thomas Date: Thu, 19 Feb 2026 20:52:47 -0800 Subject: [PATCH] fixed an issue where the wishlist deduplication was inconsistent between front and backend --- web_server.py | 148 ++++++++++++++++++-------------------------------- 1 file changed, 52 insertions(+), 96 deletions(-) diff --git a/web_server.py b/web_server.py index 6f82c91..045457b 100644 --- a/web_server.py +++ b/web_server.py @@ -10431,6 +10431,43 @@ def schedule_next_wishlist_processing(retry_count=0, max_retries=3): print(f"❌ [FATAL] Failed to schedule wishlist processing after {max_retries} attempts!") print("⚠️ MANUAL INTERVENTION REQUIRED - Wishlist auto-processing will not run!") +def _classify_wishlist_track(track): + """Classify a wishlist track as 'singles' or 'albums'. + + Uses Spotify's album_type as the primary signal (most authoritative), + falls back to total_tracks heuristic, defaults to 'albums'. + + Returns: + 'singles' or 'albums' + """ + spotify_data = track.get('spotify_data', {}) + if isinstance(spotify_data, str): + try: + import json + spotify_data = json.loads(spotify_data) + except Exception: + spotify_data = {} + + album_data = spotify_data.get('album') or {} + if not isinstance(album_data, dict): + album_data = {} + total_tracks = album_data.get('total_tracks') + album_type = album_data.get('album_type', '').lower() + + # Prioritize Spotify's album_type classification (most accurate) + if album_type in ('single', 'ep'): + return 'singles' + if album_type in ('album', 'compilation'): + return 'albums' + + # Fallback: track count heuristic + if total_tracks is not None and total_tracks > 0: + return 'singles' if total_tracks < 6 else 'albums' + + # No classification data — default to albums + return 'albums' + + def _process_wishlist_automatically(): """Main automatic processing logic that runs in background thread.""" global wishlist_auto_processing, wishlist_auto_processing_timestamp @@ -10614,48 +10651,12 @@ def _process_wishlist_automatically(): conn.commit() # Filter tracks by current cycle category - import json filtered_tracks = [] seen_track_ids_filtering = set() # Deduplicate during filtering for track in wishlist_tracks: - # Extract track count from spotify_data JSON (after sanitization) - spotify_data = track.get('spotify_data', {}) - if isinstance(spotify_data, str): - try: - spotify_data = json.loads(spotify_data) - except: - spotify_data = {} - - # CRITICAL FIX: Handle case where album is explicitly null in JSON - album_data = spotify_data.get('album') or {} - # Ensure album_data is a dict (just in case it's a string/other) - if not isinstance(album_data, dict): - album_data = {} - total_tracks = album_data.get('total_tracks') - album_type = album_data.get('album_type', 'album').lower() - - # CRITICAL FIX: Prioritize Spotify's album_type (most accurate source) - # Only fall back to track count heuristic if album_type is missing - is_single_or_ep = False - is_album = False - - # Use Spotify's album_type classification first - if album_type and album_type in ['single', 'ep', 'album', 'compilation']: - # Spotify's classification is authoritative - is_single_or_ep = album_type in ['single', 'ep'] - is_album = album_type in ['album', 'compilation'] - elif total_tracks is not None and total_tracks > 0: - # Fallback: Use track count heuristic only if album_type unavailable - # Single: 1 track, EP: 2-5 tracks, Album: 6+ tracks - is_single_or_ep = total_tracks < 6 - is_album = total_tracks >= 6 - else: - # No classification data available - default to album - is_album = True - is_single_or_ep = False - + track_category = _classify_wishlist_track(track) spotify_track_id = track.get('spotify_track_id') or track.get('id') - matches_category = (current_cycle == 'singles' and is_single_or_ep) or (current_cycle == 'albums' and is_album) + matches_category = (current_cycle == track_category) # Only add if matches category AND not a duplicate if matches_category: @@ -10871,36 +10872,21 @@ def get_wishlist_stats(): singles_count = 0 albums_count = 0 + seen_ids = set() for track in raw_tracks: - # 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) + # Deduplicate by ID (same as tracks endpoint) so counts match + track_id = track.get('spotify_track_id') or track.get('id') + if track_id: + if track_id in seen_ids: + continue + seen_ids.add(track_id) - # CRITICAL FIX: Handle case where album is explicitly null in JSON - album_data = spotify_data.get('album') or {} - # Ensure album_data is a dict (just in case) - if not isinstance(album_data, dict): - album_data = {} - total_tracks = album_data.get('total_tracks') - album_type = album_data.get('album_type', 'album').lower() - - # 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 + category = _classify_wishlist_track(track) + if category == 'singles': + singles_count += 1 else: - # Fall back to Spotify's album_type - if album_type in ['single', 'ep']: - singles_count += 1 - else: - albums_count += 1 + albums_count += 1 total_count = singles_count + albums_count @@ -11138,42 +11124,12 @@ def get_wishlist_tracks(): # FILTER by category if specified if category: - import json filtered_tracks = [] seen_track_ids_filtering = set() # Deduplicate during filtering for track in sanitized_tracks: - # Extract track count from spotify_data JSON (same as stats endpoint) - spotify_data = track.get('spotify_data', {}) - if isinstance(spotify_data, str): - try: - spotify_data = json.loads(spotify_data) - except: - spotify_data = {} - - # CRITICAL FIX: Handle case where album is explicitly null in JSON - album_data = spotify_data.get('album') or {} - # Ensure album_data is a dict (just in case) - if not isinstance(album_data, dict): - album_data = {} - total_tracks = album_data.get('total_tracks') - album_type = album_data.get('album_type', 'album').lower() - - # 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' - + track_category = _classify_wishlist_track(track) spotify_track_id = track.get('spotify_track_id') or track.get('id') - matches_category = (category == 'singles' and is_single_or_ep) or (category == 'albums' and is_album) + matches_category = (category == track_category) # Only add if matches category AND not a duplicate if matches_category: