Skip to content

Commit

Permalink
Added new endpoints (#666)
Browse files Browse the repository at this point in the history
* Added an exception clause that catches `FileNotFoundError` and logs a debug message in `SpotifyOAuth.get_cached_token`, `SpotifyPKCE.get_cached_token` and `SpotifyImplicitGrant.get_cached_token`.

* Changed docs for `auth` parameter of `Spotify.init` to `access token` instead of `authorization token`. In issue #599, a user confused the access token with the authorization code.

* Updated CHANGELOG.md

* Removed `FileNotFoundError` because it does not exist in python 2.7 (*sigh*) and replaced it with a call to `os.path.exists`.

* Replaced ` os.path.exists` with `error.errno == errno.ENOENT` to supress errors when the cache file does not exist.

* Changed docs for `search` to mention that you can provide multiple multiple types to search for. The query parameters of requests are now logged. Added log messages for when the access token and refresh tokens are retrieved and when they are refreshed. Other small grammar fixes.

* Removed duplicate word "multiple" from CHANGELOG

* * Fixed the bugs in `SpotifyOAuth.refresh_access_token` and `SpotifyPKCE.refresh_access_token` which raised the incorrect exception upon receiving an error response from the server. This addresses #645.
* Fixed a bug in `RequestHandler.do_GET` in which the non-existent `state` attribute of  `SpotifyOauthError` is accessed. This bug occurs when the user clicks "cancel" in the permissions dialog that opens in the browser.
* Cleaned up the documentation for `SpotifyClientCredentials.__init__`, `SpotifyOAuth.__init__`, and `SpotifyPKCE.__init__`.

* Removed unneeded import

* Added the following endpoints:
 `Spotify.current_user_saved_episodes` `Spotify.current_user_saved_episodes_add` `Spotify.current_user_saved_episodes_delete` `Spotify.current_user_saved_episodes_contains` `Spotify.available_markets`
 Added tests for the above endpoints.

* updated CHANGELOG

* Fixed flake8 issue

Co-authored-by: Stéphane Bruckert <[email protected]>
  • Loading branch information
Peter-Schorn and stephanebruckert authored Apr 10, 2021
1 parent f420247 commit 25f9df2
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 118 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Enabled using both short and long IDs for playlist_change_details
- Added a cache handler to `SpotifyClientCredentials`
- Added the following endpoints
* `Spotify.current_user_saved_episodes`
* `Spotify.current_user_saved_episodes_add`
* `Spotify.current_user_saved_episodes_delete`
* `Spotify.current_user_saved_episodes_contains`
* `Spotify.available_markets`

### Changed

Expand Down
295 changes: 177 additions & 118 deletions spotipy/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1171,16 +1171,184 @@ def current_user_playing_track(self):
"""
return self._get("me/player/currently-playing")

def current_user_saved_tracks(self, limit=20, offset=0):
def current_user_saved_albums(self, limit=20, offset=0, market=None):
""" Gets a list of the albums saved in the current authorized user's
"Your Music" library
Parameters:
- limit - the number of albums to return
- offset - the index of the first album to return
- market - an ISO 3166-1 alpha-2 country code.
"""
return self._get("me/albums", limit=limit, offset=offset, market=market)

def current_user_saved_albums_add(self, albums=[]):
""" Add one or more albums to the current user's
"Your Music" library.
Parameters:
- albums - a list of album URIs, URLs or IDs
"""

alist = [self._get_id("album", a) for a in albums]
return self._put("me/albums?ids=" + ",".join(alist))

def current_user_saved_albums_delete(self, albums=[]):
""" Remove one or more albums from the current user's
"Your Music" library.
Parameters:
- albums - a list of album URIs, URLs or IDs
"""
alist = [self._get_id("album", a) for a in albums]
return self._delete("me/albums/?ids=" + ",".join(alist))

def current_user_saved_albums_contains(self, albums=[]):
""" Check if one or more albums is already saved in
the current Spotify user’s “Your Music” library.
Parameters:
- albums - a list of album URIs, URLs or IDs
"""
alist = [self._get_id("album", a) for a in albums]
return self._get("me/albums/contains?ids=" + ",".join(alist))

def current_user_saved_tracks(self, limit=20, offset=0, market=None):
""" Gets a list of the tracks saved in the current authorized user's
"Your Music" library
Parameters:
- limit - the number of tracks to return
- offset - the index of the first track to return
- market - an ISO 3166-1 alpha-2 country code
"""
return self._get("me/tracks", limit=limit, offset=offset)
return self._get("me/tracks", limit=limit, offset=offset, market=market)

def current_user_saved_tracks_add(self, tracks=None):
""" Add one or more tracks to the current user's
"Your Music" library.
Parameters:
- tracks - a list of track URIs, URLs or IDs
"""
tlist = []
if tracks is not None:
tlist = [self._get_id("track", t) for t in tracks]
return self._put("me/tracks/?ids=" + ",".join(tlist))

def current_user_saved_tracks_delete(self, tracks=None):
""" Remove one or more tracks from the current user's
"Your Music" library.
Parameters:
- tracks - a list of track URIs, URLs or IDs
"""
tlist = []
if tracks is not None:
tlist = [self._get_id("track", t) for t in tracks]
return self._delete("me/tracks/?ids=" + ",".join(tlist))

def current_user_saved_tracks_contains(self, tracks=None):
""" Check if one or more tracks is already saved in
the current Spotify user’s “Your Music” library.
Parameters:
- tracks - a list of track URIs, URLs or IDs
"""
tlist = []
if tracks is not None:
tlist = [self._get_id("track", t) for t in tracks]
return self._get("me/tracks/contains?ids=" + ",".join(tlist))

def current_user_saved_episodes(self, limit=20, offset=0, market=None):
""" Gets a list of the episodes saved in the current authorized user's
"Your Music" library
Parameters:
- limit - the number of episodes to return
- offset - the index of the first episode to return
- market - an ISO 3166-1 alpha-2 country code
"""
return self._get("me/episodes", limit=limit, offset=offset, market=market)

def current_user_saved_episodes_add(self, episodes=None):
""" Add one or more episodes to the current user's
"Your Music" library.
Parameters:
- episodes - a list of episode URIs, URLs or IDs
"""
elist = []
if episodes is not None:
elist = [self._get_id("episode", e) for e in episodes]
return self._put("me/episodes/?ids=" + ",".join(elist))

def current_user_saved_episodes_delete(self, episodes=None):
""" Remove one or more episodes from the current user's
"Your Music" library.
Parameters:
- episodes - a list of episode URIs, URLs or IDs
"""
elist = []
if episodes is not None:
elist = [self._get_id("episode", e) for e in episodes]
return self._delete("me/episodes/?ids=" + ",".join(elist))

def current_user_saved_episodes_contains(self, episodes=None):
""" Check if one or more episodes is already saved in
the current Spotify user’s “Your Music” library.
Parameters:
- episodes - a list of episode URIs, URLs or IDs
"""
elist = []
if episodes is not None:
elist = [self._get_id("episode", e) for e in episodes]
return self._get("me/episodes/contains?ids=" + ",".join(elist))

def current_user_saved_shows(self, limit=20, offset=0, market=None):
""" Gets a list of the shows saved in the current authorized user's
"Your Music" library
Parameters:
- limit - the number of shows to return
- offset - the index of the first show to return
- market - an ISO 3166-1 alpha-2 country code
"""
return self._get("me/shows", limit=limit, offset=offset, market=market)

def current_user_saved_shows_add(self, shows=[]):
""" Add one or more albums to the current user's
"Your Music" library.
Parameters:
- shows - a list of show URIs, URLs or IDs
"""
slist = [self._get_id("show", s) for s in shows]
return self._put("me/shows?ids=" + ",".join(slist))

def current_user_saved_shows_delete(self, shows=[]):
""" Remove one or more shows from the current user's
"Your Music" library.
Parameters:
- shows - a list of show URIs, URLs or IDs
"""
slist = [self._get_id("show", s) for s in shows]
return self._delete("me/shows/?ids=" + ",".join(slist))

def current_user_saved_shows_contains(self, shows=[]):
""" Check if one or more shows is already saved in
the current Spotify user’s “Your Music” library.
Parameters:
- shows - a list of show URIs, URLs or IDs
"""
slist = [self._get_id("show", s) for s in shows]
return self._get("me/shows/contains?ids=" + ",".join(slist))

def current_user_followed_artists(self, limit=20, after=None):
""" Gets a list of the artists followed by the current authorized user
Expand Down Expand Up @@ -1225,42 +1393,6 @@ def current_user_following_users(self, ids=None):
"me/following/contains", ids=",".join(idlist), type="user"
)

