From a5fd08fff55af26e7be1a6f1617183d977d3d0cd Mon Sep 17 00:00:00 2001 From: MakeShiftArtist Date: Sun, 29 Aug 2021 03:59:39 -0500 Subject: [PATCH 1/7] Added User, Artist, Track, and Album Objects --- spotipy/objects.py | 304 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 spotipy/objects.py diff --git a/spotipy/objects.py b/spotipy/objects.py new file mode 100644 index 00000000..fae7f7c2 --- /dev/null +++ b/spotipy/objects.py @@ -0,0 +1,304 @@ +class FreshObject: + """Base Object for Spotipy Objects + All spotify objects will inherit this + + """ + + def __init__(self, client): + self.client = client + + def __str__(self) -> str: + try: + return self.name + except Exception as e: + raise e + + def __repr__(self) -> str: + """ + Example + ------- + ObjectType + Name: objectName + ID: objectID + URI: objectURI + """ + try: + return(f"{self.__class__}\n\tName: {self.name}\n\tID: {self.id}\n\tURI: {self.uri}") + except Exception as e: + raise e + + def __eq__(self, other) -> bool: + """Checks if the artists are the same""" + try: + return self.id == other.id if (other.type == other.type) else False + except Exception as e: + raise e + + def __ne__(self, other) -> bool: + """Checks if they're NOT the same artist""" + try: + return not self.__eq__(other) + except Exception as e: + raise e + +class User(FreshObject): + """User class object + Turns user json data into an object User + + Parameters + ---------- + client : spotipy.Spotify + client used for the api + user : dict + json data given from api + + Attributes + ---------- + client : spotipy.Spotify + client used for the api + name : str + user's display name + id : str + user's unique user id + uri : stri + user's spotify uri + url : str + user's spotify url + all_urls : dict[str, str] + all external URLs for the user + followers : int + user's total amount of followers + images : list[str] + list of image urls associated with the user + type : str + type of object (should always be 'user') + """ + def __init__(self, client, user): + """Inits User""" + self.client = client + self.name = user['display_name'] + self.id = user['id'] + self.uri = user['uri'] + self.url = user['external_urls']['spotify'] + self.all_urls = user['external_urls'] + self.followers = user['followers']['total'] + images = [] + for image in user['images']: + images.append(image['url']) + self.images = images + self.type = user['type'] # should always be 'user' + + # add some functions like user_playlists() + + +class Artist(FreshObject): + """Artist class object + Turns artist json data into an object Artist + + Parameters + ---------- + client : spotipy.Spotify + client used for the api + artist : dict + json data given from api + + Attributes + ---------- + client : spotipy.Spotify + client used for the api + name : str + artist's username on spotify + id : str + artist's unique id + uri : str + artist's spotify uri + url : string + artist's spotify url + all_urls : dict[str, str] + all external URLs for the artist + followers : int + artist's total amount of followers + genres : list[str] + list of genre's associated with the artist + popularity : int + artist's popularity score + images : list[str] + list of image urls associated with the artist + type : str + type of object (should always be 'artist') + + """ + def __init__(self, client, artist: dict): + """Inits Artist""" + self.client = client + self.name = artist['name'] + self.id = artist['id'] + self.uri = artist['uri'] + self.url = artist['external_urls']['spotify'] + self.all_urls = artist['external_urls'] + self.followers = artist['followers']['total'] + self.genres = artist['genres'] + self.popularity = artist['popularity'] + images = [] + for image in artist['images']: + images.append(image['url']) + self.images = images + self.type = artist['type'] # should always be 'artist' + super().__init__(client) + + # add some methods like artist_albums() + + +class Album(FreshObject): + """Album class object + Turns album json data into an object Album + + Parameters + ---------- + client : spotipy.Spotify + client used for the api + album : dict + json data given from api + + Attributes + ---------- + client : spotipy.Spotify + client used for the api + name : str + artist's username on spotify + id : str + artist's unique id + uri : str + artist's spotify uri + url : string + artist's spotify url + all_urls : dict[str, str] + all external URLs for the artist + album_type : str + the type of album + artists : list[dict[str, str]] + lists of artists (name, uri, id) + release_date : str + date of release (year-month-day) + total_tracks : int + total amount of tracks on the album + images : list[str] + list of image urls associated with the artist + type : str + type of object (should always be 'artist') + + """ + def __init__(self, client, album): + """Inits Album""" + self.client = client + self.name = album['name'] + self.id = album['id'] + self.uri = album['uri'] + self.url = album['external_urls']['spotify'] + self.all_urls = album['external_urls'] + self.album_type = album['album_type'] + artists = album['artists'] + all_artists = [] + for artist in artists: + all_artists.append({ + "name": artist['name'], + "uri": artist['uri'], + "id": artist['id'], + }) + self.artists = all_artists + self.release_date = album['release_date'] + self.total_tracks = album['total_tracks'] + images = [] + for image in album['images']: + images.append(image['url']) + self.images = images + self.type = album['type'] # should always be 'album' + super().__init__(client) + + def __len__(self): + return self.total_tracks + + # add some methods like get_artists() and get_tracks() + + +class Track(FreshObject): + """Track class object + Turns album json data into an object Album + + Parameters + ---------- + client : spotipy.Spotify + client used for the api + track : dict + json data given from api + + Attributes + ---------- + client : spotipy.Spotify + client used for the api + name : str + artist's username on spotify + id : str + artist's unique id + uri : str + artist's spotify uri + url : string + artist's spotify url + all_urls : dict[str, str] + all external URLs for the artist + popularity : int + artist's popularity score + track_number : int + tracks position on the album + explicit : bool + is the track explicit + duration : int + duration of the track in ms + is_local : bool + is the track local + album : dict[str, str] + name, id, and uri of the album + artists : list[dict[str, str]] + list of artists on the track (name, id, uri) + type : str + type of object (should always be 'artist') + + """ + def __init__(self, client, track): + """Inits Track""" + self.client = client + self.name = track['name'] + self.id = track['id'] + self.uri = track['uri'] + self.url = track['external_urls']['spotify'] + self.all_urls = track['external_urls'] + self.popularity = track['popularity'] + self.track_number = track['track_number'] + self.explicit = track['explicit'] + self.duration = track['duration_ms'] + self.is_local = track['is_local'] + album = track['album'] + self.album = { + "name": album['name'], + "uri": album['uri'], + "id": album['id'], + } + artists = track['artists'] + all_artists = [] + for artist in artists: + all_artists.append({ + "name": artist['name'], + "uri": artist['uri'], + "id": artist['id'], + }) + self.artists = all_artists + self.type = track['type'] # should always be 'track' + super().__init__(client) + + def __len__(self): + """ + Returns + ------- + self.duration : int + """ + return self.duration From 935ffcfda892a4ad1bef129066603baad81b4310 Mon Sep 17 00:00:00 2001 From: MakeShiftArtist Date: Sun, 29 Aug 2021 04:00:45 -0500 Subject: [PATCH 2/7] added search generators for artists, albums, and tracks --- spotipy/client.py | 80 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/spotipy/client.py b/spotipy/client.py index 8c1fc8a2..98208321 100644 --- a/spotipy/client.py +++ b/spotipy/client.py @@ -13,6 +13,7 @@ import urllib3 from spotipy.exceptions import SpotifyException +from spotpy.objects import User, Artist, Album, Track logger = logging.getLogger(__name__) @@ -548,6 +549,85 @@ def search(self, q, limit=10, offset=0, type="track", market=None): "search", q=q, limit=limit, offset=offset, type=type, market=market ) + def search_artists(self, q, limit = 10, offset, market=None): + """ searches for an artist + + Parameters: + - q - the search query (see how to write a query in the + official documentation https://developer.spotify.com/documentation/web-api/reference/search/) # noqa + - limit - the number of items to return (min = 1, default = 10, max = 50). The limit is applied + within each type, not on the total response. + - offset - the index of the first item to return + + - market - An ISO 3166-1 alpha-2 country code or the string + from_token. + """ + results = self._get( + "search", q=q, limit=limit, offset=offset, type="artist", market=market + )['artists']['items'] + iteration = 0 + for item in results: + try: + yield Artist(self, results[iteration]) + iteration += 1 + except KeyError: + yield None + except Exception as e: + raise e + + def search_albums(self, q, limit = 10, offset, market=None): + """ searches for an album + + Parameters: + - q - the search query (see how to write a query in the + official documentation https://developer.spotify.com/documentation/web-api/reference/search/) # noqa + - limit - the number of items to return (min = 1, default = 10, max = 50). The limit is applied + within each type, not on the total response. + - offset - the index of the first item to return + + - market - An ISO 3166-1 alpha-2 country code or the string + from_token. + """ + results = self._get( + "search", q=q, limit=limit, offset=offset, type="album", market=market + )['albums']['items'] + iteration = 0 + for item in results: + try: + yield Album(self, results[iteration]) + iteration += 1 + except KeyError: + yield None + except Exception as e: + raise e + + def search_tracks(self, q, limit = 10, offset, market=None): + """ searches for an artist + + Parameters: + - q - the search query (see how to write a query in the + official documentation https://developer.spotify.com/documentation/web-api/reference/search/) # noqa + - limit - the number of items to return (min = 1, default = 10, max = 50). The limit is applied + within each type, not on the total response. + - offset - the index of the first item to return + + - market - An ISO 3166-1 alpha-2 country code or the string + from_token. + """ + results = self._get( + "search", q=q, limit=limit, offset=offset, type="track", market=market + )['tracks']['items'] + iteration = 0 + for item in results: + try: + yield Track(self, results[iteration]) + iteration += 1 + except KeyError: + yield None + except Exception as e: + raise e + + def search_markets(self, q, limit=10, offset=0, type="track", markets=None, total=None): """ (experimental) Searches multiple markets for an item From f905b63268fe26a9ba86fb3aaa446a42e578ff5c Mon Sep 17 00:00:00 2001 From: MakeShiftArtist Date: Sun, 29 Aug 2021 04:01:53 -0500 Subject: [PATCH 3/7] Fixed typo in imports --- spotipy/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spotipy/client.py b/spotipy/client.py index 98208321..3102ec12 100644 --- a/spotipy/client.py +++ b/spotipy/client.py @@ -13,7 +13,7 @@ import urllib3 from spotipy.exceptions import SpotifyException -from spotpy.objects import User, Artist, Album, Track +from spotipy.objects import User, Artist, Album, Track logger = logging.getLogger(__name__) From 0df16b54c4363cd9c5d96203c95271972707bf81 Mon Sep 17 00:00:00 2001 From: MakeShiftArtist Date: Mon, 30 Aug 2021 22:26:13 -0500 Subject: [PATCH 4/7] fixed offset default value --- spotipy/client.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spotipy/client.py b/spotipy/client.py index 3102ec12..284cb999 100644 --- a/spotipy/client.py +++ b/spotipy/client.py @@ -13,7 +13,7 @@ import urllib3 from spotipy.exceptions import SpotifyException -from spotipy.objects import User, Artist, Album, Track +from spotipy.objects import * logger = logging.getLogger(__name__) @@ -549,7 +549,7 @@ def search(self, q, limit=10, offset=0, type="track", market=None): "search", q=q, limit=limit, offset=offset, type=type, market=market ) - def search_artists(self, q, limit = 10, offset, market=None): + def search_artists(self, q, limit=10, offset=0, market=None): """ searches for an artist Parameters: @@ -575,7 +575,7 @@ def search_artists(self, q, limit = 10, offset, market=None): except Exception as e: raise e - def search_albums(self, q, limit = 10, offset, market=None): + def search_albums(self, q, limit=10, offset=0, market=None): """ searches for an album Parameters: @@ -601,7 +601,7 @@ def search_albums(self, q, limit = 10, offset, market=None): except Exception as e: raise e - def search_tracks(self, q, limit = 10, offset, market=None): + def search_tracks(self, q, limit=10, offset=0, market=None): """ searches for an artist Parameters: From 3f94887148d35e01af7d923e717996abc9f94f37 Mon Sep 17 00:00:00 2001 From: MakeShiftArtist Date: Mon, 30 Aug 2021 22:28:19 -0500 Subject: [PATCH 5/7] updated version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 69bfff9e..08b033e5 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ setup( name='spotipy', - version='2.18.0', + version='2.18.1', description='A light weight Python library for the Spotify Web API', long_description=long_description, long_description_content_type="text/markdown", From 96f382e4e2f4573efbe2d095c65807849d130f43 Mon Sep 17 00:00:00 2001 From: MakeShiftArtist Date: Tue, 31 Aug 2021 02:32:46 -0500 Subject: [PATCH 6/7] fixed objects, added new methods --- spotipy/objects.py | 105 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 91 insertions(+), 14 deletions(-) diff --git a/spotipy/objects.py b/spotipy/objects.py index fae7f7c2..3d4c1304 100644 --- a/spotipy/objects.py +++ b/spotipy/objects.py @@ -2,12 +2,27 @@ class FreshObject: """Base Object for Spotipy Objects All spotify objects will inherit this + Parameters + ---------- + client : spotipy.Spotify + client used for the api + + Attributes + ---------- + client : spotipy.Spotify + client used for the api + """ - def __init__(self, client): + def __init__(self, client) -> None: self.client = client def __str__(self) -> str: + """ + Returns + ------- + self.name : str + """ try: return self.name except Exception as e: @@ -28,14 +43,14 @@ def __repr__(self) -> str: raise e def __eq__(self, other) -> bool: - """Checks if the artists are the same""" + """Checks if the objects are the same""" try: return self.id == other.id if (other.type == other.type) else False except Exception as e: raise e def __ne__(self, other) -> bool: - """Checks if they're NOT the same artist""" + """Checks if they're NOT the same object""" try: return not self.__eq__(other) except Exception as e: @@ -75,7 +90,7 @@ class User(FreshObject): """ def __init__(self, client, user): """Inits User""" - self.client = client + super().__init__(client) self.name = user['display_name'] self.id = user['id'] self.uri = user['uri'] @@ -128,9 +143,9 @@ class Artist(FreshObject): type of object (should always be 'artist') """ - def __init__(self, client, artist: dict): + def __init__(self, client, artist: dict) -> None: """Inits Artist""" - self.client = client + super().__init__(client) self.name = artist['name'] self.id = artist['id'] self.uri = artist['uri'] @@ -144,14 +159,12 @@ def __init__(self, client, artist: dict): images.append(image['url']) self.images = images self.type = artist['type'] # should always be 'artist' - super().__init__(client) # add some methods like artist_albums() class Album(FreshObject): """Album class object - Turns album json data into an object Album Parameters ---------- @@ -188,9 +201,9 @@ class Album(FreshObject): type of object (should always be 'artist') """ - def __init__(self, client, album): + def __init__(self, client, album: dict) -> None: """Inits Album""" - self.client = client + super().__init__(client) self.name = album['name'] self.id = album['id'] self.uri = album['uri'] @@ -213,13 +226,67 @@ def __init__(self, client, album): images.append(image['url']) self.images = images self.type = album['type'] # should always be 'album' - super().__init__(client) - def __len__(self): + # data given if requested specifically + self.label = album.get('label') + self.genres = album.get('genres') + self.copyrights = album.get('copyrights') + self.external_ids = album.get('external_ids') + tracks = [] + if album.get('tracks') is not None: + for track in album['tracks'].get('items'): + tracks.append(Track(track)) + self.tracks = tracks + + def __len__(self) -> int: + """ + Returns + ------- + self.total_tracks : int + """ return self.total_tracks # add some methods like get_artists() and get_tracks() + def get_tracks(self) -> Track: + """ + Yields + ------ + track : Track + """ + tracks = self.client.album_tracks(self.id)['items'] + for track in tracks: + yield Track(self.client, track) + + def get_artists(self) -> Artist: + """ + Yields + ------ + artist : Artist + """ + uris = [artist['uri'] for artist in self.artists] + artists = self.client.artists(uris)['artists'] + new_artists = [] + for artist in artists: + yield Artist(self.client, artist) + + def get_artist(self) -> Artist: + """ + Returns + ------- + artist : Artist + """ + return Artist(self.client, self.client.artist(self.artists[0]['uri'])) + + def album_info(self) -> Album: + """ + Returns + ------- + self : Album + """ + album = self.client.album(self.uri) + self = self.__init__(album) + return self class Track(FreshObject): """Track class object @@ -264,7 +331,7 @@ class Track(FreshObject): type of object (should always be 'artist') """ - def __init__(self, client, track): + def __init__(self, client, track) -> None: """Inits Track""" self.client = client self.name = track['name'] @@ -295,10 +362,20 @@ def __init__(self, client, track): self.type = track['type'] # should always be 'track' super().__init__(client) - def __len__(self): + def __len__(self) -> int: """ Returns ------- self.duration : int """ return self.duration + + def get_album(self) -> Album: + """ + Returns + ------- + self.album : Album + """ + album = Album(self.client, self.client.album(self.album[id])) + self.album = album + return self.album From c98388c17d456b4e079a7e80d5700f187c13a166 Mon Sep 17 00:00:00 2001 From: MakeShiftArtist Date: Tue, 31 Aug 2021 02:33:19 -0500 Subject: [PATCH 7/7] cleaned client.search_ methods --- spotipy/client.py | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/spotipy/client.py b/spotipy/client.py index 284cb999..249defb6 100644 --- a/spotipy/client.py +++ b/spotipy/client.py @@ -439,7 +439,7 @@ def album_tracks(self, album_id, limit=50, offset=0, market=None): trid = self._get_id("album", album_id) return self._get( "albums/" + trid + "/tracks/", limit=limit, offset=offset, market=market - ) + )['items'] def albums(self, albums): """ returns a list of albums given the album IDs, URIs, or URLs @@ -447,7 +447,6 @@ def albums(self, albums): Parameters: - albums - a list of album IDs, URIs or URLs """ - tlist = [self._get_id("album", a) for a in albums] return self._get("albums/?ids=" + ",".join(tlist)) @@ -565,15 +564,8 @@ def search_artists(self, q, limit=10, offset=0, market=None): results = self._get( "search", q=q, limit=limit, offset=offset, type="artist", market=market )['artists']['items'] - iteration = 0 for item in results: - try: - yield Artist(self, results[iteration]) - iteration += 1 - except KeyError: - yield None - except Exception as e: - raise e + yield Artist(self, item) def search_albums(self, q, limit=10, offset=0, market=None): """ searches for an album @@ -591,15 +583,8 @@ def search_albums(self, q, limit=10, offset=0, market=None): results = self._get( "search", q=q, limit=limit, offset=offset, type="album", market=market )['albums']['items'] - iteration = 0 for item in results: - try: - yield Album(self, results[iteration]) - iteration += 1 - except KeyError: - yield None - except Exception as e: - raise e + yield Album(self, item) def search_tracks(self, q, limit=10, offset=0, market=None): """ searches for an artist @@ -617,15 +602,8 @@ def search_tracks(self, q, limit=10, offset=0, market=None): results = self._get( "search", q=q, limit=limit, offset=offset, type="track", market=market )['tracks']['items'] - iteration = 0 for item in results: - try: - yield Track(self, results[iteration]) - iteration += 1 - except KeyError: - yield None - except Exception as e: - raise e + yield Track(self, item) def search_markets(self, q, limit=10, offset=0, type="track", markets=None, total=None):