def fetch_youtube_url(search_term): """For each song name/artist name combo, fetch the YouTube URL and return the list of URLs""" YOUTUBE_DEV_KEY = getenv('YOUTUBE_DEV_KEY') youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=YOUTUBE_DEV_KEY) log.info(u"Searching for {}".format(search_term)) search_response = youtube.search().list(q=search_term, part='id, snippet').execute() for v in search_response['items']: if v['id']['kind'] == VIDEO: log.debug("Adding Video id {}".format(v['id']['videoId'])) return YOUTUBE_VIDEO_URL + v['id']['videoId']
def save_to_cache(search_term, video_id): """ Saves the search term and video id to the database cache so it can be looked up later :param search_term: Search term to be saved to in the cache :param video_id: Video id to be saved to in the cache :return Video id saved in the cache """ song_info, saved = Song.get_or_create(search_term=search_term, video_id=video_id) log.info(f"Saved: {saved} video id {song_info.video_id} in cache") return song_info.video_id
def check_if_in_cache(search_term): """ Checks if the specified search term is in the local database cache and returns the video id if it exists. :param search_term: String to be searched for in the cache :return A tuple with Boolean and video id if it exists """ try: song = Song.get(search_term=search_term) log.info(f"Found id {song.video_id} for {search_term} in cache") return True, song.video_id except DoesNotExist: log.info(f"Couldn't find id for {search_term} in cache") return False, None
def validate_spotify_url(url): """ Validate the URL and determine if the item type is supported. :return Boolean indicating whether or not item is supported """ item_type, item_id = parse_spotify_url(url) log.debug(f"Got item type {item_type} and item_id {item_id}") if item_type not in ['album', 'track', 'playlist']: log.info("Only albums/tracks/playlists are supported") return False if item_id is None: log.info("Couldn't get a valid id") return False return True
def fetch_tracks(sp, item_type, url): """ Fetches tracks from the provided URL. :param sp: Spotify client :param type: Type of item being requested for: album/playlist/track :param url: URL of the item :return Dictionary of song and artist """ songs_dict = {} offset = 0 if item_type == 'playlist': while True: items = sp.playlist_items( playlist_id=url, fields= 'items.track.name,items.track.artists(name),items.track.album(name),total,next,offset', additional_types=['track'], offset=offset) total_songs = items.get('total') for item in items['items']: track_name = item['track']['name'] track_artist = ", ".join( [artist['name'] for artist in item['track']['artists']]) songs_dict.update({track_name: track_artist}) offset += 1 log.info(f"Fetched {offset}/{total_songs} songs in the playlist") if total_songs == offset: log.info( 'All pages fetched, time to leave. Added %s songs in total', offset) break elif item_type == 'album': while True: items = sp.album_tracks(album_id=url) total_songs = items.get('total') for item in items['items']: track_name = item['name'] track_artist = " ".join( [artist['name'] for artist in item['artists']]) songs_dict.update({track_name: track_artist}) offset += 1 log.info(f"Fetched {offset}/{total_songs} songs in the album") if total_songs == offset: log.info( 'All pages fetched, time to leave. Added %s songs in total', offset) break elif item_type == 'track': items = sp.track(track_id=url) track_name = items['name'] track_artist = " ".join( [artist['name'] for artist in items['artists']]) songs_dict.update({track_name: track_artist}) return songs_dict
def download_spotify(sp, args): if args.output: item_type, item_id = parse_spotify_url(args.url) directory_name = get_item_name(sp, item_type, item_id) save_path = Path(args.output) if args.createdir: save_path = Path( PurePath.joinpath(Path(args.output), Path(directory_name))) save_path.mkdir(parents=True, exist_ok=True) log.info("Saving songs to: {}".format(directory_name)) songs = fetch_tracks(sp, item_type, args.url) if args.download is True: download_songs(songs, save_path, args.format_str, args.skip_mp3, args.keep_playlist_order)
def download_youtube(sp, args): if args.output: directory_name, item_type, songs = fetch_tracks_yt(args.url) save_path = Path(args.output) if args.createdir: save_path = Path( PurePath.joinpath(Path(args.output), Path(directory_name))) save_path.mkdir(parents=True, exist_ok=True) log.info("Saving songs to: {}".format(directory_name)) if args.download is True: download_songs(songs, save_path, args.format_str, args.skip_mp3, args.keep_playlist_order, is_yt=True)
def fetch_youtube_url(search_term, dev_key): """For each song name/artist name combo, fetch the YouTube URL and return the list of URLs""" YOUTUBE_DEV_KEY = dev_key youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=YOUTUBE_DEV_KEY, cache_discovery=False) log.info(u"Searching for {}".format(search_term)) try: search_response = youtube.search().list(q=search_term, part='id, snippet').execute() for v in search_response['items']: if v['id']['kind'] == VIDEO: log.debug("Adding Video id {}".format(v['id']['videoId'])) return YOUTUBE_VIDEO_URL + v['id']['videoId'] except HttpError as err: err_details = loads( err.content.decode('utf-8')).get('error').get('errors') secho("Couldn't complete search due to following errors: ", fg='red') for e in err_details: error_reason = e.get('reason') error_domain = e.get('domain') error_message = e.get('message') if error_reason == 'quotaExceeded' or error_reason == 'dailyLimitExceeded': secho( f"\tYou're over daily allowed quota. Unfortunately, YouTube restricts API keys to a max of 10,000 requests per day which translates to a maximum of 100 searches.", fg='red') secho( f"\tThe quota will be reset at midnight Pacific Time (PT).", fg='red') secho( f"\tYou can request for Quota increase from https://console.developers.google.com/apis/api/youtube.googleapis.com/quotas.", fg='red') else: secho( f"\t Search failed due to {error_domain}:{error_reason}, message: {error_message}" ) return None
def spotify_dl(): """Main entry point of the script.""" parser = argparse.ArgumentParser(prog='spotify_dl') parser.add_argument('-d', '--download', action='store_true', help='Download using youtube-dl', default=True) parser.add_argument('-p', '--playlist', action='store', help='Download from playlist id instead of' ' saved tracks') parser.add_argument('-V', '--verbose', action='store_true', help='Show more information on what' 's happening.') parser.add_argument('-v', '--version', action='store_true', help='Shows current version of the program') parser.add_argument('-o', '--output', type=str, action='store', help='Specify download directory.') parser.add_argument('-u', '--user_id', action='store', help='Specify the playlist owner\'s userid when it' ' is different than your spotify userid') parser.add_argument('-i', '--uri', type=str, action='store', nargs='*', help='Given a URI, download it.') parser.add_argument('-f', '--format_str', type=str, action='store', help='Specify youtube-dl format string.', default='bestaudio/best') parser.add_argument('-m', '--skip_mp3', action='store_true', help='Don\'t convert downloaded songs to mp3') parser.add_argument('-l', '--url', action="store", help="Spotify Playlist link URL") parser.add_argument('-s', '--scrape', action="store", help="Use HTML Scraper for YouTube Search", default=True) args = parser.parse_args() playlist_url_pattern = re.compile(r'^https://open.spotify.com/(.+)$') if args.version: print("spotify_dl v{}".format(VERSION)) exit(0) db.connect() db.create_tables([Song]) if os.path.isfile(os.path.expanduser('~/.spotify_dl_settings')): with open(os.path.expanduser('~/.spotify_dl_settings')) as file: config = json.loads(file.read()) for key, value in config.items(): if value and (value.lower() == 'true' or value.lower() == 't'): setattr(args, key, True) else: setattr(args, key, value) if args.verbose: log.setLevel(DEBUG) log.info('Starting spotify_dl') log.debug('Setting debug mode on spotify_dl') if not check_for_tokens(): exit(1) token = authenticate() sp = spotipy.Spotify(auth=token) log.debug('Arguments: {}'.format(args)) if args.url: url_match = playlist_url_pattern.match(args.url) if url_match and len(url_match.groups()) > 0: uri = "spotify:" + url_match.groups()[0].replace('/', ':') args.uri = [uri] else: raise Exception('Invalid playlist URL ') if args.uri: current_user_id, playlist_id = extract_user_and_playlist_from_uri( args.uri[0], sp) else: if args.user_id is None: current_user_id = sp.current_user()['id'] else: current_user_id = args.user_id if args.output: if args.uri: uri = args.uri[0] playlist = playlist_name(uri, sp) else: playlist = get_playlist_name_from_id(args.playlist, current_user_id, sp) log.info("Saving songs to: {}".format(playlist)) download_directory = args.output + '/' + playlist if len(download_directory) >= 0 and download_directory[-1] != '/': download_directory += '/' if not os.path.exists(download_directory): os.makedirs(download_directory) else: download_directory = '' if args.uri: songs = fetch_tracks(sp, playlist_id, current_user_id) else: songs = fetch_tracks(sp, args.playlist, current_user_id) url = [] for song, artist in songs.items(): link = fetch_youtube_url(song + ' - ' + artist, get_youtube_dev_key()) if link: url.append((link, song, artist)) save_songs_to_file(url, download_directory) if args.download is True: download_songs(url, download_directory, args.format_str, args.skip_mp3)
def fetch_tracks(sp, item_type, url): """ Fetches tracks from the provided URL. :param sp: Spotify client :param type: Type of item being requested for: album/playlist/track :param url: URL of the item :return Dictionary of song and artist """ songs_list = [] offset = 0 if item_type == 'playlist': while True: items = sp.playlist_items( playlist_id=url, fields='items.track.name,items.track.artists(name, uri),' 'items.track.album(name, release_date, total_tracks, images),' 'items.track.track_number,total, next,offset', additional_types=['track'], offset=offset) total_songs = items.get('total') for item in items['items']: track_name = item['track']['name'] track_artist = ", ".join( [artist['name'] for artist in item['track']['artists']]) track_album = item['track']['album']['name'] track_year = item['track']['album']['release_date'][:4] album_total = item['track']['album']['total_tracks'] track_num = item['track']['track_number'] cover = item['track']['album']['images'][0]['url'] if (len( sp.artist(artist_id=item['track']['artists'][0]['uri']) ['genres']) > 0): genre = sp.artist(artist_id=item['track']['artists'][0] ['uri'])['genres'][0] else: genre = "" songs_list.append({ "name": track_name, "artist": track_artist, "album": track_album, "year": track_year, "num_tracks": album_total, "num": track_num, "cover": cover, "genre": genre }) offset += 1 log.info(f"Fetched {offset}/{total_songs} songs in the playlist") if total_songs == offset: log.info( 'All pages fetched, time to leave. Added %s songs in total', offset) break elif item_type == 'album': while True: album_info = sp.album(album_id=url) items = sp.album_tracks(album_id=url) total_songs = items.get('total') track_album = album_info['name'] track_year = album_info['release_date'][:4] album_total = album_info['total_tracks'] cover = album_info['images'][0]['url'] if (len( sp.artist( artist_id=album_info['artists'][0]['uri'])['genres']) > 0): genre = sp.artist( artist_id=album_info['artists'][0]['uri'])['genres'][0] else: genre = "" for item in items['items']: track_name = item['name'] track_artist = ", ".join( [artist['name'] for artist in item['artists']]) track_num = item['track_number'] songs_list.append({ "name": track_name, "artist": track_artist, "album": track_album, "year": track_year, "num_tracks": album_total, "num": track_num, "cover": cover, "genre": genre }) offset += 1 log.info(f"Fetched {offset}/{total_songs} songs in the album") if total_songs == offset: log.info( 'All pages fetched, time to leave. Added %s songs in total', offset) break elif item_type == 'track': items = sp.track(track_id=url) track_name = items['name'] track_artist = ", ".join( [artist['name'] for artist in items['artists']]) track_album = items['album']['name'] track_year = items['album']['release_date'][:4] album_total = items['album']['total_tracks'] track_num = items['track_number'] cover = items['album']['images'][0]['url'] if (len(sp.artist(artist_id=items['artists'][0]['uri'])['genres']) > 0): genre = sp.artist( artist_id=items['artists'][0]['uri'])['genres'][0] else: genre = "" songs_list.append({ "name": track_name, "artist": track_artist, "album": track_album, "year": track_year, "num_tracks": album_total, "num": track_num, "cover": cover, "genre": genre }) return songs_list
def spotify_dl(): """Main entry point of the script.""" parser = argparse.ArgumentParser(prog='spotify_dl') parser.add_argument('-l', '--url', action="store", help="Spotify Playlist link URL", type=str, required=True) parser.add_argument('-o', '--output', type=str, action='store', help='Specify download directory.', required=True) parser.add_argument('-d', '--download', action='store_true', help='Download using youtube-dl', default=True) parser.add_argument('-f', '--format_str', type=str, action='store', help='Specify youtube-dl format string.', default='bestaudio/best') parser.add_argument( '-k', '--keep_playlist_order', type=bool, default=False, action=argparse.BooleanOptionalAction, help='Whether to keep original playlist ordering or not.') parser.add_argument('-m', '--skip_mp3', action='store_true', help='Don\'t convert downloaded songs to mp3') parser.add_argument('-s', '--scrape', action="store", help="Use HTML Scraper for YouTube Search", default=True) parser.add_argument('-V', '--verbose', action='store_true', help='Show more information on what' 's happening.') parser.add_argument('-v', '--version', action='store_true', help='Shows current version of the program') parser.add_argument('-c', '--createdir', action='store_true', help='Create a subdirectory') args = parser.parse_args() if args.version: print("spotify_dl v{}".format(VERSION)) exit(0) db.connect() db.create_tables([Song]) if os.path.isfile(os.path.expanduser('~/.spotify_dl_settings')): with open(os.path.expanduser('~/.spotify_dl_settings')) as file: config = json.loads(file.read()) for key, value in config.items(): if value and (value.lower() == 'true' or value.lower() == 't'): setattr(args, key, True) else: setattr(args, key, value) if args.verbose: log.setLevel(DEBUG) log.info('Starting spotify_dl') log.debug('Setting debug mode on spotify_dl') if not check_for_tokens(): exit(1) sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials()) log.debug('Arguments: {}'.format(args)) # TODO: make the logic less dumb if args.url: valid_item = validate_spotify_url(args.url) valid_yt = validate_youtube_url(args.url) if valid_item: download_spotify(sp, args) elif valid_yt: download_youtube(sp, args) exit(1)
def spotify_dl(): """Main entry point of the script.""" parser = argparse.ArgumentParser(prog='spotify_dl') parser.add_argument('-l', '--url', action="store", help="Spotify Playlist link URL", type=str, required=True) parser.add_argument('-o', '--output', type=str, action='store', help='Specify download directory.', required=True) parser.add_argument('-d', '--download', action='store_true', help='Download using youtube-dl', default=True) parser.add_argument('-f', '--format_str', type=str, action='store', help='Specify youtube-dl format string.', default='bestaudio/best') parser.add_argument( '-k', '--keep_playlist_order', default=False, action='store_true', help='Whether to keep original playlist ordering or not.') parser.add_argument('-m', '--skip_mp3', action='store_true', help='Don\'t convert downloaded songs to mp3') parser.add_argument('-s', '--scrape', action="store", help="Use HTML Scraper for YouTube Search", default=True) parser.add_argument('-V', '--verbose', action='store_true', help='Show more information on what' 's happening.') parser.add_argument('-v', '--version', action='store_true', help='Shows current version of the program') args = parser.parse_args() if args.version: print("spotify_dl v{}".format(VERSION)) exit(0) db.connect() db.create_tables([Song]) if os.path.isfile(os.path.expanduser('~/.spotify_dl_settings')): with open(os.path.expanduser('~/.spotify_dl_settings')) as file: config = json.loads(file.read()) for key, value in config.items(): if value and (value.lower() == 'true' or value.lower() == 't'): setattr(args, key, True) else: setattr(args, key, value) if args.verbose: log.setLevel(DEBUG) log.info('Starting spotify_dl') log.debug('Setting debug mode on spotify_dl') if not check_for_tokens(): exit(1) sp = spotipy.Spotify(auth_manager=SpotifyClientCredentials()) log.debug('Arguments: {}'.format(args)) if args.url: valid_item = validate_spotify_url(args.url) if not valid_item: sys.exit(1) if args.output: item_type, item_id = parse_spotify_url(args.url) directory_name = get_item_name(sp, item_type, item_id) save_path = Path( PurePath.joinpath(Path(args.output), Path(directory_name))) save_path.mkdir(parents=True, exist_ok=True) log.info("Saving songs to: {}".format(directory_name)) songs = fetch_tracks(sp, item_type, args.url) if args.download is True: file_name_f = default_filename if args.keep_playlist_order: file_name_f = playlist_num_filename download_songs(songs, save_path, args.format_str, args.skip_mp3, args.keep_playlist_order, file_name_f)
def fetch_youtube_url(search_term, dev_key=None, use_invidious=False): """ For each song name/artist name combo, fetch the YouTube URL and return the list of URLs :param search_term: Search term to be looked up on YouTube :param dev_key: Youtube API key """ log.info(f"Searching for {search_term}") in_cache, video_id = check_if_in_cache(search_term) if in_cache: log.info(f"Found id {video_id} for {search_term} in cache") return YOUTUBE_VIDEO_URL + video_id if use_invidious: video_id = invidious(search_term) if video_id is not None: return YOUTUBE_VIDEO_URL + video_id if not dev_key: YOUTUBE_SEARCH_BASE = "https://www.youtube.com/results?search_query=" try: response = requests.get(YOUTUBE_SEARCH_BASE + search_term).content html_response = html.fromstring(response) video = html_response.xpath( "//a[contains(@class, 'yt-uix-tile-link')]/@href") video_id = re.search("((\?v=)[a-zA-Z0-9_-]{4,15})", video[0]).group(0)[3:] log.debug( f"Found video id {video_id} for search term {search_term}") _ = save_to_cache(search_term=search_term, video_id=video_id) return YOUTUBE_VIDEO_URL + video_id except AttributeError as e: log.warning(f"Could not find scrape details for {search_term}") capture_exception(e) return None except IndexError as e: log.warning( f"Could not perform scrape search for {search_term}, got a different HTML" ) capture_exception(e) return None else: youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=dev_key, cache_discovery=False) try: search_response = youtube.search().list( q=search_term, part='id, snippet').execute() for v in search_response['items']: if v['id']['kind'] == VIDEO: log.debug("Adding Video id {}".format(v['id']['videoId'])) return YOUTUBE_VIDEO_URL + v['id']['videoId'] except HttpError as err: err_details = loads( err.content.decode('utf-8')).get('error').get('errors') secho("Couldn't complete search due to following errors: ", fg='red') for e in err_details: error_reason = e.get('reason') error_domain = e.get('domain') error_message = e.get('message') if error_reason == 'quotaExceeded' or error_reason == 'dailyLimitExceeded': secho( f"\tYou're over daily allowed quota. Unfortunately, YouTube restricts API keys to a max of 10,000 requests per day which translates to a maximum of 100 searches.", fg='red') secho( f"\tThe quota will be reset at midnight Pacific Time (PT).", fg='red') secho( f"\tYou can request for Quota increase from https://console.developers.google.com/apis/api/youtube.googleapis.com/quotas.", fg='red') else: secho( f"\t Search failed due to {error_domain}:{error_reason}, message: {error_message}" ) return None
def fetch_tracks(sp, item_type, url): """ Fetches tracks from the provided URL. :param sp: Spotify client :param item_type: Type of item being requested for: album/playlist/track :param url: URL of the item :return Dictionary of song and artist """ songs_list = [] offset = 0 if item_type == 'playlist': while True: items = sp.playlist_items( playlist_id=url, fields='items.track.name,items.track.artists(name, uri),' 'items.track.album(name, release_date, total_tracks, images),' 'items.track.track_number,total, next,offset,' 'items.track.id', additional_types=['track'], offset=offset) total_songs = items.get('total') for item in items['items']: track_info = item.get('track') # If the user has a podcast in their playlist, there will be no track # Without this conditional, the program will fail later on when the metadata is fetched if track_info is None: offset += 1 continue track_album_info = track_info.get('album') track_num = track_info.get('track_number') spotify_id = track_info.get('id') track_name = track_info.get('name') track_artist = ", ".join( [artist['name'] for artist in track_info.get('artists')]) if track_album_info: track_album = track_album_info.get('name') track_year = track_album_info.get( 'release_date')[:4] if track_album_info.get( 'release_date') else '' album_total = track_album_info.get('total_tracks') if len(item['track']['album']['images']) > 0: cover = item['track']['album']['images'][0]['url'] else: cover = None artists = track_info.get('artists') main_artist_id = artists[0].get( 'uri', None) if len(artists) > 0 else None genres = sp.artist(artist_id=main_artist_id).get( 'genres', []) if main_artist_id else [] if len(genres) > 0: genre = genres[0] else: genre = "" songs_list.append({ "name": track_name, "artist": track_artist, "album": track_album, "year": track_year, "num_tracks": album_total, "num": track_num, "playlist_num": offset + 1, "cover": cover, "genre": genre, "spotify_id": spotify_id }) offset += 1 log.info(f"Fetched {offset}/{total_songs} songs in the playlist") if total_songs == offset: log.info( 'All pages fetched, time to leave. Added %s songs in total', offset) break elif item_type == 'album': while True: album_info = sp.album(album_id=url) items = sp.album_tracks(album_id=url) total_songs = items.get('total') track_album = album_info.get('name') track_year = album_info.get('release_date')[:4] if album_info.get( 'release_date') else '' album_total = album_info.get('total_tracks') if len(album_info['images']) > 0: cover = album_info['images'][0]['url'] else: cover = None if len( sp.artist(artist_id=album_info['artists'][0]['uri']) ['genres']) > 0: genre = sp.artist( artist_id=album_info['artists'][0]['uri'])['genres'][0] else: genre = "" for item in items['items']: track_name = item.get('name') track_artist = ", ".join( [artist['name'] for artist in item['artists']]) track_num = item['track_number'] spotify_id = item.get('id') songs_list.append({ "name": track_name, "artist": track_artist, "album": track_album, "year": track_year, "num_tracks": album_total, "num": track_num, "playlist_num": offset + 1, "cover": cover, "genre": genre, "spotify_id": spotify_id }) offset += 1 log.info(f"Fetched {offset}/{total_songs} songs in the album") if total_songs == offset: log.info( 'All pages fetched, time to leave. Added %s songs in total', offset) break elif item_type == 'track': items = sp.track(track_id=url) track_name = items.get('name') album_info = items.get('album') track_artist = ", ".join( [artist['name'] for artist in items['artists']]) if album_info: track_album = album_info.get('name') track_year = album_info.get('release_date')[:4] if album_info.get( 'release_date') else '' album_total = album_info.get('total_tracks') track_num = items['track_number'] spotify_id = items['id'] if len(items['album']['images']) > 0: cover = items['album']['images'][0]['url'] else: cover = None if len(sp.artist(artist_id=items['artists'][0]['uri'])['genres']) > 0: genre = sp.artist( artist_id=items['artists'][0]['uri'])['genres'][0] else: genre = "" songs_list.append({ "name": track_name, "artist": track_artist, "album": track_album, "year": track_year, "num_tracks": album_total, "num": track_num, "playlist_num": offset + 1, "cover": cover, "genre": genre, "spotify_id": spotify_id }) return songs_list
def fetching(client, type, url): """ Fetches tracks from the URL. :param client: Spotify client :param type: item type requested for: album/playlist/track :param url: URL of the item :return Dictionary {song :artist} """ list_of_songs = [ ] ## an empty list to be filled(appended) with list of songs offset = 0 if type == 'playlist': while 1: items = client.playlist_items( playlist_id=url, fields='items.track.name,items.track.artists(name, uri),' 'items.track.album(name, release_date, total_tracks, images),' 'items.track.track_number,total, next,offset', additional_types=['track'], offset=offset) """takes the url from the request library and returns response which is a json file. we extract the fields from the json file in the form of key-value pairs(dicitonaries)""" total_no_of_songs = items.get('total') for item in items['items']: track_name = item['track']['name'] track_artist = ", ".join( [artist['name'] for artist in item['track']['artists']]) track_album = item['track']['album']['name'] track_year = item['track']['album']['release_date'][:4] album_total = item['track']['album']['total_tracks'] track_num = item['track']['track_number'] if len(item['track']['album']['images']) > 0: cover = item['track']['album']['images'][0]['url'] else: cover = None if len( client.artist(artist_id=item['track']['artists'][0] ['uri'])['genres']) > 0: genre = client.artist(artist_id=item['track']['artists'][0] ['uri'])['genres'][0] else: genre = "" list_of_songs.append({ "name": track_name, "artist": track_artist, "album": track_album, "year": track_year, "num_tracks": album_total, "num": track_num, "playlist_num": offset + 1, "cover": cover, "genre": genre }) offset += 1 log.info( f"Fetched {offset}/{total_no_of_songs} songs in the playlist") if total_no_of_songs == offset: log.info('All pages fetched. Added %s songs in total', offset) break elif type == 'album': while 1: album_info = client.album(album_id=url) items = client.album_tracks(album_id=url) total_songs = items.get('total') track_album = album_info['name'] track_year = album_info['release_date'][:4] album_total = album_info['total_tracks'] if len(album_info['images']) > 0: cover = album_info['images'][0]['url'] else: cover = None if len( client.artist(artist_id=album_info['artists'][0]['uri']) ['genres']) > 0: genre = client.artist( artist_id=album_info['artists'][0]['uri'])['genres'][0] else: genre = "" for item in items['items']: track_name = item['name'] track_artist = ", ".join( [artist['name'] for artist in item['artists']]) track_num = item['track_number'] list_of_songs.append({ "name": track_name, "artist": track_artist, "album": track_album, "year": track_year, "num_tracks": album_total, "num": track_num, "playlist_num": offset + 1, "cover": cover, "genre": genre }) offset += 1 log.info(f"Fetched {offset}/{total_songs} songs in the album") if total_songs == offset: log.info( 'All pages fetched, time to leave. Added %s songs in total', offset) break elif type == 'track': items = client.track(track_id=url) track_name = items['name'] track_artist = ", ".join( [artist['name'] for artist in items['artists']]) track_album = items['album']['name'] track_year = items['album']['release_date'][:4] album_total = items['album']['total_tracks'] track_num = items['track_number'] if len(items['album']['images']) > 0: cover = items['album']['images'][0]['url'] else: cover = None if len(client.artist( artist_id=items['artists'][0]['uri'])['genres']) > 0: genre = client.artist( artist_id=items['artists'][0]['uri'])['genres'][0] else: genre = "" list_of_songs.append({ "name": track_name, "artist": track_artist, "album": track_album, "year": track_year, "num_tracks": album_total, "num": track_num, "playlist_num": offset + 1, "cover": cover, "genre": genre }) return list_of_songs