From 55ae33a8872bbe29bff185bd58bc448d3522e253 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 26 Feb 2026 20:35:26 +0000 Subject: [PATCH] Migrate Spotify API endpoints for Feb 2026 Dev Mode changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Affected endpoints (breaking March 9, 2026): - GET /artists/{id}/top-tracks removed → replaced with track search via GET /search?q=artist:{name}&type=track with artist filter - POST /users/{user_id}/playlists removed → replaced with POST /me/playlists - POST /playlists/{id}/tracks renamed → POST /playlists/{id}/items - GET /playlists/{id}/tracks renamed → GET /playlists/{id}/items - Search limit enforced to max 10 per request (new Dev Mode restriction) --- app/helper/create_playlist.py | 10 ++++++---- app/helper/lastfm_profile.py | 5 +++-- app/helper/recommendations.py | 35 +++++++++++++++++++---------------- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/app/helper/create_playlist.py b/app/helper/create_playlist.py index 93425b7..15a5e7f 100755 --- a/app/helper/create_playlist.py +++ b/app/helper/create_playlist.py @@ -24,8 +24,9 @@ def create_playlist(username, token, data): if not uris: return False - # Create new playlist - endpoint_url = f"https://api.spotify.com/v1/users/{username}/playlists" + # Create new playlist via /me/playlists + # Note: POST /users/{user_id}/playlists was removed in Feb 2026 Dev Mode changes + endpoint_url = "https://api.spotify.com/v1/me/playlists" request_body = json.dumps({ "name": playlist_name, "description": "Created by Magic Music Generator", @@ -47,8 +48,9 @@ def create_playlist(username, token, data): if not playlist_id: return False - # Add tracks to playlist - endpoint_url = f"https://api.spotify.com/v1/playlists/{playlist_id}/tracks" + # Add tracks to playlist via /items + # Note: POST /playlists/{id}/tracks was renamed to /items in Feb 2026 Dev Mode changes + endpoint_url = f"https://api.spotify.com/v1/playlists/{playlist_id}/items" request_body = json.dumps({"uris": uris}) response = requests.post( diff --git a/app/helper/lastfm_profile.py b/app/helper/lastfm_profile.py index 1882f1d..6fe2bb3 100644 --- a/app/helper/lastfm_profile.py +++ b/app/helper/lastfm_profile.py @@ -267,9 +267,10 @@ def get_user_playlists(token, limit=50): def get_playlist_tracks(token, playlist_id, limit=50): """ Get tracks from a specific playlist. + Note: /playlists/{id}/tracks was renamed to /playlists/{id}/items in Feb 2026 Dev Mode changes. """ - url = f"https://api.spotify.com/v1/playlists/{playlist_id}/tracks" - params = {"limit": limit, "fields": "items(track(name,artists,uri,album))"} + url = f"https://api.spotify.com/v1/playlists/{playlist_id}/items" + params = {"limit": min(limit, 50), "fields": "items(track(name,artists,uri,album))"} headers = {"Authorization": f"Bearer {token}"} try: diff --git a/app/helper/recommendations.py b/app/helper/recommendations.py index face29f..b8d1e1d 100755 --- a/app/helper/recommendations.py +++ b/app/helper/recommendations.py @@ -93,15 +93,18 @@ def search_track_on_spotify(track_name, artist_name, token): def search_artist_top_tracks_spotify(artist_name, token, limit=3): """ - Search for an artist on Spotify and get their top tracks. - Fallback when Last.fm is not available. + Get top tracks for an artist via Spotify search. + Note: GET /artists/{id}/top-tracks was removed in Feb 2026 Dev Mode changes. + We now use search with artist filter instead. """ - # First, find the artist + # Enforce the new max limit of 10 + limit = min(limit, 10) + url = "https://api.spotify.com/v1/search" params = { "q": f'artist:"{artist_name}"', - "type": "artist", - "limit": 1, + "type": "track", + "limit": limit, "market": "DE" } headers = {"Authorization": f"Bearer {token}"} @@ -112,19 +115,17 @@ def search_artist_top_tracks_spotify(artist_name, token, limit=3): return [] data = response.json() - if not data["artists"]["items"]: - return [] - - artist_id = data["artists"]["items"][0]["id"] + tracks = data.get("tracks", {}).get("items", []) - # Get top tracks for this artist - top_tracks_url = f"https://api.spotify.com/v1/artists/{artist_id}/top-tracks" - params = {"market": "DE"} - response = requests.get(top_tracks_url, params=params, headers=headers, timeout=10) + # Filter to only include tracks actually by this artist + artist_lower = artist_name.lower() + filtered = [ + t for t in tracks + if any(artist_lower in a["name"].lower() for a in t.get("artists", [])) + ] - if response.status_code == 200: - tracks_data = response.json() - return tracks_data.get("tracks", [])[:limit] + # Fall back to unfiltered if strict match returns nothing + return (filtered or tracks)[:limit] except Exception: pass @@ -135,7 +136,9 @@ def search_by_genre_spotify(genre, token, limit=10): """ Search for tracks by genre on Spotify. Fallback method when artist-based search yields few results. + Note: max limit is 10 in Dev Mode since Feb 2026. """ + limit = min(limit, 10) # Enforce new Dev Mode limit url = "https://api.spotify.com/v1/search" params = { "q": f'genre:"{genre}"',