mirror of
				https://github.com/THIS-IS-NOT-A-BACKUP/zspotify.git
				synced 2025-11-03 21:10:34 +00:00 
			
		
		
		
	Added --output argument for output templating
This commit is contained in:
		
							parent
							
								
									ad43153d4c
								
							
						
					
					
						commit
						543567079b
					
				@ -47,12 +47,9 @@ def get_artist_albums(artist_id):
 | 
				
			|||||||
def download_album(album):
 | 
					def download_album(album):
 | 
				
			||||||
    """ Downloads songs from an album """
 | 
					    """ Downloads songs from an album """
 | 
				
			||||||
    artist, album_name = get_album_name(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)
 | 
					    tracks = get_album_tracks(album)
 | 
				
			||||||
    for n, track in tqdm(enumerate(tracks, start=1), unit_scale=True, unit='Song', total=len(tracks)):
 | 
					    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}',
 | 
					        download_track('album', track[ID], extra_keys={'album_num': str(n).zfill(2), 'artist': artist, 'album': album}, disable_progressbar=True)
 | 
				
			||||||
                       prefix=True, prefix_value=str(n), disable_progressbar=True)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def download_artist_albums(artist):
 | 
					def download_artist_albums(artist):
 | 
				
			||||||
 | 
				
			|||||||
