diff --git a/spotipy/client.py b/spotipy/client.py index 6abe3eb..01863d9 100644 --- a/spotipy/client.py +++ b/spotipy/client.py @@ -508,40 +508,49 @@ class Spotify(object): 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, total=None): + def search(self, q, limit=10, offset=0, type="track", market=None): """ searches for an item + Parameters: + - q - the search query (see how to write a query in the + official documentation https://developer.spotify.com/documentation/web-api/reference/search/search/) # noqa + - 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', 'playlist', 'show', or 'episode' + - market - An ISO 3166-1 alpha-2 country code or the string + from_token. + """ + return self._get( + "search", q=q, limit=limit, offset=offset, type=type, market=market + ) + + def search_markets(self, q, limit=10, offset=0, type="track", markets=None, total=None): + """ searches multple markets for an item + Parameters: - q - the search query (see how to write a query in the official documentation https://developer.spotify.com/documentation/web-api/reference/search/search/) # noqa - limit - the number of items to return (min = 1, default = 10, max = 50). If a search is to be done on multiple markets, then this limit is applied to each market. (e.g. search US, CA, MX each with a limit of 10). - offset - the index of the first item to return - - type - the type of item to return. One of 'artist', 'album', - 'track', 'playlist', 'show', or 'episode' - - market - An ISO 3166-1 alpha-2 country code or the string - from_token. Can supply list of markets. Pass "ALL" to search all country codes. + - type - the type's of item's to return. One or more of 'artist', 'album', + 'track', 'playlist', 'show', or 'episode'. If multiple types are desired, pass in a comma separated list. + - markets - A list of ISO 3166-1 alpha-2 country codes. Search all country markets by default. - total - the total number of results to return if multiple markets are supplied in the search. + If multiple types are specified, this only applies to the first type. """ + if not markets: + markets = self.country_codes - if (isinstance(market, str) and market.upper() == "ALL"): - warnings.warn( - "Searching all markets is poorly performing.", - UserWarning, - ) - return self._search_multiple_markets(q, limit, offset, type, self.country_codes, total) + if not (isinstance(markets, list) or isinstance(markets, tuple)): + markets = [] - elif isinstance(market, list) or isinstance(market, tuple): - warnings.warn( - "Searching multiple markets is poorly performing.", - UserWarning, - ) - return self._search_multiple_markets(q, limit, offset, type, market, total) - - else: - return self._get( - "search", q=q, limit=limit, offset=offset, type=type, market=market - ) + warnings.warn( + "Searching multiple markets is poorly performing.", + UserWarning, + ) + return self._search_multiple_markets(q, limit, offset, type, markets, total) def user(self, user): """ Gets basic profile information about a Spotify User @@ -1562,26 +1571,30 @@ class Spotify(object): return "spotify:" + type + ":" + self._get_id(type, id) def _search_multiple_markets(self, q, limit, offset, type, markets, total): - results = { - type + 's': { - 'href': [], - 'items': [], - 'limit': limit, - 'next': None, - 'offset': 0, - 'previous': None, - 'total': 0 - } - } + if total and limit > total: + limit = total + warnings.warn( + "limit was auto-adjusted to equal {} as it must not be higher than total".format( + total), + UserWarning, + ) + + results = {} + first_type = type.split(",")[0] + 's' + count = 0 + for country in markets: result = self._get( "search", q=q, limit=limit, offset=offset, type=type, market=country ) - results[type + 's']['href'].append(result[type + 's']['href']) - results[type + 's']['items'] += result[type + 's']['items'] - results[type + 's']['total'] += result[type + 's']['total'] - if total and len(results[type + 's']['items']) >= total: - # splice 'items' to only include number of results requested - results[type + 's']['items'] = results[type + 's']['items'][:total] - return results + results[country] = result + + count += len(result[first_type]['items']) + if total and count >= total: + break + if total and limit > total - count: + # when approaching `total` results, adjust `limit` to not request more + # items than needed + limit = total - count + return results diff --git a/tests/integration/test_non_user_endpoints.py b/tests/integration/test_non_user_endpoints.py index 0a2a0b8..7e9c666 100644 --- a/tests/integration/test_non_user_endpoints.py +++ b/tests/integration/test_non_user_endpoints.py @@ -176,38 +176,48 @@ class AuthTestSpotipy(unittest.TestCase): self.assertTrue(results['artists']['items'][0]['name'] == 'Weezer') def test_artist_search_with_multiple_markets(self): - TOTAL = 3 - results_single = self.spotify.search(q='weezer', type='artist', market='US') - results_multiple = self.spotify.search(q='weezer', type='artist', - market=['GB', 'US', 'AU']) - results_all = self.spotify.search(q='weezer', type='artist', market="ALL") - results_limited = self.spotify.search(q='weezer', type='artist', - market=['GB', 'US', 'AU'], total=TOTAL) + total = 5 + countries_list = ['GB', 'US', 'AU'] + countries_tuple = ('GB', 'US', 'AU') - results_tuple = self.spotify.search(q='weezer', type='artist', - market=('GB', 'US', 'AU'), total=TOTAL) + results_multiple = self.spotify.search_markets(q='weezer', type='artist', + markets=countries_list) + results_all = self.spotify.search_markets(q='weezer', type='artist') + results_tuple = self.spotify.search_markets(q='weezer', type='artist', + markets=countries_tuple) + results_limited = self.spotify.search_markets(q='weezer', limit=3, type='artist', + markets=countries_list, total=total) - self.assertTrue('artists' in results_multiple) - self.assertTrue('artists' in results_all) - self.assertTrue('artists' in results_limited) - self.assertTrue('artists' in results_tuple) + self.assertTrue( + all('artists' in results_multiple[country] for country in results_multiple)) + self.assertTrue(all('artists' in results_all[country] for country in results_all)) + self.assertTrue(all('artists' in results_tuple[country] for country in results_tuple)) + self.assertTrue(all('artists' in results_limited[country] for country in results_limited)) - self.assertTrue(len(results_multiple['artists']['items']) > 0) - self.assertTrue(len(results_all['artists']['items']) > 0) - self.assertTrue(len(results_limited['artists']['items']) > 0) - self.assertTrue(len(results_tuple['artists']['items']) > 0) + self.assertTrue( + all(len(results_multiple[country]['artists']['items']) > 0 for country in + results_multiple)) + self.assertTrue(all(len(results_all[country]['artists'] + ['items']) > 0 for country in results_all)) + self.assertTrue( + all(len(results_tuple[country]['artists']['items']) > 0 for country in results_tuple)) + self.assertTrue( + all(len(results_limited[country]['artists']['items']) > 0 for country in + results_limited)) - self.assertTrue(len(results_all['artists']['items']) > - len(results_multiple['artists']['items'])) - self.assertTrue(len(results_multiple['artists']['items']) - > len(results_single['artists']['items'])) + self.assertTrue(all(results_multiple[country]['artists']['items'] + [0]['name'] == 'Weezer' for country in results_multiple)) + self.assertTrue(all(results_all[country]['artists']['items'] + [0]['name'] == 'Weezer' for country in results_all)) + self.assertTrue(all(results_tuple[country]['artists']['items'] + [0]['name'] == 'Weezer' for country in results_tuple)) + self.assertTrue(all(results_limited[country]['artists']['items'] + [0]['name'] == 'Weezer' for country in results_limited)) - self.assertTrue(results_multiple['artists']['items'][0]['name'] == 'Weezer') - self.assertTrue(results_all['artists']['items'][0]['name'] == 'Weezer') - self.assertTrue(results_limited['artists']['items'][0]['name'] == 'Weezer') - self.assertTrue(results_tuple['artists']['items'][0]['name'] == 'Weezer') - - self.assertTrue(len(results_limited['artists']['items']) <= TOTAL) + total_limited_results = 0 + for country in results_limited: + total_limited_results += len(results_limited[country]['artists']['items']) + self.assertTrue(total_limited_results <= total) def test_artist_albums(self): results = self.spotify.artist_albums(self.weezer_urn)