diff --git a/deezer/__init__.py b/deezer/__init__.py index 7cfaaf5..d156e44 100644 --- a/deezer/__init__.py +++ b/deezer/__init__.py @@ -2,8 +2,9 @@ import eventlet requests = eventlet.import_patched('requests') from deezer.gw import GW from deezer.api import API +import json -__version__ = "0.0.1" +__version__ = "0.0.2" class TrackFormats(): """Number associtation for formats""" @@ -27,6 +28,11 @@ class Deezer: self.session.mount('http://', requests.adapters.HTTPAdapter(pool_maxsize=100)) self.session.mount('https://', requests.adapters.HTTPAdapter(pool_maxsize=100)) + self.logged_in = False + self.current_user = {} + self.childs = [] + self.selected_account = 0 + self.api = API(self.session, self.http_headers) self.gw = GW(self.session, self.http_headers) @@ -36,28 +42,78 @@ class Deezer: def get_accept_language(self): return self.http_headers['Accept-Language'] - def get_countries_charts(self): - temp = self.api.get_user_playlists('637006841')['data'] - result = sorted(temp, key=lambda k: k['title']) # Sort all playlists - if not result[0]['title'].startswith('Top'): result = result[1:] # Remove loved tracks playlist - return result + def login(self, email, password, re_captcha_token, child=0): + # Check if user already logged in + user_data = self.gw.get_user_data() + if user_data['USER']['USER_ID'] == 0: + self.logged_in = False + return False + # Get the checkFormLogin + check_form_login = user_data['checkFormLogin'] + login = self.session.post( + "https://www.deezer.com/ajax/action.php", + data={ + 'type': 'login', + 'mail': email, + 'password': password, + 'checkFormLogin': check_form_login, + 'reCaptchaToken': re_captcha_token + }, + headers={'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', **self.http_headers} + ) + # Check if user logged in + if 'success' not in login.text: + self.logged_in = False + return False + user_data = self.gw.get_user_data() + self._post_login(user_data) + self.change_account(child) + self.logged_in = True + return True - def get_track_id_from_metadata(self, artist, track, album): - artist = artist.replace("–", "-").replace("’", "'") - track = track.replace("–", "-").replace("’", "'") - album = album.replace("–", "-").replace("’", "'") + def login_via_arl(self, arl, child=0): + arl = arl.strip() + cookie_obj = requests.cookies.create_cookie( + domain='.deezer.com', + name='arl', + value=arl, + path="/", + rest={'HttpOnly': True} + ) + self.session.cookies.set_cookie(cookie_obj) + user_data = self.gw.get_user_data() + # Check if user logged in + if user_data["USER"]["USER_ID"] == 0: + self.logged_in = False + return False + self._post_login(user_data) + self.change_account(child) + self.logged_in = True + return True - resp = self.api.advanced_search(artist=artist, track=track, album=album, limit=1) - if len(resp['data']) > 0: return resp['data'][0]['id'] + def _post_login(self, user_data): + self.childs = [] + family = user_data["USER"]["MULTI_ACCOUNT"]["ENABLED"] + if family: + childs = self.gw.get_child_accounts() + for child in childs: + self.childs.append({ + 'id': child["USER_ID"], + 'name': child["BLOG_NAME"], + 'picture': child.get("USER_PICTURE", "") + }) + else: + self.childs.append({ + 'id': user_data["USER"]["USER_ID"], + 'name': user_data["USER"]["BLOG_NAME"], + 'picture': user_data["USER"].get("USER_PICTURE", "") + }) - resp = self.api.advanced_search(artist=artist, track=track, limit=1) - if len(resp['data']) > 0: return resp['data'][0]['id'] - - # Try removing version - if "(" in track and ")" in track and track.find("(") < track.find(")"): - resp = self.api.advanced_search(artist=artist, track=track[:track.find("(")], limit=1) - if len(resp['data']) > 0: return resp['data'][0]['id'] - elif " - " in track: - resp = self.api.advanced_search(artist=artist, track=track[:track.find(" - ")], limit=1) - if len(resp['data']) > 0: return resp['data'][0]['id'] - return "0" + def change_account(self, child_n): + if len(self.childs)-1 >= child_n: + self.current_user =self.childs[child_n] + self.selected_account = child_n + else: + self.current_user = self.childs[0] + self.selected_account = 0 + return (self.current_user, self.selected_account) diff --git a/deezer/api.py b/deezer/api.py index b38977a..b0677be 100644 --- a/deezer/api.py +++ b/deezer/api.py @@ -53,7 +53,7 @@ class API: def get_album(self, album_id): return self.api_call(f'album/{str(album_id)}') - def get_track_by_UPC(self, upc): + def get_album_by_UPC(self, upc): return self.get_album(f'upc:{upc}') def get_album_comments(self, album_id, index=0, limit=10): @@ -262,6 +262,34 @@ class API: def get_user_tracks(self, user_id, index=0, limit=25): return self.api_call(f'user/{str(user_id)}/tracks', {'index': index, 'limit': limit}) + # Extra calls + + def get_countries_charts(self): + temp = self.get_user_playlists('637006841') + result = sorted(temp, key=lambda k: k['title']) # Sort all playlists + if not result[0]['title'].startswith('Top'): result = result[1:] # Remove loved tracks playlist + return result + + def get_track_id_from_metadata(self, artist, track, album): + artist = artist.replace("–", "-").replace("’", "'") + track = track.replace("–", "-").replace("’", "'") + album = album.replace("–", "-").replace("’", "'") + + resp = self.advanced_search(artist=artist, track=track, album=album, limit=1) + if len(resp['data']) > 0: return resp['data'][0]['id'] + + resp = self.advanced_search(artist=artist, track=track, limit=1) + if len(resp['data']) > 0: return resp['data'][0]['id'] + + # Try removing version + if "(" in track and ")" in track and track.find("(") < track.find(")"): + resp = self.advanced_search(artist=artist, track=track[:track.find("(")], limit=1) + if len(resp['data']) > 0: return resp['data'][0]['id'] + elif " - " in track: + resp = self.advanced_search(artist=artist, track=track[:track.find(" - ")], limit=1) + if len(resp['data']) > 0: return resp['data'][0]['id'] + return 0 + class APIError(Exception): """Base class for Deezer exceptions""" pass diff --git a/deezer/gw.py b/deezer/gw.py index ae6289f..f857932 100644 --- a/deezer/gw.py +++ b/deezer/gw.py @@ -1,6 +1,8 @@ import eventlet requests = eventlet.import_patched('requests') +from deezer.utils import map_artist_album + class LyricsStatus(): """Explicit Content Lyrics""" @@ -211,6 +213,46 @@ class GW: } return self.api_call('page.get', params=params) + # Extra calls + + def get_artist_discography_tabs(self, art_id, limit=100): + index = 0 + releases = [] + result = {'all': []} + ids = [] + + # Get all releases + while True: + response = self.get_artist_discography(art_id, index=index, limit=limit) + releases += response['data'] + index += limit + if index > response['total']: + break + + for release in releases: + if release['ALB_ID'] not in ids: + ids.append(release['ALB_ID']) + obj = map_artist_album(release) + if (release['ART_ID'] == art_id or release['ART_ID'] != art_id and release['ROLE_ID'] == 0) and release['ARTISTS_ALBUMS_IS_OFFICIAL']: + # Handle all base record types + if not obj['record_type'] in result: + result[obj['record_type']] = [] + result[obj['record_type']].append(obj) + result['all'].append(obj) + else: + # Handle albums where the artist is featured + if release['ROLE_ID'] == 5: + if not 'featured' in result: + result['featured'] = [] + result['featured'].append(obj) + # Handle "more" albums + elif release['ROLE_ID'] == 0: + if not 'more' in result: + result['more'] = [] + result['more'].append(obj) + result['all'].append(obj) + return result + class APIError(Exception): """Base class for Deezer exceptions""" pass diff --git a/deezer/utils.py b/deezer/utils.py index bcc6861..9c15a39 100644 --- a/deezer/utils.py +++ b/deezer/utils.py @@ -131,3 +131,57 @@ def map_album(album): # Extras 'nb_disk': album['NUMBER_DISK'] } + +# maps gw-light api artist/albums to standard api +def map_artist_album(album): + return { + 'id': album['ALB_ID'], + 'title': album['ALB_TITLE'], + 'link': f"https://www.deezer.com/album/{album['ALB_ID']}", + 'cover': f"https://api.deezer.com/album/{album['ALB_ID']}/image", + 'cover_small': f"https://cdns-images.dzcdn.net/images/cover/{album['ALB_PICTURE']}/56x56-000000-80-0-0.jpg", + 'cover_medium': f"https://cdns-images.dzcdn.net/images/cover/{album['ALB_PICTURE']}/250x250-000000-80-0-0.jpg", + 'cover_big': f"https://cdns-images.dzcdn.net/images/cover/{album['ALB_PICTURE']}/500x500-000000-80-0-0.jpg", + 'cover_xl': f"https://cdns-images.dzcdn.net/images/cover/{album['ALB_PICTURE']}/1000x1000-000000-80-0-0.jpg", + 'genre_id': album['GENRE_ID'], + 'fans': album['RANK'], + 'release_date': album['PHYSICAL_RELEASE_DATE'], + 'record_type': RELEASE_TYPE.get(int(album['TYPE']), "unknown"), + 'tracklist': f"https://api.deezer.com/album/{album['ALB_ID']}/tracks", + 'explicit_lyrics': int(album['EXPLICIT_LYRICS']) > 0, + 'type': album['__TYPE__'], + # Extras + 'nb_tracks': album['NUMBER_TRACK'], + 'nb_disk': album['NUMBER_DISK'] + } + +# maps gw-light api playlists to standard api +def map_playlist(playlist): + return { + 'id': playlist['PLAYLIST_ID'], + 'title': playlist['TITLE'], + 'description': playlist['DESCRIPTION'], + 'duration': playlist['DURATION'], + 'public': playlist['STATUS'] == 1, + 'is_loved_track': playlist['TYPE'] == 4, + 'collaborative': playlist['STATUS'] == 2, + 'nb_tracks': playlist['NB_SONG'], + 'fans': playlist['NB_FAN'], + 'link': "https://www.deezer.com/playlist/"+playlist['PLAYLIST_ID'], + 'share': "https://www.deezer.com/playlist/"+playlist['PLAYLIST_ID'], + 'picture': "https://api.deezer.com/playlist/"+playlist['PLAYLIST_ID']+"/image", + 'picture_small': "https://cdns-images.dzcdn.net/images/"+playlist['PICTURE_TYPE']+"/"+playlist['PLAYLIST_PICTURE']+"/56x56-000000-80-0-0.jpg", + 'picture_medium': "https://cdns-images.dzcdn.net/images/"+playlist['PICTURE_TYPE']+"/"+playlist['PLAYLIST_PICTURE']+"/250x250-000000-80-0-0.jpg", + 'picture_big': "https://cdns-images.dzcdn.net/images/"+playlist['PICTURE_TYPE']+"/"+playlist['PLAYLIST_PICTURE']+"/500x500-000000-80-0-0.jpg", + 'picture_xl': "https://cdns-images.dzcdn.net/images/"+playlist['PICTURE_TYPE']+"/"+playlist['PLAYLIST_PICTURE']+"/1000x1000-000000-80-0-0.jpg", + 'checksum': playlist['CHECKSUM'], + 'tracklist': "https://api.deezer.com/playlist/"+playlist['PLAYLIST_ID']+"/tracks", + 'creation_date': playlist['DATE_ADD'], + 'creator': { + 'id': playlist['PARENT_USER_ID'], + 'name': playlist['PARENT_USERNAME'], + 'tracklist': "https://api.deezer.com/user/"+playlist['PARENT_USER_ID']+"/flow", + 'type': "user" + }, + 'type': "playlist" + } diff --git a/setup.py b/setup.py index 8e313ca..c589b1d 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ README = (HERE / "README.md").read_text() setup( name="deezer-py", - version="0.0.1", + version="0.0.2", description="A wrapper for all Deezer's APIs", long_description=README, long_description_content_type="text/markdown",