def get_spotify_data(keywords, num_playlists): """Master function get retrieve data from Spotify.""" # Create instance of Spotify class SpotifyMaster = Spotify(CLIENT_ID, CLIENT_SECRET) # Only retrieve playlists if not at num_playlists playlist_table_size = return_table_len("Playlists") if playlist_table_size < num_playlists - 10: # Pull playlist data a keyword print("Getting Spotify playlists") cache_dict = json_helper.read_cache() keyword_index = cache_dict["keyword_index"] keyword = keywords[keyword_index] print("Keyword: " + keyword) # Get playlists json_result = SpotifyMaster.search(keyword, "playlist") playlists = json_result["playlists"]["items"] # Write playlists to database write_playlists_to_database(SpotifyMaster, playlists) playlist_table_size = return_table_len("Playlists") print("Playlist table size: " + str(playlist_table_size)) return # Otherwise, start getting tracks until reach limit tracks_table_size = return_table_len("Tracks") track_features_table_size = return_table_len("TrackFeatures") # Finish if over 100 rows for either if tracks_table_size > 120 and track_features_table_size > 120: print("Gathered sufficient data for the database.") return if tracks_table_size != num_playlists * 10: print("Getting Spotify Tracks") # Get the correct playlist href and increment the index counter cache_dict = json_helper.read_cache() cache_dict["playlist_href_index"] = cache_dict.get( "playlist_href_index", -1) + 1 playlist_href_index = cache_dict["playlist_href_index"] json_helper.write_cache(cache_dict) playlist_href = cache_dict["playlist_hrefs"][playlist_href_index] # Get track ids from the playlist and write to database track_ids = SpotifyMaster.get_tracks_from_playlist(playlist_href) write_tracks_and_features_to_database(SpotifyMaster, track_ids, playlist_href, playlist_href_index + 1) print("Tracks table size: " + str(tracks_table_size)) print("Track Features table size: " + str(track_features_table_size)) return # Done getting data, JOIN time. print("Done retrieving Spotify playlists and track data.")
class MusicDownloader(object): def __init__(self): self.__youtube = Youtube() self.__spotify = Spotify() self.__editor = TagEditor() self.__last = LastFM() self.__apple = AppleMusic() self.__deezer = Deezer() def __downloadMusicFromYoutube(self, name, uri, dur): #finding song on youtube self.__youtube.get(name, dur) #downloading video from youtube if self.__youtube.download(url=self.__youtube.getResult(), path=uri, filename=uri): #converting video to mp3 file self.__youtube.convertVideoToMusic(uri=uri) return True else: return False def __getSongInfoFromSpotify(self, uri): try: return self.__spotify.getSongInfo(uri) except: return None def getNameFromYoutube(self, url): return self.__youtube.getNameFromYoutube(url) def getData(self, uri): try: return self.__spotify.getSongInfo(uri) except: return None def getLastFMTags(self, name): return self.__last.get(name) def getYoutubeMusicInfo(self, url): return self.__youtube.getNameFromYoutube(url) def downloadBySpotifyUri(self, uri, path): #get info info = self.__getSongInfoFromSpotify(uri) if info: fixed_name = f'{info["artist"][0]} - {info["name"]}' fixed_name = fixed_name.replace('.', '') fixed_name = fixed_name.replace(',', '') fixed_name = fixed_name.replace("'", '') fixed_name = fixed_name.replace("/", "") #finding and download from YouTube and tagging if self.__downloadMusicFromYoutube(fixed_name, info['uri'], info['duration_ms']): self.__editor.setTags(data=info) cachepath = os.getcwd() + '/cache' fullpath = os.getcwd() + '/Downloads' if not os.path.exists(fullpath): os.makedirs(fullpath) name = f'{info["artist"][0]} - {info["name"]}' os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3", f"{fullpath}/{getCorrect(name)}.mp3") print(path) if path: os.rename(f"{fullpath}/{getCorrect(name)}.mp3", f"{path}/{getCorrect(name)}.mp3") #deleting cache try: shutil.rmtree(f"cache/{info['uri']}") except: pass notify.send(f'{info["artist"][0]} - {info["name"]}') return True return False def downloadBySearchQuery(self, query, path=None): #get info info = self.__spotify.search(query=query) if not info: info = self.__last.get(query) if info: fixed_name = f'{info["artist"][0]} - {info["name"]}' fixed_name = fixed_name.replace('.', '') fixed_name = fixed_name.replace(',', '') fixed_name = fixed_name.replace("'", '') fixed_name = fixed_name.replace("/", "") #finding and download from YouTube and tagging self.__downloadMusicFromYoutube(fixed_name, info['uri'], info['duration_ms']) self.__editor.setTags(data=info) cachepath = os.getcwd() + '/cache' fullpath = os.getcwd() + '/Downloads' if not os.path.exists(fullpath): os.makedirs(fullpath) name = f'{info["artist"][0]} - {info["name"]}' os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3", f"{fullpath}/{getCorrect(name)}.mp3") if path: os.rename(f"{fullpath}/{getCorrect(name)}.mp3", f"{path}/{getCorrect(name)}.mp3") #deleting cache try: shutil.rmtree(f"cache/{info['uri']}") except: pass notify.send(f'{info["artist"][0]} - {info["name"]}') return True, info else: return False, None def downloadBySpotifyUriFromFile(self, filename): try: with open(filename, 'r') as f: data = f.readlines() except FileNotFoundError: print(f'No such file or directory: "{filename}"') exit(2) #normalize try: data.remove('\n') except: pass links = [str(item).replace('\n', '') for item in data] for i, song in zip(range(len(links)), links): print(f'[{i+1}] - {song}') try: state = self.downloadBySpotifyUri(str(song).split(':')[-1]) if not state: notify.send(f'Failed to download', True) except: print('Something went wrong!') def downloadBySpotifyUriPlaylistMode(self, playlist_uri, path): user = Spotify.User() playlist = user.getPlaylistTracks(playlist_uri) for info, i in zip(playlist, range(len(playlist))): print(f'Downloading {i+1} of {len(playlist)}') fixed_name = f'{info["artist"][0]} - {info["name"]}' fixed_name = fixed_name.replace('.', '') fixed_name = fixed_name.replace(',', '') fixed_name = fixed_name.replace("'", '') fixed_name = fixed_name.replace("/", "") #finding and download from YouTube and tagging self.__downloadMusicFromYoutube(fixed_name, info['uri'], info['duration_ms']) self.__editor.setTags(data=info) cachepath = os.getcwd() + '/cache' fullpath = os.getcwd() + '/Downloads' if not os.path.exists(fullpath): os.makedirs(fullpath) name = f'{info["artist"][0]} - {info["name"]}' os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3", f"{fullpath}/{getCorrect(name)}.mp3") if path: os.rename(f"{fullpath}/{getCorrect(name)}.mp3", f"{path}/{getCorrect(name)}.mp3") #deleting cache try: shutil.rmtree(f"cache/{info['uri']}") except: pass notify.send(f'{info["artist"][0]} - {info["name"]}') def downloadBySpotifyUriAlbumMode(self, album_uri, path): user = Spotify() playlist = user.getAlbum(album_uri) for info, i in zip(playlist['tracks'], range(len(playlist['tracks']))): print(f'Downloading {i+1} of {len(playlist["tracks"])}') fixed_name = f'{info["artist"][0]} - {info["name"]}' fixed_name = fixed_name.replace('.', '') fixed_name = fixed_name.replace(',', '') fixed_name = fixed_name.replace("'", '') fixed_name = fixed_name.replace("/", "") #finding and downloading from YouTube and tagging self.__downloadMusicFromYoutube(fixed_name, info['uri'], info['duration_ms']) self.__editor.setTags(data=info) cachepath = os.getcwd() + '/cache' fullpath = os.getcwd() + '/Downloads' if not os.path.exists(fullpath): os.makedirs(fullpath) name = f'{info["artist"][0]} - {info["name"]}' os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3", f"{fullpath}/{getCorrect(name)}.mp3") if path: os.rename(f"{fullpath}/{getCorrect(name)}.mp3", f"{path}/{getCorrect(name)}.mp3") #deleting cache try: shutil.rmtree(f"cache/{info['uri']}") except: pass notify.send(f'{info["artist"][0]} - {info["name"]}') def downloadByDeezerUrl(self, url, path): link = str(str(url).split('/')[-1]).split('?')[0] #get info info = self.__deezer.getSongInfo(link) if info: fixed_name = f'{info["artist"][0]} - {info["name"]}' fixed_name = fixed_name.replace('.', '') fixed_name = fixed_name.replace(',', '') fixed_name = fixed_name.replace("'", '') fixed_name = fixed_name.replace("/", "") #finding and download from YouTube and tagging if self.__downloadMusicFromYoutube(fixed_name, info['uri'], info['duration_ms']): self.__editor.setTags(data=info) cachepath = os.getcwd() + '/cache' fullpath = os.getcwd() + '/Downloads' if not os.path.exists(fullpath): os.makedirs(fullpath) name = f'{info["artist"][0]} - {info["name"]}' os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3", f"{fullpath}/{getCorrect(name)}.mp3") print(path) if path: os.rename(f"{fullpath}/{getCorrect(name)}.mp3", f"{path}/{getCorrect(name)}.mp3") #deleting cache try: shutil.rmtree(f"cache/{info['uri']}") except: pass notify.send(f'{info["artist"][0]} - {info["name"]}') return True return False def downloadByDeezerUrlAlbumMode(self, album_url, path): link = str(str(album_url).split('/')[-1]).split('?')[0] #get info playlist = self.__deezer.getAlbum(link) for info, i in zip(playlist['tracks'], range(len(playlist['tracks']))): print(f'Downloading {i+1} of {len(playlist["tracks"])}') fixed_name = f'{info["artist"][0]} - {info["name"]}' fixed_name = fixed_name.replace('.', '') fixed_name = fixed_name.replace(',', '') fixed_name = fixed_name.replace("'", '') fixed_name = fixed_name.replace("/", "") #finding and downloading from YouTube and tagging self.__downloadMusicFromYoutube(fixed_name, info['uri'], info['duration_ms']) self.__editor.setTags(data=info) cachepath = os.getcwd() + '/cache' fullpath = os.getcwd() + '/Downloads' if not os.path.exists(fullpath): os.makedirs(fullpath) name = f'{info["artist"][0]} - {info["name"]}' os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3", f"{fullpath}/{getCorrect(name)}.mp3") if path: os.rename(f"{fullpath}/{getCorrect(name)}.mp3", f"{path}/{getCorrect(name)}.mp3") #deleting cache try: shutil.rmtree(f"cache/{info['uri']}") except: pass notify.send(f'{info["artist"][0]} - {info["name"]}') def downloadByDeezerUrlPlaylistMode(self, playlist_url, path): link = str(str(playlist_url).split('/')[-1]).split('?')[0] #get info playlist = self.__deezer.getPlaylist(link) for info, i in zip(playlist['tracks'], range(len(playlist['tracks']))): print(f'Downloading {i+1} of {len(playlist["tracks"])}') fixed_name = f'{info["artist"][0]} - {info["name"]}' fixed_name = fixed_name.replace('.', '') fixed_name = fixed_name.replace(',', '') fixed_name = fixed_name.replace("'", '') fixed_name = fixed_name.replace("/", "") #finding and download from YouTube and tagging self.__downloadMusicFromYoutube(fixed_name, info['uri'], info['duration_ms']) self.__editor.setTags(data=info) cachepath = os.getcwd() + '/cache' fullpath = os.getcwd() + '/Downloads' if not os.path.exists(fullpath): os.makedirs(fullpath) name = f'{info["artist"][0]} - {info["name"]}' os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3", f"{fullpath}/{getCorrect(name)}.mp3") if path: os.rename(f"{fullpath}/{getCorrect(name)}.mp3", f"{path}/{getCorrect(name)}.mp3") #deleting cache try: shutil.rmtree(f"cache/{info['uri']}") except: pass notify.send(f'{info["artist"][0]} - {info["name"]}') def downloadFromYoutubeMusic(self, url, info, path): print(info) uri = info['uri'] #downloading video from youtube if self.__youtube.download(url=url, path=uri, filename=uri): #converting video to mp3 file self.__youtube.convertVideoToMusic(uri=uri) self.__editor.setTags(data=info) cachepath = os.getcwd() + '/cache' fullpath = os.getcwd() + '/Downloads' if not os.path.exists(fullpath): os.makedirs(fullpath) name = f'{info["artist"][0]} - {info["name"]}' os.rename(f"{cachepath}/{info['uri']}/{info['uri']}.mp3", f"{fullpath}/{getCorrect(name)}.mp3") if path: os.rename(f"{fullpath}/{getCorrect(name)}.mp3", f"{path}/{getCorrect(name)}.mp3") #deleting cache try: shutil.rmtree(f"cache/{info['uri']}") except: pass notify.send(f'{info["artist"][0]} - {info["name"]}') return True, info else: return False, None def search(self, query): return self.__spotify.search(query=query)
class SpotifyClient(object): def __init__(self, host): self.host = host self.direct = Direct(self) self.server = None self.sp = None self.reconnect_time = None self.reconnect_timer = None self.ready_event = Event() self.messages = [] def start(self): if self.sp: self.sp.disconnect() self.sp = None self.sp = Spotify() self.ready_event = Event() self.messages = [] self.sp.on('error', self.on_error)\ .on('close', self.on_close) self.sp.login(self.host.username, self.host.password, self.on_login) def on_login(self): # Refresh server info self.host.refresh() # Release request hold self.ready_event.set() def on_error(self, message): self.messages.append((logging.ERROR, message)) Log.Error(message) def on_close(self, code, reason=None): # Force re-authentication self.sp.authenticated = False # Reconnect self.connect() def connect(self): # Rate-limit re-connections if self.reconnect_time: span = time.time() - self.reconnect_time Log.Debug('Last reconnection attempt was %s seconds ago', span) # Delay next reconnection if span < 120: self.connect_delayed() return Log.Info('Attempting reconnection to Spotify...') self.reconnect_time = time.time() # Hold requests while we re-connect self.ready_event = Event() # Start connecting... self.sp.connect() def connect_delayed(self): self.reconnect_timer = Timer(180, self.connect) self.reconnect_timer.start() Log.Info('Reconnection will be attempted again in 180 seconds') @property def constructed(self): return self.sp and self.ready_event @property def ready(self): if not self.constructed: return False return self.ready_event.wait(10) def shutdown(self): self.sp.api.shutdown() self.sp = None # # Public methods # def search(self, query, query_type='all', max_results=50, offset=0): """ Execute a search :param query: A query string. """ return self.sp.search(query, query_type, max_results, offset) def artist_uris(self, artist): top_tracks = self.artist_top_tracks(artist) # Top Track URIs track_uris = [] if top_tracks: track_uris = [tr.uri for tr in top_tracks.tracks if tr is not None] # Album URIs album_uris = [al.uri for al in artist.albums if al is not None] return track_uris, album_uris def artist_top_tracks(self, artist): for tt in artist.top_tracks: # TopTracks matches account region? if tt.country == self.sp.country: return tt # Unable to find TopTracks for account region return None # # Streaming # def track_url(self, track): if self.host.proxy_tracks and self.server: return self.server.get_track_url(str(track.uri), hostname=self.host.hostname) return function_path('play', uri=str(track.uri), ext='mp3') def stream_url(self, uri): if self.host.proxy_tracks and self.server: return self.server.get_track_url(str(uri), hostname=self.host.hostname) return self.direct.get(uri) def last_message(self): if not self.messages: return None, '' return self.messages[-1]