def current_user_saved_tracks_delete(self, tracks=None):
""" Remove one or more tracks from the current user's
"Your Music" library.
Parameters:
- tracks - a list of track URIs, URLs or IDs
"""
tlist = []
if tracks is not None:
tlist = [self._get_id("track", t) for t in tracks]
return self._delete("me/tracks/?ids=" + ",".join(tlist))

def current_user_saved_tracks_contains(self, tracks=None):
""" Check if one or more tracks is already saved in
the current Spotify user’s “Your Music” library.
Parameters:
- tracks - a list of track URIs, URLs or IDs
"""
tlist = []
if tracks is not None:
tlist = [self._get_id("track", t) for t in tracks]
return self._get("me/tracks/contains?ids=" + ",".join(tlist))

def current_user_saved_tracks_add(self, tracks=None):
""" Add one or more tracks to the current user's
"Your Music" library.
Parameters:
- tracks - a list of track URIs, URLs or IDs
"""
tlist = []
if tracks is not None:
tlist = [self._get_id("track", t) for t in tracks]
return self._put("me/tracks/?ids=" + ",".join(tlist))

def current_user_top_artists(
self, limit=20, offset=0, time_range="medium_term"
):
Expand Down Expand Up @@ -1310,86 +1442,6 @@ def current_user_recently_played(self, limit=50, after=None, before=None):
before=before,
)

