From 8b84300597be027e0f7d8e40b67d1df04250db57 Mon Sep 17 00:00:00 2001 From: jugdizh Date: Tue, 24 Mar 2020 14:36:25 -0700 Subject: [PATCH] Added endpoints for shows/podcasts and episodes, closes #450 https://developer.spotify.com/documentation/web-api/reference-beta/#category-shows https://developer.spotify.com/documentation/web-api/reference-beta/#category-episodes --- spotipy/client.py | 143 ++++++++++++++++++- tests/integration/test_non_user_endpoints.py | 69 +++++++++ 2 files changed, 208 insertions(+), 4 deletions(-) diff --git a/spotipy/client.py b/spotipy/client.py index 8f114a0..3a08ca7 100644 --- a/spotipy/client.py +++ b/spotipy/client.py @@ -368,6 +368,85 @@ class Spotify(object): tlist = [self._get_id("album", a) for a in albums] return self._get("albums/?ids=" + ",".join(tlist)) + def show(self, show_id, market=None): + """ returns a single show given the show's ID, URIs or URL + + Parameters: + - show_id - the show ID, URI or URL + - market - an ISO 3166-1 alpha-2 country code. + The show must be available in the given market. + If user-based authorization is in use, the user's country + takes precedence. If neither market nor user country are + provided, the content is considered unavailable for the client. + """ + + trid = self._get_id("show", show_id) + return self._get("shows/" + trid, market=market) + + def shows(self, shows, market=None): + """ returns a list of shows given the show IDs, URIs, or URLs + + Parameters: + - shows - a list of show IDs, URIs or URLs + - market - an ISO 3166-1 alpha-2 country code. + Only shows available in the given market will be returned. + If user-based authorization is in use, the user's country + takes precedence. If neither market nor user country are + provided, the content is considered unavailable for the client. + """ + + tlist = [self._get_id("show", s) for s in shows] + return self._get("shows/?ids=" + ",".join(tlist), market=market) + + def show_episodes(self, show_id, limit=50, offset=0, market=None): + """ Get Spotify catalog information about a show's episodes + + Parameters: + - show_id - the show ID, URI or URL + - limit - the number of items to return + - offset - the index of the first item to return + - market - an ISO 3166-1 alpha-2 country code. + Only episodes available in the given market will be returned. + If user-based authorization is in use, the user's country + takes precedence. If neither market nor user country are + provided, the content is considered unavailable for the client. + """ + + trid = self._get_id("show", show_id) + return self._get( + "shows/" + trid + "/episodes/", limit=limit, offset=offset, market=market + ) + + def episode(self, episode_id, market=None): + """ returns a single episode given the episode's ID, URIs or URL + + Parameters: + - episode_id - the episode ID, URI or URL + - market - an ISO 3166-1 alpha-2 country code. + The episode must be available in the given market. + If user-based authorization is in use, the user's country + takes precedence. If neither market nor user country are + provided, the content is considered unavailable for the client. + """ + + trid = self._get_id("episode", episode_id) + return self._get("episodes/" + trid, market=market) + + def episodes(self, episodes, market=None): + """ returns a list of episodes given the episode IDs, URIs, or URLs + + Parameters: + - episodes - a list of episode IDs, URIs or URLs + - market - an ISO 3166-1 alpha-2 country code. + Only episodes available in the given market will be returned. + If user-based authorization is in use, the user's country + takes precedence. If neither market nor user country are + provided, the content is considered unavailable for the client. + """ + + tlist = [self._get_id("episode", e) for e in episodes] + return self._get("episodes/?ids=" + ",".join(tlist), market=market) + def search(self, q, limit=10, offset=0, type="track", market=None): """ searches for an item @@ -377,7 +456,7 @@ class Spotify(object): - limit - the number of items to return (min = 1, default = 10, max = 50) - offset - the index of the first item to return - type - the type of item to return. One of 'artist', 'album', - 'track' or 'playlist' + 'track', 'playlist', 'show', or 'episode' - market - An ISO 3166-1 alpha-2 country code or the string from_token. """ @@ -401,7 +480,7 @@ class Spotify(object): """ return self._get("me/playlists", limit=limit, offset=offset) - def playlist(self, playlist_id, fields=None, market=None): + def playlist(self, playlist_id, fields=None, market=None, additional_types=("track",)): """ Gets playlist by id. Parameters: @@ -409,12 +488,25 @@ class Spotify(object): - fields - which fields to return - market - An ISO 3166-1 alpha-2 country code or the string from_token. + - additional_types - list of item types to return. + valid types are: track and episode """ plid = self._get_id("playlist", playlist_id) - return self._get("playlists/%s" % (plid), fields=fields, market=market) + return self._get( + "playlists/%s" % (plid), + fields=fields, + market=market, + additional_types=",".join(additional_types), + ) def playlist_tracks( - self, playlist_id, fields=None, limit=100, offset=0, market=None + self, + playlist_id, + fields=None, + limit=100, + offset=0, + market=None, + additional_types=("track",) ): """ Get full details of the tracks of a playlist. @@ -424,6 +516,8 @@ class Spotify(object): - limit - the maximum number of tracks to return - offset - the index of the first track to return - market - an ISO 3166-1 alpha-2 country code. + - additional_types - list of item types to return. + valid types are: track and episode """ plid = self._get_id("playlist", playlist_id) return self._get( @@ -432,6 +526,7 @@ class Spotify(object): offset=offset, fields=fields, market=market, + additional_types=",".join(additional_types) ) def playlist_cover_image(self, playlist_id): @@ -896,6 +991,46 @@ class Spotify(object): 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: diff --git a/tests/integration/test_non_user_endpoints.py b/tests/integration/test_non_user_endpoints.py index ae433f9..287f6b9 100644 --- a/tests/integration/test_non_user_endpoints.py +++ b/tests/integration/test_non_user_endpoints.py @@ -46,6 +46,14 @@ class AuthTestSpotipy(unittest.TestCase): pablo_honey_urn = 'spotify:album:6AZv3m27uyRxi8KyJSfUxL' radiohead_urn = 'spotify:artist:4Z8W4fKeB5YxbusRsdQVPb' angeles_haydn_urn = 'spotify:album:1vAbqAeuJVWNAe7UR00bdM' + heavyweight_urn = 'spotify:show:5c26B28vZMN8PG0Nppmn5G' + heavyweight_id = '5c26B28vZMN8PG0Nppmn5G' + heavyweight_url = 'https://open.spotify.com/show/5c26B28vZMN8PG0Nppmn5G' + reply_all_urn = 'spotify:show:7gozmLqbcbr6PScMjc0Zl4' + heavyweight_ep1_urn = 'spotify:episode:68kq3bNz6hEuq8NtdfwERG' + heavyweight_ep1_id = '68kq3bNz6hEuq8NtdfwERG' + heavyweight_ep1_url = 'https://open.spotify.com/episode/68kq3bNz6hEuq8NtdfwERG' + reply_all_ep1_urn = 'spotify:episode:1KHjbpnmNpFmNTczQmTZlR' @classmethod def setUpClass(self): @@ -215,6 +223,67 @@ class AuthTestSpotipy(unittest.TestCase): except SpotifyException: self.assertTrue(True) + def test_show_urn(self): + show = self.spotify.show(self.heavyweight_urn, market="US") + self.assertTrue(show['name'] == 'Heavyweight') + + def test_show_id(self): + show = self.spotify.show(self.heavyweight_id, market="US") + self.assertTrue(show['name'] == 'Heavyweight') + + def test_show_url(self): + show = self.spotify.show(self.heavyweight_url, market="US") + self.assertTrue(show['name'] == 'Heavyweight') + + def test_show_bad_urn(self): + with self.assertRaises(SpotifyException): + self.spotify.show("bogus_urn", market="US") + + def test_shows(self): + results = self.spotify.shows([self.heavyweight_urn, self.reply_all_urn], market="US") + self.assertTrue('shows' in results) + self.assertTrue(len(results['shows']) == 2) + + def test_show_episodes(self): + results = self.spotify.show_episodes(self.heavyweight_urn, market="US") + self.assertTrue(len(results['items']) > 1) + + def test_show_episodes_many(self): + results = self.spotify.show_episodes(self.reply_all_urn, market="US") + episodes = results['items'] + total, received = results['total'], len(episodes) + while received < total: + results = self.spotify.show_episodes( + self.reply_all_urn, offset=received, market="US") + episodes.extend(results['items']) + received = len(episodes) + + self.assertEqual(received, total) + + def test_episode_urn(self): + episode = self.spotify.episode(self.heavyweight_ep1_urn, market="US") + self.assertTrue(episode['name'] == '#1 Buzz') + + def test_episode_id(self): + episode = self.spotify.episode(self.heavyweight_ep1_id, market="US") + self.assertTrue(episode['name'] == '#1 Buzz') + + def test_episode_url(self): + episode = self.spotify.episode(self.heavyweight_ep1_url, market="US") + self.assertTrue(episode['name'] == '#1 Buzz') + + def test_episode_bad_urn(self): + with self.assertRaises(SpotifyException): + self.spotify.episode("bogus_urn", market="US") + + def test_episodes(self): + results = self.spotify.episodes( + [self.heavyweight_ep1_urn, self.reply_all_ep1_urn], + market="US" + ) + self.assertTrue('episodes' in results) + self.assertTrue(len(results['episodes']) == 2) + def test_unauthenticated_post_fails(self): with self.assertRaises(SpotifyException) as cm: self.spotify.user_playlist_create(