Use new Spotify /items endpoint with fallback
Add _get_playlist_items_page to call the new playlists/{id}/items endpoint (Feb 2026 API migration) and fall back to spotipy.playlist_items (old /tracks) on 403/404. Update _get_playlist_tracks and web_server.get_playlist_tracks to use the new helper to avoid 403 errors for Development Mode apps while preserving compatibility with Extended Quota Mode.
This commit is contained in:
parent
58df2ba33c
commit
2aa529f8e4
2 changed files with 29 additions and 7 deletions
|
|
@ -505,15 +505,37 @@ class SpotifyClient:
|
||||||
logger.error(f"Error fetching saved tracks: {e}")
|
logger.error(f"Error fetching saved tracks: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
def _get_playlist_items_page(self, playlist_id: str, limit: int = 100, offset: int = 0) -> dict:
|
||||||
|
"""Fetch playlist items using the /items endpoint (Feb 2026 Spotify API migration).
|
||||||
|
|
||||||
|
Spotipy's playlist_items() still uses the deprecated /tracks endpoint internally,
|
||||||
|
which returns 403 for Development Mode apps after the Feb 2026 API changes.
|
||||||
|
Tries the new /items endpoint first, falls back to spotipy's /tracks for
|
||||||
|
Extended Quota Mode apps where /items may not be available yet.
|
||||||
|
"""
|
||||||
|
plid = self.sp._get_id("playlist", playlist_id)
|
||||||
|
try:
|
||||||
|
return self.sp._get(
|
||||||
|
f"playlists/{plid}/items",
|
||||||
|
limit=limit,
|
||||||
|
offset=offset,
|
||||||
|
additional_types="track,episode"
|
||||||
|
)
|
||||||
|
except spotipy.SpotifyException as e:
|
||||||
|
if e.http_status in (403, 404):
|
||||||
|
# /items not available — fall back to old /tracks endpoint
|
||||||
|
return self.sp.playlist_items(playlist_id, limit=limit, offset=offset)
|
||||||
|
raise
|
||||||
|
|
||||||
@rate_limited
|
@rate_limited
|
||||||
def _get_playlist_tracks(self, playlist_id: str) -> List[Track]:
|
def _get_playlist_tracks(self, playlist_id: str) -> List[Track]:
|
||||||
if not self.is_spotify_authenticated():
|
if not self.is_spotify_authenticated():
|
||||||
return []
|
return []
|
||||||
|
|
||||||
tracks = []
|
tracks = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
results = self.sp.playlist_items(playlist_id, limit=100)
|
results = self._get_playlist_items_page(playlist_id, limit=100)
|
||||||
|
|
||||||
while results:
|
while results:
|
||||||
for item in results['items']:
|
for item in results['items']:
|
||||||
|
|
@ -522,11 +544,11 @@ class SpotifyClient:
|
||||||
if track_data and track_data.get('id'):
|
if track_data and track_data.get('id'):
|
||||||
track = Track.from_spotify_track(track_data)
|
track = Track.from_spotify_track(track_data)
|
||||||
tracks.append(track)
|
tracks.append(track)
|
||||||
|
|
||||||
results = self.sp.next(results) if results['next'] else None
|
results = self.sp.next(results) if results['next'] else None
|
||||||
|
|
||||||
return tracks
|
return tracks
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error fetching playlist tracks: {e}")
|
logger.error(f"Error fetching playlist tracks: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
|
||||||
|
|
@ -16396,7 +16396,7 @@ def get_playlist_tracks(playlist_id):
|
||||||
|
|
||||||
# Fetch all tracks with full album data
|
# Fetch all tracks with full album data
|
||||||
tracks = []
|
tracks = []
|
||||||
results = spotify_client.sp.playlist_items(playlist_id, limit=100)
|
results = spotify_client._get_playlist_items_page(playlist_id, limit=100)
|
||||||
|
|
||||||
while results:
|
while results:
|
||||||
for item in results['items']:
|
for item in results['items']:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue