diff --git a/CHANGELOG.md b/CHANGELOG.md index 86ebb8b9..7dde3027 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,14 @@ Add your changes below. - Added `personalized_playlist.py`, `track_recommendations.py`, and `audio_features_analysis.py` to `/examples`. - Discord badge in README - Added `SpotifyBaseException` and moved all exceptions to `exceptions.py` +- Marked the following methods as deprecated: + - artist_related_artists + - recommendations + - audio_features + - audio_analysis + - featured_playlists + - category_playlists +- Added FAQ entry for inaccessible playlists ### Fixed - Audiobook integration tests diff --git a/FAQ.md b/FAQ.md index f34fa19f..de3caf81 100644 --- a/FAQ.md +++ b/FAQ.md @@ -74,3 +74,9 @@ sp = spotipy.Spotify( ) ``` The error raised is a `spotipy.exceptions.SpotifyException` + +### I get a 404 when trying to access a Spotify-owned playlist + +Spotify has begun restricting access to algorithmic and Spotify-owned editorial playlists. +Only applications with an existing extended mode will still have access to these playlists. +Read more about this change here: [Introducing some changes to our Web API](https://developer.spotify.com/blog/2024-11-27-changes-to-the-web-api) diff --git a/spotipy/client.py b/spotipy/client.py index 61924a55..9261f239 100644 --- a/spotipy/client.py +++ b/spotipy/client.py @@ -458,6 +458,11 @@ def artist_related_artists(self, artist_id): Parameters: - artist_id - the artist ID, URI or URL """ + warnings.warn( + "You're using `artist_related_artists(...)`, " + "which is marked as deprecated by Spotify.", + DeprecationWarning + ) trid = self._get_id("artist", artist_id) return self._get("artists/" + trid + "/related-artists") @@ -1587,6 +1592,11 @@ def featured_playlists( (the first object). Use with limit to get the next set of items. """ + warnings.warn( + "You're using `featured_playlists(...)`, " + "which is marked as deprecated by Spotify.", + DeprecationWarning, + ) return self._get( "browse/featured-playlists", locale=locale, @@ -1671,6 +1681,11 @@ def category_playlists( (the first object). Use with limit to get the next set of items. """ + warnings.warn( + "You're using `category_playlists(...)`, " + "which is marked as deprecated by Spotify.", + DeprecationWarning, + ) return self._get( "browse/categories/" + category_id + "/playlists", country=country, @@ -1708,6 +1723,12 @@ def recommendations( attributes listed in the documentation, these values provide filters and targeting on results. """ + warnings.warn( + "You're using `recommendations(...)`, " + "which is marked as deprecated by Spotify.", + DeprecationWarning, + ) + params = dict(limit=limit) if seed_artists: params["seed_artists"] = ",".join( @@ -1754,6 +1775,11 @@ def audio_analysis(self, track_id): Parameters: - track_id - a track URI, URL or ID """ + warnings.warn( + "You're using `audio_analysis(...)`, " + "which is marked as deprecated by Spotify.", + DeprecationWarning, + ) trid = self._get_id("track", track_id) return self._get("audio-analysis/" + trid) @@ -1762,6 +1788,12 @@ def audio_features(self, tracks=[]): Parameters: - tracks - a list of track URIs, URLs or IDs, maximum: 100 ids """ + warnings.warn( + "You're using `audio_features(...)`, " + "which is marked as deprecated by Spotify.", + DeprecationWarning, + ) + if isinstance(tracks, str): trackid = self._get_id("track", tracks) results = self._get("audio-features/?ids=" + trackid) diff --git a/tests/integration/non_user_endpoints/test.py b/tests/integration/non_user_endpoints/test.py index 2aa878b5..366e24cf 100644 --- a/tests/integration/non_user_endpoints/test.py +++ b/tests/integration/non_user_endpoints/test.py @@ -73,34 +73,6 @@ def setUpClass(self): client_credentials_manager=SpotifyClientCredentials()) self.spotify.trace = False - def test_audio_analysis(self): - result = self.spotify.audio_analysis(self.four_tracks[0]) - assert ('beats' in result) - - def test_audio_features(self): - results = self.spotify.audio_features(self.four_tracks) - self.assertTrue(len(results) == len(self.four_tracks)) - for track in results: - assert ('speechiness' in track) - - def test_audio_features_with_bad_track(self): - bad_tracks = ['spotify:track:bad'] - input = self.four_tracks + bad_tracks - results = self.spotify.audio_features(input) - self.assertTrue(len(results) == len(input)) - for track in results[:-1]: - if track is not None: - assert ('speechiness' in track) - self.assertTrue(results[-1] is None) - - def test_recommendations(self): - results = self.spotify.recommendations( - seed_tracks=self.four_tracks, - min_danceability=0, - max_loudness=0, - target_popularity=50) - self.assertTrue(len(results['tracks']) == 20) - def test_artist_urn(self): artist = self.spotify.artist(self.radiohead_urn) self.assertTrue(artist['name'] == 'Radiohead') @@ -179,17 +151,6 @@ def test_artist_top_tracks(self): self.assertTrue('tracks' in results) self.assertTrue(len(results['tracks']) == 10) - def test_artist_related_artists(self): - results = self.spotify.artist_related_artists(self.weezer_urn) - self.assertTrue('artists' in results) - self.assertTrue(len(results['artists']) == 20) - - found = False - for artist in results['artists']: - if artist['name'] == 'Jimmy Eat World': - found = True - self.assertTrue(found) - def test_artist_search(self): results = self.spotify.search(q='weezer', type='artist') self.assertTrue('artists' in results) diff --git a/tests/integration/user_endpoints/test.py b/tests/integration/user_endpoints/test.py index fb1fbb21..9e5411f2 100644 --- a/tests/integration/user_endpoints/test.py +++ b/tests/integration/user_endpoints/test.py @@ -393,33 +393,6 @@ def test_categories_limit_high(self): response = self.spotify.categories(limit=50) self.assertLessEqual(len(response['categories']['items']), 50) - def test_category_playlists(self): - response = self.spotify.categories() - category = 'rock' - for cat in response['categories']['items']: - cat_id = cat['id'] - if cat_id == category: - response = self.spotify.category_playlists(category_id=cat_id) - self.assertGreater(len(response['playlists']["items"]), 0) - - def test_category_playlists_limit_low(self): - response = self.spotify.categories() - category = 'rock' - for cat in response['categories']['items']: - cat_id = cat['id'] - if cat_id == category: - response = self.spotify.category_playlists(category_id=cat_id, limit=1) - self.assertEqual(len(response['categories']['items']), 1) - - def test_category_playlists_limit_high(self): - response = self.spotify.categories() - category = 'rock' - for cat in response['categories']['items']: - cat_id = cat['id'] - if cat_id == category: - response = self.spotify.category_playlists(category_id=cat_id, limit=50) - self.assertLessEqual(len(response['categories']['items']), 50) - def test_new_releases(self): response = self.spotify.new_releases() self.assertGreater(len(response['albums']['items']), 0) @@ -432,10 +405,6 @@ def test_new_releases_limit_high(self): response = self.spotify.new_releases(limit=50) self.assertLessEqual(len(response['albums']['items']), 50) - def test_featured_releases(self): - response = self.spotify.featured_playlists() - self.assertGreater(len(response['playlists']), 0) - class SpotipyFollowApiTests(unittest.TestCase): @classmethod