diff --git a/zspotify/album.py b/zspotify/album.py index 5325caa9..4392f5f5 100644 --- a/zspotify/album.py +++ b/zspotify/album.py @@ -47,12 +47,9 @@ def get_artist_albums(artist_id): def download_album(album): """ Downloads songs from an album """ artist, album_name = get_album_name(album) - artist_fixed = fix_filename(artist) - album_name_fixed = fix_filename(album_name) tracks = get_album_tracks(album) for n, track in tqdm(enumerate(tracks, start=1), unit_scale=True, unit='Song', total=len(tracks)): - download_track(track[ID], f'{artist_fixed}/{album_name_fixed}', - prefix=True, prefix_value=str(n), disable_progressbar=True) + download_track('album', track[ID], extra_keys={'album_num': str(n).zfill(2), 'artist': artist, 'album': album}, disable_progressbar=True) def download_artist_albums(artist): diff --git a/zspotify/app.py b/zspotify/app.py index 7b6bbffb..6cc67194 100644 --- a/zspotify/app.py +++ b/zspotify/app.py @@ -54,7 +54,7 @@ def client(args) -> None: print( '### SKIPPING: SONG DOES NOT EXIST ON SPOTIFY ANYMORE ###') else: - download_track(song[TRACK][ID], 'Liked Songs/') + download_track('liked', song[TRACK][ID]) print('\n') if args.search_spotify: @@ -75,7 +75,7 @@ def download_from_urls(urls: list[str]) -> bool: if track_id is not None: download = True - download_track(track_id) + download_track('single', track_id) elif artist_id is not None: download = True download_artist_albums(artist_id) @@ -87,8 +87,7 @@ def download_from_urls(urls: list[str]) -> bool: playlist_songs = get_playlist_songs(playlist_id) name, _ = get_playlist_info(playlist_id) for song in playlist_songs: - download_track(song[TRACK][ID], - fix_filename(name) + '/') + download_track('playlist', song[TRACK][ID], extra_keys={'playlist': name}) print('\n') elif episode_id is not None: download = True @@ -273,7 +272,7 @@ def search(search_term): print_pos = dics.index(dic) + 1 if print_pos == position: if dic['type'] == TRACK: - download_track(dic[ID]) + download_track('single', dic[ID]) elif dic['type'] == ALBUM: download_album(dic[ID]) elif dic['type'] == ARTIST: diff --git a/zspotify/config.py b/zspotify/config.py index ca10910d..4e2e081c 100644 --- a/zspotify/config.py +++ b/zspotify/config.py @@ -20,6 +20,7 @@ LANGUAGE = 'LANGUAGE' BITRATE = 'BITRATE' SONG_ARCHIVE = 'SONG_ARCHIVE' CREDENTIALS_LOCATION = 'CREDENTIALS_LOCATION' +OUTPUT = 'OUTPUT' CONFIG_VALUES = { ROOT_PATH: { 'default': '../ZSpotify Music/', 'type': str, 'arg': '--root-path' }, @@ -37,8 +38,14 @@ CONFIG_VALUES = { BITRATE: { 'default': '', 'type': str, 'arg': '--bitrate' }, SONG_ARCHIVE: { 'default': '.song_archive', 'type': str, 'arg': '--song-archive' }, CREDENTIALS_LOCATION: { 'default': 'credentials.json', 'type': str, 'arg': '--credentials-location' }, + OUTPUT: { 'default': '', 'type': str, 'arg': '--output' }, } +OUTPUT_DEFAULT_PLAYLIST = '{playlist}/{artist} - {song_name}.{ext}' +OUTPUT_DEFAULT_PLAYLIST_EXT = '{playlist}/{playlist_num} - {artist} - {song_name}.{ext}' +OUTPUT_DEFAULT_LIKED_SONGS = 'Liked Songs/{artist} - {song_name}.{ext}' +OUTPUT_DEFAULT_SINGLE = '{artist} - {song_name}.{ext}' +OUTPUT_DEFAULT_ALBUM = '{artist}/{album}/{album_num} - {artist} - {song_name}.{ext}' class Config: Values = {} @@ -165,3 +172,20 @@ class Config: @classmethod def get_credentials_location(cls) -> str: return cls.get(CREDENTIALS_LOCATION) + + @classmethod + def get_output(cls, mode: str) -> str: + v = cls.get(OUTPUT) + if v: + return v + if mode == 'playlist': + return OUTPUT_DEFAULT_PLAYLIST + if mode == 'extplaylist': + return OUTPUT_DEFAULT_PLAYLIST_EXT + if mode == 'liked': + return OUTPUT_DEFAULT_LIKED_SONGS + if mode == 'single': + return OUTPUT_DEFAULT_SINGLE + if mode == 'album': + return OUTPUT_DEFAULT_ALBUM + raise ValueError() diff --git a/zspotify/playlist.py b/zspotify/playlist.py index 4a2b8916..3f169340 100644 --- a/zspotify/playlist.py +++ b/zspotify/playlist.py @@ -54,8 +54,7 @@ def download_playlist(playlist): p_bar = tqdm(playlist_songs, unit='song', total=len(playlist_songs), unit_scale=True) enum = 1 for song in p_bar: - download_track(song[TRACK][ID], fix_filename(playlist[NAME].strip()) + '/', - prefix=True, prefix_value=str(enum) ,disable_progressbar=True) + download_track('extplaylist', song[TRACK][ID], extra_keys={'playlist': playlist, 'playlist_num': str(enum).zfill(2)}, disable_progressbar=True) p_bar.set_description(song[TRACK][NAME]) enum += 1 diff --git a/zspotify/track.py b/zspotify/track.py index 884d1329..ae5e54b3 100644 --- a/zspotify/track.py +++ b/zspotify/track.py @@ -69,39 +69,45 @@ def get_song_duration(song_id: str) -> float: return duration # noinspection PyBroadException -def download_track(track_id: str, extra_paths='', prefix=False, prefix_value='', disable_progressbar=False) -> None: +def download_track(mode: str, track_id: str, extra_keys={}, disable_progressbar=False) -> None: """ Downloads raw song audio from Spotify """ try: + output_template = ZSpotify.CONFIG.get_output(mode) + (artists, album_name, name, image_url, release_year, disc_number, track_number, scraped_song_id, is_playable, duration_ms) = get_song_info(track_id) - if ZSpotify.CONFIG.get_split_album_discs(): - download_directory = os.path.join(os.path.dirname( - __file__), ZSpotify.CONFIG.get_root_path(), extra_paths, f'Disc {disc_number}') - else: - download_directory = os.path.join(os.path.dirname( - __file__), ZSpotify.CONFIG.get_root_path(), extra_paths) - song_name = fix_filename(artists[0]) + ' - ' + fix_filename(name) - if prefix: - song_name = f'{prefix_value.zfill(2)} - {song_name}' if prefix_value.isdigit( - ) else f'{prefix_value} - {song_name}' - filename = os.path.join( - download_directory, f'{song_name}.{EXT_MAP.get(ZSpotify.CONFIG.get_download_format().lower())}') + for k in extra_keys: + output_template = output_template.replace("{"+k+"}", fix_filename(extra_keys[k])) + + output_template = output_template.replace("{artist}", fix_filename(artists[0])) + output_template = output_template.replace("{album}", fix_filename(album_name)) + output_template = output_template.replace("{song_name}", fix_filename(name)) + output_template = output_template.replace("{release_year}", fix_filename(release_year)) + output_template = output_template.replace("{disc_number}", fix_filename(disc_number)) + output_template = output_template.replace("{track_number}", fix_filename(track_number)) + output_template = output_template.replace("{id}", fix_filename(scraped_song_id)) + output_template = output_template.replace("{track_id}", fix_filename(track_id)) + output_template = output_template.replace("{ext}", EXT_MAP.get(ZSpotify.CONFIG.get_download_format().lower())) + + filename = os.path.join(os.path.dirname(__file__), ZSpotify.CONFIG.get_root_path(), output_template) + filedir = os.path.dirname(filename) check_name = os.path.isfile(filename) and os.path.getsize(filename) - check_id = scraped_song_id in get_directory_song_ids(download_directory) + check_id = scraped_song_id in get_directory_song_ids(filedir) check_all_time = scraped_song_id in get_previously_downloaded() # a song with the same name is installed if not check_id and check_name: - c = len([file for file in os.listdir(download_directory) - if re.search(f'^{song_name}_', file)]) + 1 + c = len([file for file in os.listdir(filedir) if re.search(f'^{filename}_', str(file))]) + 1 - filename = os.path.join( - download_directory, f'{song_name}_{c}.{EXT_MAP.get(ZSpotify.CONFIG.get_download_format())}') + fname = os.path.splitext(os.path.basename(filename))[0] + ext = os.path.splitext(os.path.basename(filename))[1] + + filename = os.path.join(filedir, f'{fname}_{c}{ext}') except Exception as e: @@ -127,7 +133,7 @@ def download_track(track_id: str, extra_paths='', prefix=False, prefix_value='', track_id = TrackId.from_base62(track_id) stream = ZSpotify.get_content_stream( track_id, ZSpotify.DOWNLOAD_QUALITY) - create_download_directory(download_directory) + create_download_directory(filedir) total_size = stream.input_stream.size with open(filename, 'wb') as file, tqdm( @@ -155,7 +161,7 @@ def download_track(track_id: str, extra_paths='', prefix=False, prefix_value='', add_to_archive(scraped_song_id, artists[0], name) # add song id to download directory's .song_ids file if not check_id: - add_to_directory_song_ids(download_directory, scraped_song_id) + add_to_directory_song_ids(filedir, scraped_song_id) if not ZSpotify.CONFIG.get_anti_ban_wait_time(): time.sleep(ZSpotify.CONFIG.get_anti_ban_wait_time()) diff --git a/zspotify/utils.py b/zspotify/utils.py index 84bd9354..d7627d95 100644 --- a/zspotify/utils.py +++ b/zspotify/utils.py @@ -256,4 +256,4 @@ def fix_filename(name): >>> all('_' == fix_filename(chr(i)) for i in list(range(32))) True """ - return re.sub(r'[/\\:|<>"?*\0-\x1f]|^(AUX|COM[1-9]|CON|LPT[1-9]|NUL|PRN)(?![^.])|^\s|[\s.]$', "_", name, flags=re.IGNORECASE) + return re.sub(r'[/\\:|<>"?*\0-\x1f]|^(AUX|COM[1-9]|CON|LPT[1-9]|NUL|PRN)(?![^.])|^\s|[\s.]$', "_", str(name), flags=re.IGNORECASE)