def download_playlistspo(URL, output=stock_output + "/", recursive_download=stock_recursive_download, not_interface=stock_not_interface, zips=stock_zip): global spo array = [] URL = (URL.split("?")[0].split("/")) try: tracks = spo.user_playlist_tracks(URL[-3], URL[-1]) except Exception as a: if not "The access token expired" in str(a): raise exceptions.InvalidLink("Invalid link ;)") spo = Spotify(generate_token()) tracks = spo.user_playlist_tracks(URL[-3], URL[-1]) def lazy(tracks): for a in tracks['items']: try: array.append( download_trackspo(a['track']['external_urls']['spotify'], output, recursive_download, not_interface)) except: print("Track not found :(") array.append("None") lazy(tracks) tot = tracks['total'] for a in range(tot // 100 - 1): try: tracks = spo.next(tracks) except: spo = Spotify(generate_token()) tracks = spo.next(tracks) lazy(tracks) if zips: zip_name = "{}playlist {}.zip".format(output, URL[-1]) create_zip(zip_name, array) return array, zip_name return array
class SpotifyAPI: def __init__(self): credentials = SpotifyClientCredentials( get_config("spotify", "CLIENT_ID"), get_config("spotify", "CLIENT_SECRET")) self.sp = Spotify(client_credentials_manager=credentials) def get_playlist(self, user: str, playlist: str, ignores: list) -> list: artists = {} try: res = self.sp.user_playlist(user, playlist) while res: try: tracks = res['tracks'] except KeyError: break for i, item in enumerate(tracks['items']): track = item['track'] artist = track['artists'][0]['name'] if artist and artist not in ignores: artists[(track['artists'][0]['name'])] = 1 if tracks['next']: res = self.sp.next(tracks) except SpotifyException as e: raise SpotifyAPIError(str(e)) return list(artists.keys())
def resolve_playlist( spotify: spotipy.Spotify, username: str, title: str, description: str, create: bool = False, collab: bool = False, ) -> Dict[str, Dict]: existing_playlists = spotify.user_playlists(username) playlists_list: list = existing_playlists["items"] ret: Dict[str, Dict] = dict() while playlists_list: playlist = playlists_list.pop() if playlist["description"] == description and ( not collab or (collab and playlist.get("collaborative", False))): ret[playlist["id"]] = playlist if len(playlists_list) == 0 and existing_playlists["next"]: existing_playlists = spotify.next(existing_playlists) playlists_list = existing_playlists["items"] else: playlist = None if not ret and create: playlist = spotify.user_playlist_create(username, title, public=False, description=description) ret[playlist["id"]] = playlist return ret
def get_all(spotify: spotipy.Spotify, results: dict): """Grab more results until none remain.""" items = results["items"] while results["next"]: results = spotify.next(results) items.extend(results["items"]) return items
def get_playlist_tracks(sp: Spotify, playlist_id: str) -> List[Dict[str, Any]]: """Get playlists tracks""" tracks = [] # type: List[Dict[str, Any]] results = sp.playlist_items(playlist_id, limit=100) tracks.extend(results["items"]) while results["next"]: results = sp.next(results) tracks.extend(results["items"]) return tracks
def get_playlists(sp: Spotify) -> List[Dict[str, Any]]: """Get user's playlists""" playlists = [] # type: List[Dict[str, Any]] results = sp.current_user_playlists(limit=50) playlists.extend(results["items"]) while results["next"]: results = sp.next(results) playlists.extend(results["items"]) return playlists
def get_saved_tracks(sp: Spotify) -> List[Dict[str, Any]]: """Returns the list of tracks saved in user library""" tracks = [] # type: List[Dict[str, Any]] results = sp.current_user_saved_tracks(limit=50) tracks.extend(results["items"]) while results["next"]: results = sp.next(results) tracks.extend(results["items"]) return tracks
def auto_paginate(spotify: spotipy.Spotify, paginated): while True: for item in paginated["items"]: yield item if paginated["next"]: paginated = spotify.next(paginated) else: break
def get_followed_artists(sp: Spotify) -> List[Dict[str, Any]]: """Returns the full list of followed artists""" artists = [] # type: List[Dict[str, Any]] results = sp.current_user_followed_artists(limit=50)["artists"] artists.extend(results["items"]) while results["next"]: results = sp.next(results)["artists"] artists.extend(results["items"]) artists.sort(key=lambda artist: artist["name"].lower()) return artists
def get_all_objects(initial_results: Dict[str, Any], spotify_client: Spotify) -> List[Any]: results = initial_results objects = results["items"] while results["next"] is not None: results = spotify_client.next(results) objects.extend(results["items"]) return objects
def get_song_ids(sp: spotipy.Spotify, playlist_id: str) -> List[str]: playlist = sp.playlist_tracks(playlist_id, fields="items(track(id)), next") ret = [] cond = True while cond: items = playlist["items"] ret.extend([item["track"]["id"] for item in items]) cond = playlist.get("next", None) if cond: playlist = sp.next(playlist) return ret
def get(_spotify: spotipy.Spotify, playlistId: str, publicOnly=False) -> dict: result = _spotify.playlist_items(playlist_id=playlistId) while result['next']: tmp = _spotify.next(result) result['items'].extend(tmp['items']) result['next'] = tmp['next'] if publicOnly: result['items'] = [ item for item in result['items'] if not item['is_local'] ] return result
class SpotifyDumper: def __init__(self): self.sp = Spotify(auth_manager=SpotifyOAuth(scope="user-library-read")) def dump_saved_songs(self, spotify_user_id: str): with track_writer() as w: for track in self._get_liked_artists(): w.write_track(track) def _get_liked_artists(self) -> Iterable[Track]: results = self.sp.current_user_saved_tracks() yield from self._consume_all(_parse_saved_tracks_chunk, results) def _consume_all(self, parser: Callable[[dict], Iterable[T]], result: dict) -> Iterable[T]: yield from parser(result) while result['next']: result = self.sp.next(result) yield from parser(result)
async def get_spotify_playlist(url): # Url can be a spotify url or uri. user = '' playlist_id = '' songs = [] token = prompt_for_user_token('mdeception', 'user-library-read') spotify = Spotify(auth=token) if not 'http' in url: user = url.split('user:'******':')[0] playlist_id = url.split(':')[-1] else: user = url.split('user/')[1].split('/')[0] playlist_id = url.split('/')[-1] playlist = spotify.user_playlist(user, playlist_id, fields='tracks, next, name') tracks = playlist['tracks'] for t in tracks['items']: track = t['track'] songs.append('%s %s' % (track['name'], ' & '.join(x['name'] for x in track['artists']))) while tracks['next']: tracks = spotify.next(tracks) for t in tracks['items']: track = t['track'] songs.append( '%s %s' % (track['name'], ' & '.join(x['name'] for x in track['artists']))) return (playlist['name'], user, songs)
def trackInPlaylist(spotify: Spotify, trackname, playlist): result = spotify.playlist_tracks(playlist, offset=0, limit=50) while result["next"]: try: if result["items"]["track"][trackname]: print("match") return True except KeyError: result = spotify.next(result) '''for track in result["items"]: track = track["track"] if track["name"] == trackname: print("match") print(trackname) return True''' #result = spotify.next(result) print("miss") print(trackname) return False
class Login: def __init__(self, token): self.spo = Spotify(generate_token()) self.req = Session() self.req.cookies['arl'] = token user_id = (self.req.get("https://www.deezer.com/").text.split( "'deezer_user_id': ")[1].split(",")[0]) if user_id == "0": raise exceptions.BadCredentials("Wrong token: %s :(" % token) def get_api(self, method, api_token="null", json=None): params = { "api_version": "1.0", "api_token": api_token, "input": "3", "method": method } try: return self.req.post(api_link, params=params, json=json).json()['results'] except: return self.req.post(api_link, params=params, json=json).json()['results'] def download(self, link, name, quality, recursive_quality, recursive_download, datas, not_interface, directory, zips=False): if not quality in qualities: raise exceptions.QualityNotFound( "The qualities have to be FLAC or MP3_320 or MP3_256 or MP3_128" ) self.token = self.get_api("deezer.getUserData")['checkForm'] def get_infos(method, json): infos = None while not "MD5_ORIGIN" in str(infos): infos = self.get_api(method, self.token, json) return infos def ultimatum(infos, datas, name, quality): extension = ".mp3" ids = infos['SNG_ID'] key = "FILESIZE_" + quality if int(infos[key]) > 0 and quality == "FLAC": quality = "9" extension = ".flac" qualit = "FLAC" elif int(infos[key]) > 0 and quality == "MP3_320": quality = "3" qualit = "320" elif int(infos[key]) > 0 and quality == "MP3_256": quality = "5" qualit = "256" elif int(infos[key]) > 0 and quality == "MP3_128": quality = "1" qualit = "128" else: if not recursive_quality: raise exceptions.QualityNotFound( "The quality chosen can't be downloaded") for a in qualities: if int(infos['FILESIZE_' + a]) > 0: quality = qualities[a]['quality'] extension = qualities[a]['extension'] qualit = qualities[a]['qualit'] break else: if a == "MP3_128": raise exceptions.TrackNotFound( "There isn't any quality avalaible for download this song: %s" % name) name += " ({}){}".format(qualit, extension) if os.path.isfile(name): if recursive_download: return name ans = input( "Track %s already exists, do you want to redownload it?(y or n):" % name) if not ans in answers: return name try: md5 = infos['FALLBACK']['MD5_ORIGIN'] except KeyError: md5 = infos['MD5_ORIGIN'] hashs = genurl(md5, quality, ids, infos['MEDIA_VERSION']) try: crypt = request( "https://e-cdns-proxy-{}.dzcdn.net/mobile/1/{}".format( md5[0], hashs)) except IndexError: raise exceptions.TrackNotFound("Track: %s not found:(" % name) if len(crypt.content) == 0: raise exceptions.TrackNotFound( "Something is wrong with %s :(" % name) encry = open(name, "wb") encry.write(crypt.content) encry.close() decry = open(name, "wb") decryptfile(crypt.iter_content(2048), calcbfkey(ids), decry) write_tags(name, add_more_tags(datas, infos, ids)) return name def add_more_tags(datas, infos, ids): json = {"sng_id": ids} try: datas['author'] = " & ".join( infos['SNG_CONTRIBUTORS']['author']) except: datas['author'] = "" try: datas['composer'] = " & ".join( infos['SNG_CONTRIBUTORS']['composer']) except: datas['composer'] = "" try: datas['lyricist'] = " & ".join( infos['SNG_CONTRIBUTORS']['lyricist']) except: datas['lyricist'] = "" try: datas['version'] = infos['VERSION'] except KeyError: datas['version'] = "" need = self.get_api("song.getLyrics", self.token, json) try: datas['lyric'] = need['LYRICS_TEXT'] datas['copyright'] = need['LYRICS_COPYRIGHTS'] datas['lyricist'] = need['LYRICS_WRITERS'] except KeyError: datas['lyric'] = "" datas['copyright'] = "" datas['lyricist'] = "" return datas ids = link.split("/")[-1] if "track" in link: json = {"sng_id": ids} infos = get_infos(method_get_track, json) image = choose_img(infos['ALB_PICTURE']) datas['image'] = image song = "{} - {}".format(datas['music'], datas['artist']) if not not_interface: print("Downloading: %s" % song) try: nams = ultimatum(infos, datas, name, quality) except exceptions.TrackNotFound: url = request( "https://api.deezer.com/search/track/?q=%s" % song.replace("#", ""), True).json() try: for b in range(url['total'] + 1): if url['data'][b]['title'] == datas['music'] or datas[ 'music'] in url['data'][b]['title_short']: URL = url['data'][b]['link'] break except IndexError: raise exceptions.TrackNotFound("Track not found: %s" % song) json = {"sng_id": URL.split("/")[-1]} infos = get_infos(method_get_track, json) nams = ultimatum(infos, datas, name, quality) return nams nams = [] detas = {} zip_name = "" quali = "" json = {"alb_id": ids, "nb": -1} infos = get_infos(method_get_album, json)['data'] image = choose_img(infos[0]['ALB_PICTURE']) detas['image'] = image detas['album'] = datas['album'] detas['year'] = datas['year'] detas['genre'] = datas['genre'] detas['ar_album'] = datas['ar_album'] detas['label'] = datas['label'] for a in tqdm(range(len(name)), disable=not_interface): detas['music'] = datas['music'][a] detas['artist'] = datas['artist'][a] detas['tracknum'] = datas['tracknum'][a] detas['discnum'] = datas['discnum'][a] detas['bpm'] = datas['bpm'][a] detas['gain'] = datas['gain'][a] detas['duration'] = datas['duration'][a] detas['isrc'] = datas['isrc'][a] song = "{} - {}".format(detas['music'], detas['artist']) try: nams.append(ultimatum(infos[a], detas, name[a], quality)) except exceptions.TrackNotFound: try: url = request( "https://api.deezer.com/search/track/?q=%s" % song.replace("#", ""), True).json() for b in range(url['total'] + 1): if url['data'][b]['title'] == detas['music'] or detas[ 'music'] in url['data'][b]['title_short']: URL = url['data'][b]['link'] break json = {"sng_id": URL.split("/")[-1]} nams.append( ultimatum(get_infos(method_get_track, json), detas, name[a], quality)) except (exceptions.TrackNotFound, IndexError, exceptions.InvalidLink): nams.append(name[a]) print("Track not found: %s :(" % song) continue quali = (nams[a].split("(")[-1].split(")")[0]) if zips: zip_name = ("%s%s (%s).zip" % (directory, directory.split("/")[-2], quali)) create_zip(zip_name, nams) return nams, zip_name def download_trackdee(self, URL, output=stock_output + "/", quality=stock_quality, recursive_quality=stock_recursive_quality, recursive_download=stock_recursive_download, not_interface=stock_not_interface): datas = {} URL = URL.split("?utm")[0] URL1 = "https://www.deezer.com/track/%s" % URL.split("/")[-1] URL2 = "https://api.deezer.com/track/%s" % URL.split("/")[-1] url = request(URL2, True).json() url1 = request("http://api.deezer.com/album/%d" % url['album']['id'], True).json() datas['music'] = url['title'] array = [] for a in url['contributors']: if a['name'] != "": array.append(a['name']) array.append(url['artist']['name']) datas['artist'] = artist_sort(array) datas['album'] = url1['title'] datas['tracknum'] = str(url['track_position']) datas['discnum'] = str(url['disk_number']) datas['year'] = url['release_date'] datas['genre'] = [] try: for a in url1['genres']['data']: datas['genre'].append(a['name']) except KeyError: pass datas['genre'] = " & ".join(datas['genre']) datas['ar_album'] = [] for a in url1['contributors']: if a['role'] == "Main": datas['ar_album'].append(a['name']) datas['ar_album'] = " & ".join(datas['ar_album']) datas['label'] = url1['label'] datas['bpm'] = str(url['bpm']) datas['gain'] = str(url['gain']) datas['duration'] = str(url['duration']) datas['isrc'] = url['isrc'] album = var_excape(datas['album']) directory = ("%s%s %s/" % (output, album, url1['upc'])) check_dir(directory) name = ("%s%s CD %s TRACK %s" % (directory, album, datas['discnum'], datas['tracknum'])) name = self.download(URL, name, quality, recursive_quality, recursive_download, datas, not_interface, directory) return name def download_albumdee(self, URL, output=stock_output + "/", quality=stock_quality, recursive_quality=stock_recursive_quality, recursive_download=stock_recursive_download, not_interface=stock_not_interface, zips=stock_zip): datas = {} datas['music'] = [] datas['artist'] = [] datas['tracknum'] = [] datas['discnum'] = [] datas['bpm'] = [] datas['gain'] = [] datas['duration'] = [] datas['isrc'] = [] names = [] array = [] URL = URL.split("?utm")[0] URL1 = "https://www.deezer.com/album/%s" % URL.split("/")[-1] URL2 = "https://api.deezer.com/album/%s" % URL.split("/")[-1] url = request(URL2, True).json() datas['album'] = url['title'] datas['label'] = url['label'] datas['year'] = url['release_date'] datas['genre'] = [] try: for a in url['genres']['data']: datas['genre'].append(a['name']) except KeyError: pass datas['genre'] = " & ".join(datas['genre']) datas['ar_album'] = [] for a in url['contributors']: if a['role'] == "Main": datas['ar_album'].append(a['name']) datas['ar_album'] = " & ".join(datas['ar_album']) album = var_excape(datas['album']) directory = ("%s%s %s/" % (output, album, url['upc'])) for a in url['tracks']['data']: del array[:] datas['music'].append(a['title']) ur = request("https://api.deezer.com/track/%d" % a['id'], True).json() discnum = str(ur['disk_number']) tracknum = str(ur['track_position']) names.append("%s%s CD %s TRACK %s" % (directory, album, discnum, tracknum)) datas['tracknum'].append(tracknum) datas['discnum'].append(discnum) datas['bpm'].append(str(ur['bpm'])) datas['gain'].append(str(ur['gain'])) datas['duration'].append(str(ur['duration'])) datas['isrc'].append(ur['isrc']) for a in ur['contributors']: if a['name'] != "": array.append(a['name']) array.append(ur['artist']['name']) datas['artist'].append(artist_sort(array)) check_dir(directory) names, zip_name = self.download(URL, names, quality, recursive_quality, recursive_download, datas, not_interface, directory, zips) if zips: return names, zip_name return names def download_playlistdee(self, URL, output=stock_output + "/", quality=stock_quality, recursive_quality=True, recursive_download=True, not_interface=stock_not_interface, zips=stock_zip): array = [] URL = URL.split("?utm")[0] ids = URL.split("/")[-1] url = request("https://api.deezer.com/playlist/%s" % ids, True).json() for a in url['tracks']['data']: try: array.append( self.download_trackdee(a['link'], output, quality, recursive_quality, recursive_download, not_interface)) except (exceptions.TrackNotFound, exceptions.NoDataApi): song = "{} - {}".format(a['title'], a['artist']['name']) print("Track not found: %s" % song) array.append(song) if zips: zip_name = "{}playlist {}.zip".format(output, ids) create_zip(zip_name, array) return array, zip_name return array def download_trackspo(self, URL, output=stock_output + "/", quality=stock_quality, recursive_quality=stock_recursive_quality, recursive_download=stock_recursive_download, not_interface=stock_not_interface): URL = URL.split("?")[0] try: url = self.spo.track(URL) except Exception as a: if not "The access token expired" in str(a): raise exceptions.InvalidLink("Invalid link ;)") self.spo = Spotify(generate_token()) url = self.spo.track(URL) isrc = url['external_ids']['isrc'] url = request("https://api.deezer.com/track/isrc:%s" % isrc, True).json() name = self.download_trackdee(url['link'], output, quality, recursive_quality, recursive_download, not_interface) return name def download_albumspo(self, URL, output=stock_output + "/", quality=stock_quality, recursive_quality=stock_recursive_quality, recursive_download=stock_recursive_download, not_interface=stock_not_interface, zips=stock_zip): URL = URL.split("?")[0] try: tracks = self.spo.album(URL) except Exception as a: if not "The access token expired" in str(a): raise exceptions.InvalidLink("Invalid link ;)") self.spo = Spotify(generate_token()) tracks = self.spo.album(URL) tot = tracks['total_tracks'] try: upc = tracks['external_ids']['upc'] while upc[0] == "0": upc = upc[1:] url = request("https://api.deezer.com/album/upc:%s" % upc).json() names = self.download_albumdee(url['link'], output, quality, recursive_quality, recursive_download, not_interface, zips) except KeyError: search = tot // 5 try: url = self.spo.track(tracks['tracks']['items'][search] ['external_urls']['spotify']) except: self.spo = Spotify(generate_token()) url = self.spo.track(tracks['tracks']['items'][search] ['external_urls']['spotify']) isrc = url['external_ids']['isrc'] try: ids = request("https://api.deezer.com/track/isrc:%s" % isrc, True).json()['album']['id'] tracks = request("https://api.deezer.com/album/%d" % ids, True).json() if tot != tracks['nb_tracks']: raise exceptions.TrackNotFound("") names = self.download_albumdee(tracks['link'], output, quality, recursive_quality, recursive_download, not_interface, zips) except (exceptions.TrackNotFound, exceptions.NoDataApi): raise exceptions.AlbumNotFound("Album %s not found :(" % tracks['name']) return names def download_playlistspo(self, URL, output=stock_output + "/", quality=stock_quality, recursive_quality=stock_recursive_quality, recursive_download=stock_recursive_download, not_interface=stock_not_interface, zips=stock_zip): array = [] URL = URL.split("?")[0].split("/") try: tracks = self.spo.user_playlist_tracks(URL[-3], playlist_id=URL[-1]) except Exception as a: if not "The access token expired" in str(a): raise exceptions.InvalidLink("Invalid link ;)") self.spo = Spotify(generate_token()) tracks = self.spo.user_playlist_tracks(URL[-3], playlist_id=URL[-1]) for a in tracks['items']: try: array.append( self.download_trackspo( a['track']['external_urls']['spotify'], output, quality, recursive_quality, recursive_download, not_interface)) except: print("Track not found :(") array.append("None") if tracks['total'] != 100: for a in range(tracks['total'] // 100): try: tracks = self.spo.next(tracks) except: self.spo = Spotify(generate_token()) tracks = self.spo.next(tracks) for a in tracks['items']: try: array.append( self.download_trackspo( a['track']['external_urls']['spotify'], output, quality, recursive_quality, recursive_download, not_interface)) except: print("Track not found :(") array.append("None") if zips: zip_name = "{}playlist {}.zip".format(output, URL[-1]) create_zip(zip_name, array) return array, zip_name return array def download_name(self, artist, song, output=stock_output + "/", quality=stock_quality, recursive_quality=stock_recursive_quality, recursive_download=stock_recursive_download, not_interface=stock_not_interface): query = "track:{} artist:{}".format(song, artist) try: search = self.spo.search(query) except: self.spo = Spotify(generate_token()) search = self.spo.search(query) try: return self.download_trackspo( search['tracks']['items'][0]['external_urls']['spotify'], output, quality, recursive_quality, recursive_download, not_interface) except IndexError: raise exceptions.TrackNotFound("Track not found: :(")
scope = 'user-library-read' client_id = os.environ.get('CLIENT_ID') client_secret = os.environ.get('CLIENT_SECRET') token = util.prompt_for_user_token(username, scope, client_id=client_id, client_secret=client_secret,\ redirect_uri='http://localhost:5000') sp = Spotify(auth=token) saved_tracks = sp.current_user_saved_tracks(limit=20) songs = list() while saved_tracks: for track in saved_tracks['items']: name = track['track']['name'] artists = list() for artist in track['track']['artists']: artists.append(artist['name']) songs.append(name + ' ' + ' '.join(artists)) print('done....') if saved_tracks['next']: saved_tracks = sp.next(saved_tracks) else: saved_tracks = None f = open('likes.txt', 'w') for song in songs: f.write(song + os.linesep)
class Sortify(object): def __init__(self, client_id, client_secret, redirect_uri, user_id): """read readme pls Arguments: client_id {str} -- Client ID for Spotify app client_secret {str} -- Client secret for Spotify app redirect_uri {str} -- Redirect URI for Spotify app user_id {str} -- Your user ID """ self.user_id = user_id os.environ["SPOTIPY_CLIENT_ID"] = CLIENT_ID os.environ["SPOTIPY_CLIENT_SECRET"] = CLIENT_SECRET os.environ["SPOTIPY_REDIRECT_URI"] = REDIRECT_URI self.sp = Spotify(auth=util.prompt_for_user_token( self.user_id, "playlist-modify-public")) self.sp.trace = False def sort_by_attributes(self, playlist, attrs, attr_ix): """Recursively sorts the playlist one attribute at a time Arguments: playlist {list(str)} -- Playlist response object. attrs {list(str)} -- List of attributes to sort by. attr_ix {int} -- Index of the attributes to recurse with. Returns: {list(str)} -- Sorted playlist - who could've guessed? """ if attr_ix: attr = attrs[attr_ix - 1] attr_ix -= 1 if "artists" in attr: playlist.sort(key=lambda x: x["artists"][0]["name"].lower()) if "reversed" in attr: playlist.reverse() return self.sort_by_attributes(playlist, attrs, attr_ix) elif "tracks" in attr: playlist.sort(key=lambda x: x["name"].lower()) if "reversed" in attr: playlist.reverse() return self.sort_by_attributes(playlist, attrs, attr_ix) elif "album_release_date" in attr: playlist.sort(key=lambda x: x["album"]["release_date"]) if "reversed" in attr: playlist.reverse() return self.sort_by_attributes(playlist, attrs, attr_ix) elif "track_number" in attr: playlist.sort(key=lambda x: x["track_number"]) if "reversed" in attr: playlist.reverse() return self.sort_by_attributes(playlist, attrs, attr_ix) raise AttributeError( "Invalid attribute provided. For a customized " "attribute, use the `custom` parameter.") return playlist def dump(self, playlist_name, playlist_id, playlist): """Dumps the track number, track name and artist in the updated playlist Arguments: playlist_name {str} -- Parameter name describes itself >:( playlist_id {str} -- Parameter name describes itself >:( playlist {list(obj)} -- Parameter name describes itself >:( """ print("Playlist name: {}".format(playlist_name)) print("Playlist ID: {}\n".format(playlist_id)) print("{:6s} {:60s} {:s}".format("", "Track", "Artist")) print("{:6s} {:60s} {:s}".format("", "-----", "------")) for i, track in enumerate(playlist): print(" {:3d} {:60s} {:s}".format(i + 1, track["name"], track["artists"][0]["name"])) def sort_playlist(self, playlist_names, sort_by, ready, reverse=False, custom=None, dump=False): """Sorts playlist lol Arguments: playlist_names {list(str)} -- Names of the playlists duh. Will raise an exception if any playlist isn't associated with the username. Which is also a good indication the user does not own the playlist. sort_by {list(str)} -- List of attributes to sort by. ready {bool} -- Option to commit changes to the updated playlist. custom {func} -- Optional custom sorting key, like if you wanna sort by track name string length or something similarly absurd. dump {bool} (default: False) -- Option to dump the reordered playlist in a purdy format. """ playlists = self.sp.user_playlists(self.user_id) new_playlist = [] for playlist in playlists["items"]: if playlist["name"] in playlist_names: playlist_names.remove(playlist["name"]) tracks = self.sp.user_playlist(self.user_id, playlist["id"])["tracks"] new_playlist.extend(i["track"] for i in tracks["items"]) while tracks["next"]: tracks = self.sp.next(tracks) new_playlist.extend(i["track"] for i in tracks["items"]) new_playlist = self.sort_by_attributes(new_playlist, sort_by, len(sort_by)) if custom: new_playlist.sort(key=custom) if reverse: new_playlist.reverse() if dump: self.dump(playlist["name"], playlist["id"], new_playlist) if ready: if len(new_playlist) > 99: max_tracks = 100 new_playlist = [ new_playlist[i * max_tracks:(i + 1) * max_tracks] for i in range((len(new_playlist) + max_tracks - 1) // max_tracks) ] self.sp.user_playlist_replace_tracks( self.user_id, playlist["id"], [x["uri"] for x in new_playlist[0]]) for playlist_chunk in new_playlist[1:]: self.sp.user_playlist_add_tracks( self.user_id, playlist["id"], [x["uri"] for x in playlist_chunk]) else: self.sp.user_playlist_replace_tracks( self.user_id, playlist["id"], [x["uri"] for x in new_playlist]) print("Playlist successfully updated") new_playlist = [] if len(playlist_names): raise AttributeError("Some playlists were not found: {}".format( " ,".join(playlist_names)))
class AuthTestSpotipy(unittest.TestCase): """ These tests require user authentication - provide client credentials using the following environment variables :: 'SPOTIPY_CLIENT_USERNAME' 'SPOTIPY_CLIENT_ID' 'SPOTIPY_CLIENT_SECRET' 'SPOTIPY_REDIRECT_URI' """ playlist = "spotify:user:plamere:playlist:2oCEWyyAPbZp9xhVSxZavx" playlist_new_id = "spotify:playlist:7GlxpQjjxRjmbb3RP2rDqI" four_tracks = ["spotify:track:6RtPijgfPKROxEzTHNRiDp", "spotify:track:7IHOIqZUUInxjVkko181PB", "4VrWlk8IQxevMvERoX08iC", "http://open.spotify.com/track/3cySlItpiPiIAzU3NyHCJf"] two_tracks = ["spotify:track:6RtPijgfPKROxEzTHNRiDp", "spotify:track:7IHOIqZUUInxjVkko181PB"] other_tracks = ["spotify:track:2wySlB6vMzCbQrRnNGOYKa", "spotify:track:29xKs5BAHlmlX1u4gzQAbJ", "spotify:track:1PB7gRWcvefzu7t3LJLUlf"] album_ids = ["spotify:album:6kL09DaURb7rAoqqaA51KU", "spotify:album:6RTzC0rDbvagTSJLlY7AKl"] bad_id = 'BAD_ID' @classmethod def setUpClass(self): if sys.version_info >= (3, 2): # >= Python3.2 only warnings.filterwarnings( "ignore", category=ResourceWarning, # noqa message="unclosed.*<ssl.SSLSocket.*>") missing = list(filter(lambda var: not os.getenv(CCEV[var]), CCEV)) if missing: raise Exception( ('Please set the client credentials for the test application' ' using the following environment variables: {}').format( CCEV.values())) self.username = os.getenv(CCEV['client_username']) self.scope = ( 'playlist-modify-public ' 'user-library-read ' 'user-follow-read ' 'user-library-modify ' 'user-read-private ' 'user-top-read ' 'user-follow-modify ' 'user-read-recently-played ' 'ugc-image-upload' ) self.token = prompt_for_user_token(self.username, scope=self.scope) self.spotify = Spotify(auth=self.token) # Helper def get_or_create_spotify_playlist(self, playlist_name): playlists = self.spotify.user_playlists(self.username) while playlists: for item in playlists['items']: if item['name'] == playlist_name: return item playlists = self.spotify.next(playlists) return self.spotify.user_playlist_create( self.username, playlist_name) # Helper def get_as_base64(self, url): import base64 return base64.b64encode(requests.get(url).content).decode("utf-8") def test_track_bad_id(self): try: self.spotify.track(self.bad_id) self.assertTrue(False) except SpotifyException: self.assertTrue(True) def test_basic_user_profile(self): user = self.spotify.user(self.username) self.assertTrue(user['id'] == self.username.lower()) def test_current_user(self): user = self.spotify.current_user() self.assertTrue(user['id'] == self.username.lower()) def test_me(self): user = self.spotify.me() self.assertTrue(user['id'] == self.username.lower()) def test_user_playlists(self): playlists = self.spotify.user_playlists(self.username, limit=5) self.assertTrue('items' in playlists) self.assertTrue(len(playlists['items']) == 5) def test_user_playlist_tracks(self): playlists = self.spotify.user_playlists(self.username, limit=5) self.assertTrue('items' in playlists) for playlist in playlists['items']: user = playlist['owner']['id'] pid = playlist['id'] results = self.spotify.user_playlist_tracks(user, pid) self.assertTrue(len(results['items']) >= 0) def test_current_user_saved_albums(self): # List albums = self.spotify.current_user_saved_albums() self.assertTrue(len(albums['items']) > 1) # Add self.spotify.current_user_saved_albums_add(self.album_ids) # Contains self.assertTrue( self.spotify.current_user_saved_albums_contains( self.album_ids) == [ True, True]) # Remove self.spotify.current_user_saved_albums_delete(self.album_ids) albums = self.spotify.current_user_saved_albums() self.assertTrue(len(albums['items']) > 1) def test_current_user_playlists(self): playlists = self.spotify.current_user_playlists(limit=10) self.assertTrue('items' in playlists) self.assertTrue(len(playlists['items']) == 10) def test_user_playlist_follow(self): self.spotify.user_playlist_follow_playlist( 'plamere', '4erXB04MxwRAVqcUEpu30O') follows = self.spotify.user_playlist_is_following( 'plamere', '4erXB04MxwRAVqcUEpu30O', [ self.spotify.current_user()['id']]) self.assertTrue(len(follows) == 1, 'proper follows length') self.assertTrue(follows[0], 'is following') self.spotify.user_playlist_unfollow( 'plamere', '4erXB04MxwRAVqcUEpu30O') follows = self.spotify.user_playlist_is_following( 'plamere', '4erXB04MxwRAVqcUEpu30O', [ self.spotify.current_user()['id']]) self.assertTrue(len(follows) == 1, 'proper follows length') self.assertFalse(follows[0], 'is no longer following') def test_current_user_saved_tracks(self): tracks = self.spotify.current_user_saved_tracks() self.assertTrue(len(tracks['items']) > 0) def test_current_user_save_and_unsave_tracks(self): tracks = self.spotify.current_user_saved_tracks() total = tracks['total'] self.spotify.current_user_saved_tracks_add(self.four_tracks) tracks = self.spotify.current_user_saved_tracks() new_total = tracks['total'] self.assertTrue(new_total - total == len(self.four_tracks)) tracks = self.spotify.current_user_saved_tracks_delete( self.four_tracks) tracks = self.spotify.current_user_saved_tracks() new_total = tracks['total'] self.assertTrue(new_total == total) def test_categories(self): response = self.spotify.categories() self.assertTrue(len(response['categories']) > 0) def test_category_playlists(self): response = self.spotify.categories() for cat in response['categories']['items']: cat_id = cat['id'] response = self.spotify.category_playlists(category_id=cat_id) if len(response['playlists']["items"]) > 0: break self.assertTrue(True) def test_new_releases(self): response = self.spotify.new_releases() self.assertTrue(len(response['albums']) > 0) def test_featured_releases(self): response = self.spotify.featured_playlists() self.assertTrue(len(response['playlists']) > 0) def test_current_user_follows(self): response = self.spotify.current_user_followed_artists() artists = response['artists'] self.assertTrue(len(artists['items']) > 0) def test_current_user_top_tracks(self): response = self.spotify.current_user_top_tracks() items = response['items'] self.assertTrue(len(items) > 0) def test_current_user_top_artists(self): response = self.spotify.current_user_top_artists() items = response['items'] self.assertTrue(len(items) > 0) def test_current_user_recently_played(self): # No cursor res = self.spotify.current_user_recently_played() self.assertTrue(len(res['items']) <= 50) played_at = res['items'][0]['played_at'] # Using `before` gives tracks played before res = self.spotify.current_user_recently_played( before=res['cursors']['after']) self.assertTrue(len(res['items']) <= 50) self.assertTrue(res['items'][0]['played_at'] < played_at) played_at = res['items'][0]['played_at'] # Using `after` gives tracks played after res = self.spotify.current_user_recently_played( after=res['cursors']['before']) self.assertTrue(len(res['items']) <= 50) self.assertTrue(res['items'][0]['played_at'] > played_at) def test_user_playlist_ops(self): sp = self.spotify # create empty playlist playlist = self.get_or_create_spotify_playlist( 'spotipy-testing-playlist-1') playlist_id = playlist['id'] # remove all tracks from it sp.user_playlist_replace_tracks( self.username, playlist_id, []) playlist = sp.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 0) self.assertTrue(len(playlist['tracks']['items']) == 0) # add tracks to it sp.user_playlist_add_tracks( self.username, playlist_id, self.four_tracks) playlist = sp.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 4) self.assertTrue(len(playlist['tracks']['items']) == 4) # remove two tracks from it sp.user_playlist_remove_all_occurrences_of_tracks(self.username, playlist_id, self.two_tracks) playlist = sp.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 2) self.assertTrue(len(playlist['tracks']['items']) == 2) # replace with 3 other tracks sp.user_playlist_replace_tracks(self.username, playlist_id, self.other_tracks) playlist = sp.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 3) self.assertTrue(len(playlist['tracks']['items']) == 3) def test_playlist(self): # New playlist ID pl = self.spotify.playlist(self.playlist_new_id) self.assertTrue(pl["tracks"]["total"] > 0) # Old playlist ID pl = self.spotify.playlist(self.playlist) self.assertTrue(pl["tracks"]["total"] > 0) def test_playlist_tracks(self): # New playlist ID pl = self.spotify.playlist_tracks(self.playlist_new_id, limit=2) self.assertTrue(len(pl["items"]) == 2) self.assertTrue(pl["total"] > 0) # Old playlist ID pl = self.spotify.playlist_tracks(self.playlist, limit=2) self.assertTrue(len(pl["items"]) == 2) self.assertTrue(pl["total"] > 0) def test_playlist_upload_cover_image(self): pl1 = self.get_or_create_spotify_playlist('spotipy-testing-playlist-1') plid = pl1['uri'] old_b64 = pl1['images'][0]['url'] # Upload random dog image r = requests.get('https://dog.ceo/api/breeds/image/random') dog_base64 = self.get_as_base64(r.json()['message']) self.spotify.playlist_upload_cover_image(plid, dog_base64) # Image must be different pl1 = self.spotify.playlist(plid) new_b64 = self.get_as_base64(pl1['images'][0]['url']) self.assertTrue(old_b64 != new_b64) def test_playlist_cover_image(self): pl = self.get_or_create_spotify_playlist('spotipy-testing-playlist-1') plid = pl['uri'] res = self.spotify.playlist_cover_image(plid) self.assertTrue(len(res) > 0) first_image = res[0] self.assertTrue('width' in first_image) self.assertTrue('height' in first_image) self.assertTrue('url' in first_image) def test_user_follows_and_unfollows_artist(self): # Initially follows 1 artist res = self.spotify.current_user_followed_artists() self.assertTrue(res['artists']['total'] == 1) # Follow 2 more artists artists = ["6DPYiyq5kWVQS4RGwxzPC7", "0NbfKEOTQCcwd6o7wSDOHI"] self.spotify.user_follow_artists(artists) res = self.spotify.current_user_followed_artists() self.assertTrue(res['artists']['total'] == 3) # Unfollow these 2 artists self.spotify.user_unfollow_artists(artists) res = self.spotify.current_user_followed_artists() self.assertTrue(res['artists']['total'] == 1) def test_user_follows_and_unfollows_user(self): # TODO improve after implementing `me/following/contains` users = ["11111204", "xlqeojt6n7on0j7coh9go8ifd"] # Follow 2 more users self.spotify.user_follow_users(users) # Unfollow these 2 users self.spotify.user_unfollow_users(users) def test_deprecated_starred(self): pl = self.spotify.user_playlist(self.username) self.assertTrue(pl["tracks"] is None) self.assertTrue(pl["owner"] is None) def test_deprecated_user_playlist(self): # Test without user due to change from # https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/ pl = self.spotify.user_playlist(None, self.playlist) self.assertTrue(pl["tracks"]["total"] > 0) def test_deprecated_user_playlis(self): # Test without user due to change from # https://developer.spotify.com/community/news/2018/06/12/changes-to-playlist-uris/ pl = self.spotify.user_playlist_tracks(None, self.playlist, limit=2) self.assertTrue(len(pl["items"]) == 2) self.assertTrue(pl["total"] > 0)
def Link(link, chat_id, quality, msg): global spo global del1 del1 += 1 done = 0 links1 = [] links2 = [] quali = quality.split("MP3_")[-1] link = link.split("?")[0] try: if "spotify" in link: if "track/" in link: try: url = spo.track(link) except Exception as a: if not "The access token expired" in str(a): sendMessage( chat_id, "Invalid link %s ;)" % link, reply_to_message_id = msg['message_id'] ) delete(chat_id) return spo = Spotify( generate_token() ) url = spo.track(link) try: image1 = url['album']['images'][0]['url'] except IndexError: image1 = "https://e-cdns-images.dzcdn.net/images/cover/1000x1000-000000-80-0-0.jpg" sendPhoto( chat_id, image1, caption = ( send_image_track_query % ( url['name'], url['album']['artists'][0]['name'], url['album']['name'], url['album']['release_date'] ) ) ) track(link, chat_id, quality) elif "album/" in link: try: tracks = spo.album(link) except Exception as a: if not "The access token expired" in str(a): sendMessage( chat_id, "Invalid link %s ;)" % link, reply_to_message_id = msg['message_id'] ) delete(chat_id) return spo = Spotify( generate_token() ) tracks = spo.album(link) try: image3 = tracks['images'][2]['url'] image2 = tracks['images'][1]['url'] image1 = tracks['images'][0]['url'] except IndexError: image3 = "https://e-cdns-images.dzcdn.net/images/cover/90x90-000000-80-0-0.jpg" image2 = "https://e-cdns-images.dzcdn.net/images/cover/320x320-000000-80-0-0.jpg" image1 = "https://e-cdns-images.dzcdn.net/images/cover/1000x1000-000000-80-0-0.jpg" tot = tracks['total_tracks'] conn = connect(db_file) c = conn.cursor() lin = "album/%s" % link.split("/")[-1] count = [0] sendPhoto( chat_id, image1, caption = ( send_image_album_query % ( tracks['name'], tracks['artists'][0]['name'], tracks['release_date'], tot ) ) ) def lazy(a): count[0] += a['duration_ms'] lin = "track/%s" % a['external_urls']['spotify'].split("/")[-1] c.execute( where_query.format(lin, quali) ) links2.append(lin) if c.fetchone(): links1.append(lin) for a in tracks['tracks']['items']: lazy(a) tracks = tracks['tracks'] if tot != 50: for a in range(tot // 50): try: tracks = spo.next(tracks) except: spo = Spotify( generate_token() ) tracks = spo.next(tracks) for a in tracks['items']: lazy(a) conn.close() if (count[0] / 1000) > 40000: sendMessage(chat_id, "If you do this again I will come to your home and I will ddos your ass :)") delete(chat_id) return if len(links1) != tot: z = downloa.download_albumspo( link, quality = quality, recursive_quality = True, recursive_download = True, not_interface = True ) else: for a in links2: track(a, chat_id, quality) done = 1 elif "playlist/" in link: musi = link.split("/") try: tracks = spo.user_playlist(musi[-3], musi[-1]) except Exception as a: if not "The access token expired" in str(a): sendMessage( chat_id, "Invalid link ;)", reply_to_message_id = msg['message_id'] ) delete(chat_id) return spo = Spotify( generate_token() ) tracks = spo.user_playlist(musi[-3], musi[-1]) try: image1 = tracks['images'][0]['url'] except IndexError: image1 = "https://e-cdns-images.dzcdn.net/images/cover/1000x1000-000000-80-0-0.jpg" tot = tracks['tracks']['total'] if tot > 400: sendMessage(chat_id, "F**k you") delete(chat_id) return sendPhoto( chat_id, image1, caption = ( send_image_playlist_query % ( tracks['tracks']['items'][0]['added_at'], tracks['owner']['display_name'], tot ) ) ) def lazy(a): try: track( a['track']['external_urls']['spotify'], chat_id, quality ) except: try: sendMessage(chat_id, "%s Not found :(" % a['track']['name']) except: sendMessage(chat_id, "Error :(") for a in tracks['tracks']['items']: lazy(a) tot = tracks['tracks']['total'] tracks = tracks['tracks'] if tot != 100: for a in range(tot // 100): try: tracks = spo.next(tracks) except: spo = Spotify( generate_token() ) tracks = spo.next(tracks) for a in tracks['items']: lazy(a) done = 1 else: sendMessage(chat_id, "Sorry :( The bot doesn't support this link") elif "deezer" in link: ids = link.split("/")[-1] if "track/" in link: try: url = request( "https://api.deezer.com/track/%s" % ids, chat_id, True ).json() except AttributeError: delete(chat_id) return image1 = check_image( url['album']['cover_xl'], ids ) sendPhoto( chat_id, image1, caption = ( send_image_track_query % ( url['title'], url['artist']['name'], url['album']['title'], url['album']['release_date'] ) ) ) track(link, chat_id, quality) elif "album/" in link: try: url = request( "https://api.deezer.com/album/%s" % ids, chat_id, True ).json() except AttributeError: delete(chat_id) return if url['duration'] > 40000: sendMessage(chat_id, "If you do this again I will come to your home and I will ddos your ass :)") delete(chat_id) return image1 = url['cover_xl'] if not image1: URL = "https://www.deezer.com/album/%s" % ids image1 = request(URL).text image1 = ( BeautifulSoup(image1, "html.parser") .find("img", class_ = "img_main") .get("src") .replace("200x200", "1000x1000") ) ima = request(image1).content if len(ima) == 13: image1 = "https://e-cdns-images.dzcdn.net/images/cover/1000x1000-000000-80-0-0.jpg" image2 = image1.replace("1000x1000", "320x320") image3 = image1.replace("1000x1000", "90x90") conn = connect(db_file) c = conn.cursor() lin = "album/%s" % ids for a in url['tracks']['data']: lin = "track/%s" % a['link'].split("/")[-1] c.execute( where_query.format(lin, quali) ) links2.append(lin) if c.fetchone(): links1.append(lin) conn.close() tot = url['nb_tracks'] sendPhoto( chat_id, image1, caption = ( send_image_album_query % ( url['title'], url['artist']['name'], url['release_date'], tot ) ) ) if len(links1) != tot: z = downloa.download_albumdee( link, quality = quality, recursive_quality = True, recursive_download = True, not_interface = True ) else: for a in links2: track(a, chat_id, quality) done = 1 elif "playlist/" in link: try: url = request( "https://api.deezer.com/playlist/%s" % ids, chat_id, True ).json() except AttributeError: delete(chat_id) return tot = url['nb_tracks'] if tot > 400: sendMessage(chat_id, "F**k you") delete(chat_id) return sendPhoto( chat_id, url['picture_xl'], caption = ( send_image_playlist_query % ( url['creation_date'], url['creator']['name'], tot ) ) ) for a in url['tracks']['data']: try: track(a['link'], chat_id, quality) except: song = "{} - {}".format(a['title'], a['artist']['name']) sendMessage(chat_id, "Cannot download %s :(" % song) done = 1 elif "artist/" in link: link = "https://api.deezer.com/artist/%s" % ids try: url = request(link, chat_id, True).json() except AttributeError: delete(chat_id) return sendPhoto( chat_id, url['picture_xl'], caption = ( "👤 Artist: %s \n💽 Album numbers: %d \n👥 Fans on Deezer: %d" % ( url['name'], url['nb_album'], url['nb_fan'] ) ), reply_markup = InlineKeyboardMarkup( inline_keyboard = [ [ InlineKeyboardButton( text = "TOP 30 🔝", callback_data = "%s/top?limit=30" % link ), InlineKeyboardButton( text = "ALBUMS 💽", callback_data = "%s/albums" % link ) ], [ InlineKeyboardButton( text = "RADIO 📻", callback_data = "%s/radio" % link ), InlineKeyboardButton( text = "RELATED 🗣", callback_data = "%s/related" % link ) ] ] ) ) else: sendMessage(chat_id, "Sorry :( The bot doesn't support this link %s :(" % link) else: sendMessage(chat_id, "Sorry :( The bot doesn't support this link %s :(" % link) try: image3 = request(image3).content for a in range( len(z) ): sendAudio(chat_id, z[a], links2[a], image3) except NameError: pass except exceptions.QuotaExceeded: sendMessage(chat_id, "Please send the link %s again :(" % link) except exceptions.AlbumNotFound: sendMessage(chat_id, "Album %s didn't find on Deezer :(" % link) sendMessage(chat_id, "Try to search it throught inline mode or search the link on Deezer") except Exception as a: logging.warning(a) logging.warning(quality) logging.warning(link) sendMessage( chat_id, "OPS :( Something went wrong please send to @An0nimia this link: {} {}, if this happens again".format(link, quality) ) if done == 1: sendMessage( chat_id, "FINISHED :) Rate me here https://t.me/BotsArchive/298", reply_to_message_id = msg['message_id'], reply_markup = InlineKeyboardMarkup( inline_keyboard = [ [ InlineKeyboardButton( text = "SHARE", url = "tg://msg?text=Start @%s for download all the songs which you want ;)" % bot_name ) ] ] ) ) delete(chat_id)
def Link(link, chat_id, quality, message_id): global spo global del1 del1 += 1 done = 0 quali = quality.split("MP3_")[-1] link = link.split("?")[0] ids = link.split("/")[-1] try: if "track/" in link: if "spotify" in link: try: url = spo.track(link) except Exception as a: if not "The access token expired" in str(a): sendMessage(chat_id, "Invalid link %s ;)" % link, reply_to_message_id=message_id) delete(chat_id) return spo = Spotify(generate_token()) url = spo.track(link) try: image1 = url['album']['images'][0]['url'] except IndexError: image1 = song_default_image name = url['name'] artist = url['album']['artists'][0]['name'] album = url['album']['name'] date = url['album']['release_date'] elif "deezer" in link: kind = "track" api_link = api_track % ids try: url = reque(api_link, chat_id, True).json() except AttributeError: delete(chat_id) return image1 = check_image(url['album']['cover_xl'], ids, kind) name = url['title'] artist = url['artist']['name'] album = url['album']['title'] date = url['album']['release_date'] if any(a in link for a in services_supported): sendPhoto(chat_id, image1, caption=(send_image_track_query % (name, artist, album, date))) track(link, chat_id, quality) else: sendMessage(chat_id, not_supported_links % link) elif "album/" in link: links = [] count = [0] if "spotify" in link: try: tracks = spo.album(link) except Exception as a: if not "The access token expired" in str(a): sendMessage(chat_id, "Invalid link %s ;)" % link, reply_to_message_id=message_id) delete(chat_id) return spo = Spotify(generate_token()) tracks = spo.album(link) try: image3 = tracks['images'][2]['url'] image1 = tracks['images'][0]['url'] except IndexError: image3 = image_resize(song_default_image, 90) image1 = song_default_image name = tracks['name'] artist = tracks['artists'][0]['name'] date = tracks['release_date'] tot = tracks['total_tracks'] def lazy(a): count[0] += a['duration_ms'] links.append(a['external_urls']['spotify']) for a in tracks['tracks']['items']: lazy(a) tracks = tracks['tracks'] for a in range(tot // 50 - 1): try: tracks = spo.next(tracks) except: spo = Spotify(generate_token()) tracks = spo.next(tracks) for a in tracks['items']: lazy(a) count[0] //= 1000 mode = downloa.download_albumspo elif "deezer" in link: api_link = api_album % ids kind = "album" try: url = reque(api_link, chat_id, True).json() except AttributeError: delete(chat_id) return count[0] = url['duration'] image1 = check_image(url['cover_xl'], ids, kind) image3 = image_resize(image1, 90) tot = url['nb_tracks'] links = [a['link'] for a in url['tracks']['data']] name = url['title'] artist = url['artist']['name'] date = url['release_date'] mode = downloa.download_albumdee if any(a in link for a in services_supported): if count[0] > seconds_limits_album: sendMessage( chat_id, "If you do this again I will come to your home and I will ddos your ass :)" ) delete(chat_id) return message_id = sendPhoto( chat_id, image1, caption=(send_image_album_query % (name, artist, date, tot)))['message_id'] conn = connect(db_file) c = conn.cursor() exists = [] for a in links: ids = a.split("/")[-1] lins = "track/%s" % ids exist = c.execute(where_query.format(lins, quali)).fetchone() if exist: exists.append(exist) if len(exists) < len(links) // 3: z = mode(link, quality=quality, recursive_quality=True, recursive_download=True, not_interface=not_interface) image3 = get_image(image3) for a in range(len(z)): sendAudio(chat_id, z[a], links[a], image3) else: for a in links: track(a, chat_id, quality) done = 1 else: sendMessage(chat_id, not_supported_links % link) elif "playlist/" in link: links = [] if "spotify" in link: musi = link.split("/") try: tracks = spo.user_playlist(musi[-3], musi[-1]) except Exception as a: if not "The access token expired" in str(a): sendMessage(chat_id, "Invalid link ;)", reply_to_message_id=message_id) delete(chat_id) return spo = Spotify(generate_token()) tracks = spo.user_playlist(musi[-3], musi[-1]) try: image1 = tracks['images'][0]['url'] except IndexError: image1 = song_default_image def lazy(a): try: links.append(a['track']['external_urls']['spotify']) except (KeyError, TypeError): links.append("Error :(") for a in tracks['tracks']['items']: lazy(a) added = tracks['tracks']['items'][0]['added_at'] owner = tracks['owner']['display_name'] tot = tracks['tracks']['total'] tracks = tracks['tracks'] for a in range(tot // 100 - 1): try: tracks = spo.next(tracks) except: spo = Spotify(generate_token()) tracks = spo.next(tracks) for a in tracks['items']: lazy(a) elif "deezer" in link: api_link = api_playlist % ids try: url = reque(api_link, chat_id, True).json() except AttributeError: delete(chat_id) return links = [a['link'] for a in url['tracks']['data']] image1 = url['picture_xl'] tot = url['nb_tracks'] added = url['creation_date'] owner = url['creator']['name'] if any(a in link for a in services_supported): if tot > max_songs: sendMessage(chat_id, "F**k you") delete(chat_id) return sendPhoto(chat_id, image1, caption=(send_image_playlist_query % (added, owner, tot))) for a in links: if a.startswith("http"): try: track(a, chat_id, quality) except: sendMessage(chat_id, "Cannot download %s:(" % a) else: sendMessage(chat_id, a) done = 1 else: sendMessage(chat_id, not_supported_links % link) elif "artist/" in link: if "deezer" in link: api_link = api_artist % ids try: url = reque(api_link, chat_id, True).json() except AttributeError: delete(chat_id) return keyboard = [[ InlineKeyboardButton( queries['top']['text'], callback_data=queries['top']['query'] % api_link), InlineKeyboardButton( queries['albums']['text'], callback_data=queries['albums']['query'] % api_link) ], [ InlineKeyboardButton( queries['radio']['text'], callback_data=queries['radio']['query'] % api_link), InlineKeyboardButton( queries['related']['text'], callback_data=queries['related']['query'] % api_link) ]] image1 = url['picture_xl'] artist = url['name'] albums = url['nb_album'] fans = url['nb_fan'] if any(a in link for a in services_supported[1:]): sendPhoto(chat_id, image1, caption=(send_image_artist_query % (artist, albums, fans)), reply_markup=InlineKeyboardMarkup(keyboard)) else: sendMessage(chat_id, not_supported_links % link) else: sendMessage(chat_id, not_supported_links % link) except FileNotFoundError: sendMessage(chat_id, "Resend link please...", reply_to_message_id=message_id) except error.TimedOut: sendMessage(chat_id, "Retry after a few minutes") except exceptions.QuotaExceeded: sendMessage(chat_id, "Please send the link %s again :(" % link) except exceptions.AlbumNotFound: sendMessage(chat_id, "Album %s didn't find on Deezer :(" % link) sendMessage( chat_id, "Try to search it throught inline mode or search the link on Deezer" ) except Exception as a: logging.error(a) logging.error(quality) logging.error(link) sendMessage( chat_id, "OPS :( Something went wrong please send to @AmineSoukara this link: {} {}, if this happens again" .format(link, quality)) if done == 1: sendMessage(chat_id, end_message, reply_to_message_id=message_id, reply_markup=InlineKeyboardMarkup(end_keyboard)) delete(chat_id)
class SpotifyPlaylist(Playlist): def __init__(self, *, user: str, client_id: str, client_secret: str, redirect_uri: str, **kwargs): """Init SpotifyPlaylist. Args: user: A str representing the Spotify user id. client_id: A str representing the app client id. client_secret: A str representing the app client secret. redirect_uri: A str representing the app redirect URI. """ # authenticates app to access user's data self._token = util.prompt_for_user_token(username=user, scope=SCOPE, client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri) self.user = user self.spotify = Spotify(auth=self._token) self.playlist_count = Counter() def remove(self, *, playlist_id: str, n_tracks: int, **kwargs): """Delete tracks from specified playlist if adding n_tracks exceeds SONG_LIMIT. Delete n tracks from the specified playlist if the size of the playlist + n_tracks exceeds SONG_LIMIT, where n = len(playlist) + n_tracks - SONG_LIMIT Also keeps track of the size of the playlist. Args: playlist_id: The playlist to add tracks to. n_tracks: The number of tracks to add. Raises: PlaylistError: n_tracks exceeds SONG_LIMIT """ _playlist = self.spotify.user_playlist(user=self.user, playlist_id=playlist_id, fields="tracks,next,name") tracks = _playlist["tracks"] total_tracks = self.playlist_count[playlist_id] + n_tracks if total_tracks >= SONG_LIMIT: n = total_tracks - SONG_LIMIT # make sure we aren't trying to add too many tracks if n > self.playlist_count[playlist_id]: raise PlaylistError( f"{n_tracks} exceeds playlist song limit of {SONG_LIMIT}") count = 0 remove = [] for item in tracks["items"]: if count >= n: break remove.append(item["track"]["id"]) count += 1 # remove tracks from playlist self.spotify.user_playlist_remove_all_occurrences_of_tracks( user=self.user, playlist_id=playlist_id, tracks=remove) elif self.playlist_count[playlist_id] == 0: while True: # count songs in playlist self.playlist_count[playlist_id] += tracks["total"] if tracks["next"]: tracks = self.spotify.next(tracks) else: break def add(self, *, playlist_id: str, tracks: list, **kwargs): """Add tracks to specified playlist. Removes n tracks from the playlist iff the size of the playlist + len(tracks) exceeds SONG_LIMIT, where n = len(playlist) + len(tracks) - SONG_LIMIT. After ensuring the added tracks won't exceed SONG_LIMIT, add tracks to the playlist. Args: playlist_id: A str representing the playlist ID to modify. tracks: A list of track IDs to add to the specified playlist. Raises: PlaylistError: For 4xx - 5xx HTTP response codes or if the number of tracks exceeds SONG_LIMIT. Returns: i'll check """ # remove tracks if the size of the playlist will exceed SONG_LIMIT self.remove(playlist_id=playlist_id, n_tracks=len(tracks)) try: # add tracks to the playlist results = self.spotify.user_playlist_add_tracks( user=self.user, playlist_id=playlist_id, tracks=tracks) # keep track of playlist song count self.playlist_count[playlist_id] += len(tracks) return results except (SpotifyException, ) as e: raise PlaylistError(str(e))
class SpotifyConnection(object): def __init__(self, user_data): self.user_name = user_data['user_name'] token = spotipy.util.prompt_for_user_token( self.user_name, scope='user-read-recently-played', client_id=user_data['client_id'], client_secret=user_data['client_secret'], redirect_uri=user_data['redirect_uri']) self.client = Spotify(auth=token) self.db = self.init_db() def init_db(self): return PostgreSQLConnection() def get_artist(self, artist_id): artist = self.db.session.query(Artist).get(artist_id) if artist: return artist else: artist_response = self.client.artist(artist_id) artist = Artist() artist.artist_id = artist_id artist.artist_data = artist_response self.db.save_instance(artist) print("> Artist {} was not in database.".format( artist.artist_data['name'])) return self.db.session.query(Artist).get(artist_id) def get_album(self, album_id): album = self.db.session.query(Album).get(album_id) if album: return album else: album_response = self.client.album(album_id) album = Album() album.album_data = album_response album.album_id = album_response['id'] # Artists for album_artist_response in album_response['artists']: album.artists.append( self.get_artist(album_artist_response['id'])) self.db.save_instance(album) print("> Album {} was not in database.".format( album.album_data['name'])) return self.db.session.query(Album).get(album_id) def get_track(self, track_id): track = self.db.session.query(Track).get(track_id) if track: return track else: response = self.client.track(track_id) track = Track() track.track_id = track_id track.track_data = response # Album track.album = self.get_album(response['album']['id']) # Artists for artist_response in response['artists']: track.artists.append(self.get_artist(artist_response['id'])) # Audio feature audio_feature_response = self.client.audio_features(track_id)[0] if audio_feature_response: # Some tracks do not have audio features track.audio_feature_data = audio_feature_response print("> Track {} was not in database.".format( track.track_data['name'])) self.db.save_instance(track) return self.db.session.query(Track).get(track_id) def get_play_from_played_at_utc_and_track_id(self, played_at_utc, track_id): played_at_utc = convert_played_at_from_response_to_datetime( played_at_utc) played_at_utc = set_timezone_to_datetime(played_at_utc, timezone='UTC') played_at_cet = convert_datetime_from_timezone_to_timezone( played_at_utc, from_tz_code='UTC', to_tz_code='CET') # Play play = Play() play.user_name = self.user_name play.played_at_utc_timestamp = played_at_utc.timestamp() * 1000 play.played_at_utc = played_at_utc play.played_at_cet = played_at_cet play.day = played_at_cet.day play.month = played_at_cet.month play.year = played_at_cet.year play.hour = played_at_cet.hour play.minute = played_at_cet.minute play.second = played_at_cet.second play.day_of_week = played_at_cet.weekday() play.week_of_year = played_at_cet.date().isocalendar()[1] # Track track = self.get_track(track_id) play.track = track play.track_id = track_id return play def _get_play_tuples_from_response(self, response): plays = [] for item in response['items']: play_tuple = (item['played_at'], item['track']['id']) plays.append(play_tuple) return plays def _get_play_tuples(self, limit=50, after=None): play_tuples = [] response = self.client._get('me/player/recently-played', after=after, limit=limit) play_tuples.extend(self._get_play_tuples_from_response(response)) while response and 'next' in response: response = self.client.next(response) if response: play_tuples.extend( self._get_play_tuples_from_response(response)) return play_tuples def extract_plays(self): print("* Extracting latest plays of {}.".format(self.user_name)) play_tuples = self._get_play_tuples() for played_at, track_id in play_tuples: play = self.get_play_from_played_at_utc_and_track_id( played_at, track_id) self.db.save_play(play)
def main(): clientRedirect = "http://localhost/" username = "******" scope = "playlist-read-collaborative " \ "playlist-read-private " \ "user-library-read " \ "playlist-modify-public " \ "playlist-modify-private" token = util.prompt_for_user_token(username, scope, clientID, clientSecret, clientRedirect) spotify = Spotify(auth=token) result = spotify.current_user_saved_tracks(offset=0, limit=50) data = {} songs = {} exceptions = ("Depeche Mode", "Grant Miller", "Madonna", "Ministry", "The Beach Boys", "Mickey & Sylvia", "The Clovers", "Village People", "Frank Sinatra", "Rodríguez", "The Bangles", "U2", "UB40", "Tom Petty", "Faces", "Bobby McFerrin", "Dion", "Fancy", "Eddy Huntington", "Michael Jackson", "OutKast", "Gorillaz", "Diddy", "Lipps Inc.", "Chuck Berry", "Marvin Gaye", "The Kinks", "Count Basie", "Player", "Steve Lawrence", "Nelly", "The Killers", "Billy Idol", "Haddaway", "Blondie") dontwant = ("Emile Van Krieken") '''while result["next"]: for track in result["items"]: songs.update(track["track"]["name"]) result = spotify.next(result) data = {username: songs}''' num = 0 playlist_id = "" for playlist in spotify.current_user_playlists()["items"]: if playlist["name"] == "Computer Generated Old 2": playlist_id = playlist["id"] exceptions_list = [] while result["next"]: for track in result["items"]: num = num + 1 print(num) track = track["track"] '''songs.update({track["uri"]: {"track": track["name"], "artist": track["artists"][0]["name"], "artist uri": track["artists"][0]["uri"], "album": track["album"]["name"], "album uri": track["album"]["uri"] } })''' album = spotify.album(track["album"]["id"]) '''if (int(album["release_date"][0:4]) < 2000 or track["artists"][0]["name"] in exceptions): #and int(album["release_date"][0:4]) > 2006)\ #print(track["uri"]) print(track["id"]) print(track["name"]) spotify.user_playlist_add_tracks("karan.arora.28", playlist_id, [track["uri"]])''' if (int(album["release_date"][0:4]) < 2006) or track["artists"][0]["name"] in exceptions: spotify.user_playlist_add_tracks("karan.arora.28", playlist_id, [track["uri"]]) if track["artists"][0]["name"] not in exceptions_list: exceptions_list.append(track["artists"][0]["name"]) print(exceptions_list) '''else: #pid = getPlaylistIDByName(spotify, "Old??") pid = "2qSyS6sDfEGSS38cn4GR8U" if trackInPlaylist(spotify, track["name"], pid): print(track["name"]) print(track["artists"][0]["name"]) num = num +1 print(num)''' result = spotify.next(result) #spotify. #spotify.user_playlist_create(clientID, "Python Old", False, "") #albumuri #artist Name and URI #when track was added #track name and URI #From this data, get when album was released, get genres, #username -> playlists -> songs -> songs contain all the data about genres and artists, etc. '''data = songs
def download_albumspo(URL, output=stock_output + "/", recursive_download=stock_recursive_download, not_interface=stock_not_interface, zips=stock_zip): global spo datas = {} detas = {} datas['music'] = [] datas['artist'] = [] datas['tracknum'] = [] datas['discnum'] = [] datas['duration'] = [] names = [] nams = [] try: url = spo.album(URL) except Exception as a: if not "The access token expired" in str(a): raise exceptions.InvalidLink("Invalid link ;)") spo = Spotify(generate_token()) url = spo.album(URL) detas['image'] = request(url['images'][0]['url']).content detas['album'] = url['name'] detas['year'] = url['release_date'] detas['genre'] = " & ".join(url['genres']) array = [a['name'] for a in url['artists']] detas['ar_album'] = ", ".join(array) detas['label'] = url['label'] detas['bpm'] = "" detas['gain'] = "0" detas['isrc'] = "" album = var_excape(detas['album']) directory = ("%s%s %s/" % (output, album, url['external_ids']['upc'])) check_dir(directory) tot = url['total_tracks'] def lazy(a): datas['music'].append(a['name']) discnum = str(a['disc_number']) tracknum = str(a['track_number']) names.append("%s%s CD %s TRACK %s" % (directory, album, discnum, tracknum)) datas['tracknum'].append(tracknum) datas['discnum'].append(discnum) datas['duration'].append(str(a['duration_ms'] * 1000)) array = [b['name'] for b in a['artists']] datas['artist'].append(", ".join(array)) for a in url['tracks']['items']: lazy(a) for a in range(tot // 50 - 1): try: url = spo.next(url['tracks']) except: spo = Spotify(generate_token()) url = spo.next(url['tracks']) for a in url['items']: lazy(a) for a in tqdm(range(len(names)), disable=not_interface): detas['music'] = datas['music'][a] detas['artist'] = datas['artist'][a] detas['tracknum'] = datas['tracknum'][a] detas['discnum'] = datas['discnum'][a] detas['duration'] = datas['duration'][a] song = "{} - {}".format(detas['music'], detas['artist']) try: nams.append( download(directory, names[a], recursive_download, not_interface, detas)) except exceptions.TrackNotFound: nams.append(names[a]) print("Track not found: %s :(" % song) continue if zips: zip_name = "{}{}.zip".format(directory, directory.split("/")[-2]) create_zip(zip_name, nams) return nams, zip_name return nams
class Login: def __init__(self, mail, password, token=None): self.spo = Spotify(auth=generate_token()) self.req = requests.Session() check = self.get_api("deezer.getUserData")['checkFormLogin'] post_data = { "type": "login", "mail": mail, "password": password, "checkFormLogin": check } end = self.req.post("https://www.deezer.com/ajax/action.php", post_data).text if "success" == end: print("Success, you are in :)") else: if not token: raise BadCredentials(end + ", and no token provided") self.req.cookies["arl"] = token if self.req.get("https://www.deezer.com/").text.split( "'deezer_user_id': ")[1].split(",")[0] == "0": raise BadCredentials("Wrong token :(") def get_api(self, method=None, api_token="null", json=None): params = { "api_version": "1.0", "api_token": api_token, "input": "3", "method": method } try: return self.req.post("http://www.deezer.com/ajax/gw-light.php", params=params, json=json).json()['results'] except: return self.req.post("http://www.deezer.com/ajax/gw-light.php", params=params, json=json).json()['results'] def download(self, link, name, quality, recursive_quality, recursive_download, datas, create_zip=False): if not quality in qualities: raise QualityNotFound( "The qualities have to be FLAC or MP3_320 or MP3_256 or MP3_128" ) def login(method, json): infos = "" while not "MD5_ORIGIN" in str(infos): self.token = self.get_api("deezer.getUserData")['checkForm'] infos = self.get_api(method, self.token, json) return infos def ultimatum(infos, name, datas, quality, recursive_download, recursive_quality): extension = ".mp3" ids = infos['SNG_ID'] if int(infos['FILESIZE_' + quality]) > 0 and quality == "FLAC": quality = "9" extension = ".flac" qualit = "FLAC" elif int( infos['FILESIZE_' + quality]) > 0 and quality == "MP3_320": quality = "3" qualit = "320" elif int( infos['FILESIZE_' + quality]) > 0 and quality == "MP3_256": quality = "5" qualit = "256" elif int( infos['FILESIZE_' + quality]) > 0 and quality == "MP3_128": quality = "1" qualit = "128" else: if recursive_quality: raise QualityNotFound( "The quality chosen can't be downloaded") for a in qualities: if int(infos['FILESIZE_' + a]) > 0: quality = qualities[a]['quality'] extension = qualities[a]['extension'] qualit = qualities[a]['qualit'] break else: if a == "MP3_128": raise TrackNotFound( "There isn't any quality avalaible for download this song" ) name += " (" + qualit + ")" + extension if os.path.isfile(name): if not recursive_download: return name ans = input( "Track " + name + " already exists, do you want to redownload it?(y or n):") if ans != "y": return name try: md5 = infos['FALLBACK']['MD5_ORIGIN'] except KeyError: md5 = infos['MD5_ORIGIN'] hashs = genurl(md5, quality, ids, infos['MEDIA_VERSION']) try: crypt = request( "https://e-cdns-proxy-%s.dzcdn.net/mobile/1/%s" % (md5[0], hashs)) except IndexError: raise TrackNotFound("Track not found :(") if len(crypt.content) == 0: raise TrackNotFound("Error with this track :(") open(name, "wb").write(crypt.content) decry = open(name, "wb") decryptfile(crypt.iter_content(2048), calcbfkey(ids), decry) datas = add_more_tags(datas, infos, ids) write_tags(name, datas) return name def add_more_tags(datas, infos, ids): try: datas['author'] = " & ".join( infos['SNG_CONTRIBUTORS']['author']) except: datas['author'] = "" try: datas['composer'] = " & ".join( infos['SNG_CONTRIBUTORS']['composer']) except: datas['composer'] = "" try: datas['lyricist'] = " & ".join( infos['SNG_CONTRIBUTORS']['lyricist']) except: datas['lyricist'] = "" try: datas['version'] = infos['VERSION'] except KeyError: datas['version'] = "" need = self.get_api("song.getLyrics", self.token, {"sng_id": ids}) try: datas['lyric'] = need['LYRICS_TEXT'] datas['copyright'] = need['LYRICS_COPYRIGHTS'] datas['lyricist'] = need['LYRICS_WRITERS'] except KeyError: datas['lyric'] = "" datas['copyright'] = "" datas['lyricist'] = "" return datas ids = link.split("/")[-1] zip_name = "" quali = "" if "track" in link: method = "song.getData" json = {"sng_id": ids} infos = login(method, json) image = request("https://e-cdns-images.dzcdn.net/images/cover/" + infos['ALB_PICTURE'] + "/1200x1200-000000-80-0-0.jpg").content if len(image) == 13: image = request( "https://e-cdns-images.dzcdn.net/images/cover/1200x1200-000000-80-0-0.jpg" ).content datas['image'] = image song = datas['music'] + " - " + datas['artist'] song = var_excape(song) print("\nDownloading:" + song) try: nams = ultimatum(infos, name, datas, quality, recursive_download, recursive_quality) except TrackNotFound: url = request( "https://api.deezer.com/search/track/?q=" + datas['music'].replace("#", "") + " + " + datas['artist'].replace("#", ""), True).json() try: for b in range(url['total'] + 1): if url['data'][b]['title'] == datas['music'] or datas[ 'music'] in url['data'][b]['title_short']: URL = url['data'][b]['link'] break except IndexError: raise TrackNotFound("Track not found: " + song) json = {"sng_id": URL.split("/")[-1]} infos = login(method, json) nams = ultimatum(infos, name, datas, quality, recursive_download, recursive_quality) else: nams = [] detas = {} method = "song.getListByAlbum" json = {"alb_id": ids, "nb": -1} infos = login(method, json)['data'] image = request("https://e-cdns-images.dzcdn.net/images/cover/" + infos[0]['ALB_PICTURE'] + "/1200x1200-000000-80-0-0.jpg").content if len(image) == 13: image = request( "https://e-cdns-images.dzcdn.net/images/cover/1200x1200-000000-80-0-0.jpg" ).content detas['image'] = image detas['album'] = datas['album'] detas['year'] = datas['year'] detas['genre'] = datas['genre'] detas['ar_album'] = datas['ar_album'] detas['label'] = datas['label'] for a in tqdm(range(len(name))): detas['music'] = datas['music'][a] detas['artist'] = datas['artist'][a] detas['tracknum'] = datas['tracknum'][a] detas['discnum'] = datas['discnum'][a] detas['bpm'] = datas['bpm'][a] detas['gain'] = datas['gain'][a] detas['duration'] = datas['duration'][a] detas['isrc'] = datas['isrc'][a] try: nams.append( ultimatum(infos[a], name[a], detas, quality, recursive_download, recursive_quality)) except TrackNotFound: url = request( "https://api.deezer.com/search/track/?q=" + detas['music'].replace("#", "") + " + " + detas['artist'].replace("#", ""), True).json() try: for b in range(url['total'] + 1): if url['data'][b]['title'] == detas[ 'music'] or detas['music'] in url['data'][ b]['title_short']: URL = url['data'][b]['link'] break except IndexError: nams.append(name[a]) print("\nTrack not found: " + detas['music'] + " - " + detas['artist']) continue try: method = "song.getData" json = {"sng_id": URL.split("/")[-1]} nams.append( ultimatum(login(method, json), name[a], detas, quality, recursive_download, recursive_quality)) except TrackNotFound: nams.append(name[a]) print("\nTrack not found: " + detas['music'] + " - " + detas['artist']) continue quali = nams[a].split("(")[-1].split(")")[0] if create_zip: dir = "/".join(name[a].split("/")[:-1]) + "/" if len(nams) > 0: zip_name = dir + dir.split( "/")[-2] + " (" + quali + ").zip" z = zipfile.ZipFile(zip_name, "w", zipfile.ZIP_DEFLATED) for a in nams: b = a.split("/")[-1] try: z.write(a, b) except FileNotFoundError: pass z.close() return nams, zip_name def download_trackdee(self, URL, output=localdir + "/Songs/", quality="MP3_320", recursive_quality=True, recursive_download=True): datas = {} if "?utm" in URL: URL, a = URL.split("?utm") URL1 = "https://www.deezer.com/track/" + URL.split("/")[-1] URL2 = "https://api.deezer.com/track/" + URL.split("/")[-1] url = request(URL2, True).json() url1 = request( "http://api.deezer.com/album/" + str(url['album']['id']), True).json() datas['music'] = url['title'] array = [] for a in url['contributors']: array.append(a['name']) array.append(url['artist']['name']) if len(array) > 1: for a in array: for b in array: if a in b and a != b: array.remove(b) datas['artist'] = ", ".join(OrderedDict.fromkeys(array)) datas['album'] = url1['title'] datas['tracknum'] = str(url['track_position']) datas['discnum'] = str(url['disk_number']) datas['year'] = url['release_date'] datas['genre'] = [] try: for a in url1['genres']['data']: datas['genre'].append(a['name']) except KeyError: pass datas['genre'] = " & ".join(datas['genre']) datas['ar_album'] = [] for a in url1['contributors']: if a['role'] == "Main": datas['ar_album'].append(a['name']) datas['ar_album'] = " & ".join(datas['ar_album']) datas['label'] = url1['label'] datas['bpm'] = str(url['bpm']) datas['gain'] = str(url['gain']) datas['duration'] = str(url['duration']) datas['isrc'] = url['isrc'] album = var_excape(datas['album']) dir = output + album + " " + url1['upc'] + "/" try: os.makedirs(dir) except FileExistsError: pass name = dir + album + " CD " + datas['discnum'] + " TRACK " + datas[ 'tracknum'] name, a = self.download(URL, name, quality, recursive_quality, recursive_download, datas) return name def download_albumdee(self, URL, output=localdir + "/Songs/", quality="MP3_320", recursive_quality=True, recursive_download=True, create_zip=False): datas = {} datas['music'] = [] datas['artist'] = [] datas['tracknum'] = [] datas['discnum'] = [] datas['bpm'] = [] datas['gain'] = [] datas['duration'] = [] datas['isrc'] = [] names = [] array = [] if "?utm" in URL: URL, a = URL.split("?utm") URL1 = "https://www.deezer.com/album/" + URL.split("/")[-1] URL2 = "https://api.deezer.com/album/" + URL.split("/")[-1] url = request(URL2, True).json() datas['album'] = url['title'] datas['label'] = url['label'] datas['year'] = url['release_date'] datas['genre'] = [] try: for a in url['genres']['data']: datas['genre'].append(a['name']) except KeyError: pass datas['genre'] = " & ".join(datas['genre']) datas['ar_album'] = [] for a in url['contributors']: if a['role'] == "Main": datas['ar_album'].append(a['name']) datas['ar_album'] = " & ".join(datas['ar_album']) album = var_excape(datas['album']) dir = output + "/" + album + " " + url['upc'] + "/" for a in url['tracks']['data']: del array[:] datas['music'].append(a['title']) ur = request("https://api.deezer.com/track/" + str(a['id']), True).json() discnum = str(ur['disk_number']) tracknum = str(ur['track_position']) names.append(dir + album + " CD " + discnum + " TRACK " + tracknum) datas['tracknum'].append(tracknum) datas['discnum'].append(discnum) datas['bpm'].append(str(ur['bpm'])) datas['gain'].append(str(ur['gain'])) datas['duration'].append(str(ur['duration'])) datas['isrc'].append(ur['isrc']) for a in ur['contributors']: array.append(a['name']) array.append(ur['artist']['name']) if len(array) > 1: for a in array: for b in array: if a in b and a != b: array.remove(b) datas['artist'].append(", ".join(OrderedDict.fromkeys(array))) try: os.makedirs(dir) except FileExistsError: pass names, zip_name = self.download(URL, names, quality, recursive_quality, recursive_download, datas, create_zip) if create_zip: return names, zip_name return names def download_playlistdee(self, URL, output=localdir + "/Songs/", quality="MP3_320", recursive_quality=True, recursive_download=True): array = [] if "?utm" in URL: URL, a = URL.split("?utm") url = request("https://api.deezer.com/playlist/" + URL.split("/")[-1], True).json() for a in url['tracks']['data']: try: array.append( self.download_trackdee(a['link'], output, quality, recursive_quality, recursive_download)) except TrackNotFound: print("\nTrack not found " + a['title']) array.append("None") return array def download_trackspo(self, URL, output=localdir + "/Songs/", quality="MP3_320", recursive_quality=True, recursive_download=True): if "?" in URL: URL, a = URL.split("?") try: url = self.spo.track(URL) except Exception as a: if not "The access token expired" in str(a): raise InvalidLink("Invalid link ;)") self.spo = Spotify(auth=generate_token()) url = self.spo.track(URL) isrc = url['external_ids']['isrc'] url = request("https://api.deezer.com/track/isrc:" + isrc, True).json() name = self.download_trackdee(url['link'], output, quality, recursive_quality, recursive_download) return name def download_albumspo(self, URL, output=localdir + "/Songs/", quality="MP3_320", recursive_quality=True, recursive_download=True, create_zip=False): if "?" in URL: URL, a = URL.split("?") try: tracks = self.spo.album(URL) except Exception as a: if not "The access token expired" in str(a): raise InvalidLink("Invalid link ;)") self.spo = Spotify(auth=generate_token()) tracks = self.spo.album(URL) try: upc = tracks['external_ids']['upc'] while upc[0] == "0": upc = upc[1:] url = request("https://api.deezer.com/album/upc:" + upc).json() names = self.download_albumdee(url['link'], output, quality, recursive_quality, recursive_download, create_zip) except KeyError: search = len(tracks['tracks']['items']) // 8 try: url = self.spo.track(tracks['tracks']['items'][search] ['external_urls']['spotify']) except: self.spo = Spotify(auth=generate_token()) url = self.spo.track(tracks['tracks']['items'][search] ['external_urls']['spotify']) isrc = url['external_ids']['isrc'] try: url = request("https://api.deezer.com/track/isrc:" + isrc, True).json() names = self.download_albumdee(url['album']['link'], output, quality, recursive_quality, recursive_download, create_zip) except TrackNotFound: raise AlbumNotFound("Album not found :(") return names def download_playlistspo(self, URL, output=localdir + "/Songs/", quality="MP3_320", recursive_quality=True, recursive_download=True): array = [] if "?" in URL: URL, a = URL.split("?") URL = URL.split("/") try: tracks = self.spo.user_playlist_tracks(URL[-3], playlist_id=URL[-1]) except Exception as a: if not "The access token expired" in str(a): raise InvalidLink("Invalid link ;)") self.spo = Spotify(auth=generate_token()) tracks = self.spo.user_playlist_tracks(URL[-3], playlist_id=URL[-1]) for a in tracks['items']: try: array.append( self.download_trackspo( a['track']['external_urls']['spotify'], output, quality, recursive_quality, recursive_download)) except: print("\nTrack not found :(") array.append("None") if tracks['total'] != 100: for a in range(tracks['total'] // 100): try: tracks = self.spo.next(tracks) except: self.spo = Spotify(auth=generate_token()) tracks = self.spo.next(tracks) for a in tracks['items']: try: array.append( self.download_trackspo( a['track']['external_urls']['spotify'], output, quality, recursive_quality, recursive_download)) except: print("\nTrack not found :(") array.append("None") return array def download_name(self, artist, song, output=localdir + "/Songs/", quality="MP3_320", recursive_quality=True, recursive_download=True): try: search = self.spo.search(q="track:" + song + " artist:" + artist) except: self.spo = Spotify(auth=generate_token()) search = self.spo.search(q="track:" + song + " artist:" + artist) try: return self.download_trackspo( search['tracks']['items'][0]['external_urls']['spotify'], output, quality, recursive_quality, recursive_download) except IndexError: raise TrackNotFound("Track not found: " + artist + " - " + song)
class Login: def __init__(self, token): self.spo = Spotify(utils.generate_token()) self.req = Session() self.req.cookies["arl"] = token self.get_user_data = methods.method_get_user_data self.private_api_link = deezer_settings.private_api_link user_id = self.get_api(self.get_user_data)["USER"]["USER_ID"] if user_id == 0: raise exceptions.BadCredentials("Wrong token: %s :(" % token) self.qualities = deezer_settings.qualities self.songs_server = deezer_settings.songs_server self.get_song_data = methods.method_get_song_data self.get_lyric = methods.method_get_lyric self.get_album = methods.method_get_album self.get_album_data = methods.method_get_album_data self.api_track = deezer_settings.api_track self.api_album = deezer_settings.api_album self.api_playlist = deezer_settings.api_playlist def get_api(self, method, api_token="null", json_data=None): params = { "api_version": "1.0", "api_token": api_token, "input": "3", "method": method, } try: return self.req.post( self.private_api_link, params=params, json=json_data ).json()["results"] except: return self.req.post( self.private_api_link, params=params, json=json_data ).json()["results"] def download( self, link, details, recursive_quality=None, recursive_download=None, not_interface=None, zips=False, ): if not details["quality"] in self.qualities: raise exceptions.QualityNotFound( "The qualities have to be FLAC or MP3_320 or MP3_256 or MP3_128" ) self.token = self.get_api(self.get_user_data)["checkForm"] ids = utils.get_ids(link) datas = details["datas"] quality = details["quality"] output = details["output"] def get_infos(method, json_data): infos = self.get_api(method, self.token, json_data) return infos def check_quality_song(infos, datas): ids = infos["SNG_ID"] num_quality = self.qualities[quality]["n_quality"] file_format = self.qualities[quality]["f_format"] song_quality = self.qualities[quality]["s_quality"] song_md5, version = utils.check_md5_song(infos) song_hash = download_utils.genurl(song_md5, num_quality, ids, version) try: crypted_audio = utils.song_exist(song_md5[0], song_hash) except (IndexError, exceptions.TrackNotFound): if not recursive_quality: raise exceptions.QualityNotFound( "The quality chosen can't be downloaded" ) for a in self.qualities: if details["quality"] == a: continue num_quality = self.qualities[a]["n_quality"] file_format = self.qualities[a]["f_format"] song_quality = self.qualities[a]["s_quality"] song_hash = download_utils.genurl( song_md5, num_quality, ids, infos["MEDIA_VERSION"] ) try: crypted_audio = utils.song_exist(song_md5[0], song_hash) except exceptions.TrackNotFound: raise exceptions.TrackNotFound("Error with this song %s" % link) album = utils.var_excape(datas["album"]) directory = "%s%s %s/" % ("%s/" % output, album, datas["upc"]) name = "%s%s CD %s TRACK %s" % ( directory, album, datas["discnum"], datas["tracknum"], ) utils.check_dir(directory) name += " ({}){}".format(song_quality, file_format) if isfile(name): if recursive_download: return name ans = input( "Track %s already exists, do you want to redownload it? (y or n):" % name ) if not ans in answers: return name decrypted_audio = open(name, "wb") download_utils.decryptfile( crypted_audio.iter_content(2048), download_utils.calcbfkey(ids), decrypted_audio, ) utils.write_tags(name, add_more_tags(datas, infos, ids)) return name def add_more_tags(datas, infos, ids): json_data = {"sng_id": ids} try: datas["author"] = " & ".join(infos["SNG_CONTRIBUTORS"]["author"]) except: datas["author"] = "" try: datas["composer"] = " & ".join(infos["SNG_CONTRIBUTORS"]["composer"]) except: datas["composer"] = "" try: datas["lyricist"] = " & ".join(infos["SNG_CONTRIBUTORS"]["lyricist"]) except: datas["lyricist"] = "" try: datas["version"] = infos["VERSION"] except KeyError: datas["version"] = "" need = get_infos(self.get_lyric, json_data) try: datas["lyric"] = need["LYRICS_TEXT"] datas["copyright"] = need["LYRICS_COPYRIGHTS"] datas["lyricist"] = need["LYRICS_WRITERS"] except KeyError: datas["lyric"] = "" datas["copyright"] = "" datas["lyricist"] = "" return datas def tracking2(infos, datas): image = utils.choose_img(infos["ALB_PICTURE"]) datas["image"] = image song = "{} - {}".format(datas["music"], datas["artist"]) if not not_interface: print("Downloading: %s" % song) try: nams = check_quality_song(infos, datas) except exceptions.TrackNotFound: try: ids = utils.not_found(song, datas["music"]) except IndexError: raise exceptions.TrackNotFound("Track not found: %s" % song) json_data = {"sng_id": ids} infos = get_infos(self.get_song_data, json_data) nams = check_quality_song(infos, datas) return nams if "track" in link: json_data = {"sng_id": ids} infos = get_infos(self.get_song_data, json_data) nams = tracking2(infos, datas) return nams zip_name = "" if "album" in link: nams = [] detas = {} quali = "" json_data = {"alb_id": ids, "nb": -1} infos = get_infos(self.get_album, json_data)["data"] image = utils.choose_img(infos[0]["ALB_PICTURE"]) detas["image"] = image detas["album"] = datas["album"] detas["year"] = datas["year"] detas["genre"] = datas["genre"] detas["ar_album"] = datas["ar_album"] detas["label"] = datas["label"] detas["upc"] = datas["upc"] t = tqdm(range(len(infos)), desc=detas["album"], disable=not_interface) for a in t: detas["music"] = datas["music"][a] detas["artist"] = datas["artist"][a] detas["tracknum"] = datas["tracknum"][a] detas["discnum"] = datas["discnum"][a] detas["bpm"] = datas["bpm"][a] detas["duration"] = datas["duration"][a] detas["isrc"] = datas["isrc"][a] song = "{} - {}".format(detas["music"], detas["artist"]) t.set_description_str(song) try: nams.append(check_quality_song(infos[a], detas)) except exceptions.TrackNotFound: try: ids = utils.not_found(song, detas["music"]) json = {"sng_id": ids} nams.append( check_quality_song( get_infos(self.get_song_data, json), detas ) ) except (exceptions.TrackNotFound, IndexError): nams.append(song) print("Track not found: %s :(" % song) continue quali = nams[a].split("(")[-1].split(")")[0] if zips: album = utils.var_excape(datas["album"]) directory = "%s%s %s/" % ("%s/" % output, album, datas["upc"]) zip_name = "%s%s (%s).zip" % (directory, album, quali) try: utils.create_zip(zip_name, nams) except FileNotFoundError: raise exceptions.QualityNotFound( 'Can\'t download album "{}" in {} quality'.format( album, details["quality"] ) ) elif "playlist" in link: json_data = {"playlist_id": ids, "nb": -1} infos = get_infos(methods.method_get_playlist_data, json_data)["data"] nams = [] for a in range(len(infos)): try: nams.append(tracking2(infos[a], datas[a])) except TypeError: c = infos[a] song = "{} - {}".format(c["SNG_TITLE"], c["ART_NAME"]) nams.append("Track not found") quali = "ALL" if zips: zip_name = "%s %s (%s).zip" % ("%s/playlist" % output, ids, quali) utils.create_zip(zip_name, nams) return nams, zip_name def download_trackdee( self, URL, output=stock_output, quality=stock_quality, recursive_quality=stock_recursive_quality, recursive_download=stock_recursive_download, not_interface=stock_not_interface, ): datas = {} ids = utils.get_ids(URL) URL2 = self.api_track % ids datas = utils.tracking(URL2) details = {"datas": datas, "quality": quality, "output": output} name = self.download( URL2, details, recursive_quality, recursive_download, not_interface ) return name def download_albumdee( self, URL, output=stock_output, quality=stock_quality, recursive_quality=stock_recursive_quality, recursive_download=stock_recursive_download, not_interface=stock_not_interface, zips=stock_zip, ): datas = {} datas["music"] = [] datas["artist"] = [] datas["tracknum"] = [] datas["discnum"] = [] datas["bpm"] = [] datas["duration"] = [] datas["isrc"] = [] names = [] ids = utils.get_ids(URL) URL2 = self.api_album % ids album_json = utils.request(URL2, True).json() datas["album"] = album_json["title"] datas["label"] = album_json["label"] datas["year"] = album_json["release_date"] datas["upc"] = album_json["upc"] datas["genre"] = [] try: for a in album_json["genres"]["data"]: datas["genre"].append(a["name"]) except KeyError: pass datas["genre"] = " & ".join(datas["genre"]) datas["ar_album"] = [] for a in album_json["contributors"]: if a["role"] == "Main": datas["ar_album"].append(a["name"]) datas["ar_album"] = " & ".join(datas["ar_album"]) for a in album_json["tracks"]["data"]: URL3 = self.api_track % str(a["id"]) detas = utils.tracking(URL3, True) datas["music"].append(detas["music"]) discnum = detas["discnum"] tracknum = detas["tracknum"] datas["tracknum"].append(tracknum) datas["discnum"].append(discnum) datas["bpm"].append(detas["bpm"]) datas["duration"].append(detas["duration"]) datas["isrc"].append(detas["isrc"]) datas["artist"].append(detas["artist"]) details = {"datas": datas, "quality": quality, "output": output} names, zip_name = self.download( URL2, details, recursive_quality, recursive_download, not_interface, zips ) if zips: return names, zip_name return names def download_playlistdee( self, URL, output=stock_output, quality=stock_quality, recursive_quality=stock_recursive_quality, recursive_download=stock_recursive_download, not_interface=stock_not_interface, zips=stock_zip, ): datas = [] ids = utils.get_ids(URL) URL2 = self.api_playlist % ids playlist_json = utils.request(URL2, True).json()["tracks"]["data"] for a in playlist_json: URL3 = self.api_track % str(a["id"]) try: detas = utils.tracking(URL3) datas.append(detas) except exceptions.NoDataApi: datas.append(None) details = {"datas": datas, "quality": quality, "output": output} names, zip_name = self.download( URL2, details, recursive_quality, recursive_download, not_interface, zips ) if zips: return names, zip_name return names def download_trackspo( self, URL, output=stock_output, quality=stock_quality, recursive_quality=stock_recursive_quality, recursive_download=stock_recursive_download, not_interface=stock_not_interface, ): URL = URL.split("?")[0] try: url = self.spo.track(URL) except Exception as a: if not "The access token expired" in str(a): raise exceptions.InvalidLink("Invalid link ;)") self.spo = Spotify(utils.generate_token()) url = self.spo.track(URL) isrc = "isrc:%s" % url["external_ids"]["isrc"] url = utils.request(self.api_track % isrc, True).json() name = self.download_trackdee( url["link"], output, quality, recursive_quality, recursive_download, not_interface, ) return name def download_albumspo( self, URL, output=stock_output, quality=stock_quality, recursive_quality=stock_recursive_quality, recursive_download=stock_recursive_download, not_interface=stock_not_interface, zips=stock_zip, ): URL = URL.split("?")[0] try: tracks = self.spo.album(URL) except Exception as a: if not "The access token expired" in str(a): raise exceptions.InvalidLink("Invalid link ;)") self.spo = Spotify(utils.generate_token()) tracks = self.spo.album(URL) try: upc = "0%s" % tracks["external_ids"]["upc"] while upc[0] == "0": upc = upc[1:] try: upc = "upc:%s" % upc url = utils.request(self.api_album % upc, True).json() names = self.download_albumdee( url["link"], output, quality, recursive_quality, recursive_download, not_interface, zips, ) break except exceptions.NoDataApi: if upc[0] != "0": raise KeyError except KeyError: tot = tracks["total_tracks"] for a in tracks["tracks"]["items"]: try: isrc = self.spo.track(a["external_urls"]["spotify"])[ "external_ids" ]["isrc"] except: self.spo = Spotify(utils.generate_token()) isrc = self.spo.track(a["external_urls"]["spotify"])[ "external_ids" ]["isrc"] try: isrc = "isrc:%s" % isrc ids = utils.request(self.api_track % isrc, True).json()["album"][ "id" ] tracks = utils.request(self.api_album % str(ids), True).json() if tot == tracks["nb_tracks"]: break except exceptions.NoDataApi: pass try: if tot != tracks["nb_tracks"]: raise KeyError names = self.download_albumdee( tracks["link"], output, quality, recursive_quality, recursive_download, not_interface, zips, ) except KeyError: raise exceptions.AlbumNotFound("Album not found :(") return names def download_playlistspo( self, URL, output=stock_output, quality=stock_quality, recursive_quality=stock_recursive_quality, recursive_download=stock_recursive_download, not_interface=stock_not_interface, zips=stock_zip, ): array = [] URL = URL.split("?")[0].split("/") try: tracks = self.spo.user_playlist_tracks(URL[-3], URL[-1]) except Exception as a: if not "The access token expired" in str(a): raise exceptions.InvalidLink("Invalid link ;)") self.spo = Spotify(utils.generate_token()) tracks = self.spo.user_playlist_tracks(URL[-3], URL[-1]) def lazy(tracks): for a in tracks["items"]: try: array.append( self.download_trackspo( a["track"]["external_urls"]["spotify"], output, quality, recursive_quality, recursive_download, not_interface, ) ) except: print("Track not found :(") array.append("None") lazy(tracks) tot = tracks["total"] for a in range(tot // 100 - 1): try: tracks = self.spo.next(tracks) except: self.spo = Spotify(utils.generate_token()) tracks = self.spo.next(tracks) lazy(tracks) if zips: zip_name = "{}playlist {}.zip".format(output, URL[-1]) utils.create_zip(zip_name, array) return array, zip_name return array def download_name( self, artist, song, output=stock_output, quality=stock_quality, recursive_quality=stock_recursive_quality, recursive_download=stock_recursive_download, not_interface=stock_not_interface, ): query = "track:{} artist:{}".format(song, artist) try: search = self.spo.search(query) except: self.spo = Spotify(utils.generate_token()) search = self.spo.search(query) try: return self.download_trackspo( search["tracks"]["items"][0]["external_urls"]["spotify"], output, quality, recursive_quality, recursive_download, not_interface, ) except IndexError: raise exceptions.TrackNotFound("Track not found")
class SpotipyUtils: def __init__(self, client_id, client_secret, redirect_uri, scope): self._sp = Spotify( auth_manager=SpotifyOAuth(client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, scope=scope)) def delete_all_tracks(self, playlist): """ Deletes all tracks in the given playlist """ tracks = self.retrieve_all_tracks_for_playlist(playlist) SpotipyUtils._list_of_uris_helper( playlist, tracks, self._sp.playlist_remove_all_occurrences_of_items) def add_all_tracks(self, playlist, tracks: List[Track]): """ Adds all the tracks to the given playlist """ SpotipyUtils._list_of_uris_helper(playlist, tracks, self._sp.playlist_add_items) def retrieve_all_tracks_for_playlist(self, playlist) -> List[Track]: """ Retrieves all tracks for the given playlist """ fields = 'next,' \ 'items(track(name,album(release_date,release_date_precision),artists(name),uri))' all_items = [] results = self._sp.playlist_items(playlist_id=playlist['id'], limit=50, fields=fields) while True: all_items.extend(results['items']) results = self._sp.next(results) if results is None: break return [SpotipyUtils.to_track(item) for item in all_items] def num_tracks_in_playlist(self, playlist) -> Optional[int]: """ Returns the number of tracks in this playlist """ playlist = self.search_playlist_in_current_user_library( playlist['name']) if playlist is None: return None return len(self.retrieve_all_tracks_for_playlist(playlist)) # TODO: have a class to represent playlist? def search_playlist_in_current_user_library(self, playlist_name: str): """ Searches for a playlist within the current user's library with the given name (case insensitive) (I don't think Spotify API offers the ability to search for a playlist within a user's library yet. See note section here: https://developer.spotify.com/documentation/web-api/reference/search/search/) """ batch_results = self._sp.current_user_playlists(limit=50) while True: for playlist in batch_results['items']: if playlist['name'].lower() == playlist_name.lower(): return playlist batch_results = self._sp.next(batch_results) if batch_results is None: break return None def create_temp_playlist(self): """ Creates a temp playlist (named "temp") in the user library. If temp already exists, delete all tracks """ playlist = self.search_playlist_in_current_user_library("temp") if playlist is None: user_id = self._sp.current_user()['id'] self._sp.user_playlist_create(user_id, "temp") return self.search_playlist_in_current_user_library("temp") @staticmethod def _list_of_uris_helper(playlist, tracks: List[Track], spotify_method): """ Helpers for functions that take in a list of Tracks """ playlist_id = playlist['id'] curr = 0 offset = 100 uris = [track.uri for track in tracks] while curr < len(tracks): spotify_method(playlist_id, uris[curr:curr + offset]) curr += offset @staticmethod def to_album_release_date(release_date: str, release_date_precision: str): if release_date_precision is None or release_date is None: return None pad_date = { 'day': lambda date: date, 'month': lambda date: f'{date}-01', 'year': lambda date: f'{date}-01-01' } return datetime.datetime.strptime( pad_date[release_date_precision](release_date), '%Y-%m-%d') @staticmethod def to_track(sp_result): sp_result = sp_result['track'] artist = sp_result['artists'][0]['name'] uri = sp_result['uri'] album_release_date = SpotipyUtils.to_album_release_date( release_date=sp_result['album']['release_date'], release_date_precision=sp_result['album'] ['release_date_precision']) return Track(album_release_date=album_release_date, artist=artist, uri=uri, name=sp_result['name'])
class AuthTestSpotipy(unittest.TestCase): """ These tests require user authentication - provide client credentials using the following environment variables :: 'SPOTIPY_CLIENT_USERNAME' 'SPOTIPY_CLIENT_ID' 'SPOTIPY_CLIENT_SECRET' 'SPOTIPY_REDIRECT_URI' """ playlist = "spotify:user:plamere:playlist:2oCEWyyAPbZp9xhVSxZavx" four_tracks = [ "spotify:track:6RtPijgfPKROxEzTHNRiDp", "spotify:track:7IHOIqZUUInxjVkko181PB", "4VrWlk8IQxevMvERoX08iC", "http://open.spotify.com/track/3cySlItpiPiIAzU3NyHCJf" ] two_tracks = [ "spotify:track:6RtPijgfPKROxEzTHNRiDp", "spotify:track:7IHOIqZUUInxjVkko181PB" ] other_tracks = [ "spotify:track:2wySlB6vMzCbQrRnNGOYKa", "spotify:track:29xKs5BAHlmlX1u4gzQAbJ", "spotify:track:1PB7gRWcvefzu7t3LJLUlf" ] bad_id = 'BAD_ID' @classmethod def setUpClass(self): missing = list(filter(lambda var: not os.getenv(CCEV[var]), CCEV)) if missing: raise Exception( 'Please set the client credentials for the test application using the following environment variables: {}' .format(CCEV.values())) self.username = os.getenv(CCEV['client_username']) self.scope = ('playlist-modify-public ' 'user-library-read ' 'user-follow-read ' 'user-library-modify ' 'user-read-private ' 'user-top-read') self.token = prompt_for_user_token(self.username, scope=self.scope) self.spotify = Spotify(auth=self.token) def test_track_bad_id(self): try: track = self.spotify.track(self.bad_id) self.assertTrue(False) except SpotifyException: self.assertTrue(True) def test_basic_user_profile(self): user = self.spotify.user(self.username) self.assertTrue(user['id'] == self.username.lower()) def test_current_user(self): user = self.spotify.current_user() self.assertTrue(user['id'] == self.username.lower()) def test_me(self): user = self.spotify.me() self.assertTrue(user['id'] == self.username.lower()) def test_user_playlists(self): playlists = self.spotify.user_playlists(self.username, limit=5) self.assertTrue('items' in playlists) self.assertTrue(len(playlists['items']) == 5) def test_user_playlist_tracks(self): playlists = self.spotify.user_playlists(self.username, limit=5) self.assertTrue('items' in playlists) for playlist in playlists['items']: user = playlist['owner']['id'] pid = playlist['id'] results = self.spotify.user_playlist_tracks(user, pid) self.assertTrue(len(results['items']) >= 0) def user_playlist_tracks(self, user, playlist_id=None, fields=None, limit=100, offset=0): # known API issue currently causes this test to fail # the issue is that the API doesn't currently respect the # limit paramter self.assertTrue(len(playlists['items']) == 5) def test_current_user_saved_tracks(self): tracks = self.spotify.current_user_saved_tracks() self.assertTrue(len(tracks['items']) > 0) def test_current_user_saved_albums(self): albums = self.spotify.current_user_saved_albums() self.assertTrue(len(albums['items']) > 0) def test_current_user_playlists(self): playlists = self.spotify.current_user_playlists(limit=10) self.assertTrue('items' in playlists) self.assertTrue(len(playlists['items']) == 10) def test_user_playlist_follow(self): self.spotify.user_playlist_follow_playlist('plamere', '4erXB04MxwRAVqcUEpu30O') follows = self.spotify.user_playlist_is_following( 'plamere', '4erXB04MxwRAVqcUEpu30O', [self.spotify.current_user()['id']]) self.assertTrue(len(follows) == 1, 'proper follows length') self.assertTrue(follows[0], 'is following') self.spotify.user_playlist_unfollow('plamere', '4erXB04MxwRAVqcUEpu30O') follows = self.spotify.user_playlist_is_following( 'plamere', '4erXB04MxwRAVqcUEpu30O', [self.spotify.current_user()['id']]) self.assertTrue(len(follows) == 1, 'proper follows length') self.assertFalse(follows[0], 'is no longer following') def test_current_user_save_and_unsave_tracks(self): tracks = self.spotify.current_user_saved_tracks() total = tracks['total'] self.spotify.current_user_saved_tracks_add(self.four_tracks) tracks = self.spotify.current_user_saved_tracks() new_total = tracks['total'] self.assertTrue(new_total - total == len(self.four_tracks)) tracks = self.spotify.current_user_saved_tracks_delete( self.four_tracks) tracks = self.spotify.current_user_saved_tracks() new_total = tracks['total'] self.assertTrue(new_total == total) def test_categories(self): response = self.spotify.categories() self.assertTrue(len(response['categories']) > 0) def test_category_playlists(self): response = self.spotify.categories() for cat in response['categories']['items']: cat_id = cat['id'] response = self.spotify.category_playlists(category_id=cat_id) if len(response['playlists']["items"]) > 0: break self.assertTrue(True) def test_new_releases(self): response = self.spotify.new_releases() self.assertTrue(len(response['albums']) > 0) def test_featured_releases(self): response = self.spotify.featured_playlists() self.assertTrue(len(response['playlists']) > 0) def test_current_user_follows(self): response = self.spotify.current_user_followed_artists() artists = response['artists'] self.assertTrue(len(artists['items']) > 0) def test_current_user_top_tracks(self): response = self.spotify.current_user_top_tracks() items = response['items'] self.assertTrue(len(items) > 0) def test_current_user_top_artists(self): response = self.spotify.current_user_top_artists() items = response['items'] self.assertTrue(len(items) > 0) def get_or_create_spotify_playlist(self, playlist_name): playlists = self.spotify.user_playlists(self.username) while playlists: for item in playlists['items']: if item['name'] == playlist_name: return item['id'] playlists = self.spotify.next(playlists) playlist = self.spotify.user_playlist_create(self.username, playlist_name) playlist_id = playlist['uri'] return playlist_id def test_user_playlist_ops(self): # create empty playlist playlist_id = self.get_or_create_spotify_playlist( 'spotipy-testing-playlist-1') # remove all tracks from it self.spotify.user_playlist_replace_tracks(self.username, playlist_id, []) playlist = self.spotify.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 0) self.assertTrue(len(playlist['tracks']['items']) == 0) # add tracks to it self.spotify.user_playlist_add_tracks(self.username, playlist_id, self.four_tracks) playlist = self.spotify.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 4) self.assertTrue(len(playlist['tracks']['items']) == 4) # remove two tracks from it self.spotify.user_playlist_remove_all_occurrences_of_tracks( self.username, playlist_id, self.two_tracks) playlist = self.spotify.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 2) self.assertTrue(len(playlist['tracks']['items']) == 2) # replace with 3 other tracks self.spotify.user_playlist_replace_tracks(self.username, playlist_id, self.other_tracks) playlist = self.spotify.user_playlist(self.username, playlist_id) self.assertTrue(playlist['tracks']['total'] == 3) self.assertTrue(len(playlist['tracks']['items']) == 3)
class SpotifySubscriber: """ Main class. Description necessary. Default storage dir: ../storage """ def __init__( self, user_id: str = None, storage_dir: str = os.path.join( os.path.dirname(os.path.abspath(__file__)), "storage"), ): self.user_id: str = user_id # Playlists are stored by ID in dictionaries for quick lookup self.user_playlists: dict = {} self.followed_playlists: dict = {} self.subscribed_playlists: dict = {} # This is the playlist in which all new songs will be pushed self.subscription_feed: SubscriptionFeed = None loaded = False save_path = os.path.join(storage_dir, "storage.p") # If a save file exists, load it. if os.path.isfile(save_path): self._load(save_path) loaded = True # Since we need the user_id, we cannot continue if it was not specified and we did not obtain it from a save file. elif user_id is None: raise Exception( "No save file found and no user_id specified! Please specify user_id." ) # If there is no save file, there may not be a storage directory either elif not os.path.isdir(storage_dir): os.mkdir(storage_dir) # We deliberately set these after loading, so they may be updated if we move the save file to a different location. self.storage_dir = storage_dir self._save_path = save_path self._cache_path = os.path.join(self.storage_dir, ".cache-{}".format(self.user_id)) self._feed_log_path = os.path.join(self.storage_dir, "feed_log.npy") # If no save file exists, load the client secret and ID from file so that we can request a token. if not loaded: self._load_client_secrets() # Refresh token and create spotify object self.token = self._get_token(self.user_id) self.sp = Spotify(auth=self.token) # If not loaded from save file, perform initial setup if not loaded: if self.user_id != "jneeven": self._follow_user("jneeven") self.subscription_feed = SubscriptionFeed(self.sp, self.user_id) self.refresh_user_playlists() self._save() # Load a token from the cache or request one from the spotify API. Will open the browser to ask for permission if necessary. def _get_token(self, username: str): """ Alternatively, I can set these variables using the command line too: export SPOTIPY_CLIENT_ID='your-spotify-client-id' export SPOTIPY_CLIENT_SECRET='your-spotify-client-secret' export SPOTIPY_REDIRECT_URI='your-app-redirect-url' """ scopes = " ".join([ "user-read-recently-played", "user-library-modify", "playlist-read-private", "playlist-modify-public", "playlist-modify-private", "user-library-read", "playlist-read-collaborative", "user-read-playback-state", "user-follow-read", "user-top-read", "user-read-currently-playing", "user-follow-modify", ]) token = sp_util.prompt_for_user_token( username, scopes, client_id=self._client_id, client_secret=self._client_secret, redirect_uri="http://localhost", cache_path=self._cache_path, ) return token # Load the client secret and ID from client_data.json def _load_client_secrets(self): data_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "client_data.json") if not os.path.isfile(data_path): raise FileNotFoundError( "client_data.json does not exist in src folder!") with open(data_path, "r") as data_file: info = json.load(data_file) self._client_id = info["client_id"] self._client_secret = info["client_secret"] # Save the entire object to a storage file def _save(self): pickle.dump(self, open(self._save_path, "wb")) # Load the storage file and overwrite the current object attributes def _load(self, save_path: str): load_obj = pickle.load(open(save_path, "rb")) # Overwrite own attributes with the ones we just loaded for attr, val in load_obj.__dict__.items(): self.__dict__[attr] = val # Obtain the playlists the user owns or follows (both private and public) def refresh_user_playlists(self): self.user_playlists = {} self.followed_playlists = {} # Only obtains 50 playlists at a time, is API limit playlists_data = self.sp.user_playlists(self.user_id) while playlists_data: for playlist in playlists_data["items"]: # If we own a playlist but it's collaborative, we treat it as a followed one since we might be interested in updates. if playlist["owner"]["id"] != self.user_id or playlist[ "collaborative"]: self.followed_playlists[playlist["id"]] = playlist else: self.user_playlists[playlist["id"]] = playlist # If there were more playlists than we just received, query the next batch if playlists_data["next"]: playlists_data = self.sp.next(playlists_data) else: break # Subscribe to multiple playlists based on their ID or patterns in their names. def subscribe_to_playlists(self, playlist_ids: list = [], contains: list = []): new_subscriptions = False for playlist_id in playlist_ids: if playlist_id not in self.followed_playlists.keys(): raise Exception( "Cannot subscribe to playlist with id {}. Either the user owns it or does not follow it." .format(playlist_id)) if playlist_id not in self.subscribed_playlists.keys(): playlist = self.followed_playlists[playlist_id] tracks = self._get_playlist_tracks(playlist["owner"]["id"], playlist_id) self.subscribed_playlists[playlist_id] = SubscribedPlaylist( playlist, tracks) new_subscriptions = True safe_print("Subscribed to playlist {} by {}".format( playlist["name"], playlist["owner"]["id"])) # Lowercase pattern matching with the playlist name for pattern in contains: pattern = pattern.lower() for playlist in self.followed_playlists.values(): if (pattern in playlist["name"].lower() and playlist["id"] not in self.subscribed_playlists.keys()): tracks = self._get_playlist_tracks(playlist["owner"]["id"], playlist["id"]) self.subscribed_playlists[ playlist["id"]] = SubscribedPlaylist(playlist, tracks) new_subscriptions = True safe_print("Subscribed to playlist {} by {}".format( playlist["name"], playlist["owner"]["id"])) # Only save if we actually changed something. TODO: Save these in a separate file. if new_subscriptions: self._save() # Unsubscribe from multiple playlists based on their ID or patterns in their names. def unsubscribe_from_playlists(self, playlist_ids: list = [], contains: list = []): removed_subscriptions = False for playlist_id in playlist_ids: if playlist_id not in self.subscribed_playlists.keys(): raise Exception( "Cannot unsubscribe from playlist with id {}, because the user is not subscripted to it." .format(playlist_id)) playlist = self.subscribed_playlists[playlist_id] removed_subscriptions = True safe_print("Unsubscribed from playlist {} by {}".format( playlist.name, playlist.owner_id)) del self.subscribed_playlists[playlist_id] # Lowercase pattern matching with the playlist name subscribed_playlists = list(self.subscribed_playlists.values()) for pattern in contains: pattern = pattern.lower() for playlist in subscribed_playlists: if pattern in playlist.name.lower(): removed_subscriptions = True safe_print("Unsubscribed from playlist {} by {}".format( playlist.name, playlist.owner_id)) del self.subscribed_playlists[playlist.id] # Only save if we actually changed something. TODO: Save these in a separate file. if removed_subscriptions: self._save() # Print an overview of the playlist the user owns, follows and is subscribed to. def print_playlists(self, own=False, follow=False, subscribed=True): if own: safe_print("Own playlists:") for playlist in self.user_playlists.values(): safe_print(playlist["name"]) if follow: safe_print("\nFollowed playlists:") for playlist in self.followed_playlists.values(): safe_print(playlist["name"], playlist["owner"]["id"]) if subscribed: safe_print("\nCurrently subscribed to the following playlists:") for playlist in self.subscribed_playlists.values(): safe_print(playlist) safe_print() # Check the subscribed playlists for new songs and add them to the feed list. def update_feed(self, add_own=False): """ Add_own denotes whether to add songs that the user added to a playlist themselves. This may happen for example in collaborative playlists. """ last_update = self.subscription_feed.last_update track_ids = [] num_added_tracks = 0 for playlist_id, playlist in self.subscribed_playlists.items(): # safe_print("Checking playlist {}".format(playlist.name)) new_tracks, snapshot = self._get_playlist_tracks( playlist.owner_id, playlist_id, min_timestamp=last_update, compare_snapshot=playlist.snapshot_id, return_snapshot=True, ) # Update the playlist snapshot so that we quickly know if it has changed next time playlist.snapshot_id = snapshot added = 0 for track in new_tracks: if track.id == None: print(f"Track with id None: {track}") if add_own or track.added_by != self.user_id: try: # Only add the track if it wasn't already in the list when we subbed if track.id not in playlist.track_ids.keys(): track_ids.append(track.id) # Add the ID to the track ID list so we know not to add it in the future playlist.track_ids[track.id] = datetime.utcnow() added += 1 # TODO: correctly upgrade objects if storage consists of SubscribedPlaylists without ID list. except AttributeError: track_ids.append(track.id) added += 1 if added > 0: safe_print("Obtained {} new tracks from playlist {}!".format( added, playlist.name)) if len(track_ids) > 0: unique_ids = np.unique(track_ids) # If a feed log exists, filter all track IDs that have already been added to the feed before. if os.path.exists(self._feed_log_path): feed_log = pickle.load(open(self._feed_log_path, "rb")) filtered_indices = np.where( ~np.isin(unique_ids, feed_log["track_ids"])) unique_ids = unique_ids[filtered_indices] # We can add at most 100 tracks to a playlist in a single request. if unique_ids.size <= 100: self.sp.user_playlist_add_tracks(self.user_id, self.subscription_feed.id, unique_ids) else: # Split array into near-equal sections that are smaller than 100 tracks for id_array in np.array_split( unique_ids, np.ceil(unique_ids.size / 100).astype(int)): self.sp.user_playlist_add_tracks(self.user_id, self.subscription_feed.id, id_array) num_added_tracks = unique_ids.size self._log_feed_updates(unique_ids) # Update the timestamp and save to file self.subscription_feed.last_update = datetime.utcnow() self._save() return num_added_tracks # Get all tracks in the specified playlist, added after min_timestamp. # If snapshot is provided, ignore playlists of which the snapshot hasn't changed. def _get_playlist_tracks( self, playlist_owner_id: str, playlist_id: str, min_timestamp: datetime = None, compare_snapshot: str = None, return_snapshot=False, ): data = self.sp.user_playlist(playlist_owner_id, playlist_id, fields="tracks, snapshot_id") return_tracks = [] if not min_timestamp: min_timestamp = datetime.fromtimestamp(0) # If the snapshot is still the same, there is nothing interesting for us to see. # NOTE: certain playlists like 'Brain Food' seem to have a different snapshot every time. if compare_snapshot and data["snapshot_id"] == compare_snapshot: # safe_print("Snapshot still the same, ignoring list contents.") return [], data["snapshot_id"] tracks = data["tracks"] while tracks: for track in tracks["items"]: added_at = track["added_at"] # Somehow, it's possible that we receive an empty track. IDK if this is a spotipy bug or what if track["track"] is None or track["track"]["id"] is None: print("WARNING: encountered None track! Ignoring.") continue timestamp = datetime.strptime(added_at, "%Y-%m-%dT%H:%M:%SZ") if timestamp > min_timestamp: return_tracks.append(Track(track, playlist_id)) # safe_print("Found track with name {} and timestamp {} ( > {})".format(track_name, timestamp, min_timestamp)) if tracks["next"]: tracks = self.sp.next(tracks) else: break if return_snapshot: return return_tracks, data["snapshot_id"] return return_tracks # Get all tracks from the users library and own playlists (including sub feed). def _get_all_user_tracks(self): tracks = {} self._get_user_library_tracks() for playlist_id, playlist in self.user_playlists.items(): tracks = self._get_playlist_tracks(playlist["owner"]["id"], playlist_id) for track in tracks: tracks[track.id] = track def _get_user_library_tracks(self): tracks = {} data = self.sp.current_user_saved_tracks() while data: for track in data["items"]: print(track["track"].keys()) exit(0) exit(0) # for playlist in playlists_data['items']: # # If we own a playlist but it's collaborative, we treat it as a followed one since we might be interested in updates. # if playlist['owner']['id'] != self.user_id or playlist['collaborative']: # self.followed_playlists[playlist['id']] = playlist # else: # self.user_playlists[playlist['id']] = playlist # # If there were more playlists than we just received, query the next batch # if playlists_data['next']: # playlists_data = self.sp.next(playlists_data) # else: # break # Store the track ids we just added to the feed in the log file. def _log_feed_updates(self, track_ids: np.ndarray): """ Log is dict with the following info: track_ids: numpy array of track ids that were added to the feed timestamps: timestamp at which each of the tracks was added to the feed """ if os.path.exists(self._feed_log_path): feed_log = pickle.load(open(self._feed_log_path, "rb")) else: feed_log = {"track_ids": [], "timestamps": []} # Create, append and overwrite timestamps new_timestamps = np.repeat(datetime.utcnow(), track_ids.size) timestamp_array = np.append(feed_log["timestamps"], new_timestamps) feed_log["timestamps"] = timestamp_array # Append and overwrite track_ids track_id_array = np.append(feed_log["track_ids"], track_ids) feed_log["track_ids"] = track_id_array pickle.dump(feed_log, open(self._feed_log_path, "wb")) # Print the tracks and timestamps saved in the feed log. def print_feed_log(self): if not os.path.exists(self._feed_log_path): print("No feed log exists yet!") return feed_log = pickle.load(open(self._feed_log_path, "rb")) num_tracks = len(feed_log["track_ids"]) print("Found {} tracks in log.".format(num_tracks)) batch_size = 50 tracks = [] start_idx = 0 print("Requesting track info...") for start_idx in tqdm(range(0, num_tracks, batch_size)): end_idx = start_idx + batch_size track_ids = feed_log["track_ids"][start_idx:end_idx] tracks += self.sp.tracks(track_ids)["tracks"] start_idx = end_idx for track, timestamp in zip(tracks, feed_log["timestamps"]): safe_print("{} - {} - {} - {}".format(track["artists"][0]["name"], track["name"], timestamp, track["id"])) # Follow a user def _follow_user(self, username: str): self.sp.user_follow_users([username]) """ STUBS