def _follow_unfollow_help(self, other, request_type): """ follow and unfollow are identical, except for the request type. This function implements that functionality to remove duplicate code. """ # Validate input if not isinstance(other, list): other = [other] for elem in other: if type(elem) not in [Artist, User, Playlist]: raise TypeError(elem) # Split up input artists = utils.separate(other, Artist) users = utils.separate(other, User) playlists = utils.separate(other, Playlist) for batch in utils.create_batches(utils.map_ids(artists)): response_json, status_code = utils.request( self._session, request_type=request_type, endpoint=Endpoints.USER_FOLLOW_ARTIST_USER, body=None, uri_params={ 'type': 'artist', 'ids': batch }) if status_code != 204: raise utils.SpotifyError(status_code, response_json) for batch in utils.create_batches(utils.map_ids(users)): response_json, status_code = utils.request( self._session, request_type=request_type, endpoint=Endpoints.USER_FOLLOW_ARTIST_USER, body=None, uri_params={ 'type': 'user', 'ids': batch }) if status_code != 204: raise utils.SpotifyError(status_code, response_json) for playlist in playlists: response_json, status_code = utils.request( self._session, request_type=request_type, endpoint=Endpoints.USER_FOLLOW_PLAYLIST % playlist, body=None, uri_params=None) if status_code != 200: raise utils.SpotifyError(status_code, response_json)
def get_artists(self, artist_ids): """ Gets the artists with the given Spotify ids. Args: artist_ids (str, List[str): The Spotify artist id(s) to get. Returns: Union[Album, List[Album]]: The requested artist(s). Raises: TypeError: for invalid types in any argument. HTTPError: if failure or partial failure. Calls endpoints: - GET /v1/artists Note: the following endpoint is not used. - GET /v1/artists/{id} """ # Type validation if not isinstance(artist_ids, str) and\ not all(isinstance(x, str) for x in artist_ids): raise TypeError('artist_ids should be str or list of str') if isinstance(artist_ids, str): artist_ids = list(artist_ids) # Construct params for API call endpoint = Endpoints.SEARCH_ALBUMS uri_params = dict() # A maximum of 50 artists can be returned per API call batches = utils.create_batches(artist_ids, 50) result = list() for batch in batches: uri_params['ids'] = batch # Execute requests response_json, status_code = utils.request( session=self, request_type=const.REQUEST_GET, endpoint=endpoint, uri_params=uri_params) if status_code != 200: raise utils.SpotifyError(status_code, response_json) items = response_json['artists'] for item in items: result.append(Artist(self, item)) return result if len(result) != 1 else result[0]
def _save_remove_help(self, other, request_type): """ save and remove are identical, except for the request type and return codes. This function implements that functionality to remove duplicate code. """ # Validate input if not isinstance(other, list): other = [other] for elem in other: if type(elem) not in [Album, Track]: raise TypeError(elem) # Split up input albums = utils.separate(other, Album) tracks = utils.separate(other, Track) for batch in utils.create_batches(utils.map_ids(albums)): response_json, status_code = utils.request( self._session, request_type=request_type, endpoint=Endpoints.USER_SAVE_ALBUMS, body=None, uri_params={'ids': batch}) # All success codes are 200, except saving an album success = 201 if request_type == const.REQUEST_PUT else 200 if status_code != success: raise utils.SpotifyError(status_code, response_json) for batch in utils.create_batches(utils.map_ids(tracks)): response_json, status_code = utils.request( self._session, request_type=request_type, endpoint=Endpoints.USER_SAVE_TRACKS, body=None, uri_params={'ids': batch}) if status_code != 200: raise utils.SpotifyError(status_code, response_json)
def has_saved(self, other): """ Check if the user has one or more things saved to their library. Args: other: check if the current user has 'other' saved to the library. Other must be one of the following: - Track - Album - List: can contain multiple of the above types Returns: List of tuples. Each tuple has an input object and whether the user has that object saved. Required token scopes: - user-library-read Calls endpoints: - GET /v1/me/albums/contains - GET /v1/me/tracks/contains """ # Validate input if not isinstance(other, list): other = [other] for elem in other: if type(elem) not in [Track, Album]: raise TypeError(elem) # Split up input tracks = utils.separate(other, Track) albums = utils.separate(other, Album) # Get boolean values for whether the user has each item saved endpoint = Endpoints.USER_HAS_SAVED track_bools = [] for batch in utils.create_batches(utils.map_ids(tracks)): response_json, status_code = utils.request( self._session, request_type=const.REQUEST_GET, endpoint=endpoint % 'tracks', body=None, uri_params={'ids': batch}) if status_code != 200: raise utils.SpotifyError(status_code, response_json) track_bools.append(response_json) album_bools = [] for batch in utils.create_batches(utils.map_ids(albums)): response_json, status_code = utils.request( self._session, request_type=const.REQUEST_GET, endpoint=endpoint % 'albums', body=None, uri_params={'ids': batch}) if status_code != 200: raise utils.SpotifyError(status_code, response_json) album_bools.append(response_json) # Zip output with input to make tuples zipped_tracks = list(zip(tracks, track_bools)) zipped_albums = list(zip(albums, album_bools)) return zipped_tracks + zipped_albums
def is_following(self, other): """ Check if the current user is following something. Args: other: check if current user is following 'other'. Other must be one of the following: - Artist - User - Playlist - List: can contain multiple of the above types Required token scopes: - user-follow-read - playlist-read-private - playlist-read-collaborative Calls endpoints: - GET /v1/me/following/contains - GET /v1/users/{user_id}/playlists Returns: List of tuples. Each tuple has an input object and whether the user follows the object. """ # Validate input if not isinstance(other, list): other = [other] for elem in other: if type(elem) not in [Artist, User, Playlist]: raise TypeError(elem) # Split up input artists = utils.separate(other, Artist) users = utils.separate(other, User) playlists = utils.separate(other, Playlist) # Get boolean values for whether the user follows each in 'other' endpoint = Endpoints.USER_FOLLOWING_CONTAINS artist_bools = [] for batch in utils.create_batches(utils.map_ids(artists)): response_json, status_code = utils.request( self._session, request_type=const.REQUEST_GET, endpoint=endpoint, body=None, uri_params={ 'type': 'artist', 'ids': batch }) if status_code != 200: raise utils.SpotifyError(status_code, response_json) artist_bools.append(response_json) user_bools = [] for batch in utils.create_batches(utils.map_ids(users)): response_json, status_code = utils.request( self._session, request_type=const.REQUEST_GET, endpoint=endpoint, body=None, uri_params={ 'type': 'user', 'ids': batch }) if status_code != 200: raise utils.SpotifyError(status_code, response_json) user_bools.append(response_json) # For each playlist in other, check if in the User's followed playlists followed_playlists = self.get_following(const.PLAYLISTS) playlist_bools = list(map(lambda p: p in followed_playlists, playlists)) # Zip output with input to make tuples artists = list(zip(artists, artist_bools)) users = list(zip(users, user_bools)) playlists = list(zip(playlists, playlist_bools)) return artists + users + playlists
def get_tracks(self, track_ids, market=const.TOKEN_REGION): """ Gets the tracks with the given Spotify ids. Args: track_ids (str, List[str]): The Spotify track id(s) to get. market (str): a :term:`market code <Market>` or sp.TOKEN_REGION, used for :term:`track relinking <Track Relinking>`. If None, no market is passed to Spotify's Web API, and its default behavior is invoked. Returns: Union[Track, List[Track]]: The requested track(s). Raises: TypeError: for invalid types in any argument. ValueError: if market type is invalid. TODO HTTPError: if failure or partial failure. Calls endpoints: - GET /v1/tracks Note: the following endpoint is not used. - GET /v1/tracks/{id} """ # Type validation if not isinstance(track_ids, str) and\ not all(isinstance(x, str) for x in track_ids): raise TypeError('track_ids should be str or list of str') if market is not None and not isinstance(market, str): raise TypeError('market should be None or str') # Argument validation if isinstance(track_ids, str): track_ids = list(track_ids) # Construct params for API call endpoint = Endpoints.SEARCH_TRACKS uri_params = dict() if market is not None: uri_params['market'] = market # A maximum of 50 tracks can be returned per API call batches = utils.create_batches(track_ids, 50) result = list() for batch in batches: uri_params['ids'] = ','.join(batch) # Execute requests response_json, status_code = utils.request( session=self, request_type=const.REQUEST_GET, endpoint=endpoint, uri_params=uri_params) if status_code != 200: raise utils.SpotifyError(status_code, response_json) items = response_json['tracks'] for item in items: result.append(Track(self, item)) return result if len(result) != 1 else result[0]