diff --git a/zspotify/app.py b/zspotify/app.py index 772d9a3..c30bceb 100644 --- a/zspotify/app.py +++ b/zspotify/app.py @@ -19,8 +19,7 @@ def client(args) -> None: """ Connects to spotify to perform query's and get songs to download """ ZSpotify(args) - if ZSpotify.CONFIG.get_print_splash(): - Printer.print(PrintChannel.SPLASH, splash()) + Printer.print(PrintChannel.SPLASH, splash()) if ZSpotify.check_premium(): Printer.print(PrintChannel.SPLASH, '[ DETECTED PREMIUM ACCOUNT - USING VERY_HIGH QUALITY ]\n\n') diff --git a/zspotify/config.py b/zspotify/config.py index 00628ae..bf795a2 100644 --- a/zspotify/config.py +++ b/zspotify/config.py @@ -70,6 +70,7 @@ 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 = {} @@ -206,15 +207,11 @@ class Config: return os.path.join(cls.get_root_path(), cls.get(TEMP_DOWNLOAD_DIR)) @classmethod - def get_allGenres(cls) -> bool: + def get_all_genres(cls) -> bool: return cls.get(MD_ALLGENRES) @classmethod - def get_print_splash(cls) -> bool: - return cls.get(PRINT_SPLASH) - - @classmethod - def get_allGenresDelimiter(cls) -> bool: + def get_all_genres_delimiter(cls) -> bool: return cls.get(MD_GENREDELIMITER) @classmethod @@ -248,3 +245,7 @@ class Config: return os.path.join(split[0], 'Disc {disc_number}', split[0]) return OUTPUT_DEFAULT_ALBUM raise ValueError() + + @classmethod + def get_retry_attempts(cls) -> int: + return cls.get(RETRY_ATTEMPTS) \ No newline at end of file diff --git a/zspotify/const.py b/zspotify/const.py index 3e76f8a..59c7d43 100644 --- a/zspotify/const.py +++ b/zspotify/const.py @@ -34,6 +34,8 @@ ITEMS = 'items' NAME = 'name' +HREF = 'href' + ID = 'id' URL = 'url' diff --git a/zspotify/track.py b/zspotify/track.py index b925bc8..e4f4b47 100644 --- a/zspotify/track.py +++ b/zspotify/track.py @@ -9,7 +9,7 @@ from librespot.metadata import TrackId from ffmpy import FFmpeg from const import TRACKS, ALBUM, GENRES, NAME, ITEMS, DISC_NUMBER, TRACK_NUMBER, IS_PLAYABLE, ARTISTS, IMAGES, URL, \ - RELEASE_DATE, ID, TRACKS_URL, SAVED_TRACKS_URL, TRACK_STATS_URL, CODEC_MAP, EXT_MAP, DURATION_MS + RELEASE_DATE, ID, TRACKS_URL, SAVED_TRACKS_URL, TRACK_STATS_URL, CODEC_MAP, EXT_MAP, DURATION_MS, HREF from termoutput import Printer, PrintChannel from utils import fix_filename, set_audio_tags, set_music_thumbnail, create_download_directory, \ get_directory_song_ids, add_to_directory_song_ids, get_previously_downloaded, add_to_archive, fmt_seconds @@ -35,7 +35,7 @@ def get_saved_tracks() -> list: return songs -def get_song_info(song_id) -> Tuple[List[str], List[str], str, str, Any, Any, Any, Any, Any, Any, int]: +def get_song_info(song_id) -> Tuple[List[str], List[Any], str, str, Any, Any, Any, Any, Any, Any, int]: """ Retrieves metadata for downloaded songs """ with Loader(PrintChannel.PROGRESS_INFO, "Fetching track information..."): (raw, info) = ZSpotify.invoke_url(f'{TRACKS_URL}?ids={song_id}&market=from_token') @@ -45,21 +45,8 @@ def get_song_info(song_id) -> Tuple[List[str], List[str], str, str, Any, Any, An try: artists = [] - genres = [] for data in info[TRACKS][0][ARTISTS]: artists.append(data[NAME]) - # query artist genres via href, which will be the api url - with Loader(PrintChannel.PROGRESS_INFO, "Fetching artist information..."): - (raw, artistInfo) = ZSpotify.invoke_url(f'{data["href"]}') - if ZSpotify.CONFIG.get_allGenres() and len(artistInfo[GENRES]) > 0: - for genre in artistInfo[GENRES]: - genres.append(genre) - elif len(artistInfo[GENRES]) > 0: - genres.append(artistInfo[GENRES][0]) - - if len(genres) == 0: - Printer.print(PrintChannel.WARNINGS, '### No Genres found for song ' + info[TRACKS][0][NAME]) - genres.append('') album_name = info[TRACKS][0][ALBUM][NAME] name = info[TRACKS][0][NAME] @@ -71,11 +58,34 @@ def get_song_info(song_id) -> Tuple[List[str], List[str], str, str, Any, Any, An is_playable = info[TRACKS][0][IS_PLAYABLE] duration_ms = info[TRACKS][0][DURATION_MS] - return artists, genres, album_name, name, image_url, release_year, disc_number, track_number, scraped_song_id, is_playable, duration_ms + return artists, info[TRACKS][0][ARTISTS], album_name, name, image_url, release_year, disc_number, track_number, scraped_song_id, is_playable, duration_ms except Exception as e: raise ValueError(f'Failed to parse TRACKS_URL response: {str(e)}\n{raw}') +def get_song_genres(rawartists: List[str], track_name: str) -> List[str]: + + try: + genres = [] + for data in rawartists: + # query artist genres via href, which will be the api url + with Loader(PrintChannel.PROGRESS_INFO, "Fetching artist information..."): + (raw, artistInfo) = ZSpotify.invoke_url(f'{data[HREF]}') + if ZSpotify.CONFIG.get_all_genres() and len(artistInfo[GENRES]) > 0: + for genre in artistInfo[GENRES]: + genres.append(genre) + elif len(artistInfo[GENRES]) > 0: + genres.append(artistInfo[GENRES][0]) + + if len(genres) == 0: + Printer.print(PrintChannel.WARNINGS, '### No Genres found for song ' + track_name) + genres.append('') + + return genres + except Exception as e: + raise ValueError(f'Failed to parse GENRES response: {str(e)}\n{raw}') + + def get_song_duration(song_id: str) -> float: """ Retrieves duration of song in second as is on spotify """ @@ -106,7 +116,7 @@ def download_track(mode: str, track_id: str, extra_keys=None, disable_progressba try: output_template = ZSpotify.CONFIG.get_output(mode) - (artists, genres, album_name, name, image_url, release_year, disc_number, + (artists, raw_artists, album_name, name, image_url, release_year, disc_number, track_number, scraped_song_id, is_playable, duration_ms) = get_song_info(track_id) song_name = fix_filename(artists[0]) + ' - ' + fix_filename(name) @@ -201,6 +211,8 @@ def download_track(mode: str, track_id: str, extra_keys=None, disable_progressba time_downloaded = time.time() + genres = get_song_genres(raw_artists, name) + convert_audio_format(filename_temp) set_audio_tags(filename_temp, artists, genres, name, album_name, release_year, disc_number, track_number) set_music_thumbnail(filename_temp, image_url) diff --git a/zspotify/utils.py b/zspotify/utils.py index ef9a7b8..47b0392 100644 --- a/zspotify/utils.py +++ b/zspotify/utils.py @@ -129,7 +129,7 @@ def set_audio_tags(filename, artists, genres, name, album_name, release_year, di tags = music_tag.load_file(filename) tags[ALBUMARTIST] = artists[0] tags[ARTIST] = conv_artist_format(artists) - tags[GENRE] = genres[0] if not ZSpotify.CONFIG.get_allGenres() else ZSpotify.CONFIG.get_allGenresDelimiter().join(genres) + tags[GENRE] = genres[0] if not ZSpotify.CONFIG.get_all_genres() else ZSpotify.CONFIG.get_all_genres_delimiter().join(genres) tags[TRACKTITLE] = name tags[ALBUM] = album_name tags[YEAR] = release_year @@ -273,9 +273,9 @@ def fmt_seconds(secs: float) -> str: h = math.floor(val) if h == 0 and m == 0 and s == 0: - return "0" + return "0s" elif h == 0 and m == 0: - return f'{s}'.zfill(2) + return f'{s}s'.zfill(2) elif h == 0: return f'{m}'.zfill(2) + ':' + f'{s}'.zfill(2) else: diff --git a/zspotify/zspotify.py b/zspotify/zspotify.py index 37afdab..75b022c 100644 --- a/zspotify/zspotify.py +++ b/zspotify/zspotify.py @@ -82,7 +82,7 @@ class ZSpotify: responsejson = response.json() if 'error' in responsejson: - if tryCount < (cls.CONFIG.retry_attemps - 1): + if tryCount < (cls.CONFIG.get_retry_attempts() - 1): Printer.print(PrintChannel.WARNINGS, f"Spotify API Error (try {tryCount + 1}) ({responsejson['error']['status']}): {responsejson['error']['message']}") time.sleep(5) return cls.invoke_url(url, tryCount + 1)