def current_user_saved_albums(self, limit=20, offset=0):
""" Gets a list of the albums saved in the current authorized user's
"Your Music" library
Parameters:
- limit - the number of albums to return
- offset - the index of the first album to return
"""
return self._get("me/albums", limit=limit, offset=offset)

def current_user_saved_albums_contains(self, albums=[]):
""" Check if one or more albums is already saved in
the current Spotify user’s “Your Music” library.
Parameters:
- albums - a list of album URIs, URLs or IDs
"""
alist = [self._get_id("album", a) for a in albums]
return self._get("me/albums/contains?ids=" + ",".join(alist))

def current_user_saved_albums_add(self, albums=[]):
""" Add one or more albums to the current user's
"Your Music" library.
Parameters:
- albums - a list of album URIs, URLs or IDs
"""
alist = [self._get_id("album", a) for a in albums]
return self._put("me/albums?ids=" + ",".join(alist))

def current_user_saved_albums_delete(self, albums=[]):
""" Remove one or more albums from the current user's
"Your Music" library.
Parameters:
- albums - a list of album URIs, URLs or IDs
"""
alist = [self._get_id("album", a) for a in albums]
return self._delete("me/albums/?ids=" + ",".join(alist))

def current_user_saved_shows(self, limit=50, offset=0):
""" Gets a list of the shows saved in the current authorized user's
"Your Music" library
Parameters:
- limit - the number of shows to return
- offset - the index of the first show to return
"""
return self._get("me/shows", limit=limit, offset=offset)

def current_user_saved_shows_contains(self, shows=[]):
""" Check if one or more shows is already saved in
the current Spotify user’s “Your Music” library.
Parameters:
- shows - a list of show URIs, URLs or IDs
"""
slist = [self._get_id("show", s) for s in shows]
return self._get("me/shows/contains?ids=" + ",".join(slist))

def current_user_saved_shows_add(self, shows=[]):
""" Add one or more albums to the current user's
"Your Music" library.
Parameters:
- shows - a list of show URIs, URLs or IDs
"""
slist = [self._get_id("show", s) for s in shows]
return self._put("me/shows?ids=" + ",".join(slist))

def current_user_saved_shows_delete(self, shows=[]):
""" Remove one or more shows from the current user's
"Your Music" library.
Parameters:
- shows - a list of show URIs, URLs or IDs
"""
slist = [self._get_id("show", s) for s in shows]
return self._delete("me/shows/?ids=" + ",".join(slist))

def user_follow_artists(self, ids=[]):
""" Follow one or more artists
Parameters:
Expand Down Expand Up @@ -1829,6 +1881,13 @@ def add_to_queue(self, uri, device_id=None):

return self._post(endpoint)

def available_markets(self):
""" Get the list of markets where Spotify is available.
Returns a list of the countries in which Spotify is available, identified by their
ISO 3166-1 alpha-2 country code with additional country codes for special territories.
"""
return self._get("markets")

def _append_device_id(self, path, device_id):
""" Append device ID to API path.
Expand Down
6 changes: 6 additions & 0 deletions tests/integration/test_non_user_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,3 +364,9 @@ def test_force_no_requests_session(self):
self.assertNotIsInstance(with_no_session._session, requests.Session)
user = with_no_session.user(user="akx")
self.assertEqual(user["uri"], "spotify:user:akx")

def test_available_markets(self):
markets = self.spotify.available_markets()["markets"]
self.assertTrue(isinstance(markets, list))
self.assertIn("US", markets)
self.assertIn("GB", markets)
19 changes: 19 additions & 0 deletions tests/integration/test_user_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ def setUpClass(cls):
"http://open.spotify.com/track/3cySlItpiPiIAzU3NyHCJf"]
cls.album_ids = ["spotify:album:6kL09DaURb7rAoqqaA51KU",
"spotify:album:6RTzC0rDbvagTSJLlY7AKl"]
cls.episode_ids = [
"spotify:episode:3OEdPEYB69pfXoBrhvQYeC",
"spotify:episode:5LEFdZ9pYh99wSz7Go2D0g"
]
cls.username = os.getenv(CCEV['client_username'])

scope = (
Expand Down Expand Up @@ -267,6 +271,21 @@ def test_current_user_saved_albums(self):
resp = self.spotify.current_user_saved_albums_contains(self.album_ids)
self.assertEqual(resp, [False, False])

def test_current_user_saved_episodes(self):
# Add
self.spotify.current_user_saved_episodes_add(self.episode_ids)
episodes = self.spotify.current_user_saved_episodes(market="US")
self.assertGreaterEqual(len(episodes['items']), 2)

# Contains
resp = self.spotify.current_user_saved_episodes_contains(self.episode_ids)
self.assertEqual(resp, [True, True])

# Remove
self.spotify.current_user_saved_episodes_delete(self.episode_ids)
resp = self.spotify.current_user_saved_episodes_contains(self.episode_ids)
self.assertEqual(resp, [False, False])


class SpotipyUserApiTests(unittest.TestCase):
@classmethod
Expand Down

0 comments on commit 25f9df2

Please sign in to comment.