def from_artist( artist_url: str, output_format: str = None, use_youtube: bool = False, lyrics_provider: str = None, threads: int = 1, ) -> List[SongObject]: """ `str` `artist_url` : Spotify Url of the artist whose tracks are to be retrieved returns a `list<songObj>` containing Url's of each track in the artist profile """ spotify_client = SpotifyClient() artist_tracks = [] artist_response = spotify_client.artist_albums( artist_url, album_type="album,single" ) if artist_response is None: raise ValueError("Wrong artist id") albums_list = artist_response["items"] albums_object: Dict[str, str] = {} # Fetch all artist albums while artist_response and artist_response["next"]: response = spotify_client.next(artist_response) if response is None: break artist_response = response albums_list.extend(artist_response["items"]) # Remove duplicate albums for album in albums_list: # return an iterable containing the string's alphanumeric characters alpha_numeric_filter = filter(str.isalnum, album["name"].lower()) # join all characters into one string album_name = "".join(alpha_numeric_filter) if albums_object.get(album_name) is None: albums_object[album_name] = album["uri"] tracks_list = [] tracks_object: Dict[str, str] = {} # Fetch all tracks from all albums for album_uri in albums_object.values(): response = spotify_client.album_tracks(album_uri) if response is None: continue album_response = response album_tracks = album_response["items"] while album_response["next"]: album_response = spotify_client.next(album_response) if album_response is None: break album_tracks.extend(album_response["items"]) tracks_list.extend(album_tracks) # Filter tracks to remove duplicates and songs without our artist for track in tracks_list: # ignore None tracks or tracks without uri if track is not None and track.get("uri") is None: continue # return an iterable containing the string's alphanumeric characters alphaNumericFilter = filter(str.isalnum, track["name"].lower()) # join all characters into one string trackName = "".join(alphaNumericFilter) if tracks_object.get(trackName) is None: for artist in track["artists"]: # get artist id from url # https://api.spotify.com/v1/artists/1fZAAHNWdSM5gqbi9o5iEA/albums # # split string # ['https:', '', 'api.spotify.com', 'v1', 'artists', # '1fZAAHNWdSM5gqbi9o5iEA', 'albums'] # # get second element from the end # '1fZAAHNWdSM5gqbi9o5iEA' artistId = artist_response["href"].split("/")[-2] # ignore tracks that are not from our artist by checking # the id if artist["id"] == artistId: tracks_object[trackName] = track["uri"] # Create song objects from track ids def get_song(track_uri): try: return from_spotify_url( f"https://open.spotify.com/track/{track_uri.split(':')[-1]}", output_format, use_youtube, lyrics_provider, None, ) except (LookupError, ValueError, OSError): return None with concurrent.futures.ThreadPoolExecutor(max_workers=threads) as executor: results = executor.map(get_song, tracks_object.values()) for result in results: if result is not None and result.youtube_link is not None: artist_tracks.append(result) return artist_tracks
def from_album( album_url: str, output_format: str = None, use_youtube: bool = False, lyrics_provider: str = None, generate_m3u: bool = False, threads: int = 1, path_template: str = None, ) -> List[SongObject]: """ Create and return list containing SongObject for every song in the album `str` `album_url` : Spotify Url of the album whose tracks are to be retrieved `str` `output_format` : output format of the song returns a `list<SongObject>` containing Url's of each track in the given album """ spotify_client = SpotifyClient() tracks = [] album_response = spotify_client.album_tracks(album_url) if album_response is None: raise ValueError("Wrong album id") album_tracks = album_response["items"] # Get all tracks from album while album_response["next"]: album_response = spotify_client.next(album_response) # Failed to get response, break the loop if album_response is None: break album_tracks.extend(album_response["items"]) # Remove songs without id album_tracks = [ track for track in album_tracks if track is not None and track.get("id") is not None ] def get_tracks(track): try: song = from_spotify_url( "https://open.spotify.com/track/" + track["id"], output_format, use_youtube, lyrics_provider, None, ) if generate_m3u: if path_template: file_path = _parse_path_template(path_template, song, output_format) else: file_path = _get_converted_file_path(song, output_format) return song, f"{file_path}\n" return song, None except (LookupError, ValueError): return None, None except OSError: if generate_m3u: song_obj = SongObject(track, album_response, {}, None, "", None) if path_template: file_path = _parse_path_template( path_template, song_obj, output_format ) else: file_path = _get_converted_file_path(song_obj, output_format) return None, f"{file_path}\n" return None, None with concurrent.futures.ThreadPoolExecutor(max_workers=threads) as executor: results = executor.map(get_tracks, album_tracks) album_text = "" for result in results: if result[1] is not None: album_text += result[1] if result[0] is not None and result[0].youtube_link is not None: tracks.append(result[0]) if album_response and generate_m3u is True: album_data = spotify_client.album(album_url) if album_data is not None: album_name = album_data["name"] else: album_name = album_tracks[0]["name"] album_name = format_name(album_name) album_file = Path(f"{album_name}.m3u") with open(album_file, "w", encoding="utf-8") as file: file.write(album_text) return tracks