@ -54,7 +54,7 @@ def client(args) -> None:
 | 
				
			|||||||
                print(
 | 
					                print(
 | 
				
			||||||
                    '###   SKIPPING:  SONG DOES NOT EXIST ON SPOTIFY ANYMORE   ###')
 | 
					                    '###   SKIPPING:  SONG DOES NOT EXIST ON SPOTIFY ANYMORE   ###')
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                download_track(song[TRACK][ID], 'Liked Songs/')
 | 
					                download_track('liked', song[TRACK][ID])
 | 
				
			||||||
            print('\n')
 | 
					            print('\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if args.search_spotify:
 | 
					    if args.search_spotify:
 | 
				
			||||||
@ -75,7 +75,7 @@ def download_from_urls(urls: list[str]) -> bool:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if track_id is not None:
 | 
					        if track_id is not None:
 | 
				
			||||||
            download = True
 | 
					            download = True
 | 
				
			||||||
            download_track(track_id)
 | 
					            download_track('single', track_id)
 | 
				
			||||||
        elif artist_id is not None:
 | 
					        elif artist_id is not None:
 | 
				
			||||||
            download = True
 | 
					            download = True
 | 
				
			||||||
            download_artist_albums(artist_id)
 | 
					            download_artist_albums(artist_id)
 | 
				
			||||||
@ -87,8 +87,7 @@ def download_from_urls(urls: list[str]) -> bool:
 | 
				
			|||||||
            playlist_songs = get_playlist_songs(playlist_id)
 | 
					            playlist_songs = get_playlist_songs(playlist_id)
 | 
				
			||||||
            name, _ = get_playlist_info(playlist_id)
 | 
					            name, _ = get_playlist_info(playlist_id)
 | 
				
			||||||
            for song in playlist_songs:
 | 
					            for song in playlist_songs:
 | 
				
			||||||
                download_track(song[TRACK][ID],
 | 
					                download_track('playlist', song[TRACK][ID], extra_keys={'playlist': name})
 | 
				
			||||||
                               fix_filename(name) + '/')
 | 
					 | 
				
			||||||
                print('\n')
 | 
					                print('\n')
 | 
				
			||||||
        elif episode_id is not None:
 | 
					        elif episode_id is not None:
 | 
				
			||||||
            download = True
 | 
					            download = True
 | 
				
			||||||
@ -273,7 +272,7 @@ def search(search_term):
 | 
				
			|||||||
                print_pos = dics.index(dic) + 1
 | 
					                print_pos = dics.index(dic) + 1
 | 
				
			||||||
                if print_pos == position:
 | 
					                if print_pos == position:
 | 
				
			||||||
                    if dic['type'] == TRACK:
 | 
					                    if dic['type'] == TRACK:
 | 
				
			||||||
                        download_track(dic[ID])
 | 
					                        download_track('single', dic[ID])
 | 
				
			||||||
                    elif dic['type'] == ALBUM:
 | 
					                    elif dic['type'] == ALBUM:
 | 
				
			||||||
                        download_album(dic[ID])
 | 
					                        download_album(dic[ID])
 | 
				
			||||||
                    elif dic['type'] == ARTIST:
 | 
					                    elif dic['type'] == ARTIST:
 | 
				
			||||||
 | 
				
			|||||||
@ -20,6 +20,7 @@ LANGUAGE = 'LANGUAGE'
 | 
				
			|||||||
BITRATE = 'BITRATE'
 | 
					BITRATE = 'BITRATE'
 | 
				
			||||||
SONG_ARCHIVE = 'SONG_ARCHIVE'
 | 
					SONG_ARCHIVE = 'SONG_ARCHIVE'
 | 
				
			||||||
CREDENTIALS_LOCATION = 'CREDENTIALS_LOCATION'
 | 
					CREDENTIALS_LOCATION = 'CREDENTIALS_LOCATION'
 | 
				
			||||||
 | 
					OUTPUT = 'OUTPUT'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG_VALUES = {
 | 
					CONFIG_VALUES = {
 | 
				
			||||||
    ROOT_PATH:                  { 'default': '../ZSpotify Music/',    'type': str,  'arg': '--root-path'                  },
 | 
					    ROOT_PATH:                  { 'default': '../ZSpotify Music/',    'type': str,  'arg': '--root-path'                  },
 | 
				
			||||||
@ -37,8 +38,14 @@ CONFIG_VALUES = {
 | 
				
			|||||||
    BITRATE:                    { 'default': '',                      'type': str,  'arg': '--bitrate'                    },
 | 
					    BITRATE:                    { 'default': '',                      'type': str,  'arg': '--bitrate'                    },
 | 
				
			||||||
    SONG_ARCHIVE:               { 'default': '.song_archive',         'type': str,  'arg': '--song-archive'               },
 | 
					    SONG_ARCHIVE:               { 'default': '.song_archive',         'type': str,  'arg': '--song-archive'               },
 | 
				
			||||||
    CREDENTIALS_LOCATION:       { 'default': 'credentials.json',      'type': str,  'arg': '--credentials-location'       },
 | 
					    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:
 | 
					class Config:
 | 
				
			||||||
    Values = {}
 | 
					    Values = {}
 | 
				
			||||||
@ -165,3 +172,20 @@ class Config:
 | 
				
			|||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def get_credentials_location(cls) -> str:
 | 
					    def get_credentials_location(cls) -> str:
 | 
				
			||||||
        return cls.get(CREDENTIALS_LOCATION)
 | 
					        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()
 | 
				
			||||||
 | 
				
			|||||||
@ -54,8 +54,7 @@ def download_playlist(playlist):
 | 
				
			|||||||
    p_bar = tqdm(playlist_songs, unit='song', total=len(playlist_songs), unit_scale=True)
 | 
					    p_bar = tqdm(playlist_songs, unit='song', total=len(playlist_songs), unit_scale=True)
 | 
				
			||||||
    enum = 1
 | 
					    enum = 1
 | 
				
			||||||
    for song in p_bar:
 | 
					    for song in p_bar:
 | 
				
			||||||
        download_track(song[TRACK][ID], fix_filename(playlist[NAME].strip()) + '/',
 | 
					        download_track('extplaylist', song[TRACK][ID], extra_keys={'playlist': playlist, 'playlist_num': str(enum).zfill(2)}, disable_progressbar=True)
 | 
				
			||||||
                       prefix=True, prefix_value=str(enum) ,disable_progressbar=True)
 | 
					 | 
				
			||||||
        p_bar.set_description(song[TRACK][NAME])
 | 
					        p_bar.set_description(song[TRACK][NAME])
 | 
				
			||||||
        enum += 1
 | 
					        enum += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -69,39 +69,45 @@ def get_song_duration(song_id: str) -> float:
 | 
				
			|||||||
    return duration
 | 
					    return duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# noinspection PyBroadException
 | 
					# 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 """
 | 
					    """ Downloads raw song audio from Spotify """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
 | 
					        output_template = ZSpotify.CONFIG.get_output(mode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        (artists, album_name, name, image_url, release_year, disc_number,
 | 
					        (artists, album_name, name, image_url, release_year, disc_number,
 | 
				
			||||||
         track_number, scraped_song_id, is_playable, duration_ms) = get_song_info(track_id)
 | 
					         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)
 | 
					        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(
 | 
					        for k in extra_keys:
 | 
				
			||||||
            download_directory, f'{song_name}.{EXT_MAP.get(ZSpotify.CONFIG.get_download_format().lower())}')
 | 
					            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_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()
 | 
					        check_all_time = scraped_song_id in get_previously_downloaded()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # a song with the same name is installed
 | 
					        # a song with the same name is installed
 | 
				
			||||||
        if not check_id and check_name:
 | 
					        if not check_id and check_name:
 | 
				
			||||||
            c = len([file for file in os.listdir(download_directory)
 | 
					            c = len([file for file in os.listdir(filedir) if re.search(f'^{filename}_', str(file))]) + 1
 | 
				
			||||||
                     if re.search(f'^{song_name}_', file)]) + 1
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            filename = os.path.join(
 | 
					            fname = os.path.splitext(os.path.basename(filename))[0]
 | 
				
			||||||
                download_directory, f'{song_name}_{c}.{EXT_MAP.get(ZSpotify.CONFIG.get_download_format())}')
 | 
					            ext = os.path.splitext(os.path.basename(filename))[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            filename = os.path.join(filedir, f'{fname}_{c}{ext}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    except Exception as e:
 | 
					    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)
 | 
					                    track_id = TrackId.from_base62(track_id)
 | 
				
			||||||
                    stream = ZSpotify.get_content_stream(
 | 
					                    stream = ZSpotify.get_content_stream(
 | 
				
			||||||
                        track_id, ZSpotify.DOWNLOAD_QUALITY)
 | 
					                        track_id, ZSpotify.DOWNLOAD_QUALITY)
 | 
				
			||||||
                    create_download_directory(download_directory)
 | 
					                    create_download_directory(filedir)
 | 
				
			||||||
                    total_size = stream.input_stream.size
 | 
					                    total_size = stream.input_stream.size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    with open(filename, 'wb') as file, tqdm(
 | 
					                    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_to_archive(scraped_song_id, artists[0], name)
 | 
				
			||||||
                    # add song id to download directory's .song_ids file
 | 
					                    # add song id to download directory's .song_ids file
 | 
				
			||||||
                    if not check_id:
 | 
					                    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():
 | 
					                    if not ZSpotify.CONFIG.get_anti_ban_wait_time():
 | 
				
			||||||
                        time.sleep(ZSpotify.CONFIG.get_anti_ban_wait_time())
 | 
					                        time.sleep(ZSpotify.CONFIG.get_anti_ban_wait_time())
 | 
				
			||||||
 | 
				
			|||||||
@ -256,4 +256,4 @@ def fix_filename(name):
 | 
				
			|||||||
    >>> all('_' == fix_filename(chr(i)) for i in list(range(32)))
 | 
					    >>> all('_' == fix_filename(chr(i)) for i in list(range(32)))
 | 
				
			||||||
    True
 | 
					    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)
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user