# XXX: Perhaps we do not need to call `spotify._get_id` # explicitly in newer versions of spotipy. # Need to confirm this and if so, remove the calls # to `spotify._get_id` in below methods. from spotdl.authorize.services import AuthorizeSpotify import spotdl.util import sys import logging logger = logging.getLogger(__name__) try: from slugify import SLUG_OK, slugify except ImportError: logger.error("Oops! `unicode-slugify` was not found.") logger.info("Please remove any other slugify library and install `unicode-slugify`.") sys.exit(5) ALBUM_BASE_URL = "https://open.spotify.com/album/" class SpotifyHelpers: def __init__(self, spotify=None): self._ALBUM_BASE_URL = ALBUM_BASE_URL if spotify is None: spotify = AuthorizeSpotify() self.spotify = spotify def prompt_for_user_playlist(self, username): """ Write user playlists to text_file """ playlists = self.fetch_user_playlist_urls(username) for i, playlist in enumerate(playlists, 1): playlist_details = "{0}. {1:<30} ({2} tracks)".format( i, playlist["name"], playlist["tracks"]["total"] ) print(playlist_details, file=sys.stderr) print("", file=sys.stderr) playlist = spotdl.util.prompt_user_for_selection(playlists) return playlist["external_urls"]["spotify"] def fetch_user_playlist_urls(self, username): """ Fetch user playlists when using the -u option. """ logger.debug('Fetching playlists for "{username}".'.format(username=username)) playlists = self.spotify.user_playlists(username) collected_playlists = [] check = 1 while True: for playlist in playlists["items"]: # in rare cases, playlists may not be found, so playlists['next'] # is None. Skip these. Also see Issue #91. if playlist["name"] is not None: collected_playlists.append(playlist) check += 1 if playlists["next"]: playlists = self.spotify.next(playlists) else: break return collected_playlists def fetch_playlist(self, playlist_url): logger.debug('Fetching playlist "{playlist}".'.format(playlist=playlist_url)) try: playlist_id = self.spotify._get_id("playlist", playlist_url) except IndexError: # Wrong format, in either case logger.error("The provided playlist URL is not in a recognized format!") sys.exit(10) try: results = self.spotify.user_playlist( user=None, playlist_id=playlist_id, fields="tracks,next,name" ) except spotipy.client.SpotifyException: logger.error("Unable to find playlist") logger.info("Make sure the playlist is set to publicly visible and then try again.") sys.exit(11) return results def write_playlist_tracks(self, playlist, text_file=None): tracks = playlist["tracks"] if not text_file: text_file = u"{0}.txt".format(slugify(playlist["name"], ok="-_()[]{}")) return self.write_tracks(tracks, text_file) def fetch_album(self, album_url): logger.debug('Fetching album "{album}".'.format(album=album_url)) album_id = self.spotify._get_id("album", album_url) album = self.spotify.album(album_id) return album def write_album_tracks(self, album, text_file=None): tracks = self.spotify.album_tracks(album["id"]) if not text_file: text_file = u"{0}.txt".format(slugify(album["name"], ok="-_()[]{}")) return self.write_tracks(tracks, text_file) def fetch_albums_from_artist(self, artist_url, album_type=None): """ This function returns all the albums from a give artist_url using the US market :param artist_url - spotify artist url :param album_type - the type of album to fetch (ex: single) the default is all albums :param return - the album from the artist """ logger.debug('Fetching all albums for "{artist}".'.format(artist=artist_url)) artist_id = self.spotify._get_id("artist", artist_url) # fetching artist's albums limitting the results to the US to avoid duplicate # albums from multiple markets results = self.spotify.artist_albums(artist_id, album_type=album_type, country="US") albums = results["items"] # indexing all pages of results while results["next"]: results = self.spotify.next(results) albums.extend(results["items"]) return albums def write_all_albums(self, albums, text_file=None): """ This function gets all albums from an artist and writes it to a file in the current working directory called [ARTIST].txt, where [ARTIST] is the artist of the album :param artist_url - spotify artist url :param text_file - file to write albums to """ # if no file if given, the default save file is in the current working # directory with the name of the artist if text_file is None: text_file = albums[0]["artists"][0]["name"] + ".txt" for album in albums: logger.info('Fetching album "{album}".'.format(album=album["name"])) self.write_album_tracks(album, text_file=text_file) def write_tracks(self, tracks, text_file): logger.info(u"Writing {0} tracks to {1}.".format(tracks["total"], text_file)) track_urls = [] with open(text_file, "a") as file_out: while True: for item in tracks["items"]: if "track" in item: track = item["track"] else: track = item try: track_url = track["external_urls"]["spotify"] file_out.write(track_url + "\n") track_urls.append(track_url) except KeyError: # FIXME: Write "{artist} - {name}" instead of Spotify URI for # "local only" tracks. logger.warning( 'Skipping track "{0}" by "{1}" (local only?)'.format( track["name"], track["artists"][0]["name"] ) ) # 1 page = 50 results # check if there are more pages if tracks["next"]: tracks = self.spotify.next(tracks) else: break return track_urls