diff --git a/core/__pycache__/wishlist_service.cpython-312.pyc b/core/__pycache__/wishlist_service.cpython-312.pyc index b1536d48..8c6bdd56 100644 Binary files a/core/__pycache__/wishlist_service.cpython-312.pyc and b/core/__pycache__/wishlist_service.cpython-312.pyc differ diff --git a/core/wishlist_service.py b/core/wishlist_service.py index 2ee0d78c..62d97cd0 100644 --- a/core/wishlist_service.py +++ b/core/wishlist_service.py @@ -47,10 +47,26 @@ class WishlistService: # Create source info source_info = source_context or {} + + # Clean up candidates to avoid TrackResult serialization issues + candidates = track_info.get('candidates', []) + cleaned_candidates = [] + for candidate in candidates: + if hasattr(candidate, '__dict__'): + # Convert TrackResult objects to simple dictionaries + cleaned_candidates.append({ + 'title': getattr(candidate, 'title', 'Unknown'), + 'artist': getattr(candidate, 'artist', 'Unknown'), + 'filename': getattr(candidate, 'filename', 'Unknown') + }) + else: + # Keep simple data as-is + cleaned_candidates.append(candidate) + source_info['original_modal_data'] = { 'download_index': track_info.get('download_index'), 'table_index': track_info.get('table_index'), - 'candidates': track_info.get('candidates', []) + 'candidates': cleaned_candidates } # Add to wishlist via database @@ -240,27 +256,97 @@ class WishlistService: return None def _spotify_track_object_to_dict(self, spotify_track) -> Dict[str, Any]: - """Convert a Spotify track object to a dictionary""" + """Convert a Spotify track object or TrackResult object to a dictionary""" try: - return { + # Add debug logging to see what we're dealing with + logger.info(f"DEBUG: Converting track object to dict. Type: {type(spotify_track)}") + logger.info(f"DEBUG: Has 'title' attribute: {hasattr(spotify_track, 'title')}") + logger.info(f"DEBUG: Has 'artist' attribute: {hasattr(spotify_track, 'artist')}") + logger.info(f"DEBUG: Has 'id' attribute: {hasattr(spotify_track, 'id')}") + + # Check if this is a TrackResult object (has title/artist but no id) + if hasattr(spotify_track, 'title') and hasattr(spotify_track, 'artist') and not hasattr(spotify_track, 'id'): + logger.info("DEBUG: Detected TrackResult object, converting...") + # Handle TrackResult objects - these don't have Spotify IDs + result = { + 'id': f"trackresult_{hash(f'{spotify_track.artist}_{spotify_track.title}')}", + 'name': getattr(spotify_track, 'title', 'Unknown Track'), + 'artists': [{'name': getattr(spotify_track, 'artist', 'Unknown Artist')}], + 'album': {'name': getattr(spotify_track, 'album', 'Unknown Album')}, + 'duration_ms': 0, # TrackResult doesn't have duration + 'preview_url': None, + 'external_urls': {}, + 'popularity': 0, + 'source': 'trackresult' # Mark as reconstructed from TrackResult + } + logger.info(f"DEBUG: TrackResult converted successfully: {result['name']} by {result['artists'][0]['name']}") + return result + + # Handle regular Spotify Track objects + logger.info("DEBUG: Processing as Spotify Track object") + + # Handle artists list carefully to avoid TrackResult serialization issues + artists_list = [] + raw_artists = getattr(spotify_track, 'artists', []) + logger.info(f"DEBUG: Raw artists: {raw_artists}, type: {type(raw_artists)}") + + for artist in raw_artists: + logger.info(f"DEBUG: Processing artist: {artist}, type: {type(artist)}") + if hasattr(artist, 'name'): + artists_list.append({'name': artist.name}) + elif isinstance(artist, str): + artists_list.append({'name': artist}) + else: + # Convert any complex objects to string to avoid serialization issues + artists_list.append({'name': str(artist)}) + + # Handle album safely + album_name = 'Unknown Album' + if hasattr(spotify_track, 'album') and spotify_track.album: + if hasattr(spotify_track.album, 'name'): + album_name = spotify_track.album.name + else: + album_name = str(spotify_track.album) + + result = { 'id': getattr(spotify_track, 'id', None), 'name': getattr(spotify_track, 'name', 'Unknown Track'), - 'artists': [ - {'name': artist.name if hasattr(artist, 'name') else str(artist)} - for artist in getattr(spotify_track, 'artists', []) - ], - 'album': { - 'name': getattr(spotify_track.album, 'name', 'Unknown Album') - if hasattr(spotify_track, 'album') and spotify_track.album - else 'Unknown Album' - }, + 'artists': artists_list, + 'album': {'name': album_name}, 'duration_ms': getattr(spotify_track, 'duration_ms', 0), 'preview_url': getattr(spotify_track, 'preview_url', None), 'external_urls': getattr(spotify_track, 'external_urls', {}), 'popularity': getattr(spotify_track, 'popularity', 0) } + + logger.info(f"DEBUG: Spotify Track converted: {result['name']} by {[a['name'] for a in result['artists']]}") + + # Test JSON serialization before returning to catch any remaining issues + try: + import json + json.dumps(result) + logger.info("DEBUG: Conversion result is JSON serializable") + except Exception as json_error: + logger.error(f"DEBUG: Conversion result is NOT JSON serializable: {json_error}") + logger.error(f"DEBUG: Result content: {result}") + # Return a safe fallback + return { + 'id': f"fallback_{hash(str(spotify_track))}", + 'name': str(getattr(spotify_track, 'name', 'Unknown Track')), + 'artists': [{'name': 'Unknown Artist'}], + 'album': {'name': 'Unknown Album'}, + 'duration_ms': 0, + 'preview_url': None, + 'external_urls': {}, + 'popularity': 0, + 'source': 'fallback' + } + + return result except Exception as e: - logger.error(f"Error converting Spotify track object to dict: {e}") + logger.error(f"Error converting track object to dict: {e}") + logger.error(f"Object type: {type(spotify_track)}") + logger.error(f"Object attributes: {dir(spotify_track)}") return {} # Global singleton instance