From 7d81eb5cc6fef6b789bc4f76b84b627d0dafb0c5 Mon Sep 17 00:00:00 2001 From: yiannisha Date: Sat, 23 Oct 2021 16:06:34 +0300 Subject: [PATCH 01/31] Rewrote the search function It still works in the same way and no other functions need to be changed. Just changed it to store needed data about each track/album/playlist in a dictionary so tracking it in the end is simpler. --- zspotify.py | 169 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 112 insertions(+), 57 deletions(-) diff --git a/zspotify.py b/zspotify.py index 25538e15..d031ea9c 100755 --- a/zspotify.py +++ b/zspotify.py @@ -351,88 +351,143 @@ def search(search_term): """ Searches Spotify's API for relevant data """ token = SESSION.tokens().get("user-read-email") + params = { + "limit" : "10", + "q" : search_term, + "type" : ["track","album","playlist"] + } + + # ADD BLOCK FOR READING OPTIONS AND CLEAN SEARCH_TERM + resp = requests.get( "https://api.spotify.com/v1/search", { - "limit": "10", + "limit": params["limit"], "offset": "0", - "q": search_term, - "type": "track,album,playlist" + "q": params["q"], + "type": ",".join(params["type"]) }, headers={"Authorization": "Bearer %s" % token}, ) # print(resp.json()) - i = 1 - tracks = resp.json()["tracks"]["items"] - if len(tracks) > 0: - print("### TRACKS ###") - for track in tracks: - if track["explicit"]: - explicit = "[E]" - else: - explicit = "" - print("%d, %s %s | %s" % ( - i, - track["name"], - explicit, - ",".join([artist["name"] for artist in track["artists"]]), - )) - i += 1 - total_tracks = i - 1 + enum = 1 + dics = [] + + # add all returned tracks to dics + if "track" in params["type"]: + tracks = resp.json()["tracks"]["items"] + if len(tracks) > 0: + print("### TRACKS ###") + for track in tracks: + if track["explicit"]: + explicit = "[E]" + else: + explicit = "" + # collect needed data + dic = { + "id" : track["id"], + "name" : track["name"], + "artists/owner" : [artist["name"] for artist in track["artists"]], + "type" : "track", + } + dics.append(dic) + + print("{}, {} {} | {}".format( + enum, + dic["name"], + explicit, + ",".join(dic["artists/owner"]), + )) + enum += 1 + total_tracks = enum - 1 print("\n") + # free up memory + del tracks else: total_tracks = 0 - albums = resp.json()["albums"]["items"] - if len(albums) > 0: - print("### ALBUMS ###") - for album in albums: - print("%d, %s | %s" % ( - i, - album["name"], - ",".join([artist["name"] for artist in album["artists"]]), - )) - i += 1 - total_albums = i - total_tracks - 1 + if "album" in params["type"]: + albums = resp.json()["albums"]["items"] + if len(albums) > 0: + print("### ALBUMS ###") + for album in albums: + # collect needed data + dic = { + "id" : album["id"], + "name" : album["name"], + "artists/owner" : [artist["name"] for artist in album["artists"]], + "type" : "album", + } + dics.append(dic) + + print("{}, {} | {}".format( + enum, + dic["name"], + ",".join(dic["artists/owner"]), + )) + enum += 1 + total_albums = enum - total_tracks - 1 print("\n") + # free up memory + del albums else: total_albums = 0 - playlists = resp.json()["playlists"]["items"] - print("### PLAYLISTS ###") - for playlist in playlists: - print("%d, %s | %s" % ( - i, - playlist["name"], - playlist['owner']['display_name'], - )) - i += 1 - print("\n") + if "playlist" in params["type"]: + playlists = resp.json()["playlists"]["items"] + print("### PLAYLISTS ###") + if len(playlists) > 0: + for playlist in playlists: + # collect needed data + dic = { + "id" : playlist["id"], + "name" : playlist["name"], + "artists/owner" : [playlist["owner"]["display_name"]], + "type" : "playlist", + } + dics.append(dic) - if len(tracks) + len(albums) + len(playlists) == 0: + print("{}, {} | {}".format( + enum, + dic["name"], + ",".join(dic['artists/owner']), + )) + + enum += 1 + total_playlists = enum - total_tracks - total_albums - 1 + print("\n") + # free up memory + del playlists + else: + total_playlists = 0 + + if total_tracks + total_albums + total_playlists == 0: print("NO RESULTS FOUND - EXITING...") else: selection = str(input("SELECT ITEM(S) BY ID: ")) inputs = split_input(selection) for pos in inputs: position = int(pos) - if position <= total_tracks: - track_id = tracks[position - 1]["id"] - download_track(track_id) - elif position <= total_albums + total_tracks: - download_album(albums[position - total_tracks - 1]["id"]) - else: - playlist_choice = playlists[position - - total_tracks - total_albums - 1] - playlist_songs = get_playlist_songs( - token, playlist_choice['id']) - for song in playlist_songs: - if song['track']['id'] is not None: - download_track(song['track']['id'], sanitize_data( - playlist_choice['name'].strip()) + "/") - print("\n") + for dic in dics: + # find dictionary + print_pos = dics.index(dic) + 1 + if print_pos == position: + # if request is for track + if dic["type"] == "track": + download_track(dic["id"]) + # if request is for album + if dic["type"] == "album": + download_album(dic["id"]) + # if request is for playlist + if dic["type"] == "playlist": + playlist_songs = get_playlist_songs(token, dic["id"]) + for song in playlist_songs: + if song["track"]["id"] is not None: + download_track(song["track"]["id"], + sanitize_data(dic["name"].strip()) + "/") + print("\n") def get_song_info(song_id): From e9ba63b9d0da14c5f125f852baeebc548bc00dfc Mon Sep 17 00:00:00 2001 From: yiannisha Date: Sat, 23 Oct 2021 17:15:41 +0300 Subject: [PATCH 02/31] Added argument parsing in search function Added a block for parsing arguments passed with options -l -limit -t -type in the search() function. Arguments are passed inside search_term. They work as follows: * -l -limit : sets the limit of results to that number and raises a ValueError if that number exceeds 50. Default is 10. * -t -type : sets the type that is requested from the API about the search query. Raises a ValueError if an arguments passed is different than track, album, playlist. Default is all three. Example: Enter search or URL: -l 30 -t track album This will result with 30 tracks and 30 albums associated with query. Options can be passed in any order but the query must be first. --- zspotify.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/zspotify.py b/zspotify.py index d031ea9c..a58790f8 100755 --- a/zspotify.py +++ b/zspotify.py @@ -354,10 +354,55 @@ def search(search_term): params = { "limit" : "10", "q" : search_term, - "type" : ["track","album","playlist"] + "type" : set(), } - # ADD BLOCK FOR READING OPTIONS AND CLEAN SEARCH_TERM + # Block for parsing passed arguments + splits = search_term.split() + for split in splits: + index = splits.index(split) + + if split[0] == "-" and len(split) > 1: + if len(splits)-1 == index: + raise IndexError("No parameters passed after option: {}\n". + format(split)) + + if split == "-l" or split == "-limit": + try: + int(splits[index+1]) + except ValueError: + raise ValueError("Paramater passed after {} option must be an integer.\n". + format(split)) + if int(splits[index+1]) > 50: + raise ValueError("Invalid limit passed. Max is 50.\n") + params["limit"] = splits[index+1] + + + if split == "-t" or split == "-type": + + allowed_types = ["track", "playlist", "album"] + for i in range(index+1, len(splits)): + if splits[i][0] == "-": + break + + if splits[i] not in allowed_types: + raise ValueError("Parameters passed after {} option must be from this list:\n{}". + format(split, '\n'.join(allowed_types))) + + params["type"].add(splits[i]) + + if len(params["type"]) == 0: + params["type"] = {"track", "album", "playlist"} + + # Clean search term + search_term_list = [] + for split in splits: + if split[0] == "-": + break + search_term_list.append(split) + if not search_term_list: + raise ValueError("Invalid query.") + params["q"] = ' '.join(search_term_list) resp = requests.get( "https://api.spotify.com/v1/search", @@ -437,8 +482,8 @@ def search(search_term): if "playlist" in params["type"]: playlists = resp.json()["playlists"]["items"] - print("### PLAYLISTS ###") if len(playlists) > 0: + print("### PLAYLISTS ###") for playlist in playlists: # collect needed data dic = { From efed684e593fe04b470fe994818bb6dd0f2beb27 Mon Sep 17 00:00:00 2001 From: yiannisha Date: Sat, 23 Oct 2021 19:13:04 +0300 Subject: [PATCH 03/31] Fixed minor bugs --- zspotify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zspotify.py b/zspotify.py index 56339034..3c04c673 100755 --- a/zspotify.py +++ b/zspotify.py @@ -515,7 +515,7 @@ def search(search_term): # pylint: disable=too-many-locals,too-many-branches else: total_artists = 0 - if total_tracks + total_albums + total_playlists == 0: + if total_tracks + total_albums + total_playlists + total_artists == 0: print("NO RESULTS FOUND - EXITING...") else: selection = str(input("SELECT ITEM(S) BY ID: ")) From b4b2985a5b4de9819c6b71232359bef18c7e035f Mon Sep 17 00:00:00 2001 From: Mazzya Date: Sun, 24 Oct 2021 14:23:26 +0200 Subject: [PATCH 04/31] CHANGELOG.md has been created and a link to view the CONTRIBUTING.md file has been added. --- CHANGELOG.md | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 88 ++-------------------------------------------------- 2 files changed, 87 insertions(+), 85 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..008e0f71 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,84 @@ +## **Changelog:** +**v2.2 (24 Oct 2021):** +- Added basic support for downloading an entire podcast series. +- Split code into multiple files for easier maintenance. +- Changed initial launch script to app.py +- Simplified audio formats. +- Added prebuild exe for Windows users. +- Added Docker file. +- Added CONTRIBUTING.md. +- Fixed artist names getting cutoff in metadata. +- Removed data sanitization of metadata tags. + +**v2.1 (23 Oct 2021):** +- Moved configuration from hard-coded values to separate zs_config.json file. +- Add subfolders for each disc. +- Can now search and download all songs by artist. +- Show single progress bar for entire album. +- Added song number at start of track name in albums. + +**v2.0 (22 Oct 2021):** +- Added progress bar for downloads. +- Added multi-select support for all results when searching. +- Added GPLv3 Licence. +- Changed welcome banner and removed unnecessary debug print statements. + +**v1.9 (22 Oct 2021):** +- Added Gitea mirror for when the Spotify Glowies come to DMCA the shit out of this. +- Changed the discord server invite to a matrix server so that won't get swatted either. +- Added option to select multiple of our saved playlists to download at once. +- Added support for downloading an entire show at once. + +**v1.8 (21 Oct 2021):** +- Improved podcast downloading a bit. +- Simplified the code that catches crashes while downloading. +- Cleaned up code using linter again. +- Added option to just paste a url in the search bar to download it. +- Added a small delay between downloading each track when downloading in bulk to help with downloading issues and potential bans. + +**v1.7 (21 Oct 2021):** +- Rewrote README.md to look a lot more professional. +- Added patch to fix edge case crash when downloading liked songs. +- Made premium account check a lot more reliable. +- Added experimental podcast support for specific episodes! + +**v1.6 (20 Oct 2021):** +- Added Pillow to requirements.txt. +- Removed websocket-client from requirements.txt because librespot-python added it to their dependency list. +- Made it hide your password when you type it in. +- Added manual override to force premium quality if zspotify cannot auto detect it. +- Added option to just download the raw audio with no re-encoding at all. +- Added Shebang line so it runs smoother on Linux. +- Made it download the entire track at once now so it is more efficient and fixed a bug users encountered. + +**v1.5 (19 Oct 2021):** +- Made downloading a lot more efficient and probably faster. +- Made the sanitizer more efficient. +- Formatted and linted all the code. + +**v1.4 (19 Oct 2021):** +- Added option to encode the downloaded tracks in the "ogg" format rather than "mp3". +- Added small improvement to sanitation function so it catches another edge case. + +**v1.3 (19 Oct 2021):** +- Added auto detection about if the current account is premium or not. If it is a premium account it automatically sets the quality to VERY_HIGH and otherwise HIGH if we are using a free account. +- Fixed conversion function so it now exports to the correct bitrate. +- Added sanitation to playlist names to help catch an edge case crash. +- Added option to download all your liked songs into a sub-folder. + +**v1.2 (18 Oct 2021):** +- Added .gitignore. +- Replaced dependency list in README.md with a proper requirements.txt file. +- Improved the readability of README.md. + +**v1.1 (16 Oct 2021):** +- Added try/except to help catch crashes where a very few specific tracks would crash either the downloading or conversion part. + +**v1.0 (14 Oct 2021):** +- Adjusted some functions so it runs again with the newer version of librespot-python. +- Improved my sanitization function so it catches more edge cases. +- Fixed an issue where sometimes spotify wouldn't provide a song id for a track we are trying to download. It will now detect and skip these invalid tracks. +- Added additional check for tracks that cannot be "played" due to licence(and similar) issues. These tracks will be skipped. + +**v0.9 (13 Oct 2021):** +- Initial upload, needs adjustments to get working again after backend rewrite. \ No newline at end of file diff --git a/README.md b/README.md index 7b8f1b0e..b0b94279 100644 --- a/README.md +++ b/README.md @@ -59,89 +59,7 @@ This isn't to say _you_ won't get banned as it is technically against Spotify's If you see this, don't worry! Just try logging back in. If you see the incorrect username or password error, reset your password and you should be able to log back in and continue using Spotify. ### Contributing -Please refer to CONTRIBUTING.md +Please refer to [CONTRIBUTING.md](CONTRIBUTING.md) -## **Changelog:** -**v2.2 (24 Oct 2021):** -- Added basic support for downloading an entire podcast series. -- Split code into multiple files for easier maintenance. -- Changed initial launch script to app.py -- Simplified audio formats. -- Added prebuild exe for Windows users. -- Added Docker file. -- Added CONTRIBUTING.md. -- Fixed artist names getting cutoff in metadata. -- Removed data sanitization of metadata tags. - -**v2.1 (23 Oct 2021):** -- Moved configuration from hard-coded values to separate zs_config.json file. -- Add subfolders for each disc. -- Can now search and download all songs by artist. -- Show single progress bar for entire album. -- Added song number at start of track name in albums. - -**v2.0 (22 Oct 2021):** -- Added progress bar for downloads. -- Added multi-select support for all results when searching. -- Added GPLv3 Licence. -- Changed welcome banner and removed unnecessary debug print statements. - -**v1.9 (22 Oct 2021):** -- Added Gitea mirror for when the Spotify Glowies come to DMCA the shit out of this. -- Changed the discord server invite to a matrix server so that won't get swatted either. -- Added option to select multiple of our saved playlists to download at once. -- Added support for downloading an entire show at once. - -**v1.8 (21 Oct 2021):** -- Improved podcast downloading a bit. -- Simplified the code that catches crashes while downloading. -- Cleaned up code using linter again. -- Added option to just paste a url in the search bar to download it. -- Added a small delay between downloading each track when downloading in bulk to help with downloading issues and potential bans. - -**v1.7 (21 Oct 2021):** -- Rewrote README.md to look a lot more professional. -- Added patch to fix edge case crash when downloading liked songs. -- Made premium account check a lot more reliable. -- Added experimental podcast support for specific episodes! - -**v1.6 (20 Oct 2021):** -- Added Pillow to requirements.txt. -- Removed websocket-client from requirements.txt because librespot-python added it to their dependency list. -- Made it hide your password when you type it in. -- Added manual override to force premium quality if zspotify cannot auto detect it. -- Added option to just download the raw audio with no re-encoding at all. -- Added Shebang line so it runs smoother on Linux. -- Made it download the entire track at once now so it is more efficient and fixed a bug users encountered. - -**v1.5 (19 Oct 2021):** -- Made downloading a lot more efficient and probably faster. -- Made the sanitizer more efficient. -- Formatted and linted all the code. - -**v1.4 (19 Oct 2021):** -- Added option to encode the downloaded tracks in the "ogg" format rather than "mp3". -- Added small improvement to sanitation function so it catches another edge case. - -**v1.3 (19 Oct 2021):** -- Added auto detection about if the current account is premium or not. If it is a premium account it automatically sets the quality to VERY_HIGH and otherwise HIGH if we are using a free account. -- Fixed conversion function so it now exports to the correct bitrate. -- Added sanitation to playlist names to help catch an edge case crash. -- Added option to download all your liked songs into a sub-folder. - -**v1.2 (18 Oct 2021):** -- Added .gitignore. -- Replaced dependency list in README.md with a proper requirements.txt file. -- Improved the readability of README.md. - -**v1.1 (16 Oct 2021):** -- Added try/except to help catch crashes where a very few specific tracks would crash either the downloading or conversion part. - -**v1.0 (14 Oct 2021):** -- Adjusted some functions so it runs again with the newer version of librespot-python. -- Improved my sanitization function so it catches more edge cases. -- Fixed an issue where sometimes spotify wouldn't provide a song id for a track we are trying to download. It will now detect and skip these invalid tracks. -- Added additional check for tracks that cannot be "played" due to licence(and similar) issues. These tracks will be skipped. - -**v0.9 (13 Oct 2021):** -- Initial upload, needs adjustments to get working again after backend rewrite. +### Changelog +Please refer to [CHANGELOG](CHANGELOG.md) From 51abecda74c8af8e5e60afa11cd9a7aaa4bb835d Mon Sep 17 00:00:00 2001 From: Mazzya <41023812+Mazzya@users.noreply.github.com> Date: Sun, 24 Oct 2021 14:24:47 +0200 Subject: [PATCH 05/31] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b0b94279..d25068fe 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ This isn't to say _you_ won't get banned as it is technically against Spotify's If you see this, don't worry! Just try logging back in. If you see the incorrect username or password error, reset your password and you should be able to log back in and continue using Spotify. ### Contributing -Please refer to [CONTRIBUTING.md](CONTRIBUTING.md) +Please refer to [CONTRIBUTING](CONTRIBUTING.md) ### Changelog Please refer to [CHANGELOG](CHANGELOG.md) From 18fca559b30baa306c5e7d7cb5e4c57b71284c6f Mon Sep 17 00:00:00 2001 From: yiannisha Date: Sun, 24 Oct 2021 18:07:12 +0300 Subject: [PATCH 06/31] Added argument parsing in search function Added a block for parsing arguments passed with options -l -limit -t -type in the search_term. They work as follows: * -l -limit : sets the limit of results to that number and raises a ValueError if that number exceeds 50. Default is 10. * -t -type : sets the type that is requested from the API about the search query. Raises a ValueError if an arguments passed are different than track, album, playlist. Default is all three. Example: Enter search or URL: -l 30 -t track album This will result with 30 tracks and 30 albums associated with query. Options can be passed in any order but the query must be first. --- src/app.py | 209 +++++++++++++++++++++++++++++++++++------------- src/const.py | 2 + src/playlist.py | 6 +- 3 files changed, 158 insertions(+), 59 deletions(-) diff --git a/src/app.py b/src/app.py index abd8b876..d34fdfec 100644 --- a/src/app.py +++ b/src/app.py @@ -4,8 +4,8 @@ from librespot.audio.decoders import AudioQuality from tabulate import tabulate from album import download_album, download_artist_albums -from const import TRACK, NAME, ID, ARTISTS, ITEMS, TRACKS, EXPLICIT, ALBUMS, OWNER, \ - PLAYLISTS, DISPLAY_NAME +from const import TRACK, NAME, ID, ARTIST, ARTISTS, ITEMS, TRACKS, EXPLICIT, ALBUM, ALBUMS, \ + OWNER, PLAYLIST, PLAYLISTS, DISPLAY_NAME from playlist import get_playlist_songs, get_playlist_info, download_from_user_playlist, download_playlist from podcast import download_episode, get_show_episodes from track import download_track, get_saved_tracks @@ -91,64 +91,159 @@ def client() -> None: def search(search_term): """ Searches Spotify's API for relevant data """ - params = {'limit': '10', 'offset': '0', 'q': search_term, 'type': 'track,album,artist,playlist'} + params = {'limit': '10', + 'offset': '0', + 'q': search_term, + 'type': 'track,album,artist,playlist'} + + # Parse args + splits = search_term.split() + for split in splits: + index = splits.index(split) + + if split[0] == '-' and len(split) > 1: + if len(splits)-1 == index: + raise IndexError('No parameters passed after option: {}\n'. + format(split)) + + if split == '-l' or split == '-limit': + try: + int(splits[index+1]) + except ValueError: + raise ValueError('Paramater passed after {} option must be an integer.\n'. + format(split)) + if int(splits[index+1]) > 50: + raise ValueError('Invalid limit passed. Max is 50.\n') + params['limit'] = splits[index+1] + + + if split == '-t' or split == '-type': + + allowed_types = ['track', 'playlist', 'album', 'artist'] + passed_types = [] + for i in range(index+1, len(splits)): + if splits[i][0] == '-': + break + + if splits[i] not in allowed_types: + raise ValueError('Parameters passed after {} option must be from this list:\n{}'. + format(split, '\n'.join(allowed_types))) + + passed_types.append(splits[i]) + params['type'] = ','.join(passed_types) + + if len(params['type']) == 0: + params['type'] = 'track,album,artist,playlist' + + # Clean search term + search_term_list = [] + for split in splits: + if split[0] == "-": + break + search_term_list.append(split) + if not search_term_list: + raise ValueError("Invalid query.") + params["q"] = ' '.join(search_term_list) + resp = ZSpotify.invoke_url_with_params(SEARCH_URL, **params) counter = 1 - tracks = resp[TRACKS][ITEMS] - if len(tracks) > 0: - print('### TRACKS ###') - track_data = [] - for track in tracks: - if track[EXPLICIT]: - explicit = '[E]' - else: - explicit = '' - track_data.append([counter, f'{track[NAME]} {explicit}', - ','.join([artist[NAME] for artist in track[ARTISTS]])]) - counter += 1 - total_tracks = counter - 1 - print(tabulate(track_data, headers=['S.NO', 'Name', 'Artists'], tablefmt='pretty')) - print('\n') + dics = [] + + if TRACK in params['type'].split(','): + tracks = resp[TRACKS][ITEMS] + if len(tracks) > 0: + print('### TRACKS ###') + track_data = [] + for track in tracks: + if track[EXPLICIT]: + explicit = '[E]' + else: + explicit = '' + + track_data.append([counter, f'{track[NAME]} {explicit}', + ','.join([artist[NAME] for artist in track[ARTISTS]])]) + dics.append({ + ID : track[ID], + NAME : track[NAME], + 'type' : TRACK, + }) + + counter += 1 + total_tracks = counter - 1 + print(tabulate(track_data, headers=['S.NO', 'Name', 'Artists'], tablefmt='pretty')) + print('\n') + del tracks + del track_data else: total_tracks = 0 - albums = resp[ALBUMS][ITEMS] - if len(albums) > 0: - print('### ALBUMS ###') - album_data = [] - for album in albums: - album_data.append([counter, album[NAME], ','.join([artist[NAME] for artist in album[ARTISTS]])]) - counter += 1 - total_albums = counter - total_tracks - 1 - print(tabulate(album_data, headers=['S.NO', 'Album', 'Artists'], tablefmt='pretty')) - print('\n') + if ALBUM in params['type'].split(','): + albums = resp[ALBUMS][ITEMS] + if len(albums) > 0: + print('### ALBUMS ###') + album_data = [] + for album in albums: + album_data.append([counter, album[NAME], + ','.join([artist[NAME] for artist in album[ARTISTS]])]) + dics.append({ + ID : album[ID], + NAME : album[NAME], + 'type' : ALBUM, + }) + + counter += 1 + total_albums = counter - total_tracks - 1 + print(tabulate(album_data, headers=['S.NO', 'Album', 'Artists'], tablefmt='pretty')) + print('\n') + del albums + del album_data else: total_albums = 0 - artists = resp[ARTISTS][ITEMS] - if len(artists) > 0: - print('### ARTISTS ###') - artist_data = [] - for artist in artists: - artist_data.append([counter, artist[NAME]]) - counter += 1 - total_artists = counter - total_tracks - total_albums - 1 - print(tabulate(artist_data, headers=['S.NO', 'Name'], tablefmt='pretty')) - print('\n') + if ARTIST in params['type'].split(','): + artists = resp[ARTISTS][ITEMS] + if len(artists) > 0: + print('### ARTISTS ###') + artist_data = [] + for artist in artists: + artist_data.append([counter, artist[NAME]]) + dics.append({ + ID : artist[ID], + NAME : artist[NAME], + 'type' : ARTIST, + }) + counter += 1 + total_artists = counter - total_tracks - total_albums - 1 + print(tabulate(artist_data, headers=['S.NO', 'Name'], tablefmt='pretty')) + print('\n') + del artists + del artist_data else: total_artists = 0 - playlists = resp[PLAYLISTS][ITEMS] - print('### PLAYLISTS ###') - playlist_data = [] - for playlist in playlists: - playlist_data.append([counter, playlist[NAME], playlist[OWNER][DISPLAY_NAME]]) - counter += 1 - print(tabulate(playlist_data, headers=['S.NO', 'Name', 'Owner'], tablefmt='pretty')) - print('\n') + if PLAYLIST in params['type'].split(','): + playlists = resp[PLAYLISTS][ITEMS] + if len(playlists) > 0: + print('### PLAYLISTS ###') + playlist_data = [] + for playlist in playlists: + playlist_data.append([counter, playlist[NAME], playlist[OWNER][DISPLAY_NAME]]) + dics.append({ + ID : playlist[ID], + NAME : playlist[NAME], + 'type' : PLAYLIST, + }) + counter += 1 + total_playlists = counter - total_artists - total_tracks - total_albums - 1 + print(tabulate(playlist_data, headers=['S.NO', 'Name', 'Owner'], tablefmt='pretty')) + print('\n') + del playlists + del playlist_data + else: + total_playlists = 0 - if len(tracks) + len(albums) + len(playlists) == 0: + if total_tracks + total_albums + total_artists + total_playlists == 0: print('NO RESULTS FOUND - EXITING...') else: selection = '' @@ -157,15 +252,17 @@ def search(search_term): inputs = split_input(selection) for pos in inputs: position = int(pos) - if position <= total_tracks: - track_id = tracks[position - 1][ID] - download_track(track_id) - elif position <= total_albums + total_tracks: - download_album(albums[position - total_tracks - 1][ID]) - elif position <= total_artists + total_tracks + total_albums: - download_artist_albums(artists[position - total_tracks - total_albums - 1][ID]) - else: - download_playlist(playlists, position - total_tracks - total_albums - total_artists) + for dic in dics: + print_pos = dics.index(dic) + 1 + if print_pos == position: + if dic['type'] == TRACK: + download_track(dic[ID]) + elif dic['type'] == ALBUM: + download_album(dic[ID]) + elif dic['type'] == ARTIST: + download_artist_albums(dic[ID]) + else: + download_playlist(dic) if __name__ == '__main__': diff --git a/src/const.py b/src/const.py index 0c9a8545..f44d8694 100644 --- a/src/const.py +++ b/src/const.py @@ -54,6 +54,8 @@ ERROR = 'error' EXPLICIT = 'explicit' +PLAYLIST = 'playlist' + PLAYLISTS = 'playlists' OWNER = 'owner' diff --git a/src/playlist.py b/src/playlist.py index 8a4357a3..9a1304c2 100644 --- a/src/playlist.py +++ b/src/playlist.py @@ -47,13 +47,13 @@ def get_playlist_info(playlist_id): return resp['name'].strip(), resp['owner']['display_name'].strip() -def download_playlist(playlists, playlist_number): +def download_playlist(playlist): """Downloads all the songs from a playlist""" - playlist_songs = [song for song in get_playlist_songs(playlists[int(playlist_number) - 1][ID]) if song[TRACK][ID]] + playlist_songs = [song for song in get_playlist_songs(playlist[ID]) if song[TRACK][ID]] p_bar = tqdm(playlist_songs, unit='song', total=len(playlist_songs), unit_scale=True) for song in p_bar: - download_track(song[TRACK][ID], sanitize_data(playlists[int(playlist_number) - 1][NAME].strip()) + '/', + download_track(song[TRACK][ID], sanitize_data(playlist[NAME].strip()) + '/', disable_progressbar=True) p_bar.set_description(song[TRACK][NAME]) From 40bc2d612fcb1932516bf2e5dcd4d1d2c7c93c54 Mon Sep 17 00:00:00 2001 From: Jared Rossberg Date: Sun, 24 Oct 2021 09:49:55 -0600 Subject: [PATCH 07/31] Fix spelling errors --- CONTRIBUTING.md | 2 +- README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1ee5eef8..0020b813 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ ZSpotify is a community-driven project. There are many different ways to contrib ### What we aren't looking for -Please don't use the issues section to request help installing or setting up the project. It should be reserved for bugs when running the code, and feature requqests. Instead use the support channel in either our Discord or Matrix server. +Please don't use the issues section to request help installing or setting up the project. It should be reserved for bugs when running the code, and feature requests. Instead use the support channel in either our Discord or Matrix server. # Ground rules diff --git a/README.md b/README.md index 7b8f1b0e..a0ecfa9f 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ Python packages: ``` -\*ffmpeg can be installed via apt for Debian-based distros or by downloading the binaries from [ffmpeg.org](https://ffmpeg.org) and placing them in your %PATH% in Windows. +\*ffmpeg can be installed via apt for Debian-based distros or by downloading the binaries from [ffmpeg.org](https://ffmpeg.org) and placing them in your %PATH% in Windows. Mac users can intall it using [Homebrew](https://brew.sh). -\*\*Git can be installed via apt for Debian-based distros or by downloading the binaries from [git-scm.com](https://git-scm.com/download/win) for Windows. +\*\*Git can be installed via apt for Debian-based distros or by downloading the binaries from [git-scm.com](https://git-scm.com/download/win) for Windows. Mac users can intall it using [Homebrew](https://brew.sh). ``` Command line usage: python app.py Loads search prompt to find then download a specific track, album or playlist @@ -43,7 +43,7 @@ Options that can be configured in zs_config.json: SKIP_EXISTING_FILES Set this to false if you want ZSpotify to overwrite files with the same name rather than skipping the song - MUSIC_FORMAT Can be "mp3" or "ogg", mp3 is required for track metadata however ogg is slightly higer quality as it is not trsnacoded. + MUSIC_FORMAT Can be "mp3" or "ogg", mp3 is required for track metadata however ogg is slightly higer quality as it is not transcoded. FORCE_PREMIUM Set this to true if ZSpotify isn't automatically detecting that you are using a premium account From 41b09712e55954a1cc2f5b8080b4e1f31339e3d5 Mon Sep 17 00:00:00 2001 From: yiannisha Date: Sun, 24 Oct 2021 19:09:58 +0300 Subject: [PATCH 08/31] Can now download all of an artists songs fixes#88 --- src/album.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/album.py b/src/album.py index 12f86eb6..19004d11 100644 --- a/src/album.py +++ b/src/album.py @@ -35,7 +35,13 @@ def get_artist_albums(artist_id): """ Returns artist's albums """ resp = ZSpotify.invoke_url(f'{ARTIST_URL}/{artist_id}/albums') # Return a list each album's id - return [resp[ITEMS][i][ID] for i in range(len(resp[ITEMS]))] + album_ids = [resp[ITEMS][i][ID] for i in range(len(resp[ITEMS]))] + # Recursive requests to get all albums including singles an EPs + while resp['next'] is not None: + resp = ZSpotify.invoke_url(resp['next']) + album_ids.extend([resp[ITEMS][i][ID] for i in range(len(resp[ITEMS]))]) + + return album_ids def download_album(album): From 40086384b0bf67a842787d686d4ea05d06deb402 Mon Sep 17 00:00:00 2001 From: yiannisha Date: Sun, 24 Oct 2021 19:18:18 +0300 Subject: [PATCH 09/31] Can now download all of an artists songs fixes#88 --- src/album.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/album.py b/src/album.py index 12f86eb6..19004d11 100644 --- a/src/album.py +++ b/src/album.py @@ -35,7 +35,13 @@ def get_artist_albums(artist_id): """ Returns artist's albums """ resp = ZSpotify.invoke_url(f'{ARTIST_URL}/{artist_id}/albums') # Return a list each album's id - return [resp[ITEMS][i][ID] for i in range(len(resp[ITEMS]))] + album_ids = [resp[ITEMS][i][ID] for i in range(len(resp[ITEMS]))] + # Recursive requests to get all albums including singles an EPs + while resp['next'] is not None: + resp = ZSpotify.invoke_url(resp['next']) + album_ids.extend([resp[ITEMS][i][ID] for i in range(len(resp[ITEMS]))]) + + return album_ids def download_album(album): From dc37d6157e34b1266ec52a2670de47f2985374cf Mon Sep 17 00:00:00 2001 From: Jared Rossberg Date: Sun, 24 Oct 2021 10:22:20 -0600 Subject: [PATCH 10/31] Fix readme cli usage --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a0ecfa9f..ad49bf7e 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ Python packages: \*\*Git can be installed via apt for Debian-based distros or by downloading the binaries from [git-scm.com](https://git-scm.com/download/win) for Windows. Mac users can intall it using [Homebrew](https://brew.sh). ``` Command line usage: - python app.py Loads search prompt to find then download a specific track, album or playlist - python app.py Downloads the track, album, playlist or podcast episode specified as a command line argument - python app.py Downloads all albums by specified artist + python src/app.py Loads search prompt to find then download a specific track, album or playlist + python src/app.py Downloads the track, album, playlist or podcast episode specified as a command line argument + python src/app.py Downloads all albums by specified artist Extra command line options: -p, --playlist Downloads a saved playlist from your account From 36be67b827bd2192380b3cfc98160bfa7eddd094 Mon Sep 17 00:00:00 2001 From: yiannisha Date: Sun, 24 Oct 2021 19:32:32 +0300 Subject: [PATCH 11/31] Revert "Can now download all of an artists songs fixes#88" This reverts commit 41b09712e55954a1cc2f5b8080b4e1f31339e3d5. --- src/album.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/album.py b/src/album.py index 19004d11..12f86eb6 100644 --- a/src/album.py +++ b/src/album.py @@ -35,13 +35,7 @@ def get_artist_albums(artist_id): """ Returns artist's albums """ resp = ZSpotify.invoke_url(f'{ARTIST_URL}/{artist_id}/albums') # Return a list each album's id - album_ids = [resp[ITEMS][i][ID] for i in range(len(resp[ITEMS]))] - # Recursive requests to get all albums including singles an EPs - while resp['next'] is not None: - resp = ZSpotify.invoke_url(resp['next']) - album_ids.extend([resp[ITEMS][i][ID] for i in range(len(resp[ITEMS]))]) - - return album_ids + return [resp[ITEMS][i][ID] for i in range(len(resp[ITEMS]))] def download_album(album): From 7c2b8fc1c997c215218a489ba9ef69faa4638a08 Mon Sep 17 00:00:00 2001 From: Jared Rossberg Date: Sun, 24 Oct 2021 10:35:04 -0600 Subject: [PATCH 12/31] Fix one more spelling error --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ad49bf7e..8007922a 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Options that can be configured in zs_config.json: SKIP_EXISTING_FILES Set this to false if you want ZSpotify to overwrite files with the same name rather than skipping the song - MUSIC_FORMAT Can be "mp3" or "ogg", mp3 is required for track metadata however ogg is slightly higer quality as it is not transcoded. + MUSIC_FORMAT Can be "mp3" or "ogg", mp3 is required for track metadata however ogg is slightly higher quality as it is not transcoded. FORCE_PREMIUM Set this to true if ZSpotify isn't automatically detecting that you are using a premium account From 96128de4a7b8f79324edcbdb05dc8e068012d311 Mon Sep 17 00:00:00 2001 From: Jared Rossberg Date: Sun, 24 Oct 2021 10:37:59 -0600 Subject: [PATCH 13/31] Fix more spelling errors --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8007922a..7113c936 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ Python packages: ``` -\*ffmpeg can be installed via apt for Debian-based distros or by downloading the binaries from [ffmpeg.org](https://ffmpeg.org) and placing them in your %PATH% in Windows. Mac users can intall it using [Homebrew](https://brew.sh). +\*ffmpeg can be installed via apt for Debian-based distros or by downloading the binaries from [ffmpeg.org](https://ffmpeg.org) and placing them in your %PATH% in Windows. Mac users can install it using [Homebrew](https://brew.sh). -\*\*Git can be installed via apt for Debian-based distros or by downloading the binaries from [git-scm.com](https://git-scm.com/download/win) for Windows. Mac users can intall it using [Homebrew](https://brew.sh). +\*\*Git can be installed via apt for Debian-based distros or by downloading the binaries from [git-scm.com](https://git-scm.com/download/win) for Windows. Mac users can install it using [Homebrew](https://brew.sh). ``` Command line usage: python src/app.py Loads search prompt to find then download a specific track, album or playlist From 5b6a41ef707cfc09d72b260b6128449b5b9df6bd Mon Sep 17 00:00:00 2001 From: Jared Rossberg Date: Sun, 24 Oct 2021 11:09:15 -0600 Subject: [PATCH 14/31] Explain homebrew better in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7113c936..91c6349e 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ Python packages: ``` -\*ffmpeg can be installed via apt for Debian-based distros or by downloading the binaries from [ffmpeg.org](https://ffmpeg.org) and placing them in your %PATH% in Windows. Mac users can install it using [Homebrew](https://brew.sh). +\*ffmpeg can be installed via apt for Debian-based distros or by downloading the binaries from [ffmpeg.org](https://ffmpeg.org) and placing them in your %PATH% in Windows. Mac users can install it with [Homebrew](https://brew.sh) by running `brew install ffmpeg`. -\*\*Git can be installed via apt for Debian-based distros or by downloading the binaries from [git-scm.com](https://git-scm.com/download/win) for Windows. Mac users can install it using [Homebrew](https://brew.sh). +\*\*Git can be installed via apt for Debian-based distros or by downloading the binaries from [git-scm.com](https://git-scm.com/download/win) for Windows. ``` Command line usage: python src/app.py Loads search prompt to find then download a specific track, album or playlist From 85344f573ca253737b79d309457d5aec714f4654 Mon Sep 17 00:00:00 2001 From: davidsalido <46091673+davidsalido@users.noreply.github.com> Date: Sun, 24 Oct 2021 17:11:01 +0000 Subject: [PATCH 15/31] Fix infinite loop --- src/app.py | 82 ++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/src/app.py b/src/app.py index abd8b876..11f75069 100644 --- a/src/app.py +++ b/src/app.py @@ -27,45 +27,18 @@ def client() -> None: print('[ DETECTED FREE ACCOUNT - USING HIGH QUALITY ]\n\n') ZSpotify.DOWNLOAD_QUALITY = AudioQuality.HIGH - while True: - if len(sys.argv) > 1: - if sys.argv[1] == '-p' or sys.argv[1] == '--playlist': - download_from_user_playlist() - elif sys.argv[1] == '-ls' or sys.argv[1] == '--liked-songs': - for song in get_saved_tracks(): - if not song[TRACK][NAME]: - print('### SKIPPING: SONG DOES NOT EXISTS ON SPOTIFY ANYMORE ###') - else: - download_track(song[TRACK][ID], 'Liked Songs/') - print('\n') - else: - track_id, album_id, playlist_id, episode_id, show_id, artist_id = regex_input_for_urls(sys.argv[1]) - - if track_id is not None: - download_track(track_id) - elif artist_id is not None: - download_artist_albums(artist_id) - elif album_id is not None: - download_album(album_id) - elif playlist_id is not None: - playlist_songs = get_playlist_songs(playlist_id) - name, _ = get_playlist_info(playlist_id) - for song in playlist_songs: - download_track(song[TRACK][ID], - sanitize_data(name) + '/') - print('\n') - elif episode_id is not None: - download_episode(episode_id) - elif show_id is not None: - for episode in get_show_episodes(show_id): - download_episode(episode) - + if len(sys.argv) > 1: + if sys.argv[1] == '-p' or sys.argv[1] == '--playlist': + download_from_user_playlist() + elif sys.argv[1] == '-ls' or sys.argv[1] == '--liked-songs': + for song in get_saved_tracks(): + if not song[TRACK][NAME]: + print('### SKIPPING: SONG DOES NOT EXISTS ON SPOTIFY ANYMORE ###') + else: + download_track(song[TRACK][ID], 'Liked Songs/') + print('\n') else: - search_text = '' - while len(search_text) == 0: - search_text = input('Enter search or URL: ') - - track_id, album_id, playlist_id, episode_id, show_id, artist_id = regex_input_for_urls(search_text) + track_id, album_id, playlist_id, episode_id, show_id, artist_id = regex_input_for_urls(sys.argv[1]) if track_id is not None: download_track(track_id) @@ -77,16 +50,41 @@ def client() -> None: playlist_songs = get_playlist_songs(playlist_id) name, _ = get_playlist_info(playlist_id) for song in playlist_songs: - download_track(song[TRACK][ID], sanitize_data(name) + '/') + download_track(song[TRACK][ID], + sanitize_data(name) + '/') print('\n') elif episode_id is not None: download_episode(episode_id) elif show_id is not None: for episode in get_show_episodes(show_id): download_episode(episode) - else: - search(search_text) - # wait() + + else: + search_text = '' + while len(search_text) == 0: + search_text = input('Enter search or URL: ') + + track_id, album_id, playlist_id, episode_id, show_id, artist_id = regex_input_for_urls(search_text) + + if track_id is not None: + download_track(track_id) + elif artist_id is not None: + download_artist_albums(artist_id) + elif album_id is not None: + download_album(album_id) + elif playlist_id is not None: + playlist_songs = get_playlist_songs(playlist_id) + name, _ = get_playlist_info(playlist_id) + for song in playlist_songs: + download_track(song[TRACK][ID], sanitize_data(name) + '/') + print('\n') + elif episode_id is not None: + download_episode(episode_id) + elif show_id is not None: + for episode in get_show_episodes(show_id): + download_episode(episode) + else: + search(search_text) def search(search_term): From 69f59bb0f6f5c24c3ad69a04a1a64844cc94a4f2 Mon Sep 17 00:00:00 2001 From: Jared Rossberg Date: Sun, 24 Oct 2021 11:15:41 -0600 Subject: [PATCH 16/31] Create main module --- README.md | 6 +++--- src/__main__.py | 4 ++++ src/app.py | 3 --- 3 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 src/__main__.py diff --git a/README.md b/README.md index 91c6349e..8a78b481 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ Python packages: \*\*Git can be installed via apt for Debian-based distros or by downloading the binaries from [git-scm.com](https://git-scm.com/download/win) for Windows. ``` Command line usage: - python src/app.py Loads search prompt to find then download a specific track, album or playlist - python src/app.py Downloads the track, album, playlist or podcast episode specified as a command line argument - python src/app.py Downloads all albums by specified artist + python src Loads search prompt to find then download a specific track, album or playlist + python src Downloads the track, album, playlist or podcast episode specified as a command line argument + python src Downloads all albums by specified artist Extra command line options: -p, --playlist Downloads a saved playlist from your account diff --git a/src/__main__.py b/src/__main__.py new file mode 100644 index 00000000..50dd3890 --- /dev/null +++ b/src/__main__.py @@ -0,0 +1,4 @@ +from app import client + +if __name__ == '__main__': + client() diff --git a/src/app.py b/src/app.py index abd8b876..2a0898a4 100644 --- a/src/app.py +++ b/src/app.py @@ -167,6 +167,3 @@ def search(search_term): else: download_playlist(playlists, position - total_tracks - total_albums - total_artists) - -if __name__ == '__main__': - client() From a0bb3c3843a0fc49add8b3d144fe77e1ac82541f Mon Sep 17 00:00:00 2001 From: Alpha Date: Sun, 24 Oct 2021 13:45:52 -0400 Subject: [PATCH 17/31] Fix typing hints --- src/podcast.py | 4 ++-- src/track.py | 4 ++-- src/utils.py | 5 +++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/podcast.py b/src/podcast.py index 43dae318..eea36c1b 100644 --- a/src/podcast.py +++ b/src/podcast.py @@ -1,5 +1,5 @@ import os -from typing import Optional +from typing import Optional, Tuple from librespot.audio.decoders import VorbisOnlyAudioQuality from librespot.metadata import EpisodeId @@ -14,7 +14,7 @@ EPISODE_INFO_URL = 'https://api.spotify.com/v1/episodes' SHOWS_URL = 'https://api.spotify.com/v1/shows' -def get_episode_info(episode_id_str) -> tuple[Optional[str], Optional[str]]: +def get_episode_info(episode_id_str) -> Tuple[Optional[str], Optional[str]]: info = ZSpotify.invoke_url(f'{EPISODE_INFO_URL}/{episode_id_str}') if ERROR in info: return None, None diff --git a/src/track.py b/src/track.py index 3fb088f7..9a928eca 100644 --- a/src/track.py +++ b/src/track.py @@ -1,6 +1,6 @@ import os import time -from typing import Any +from typing import Any, Tuple, List from librespot.audio.decoders import AudioQuality from librespot.metadata import TrackId @@ -31,7 +31,7 @@ def get_saved_tracks() -> list: return songs -def get_song_info(song_id) -> tuple[list[str], str, str, Any, Any, Any, Any, Any, Any]: +def get_song_info(song_id) -> Tuple[List[str], str, str, Any, Any, Any, Any, Any, Any]: """ Retrieves metadata for downloaded songs """ info = ZSpotify.invoke_url(f'{TRACKS_URL}?ids={song_id}&market=from_token') diff --git a/src/utils.py b/src/utils.py index 3fd91612..b8188d95 100644 --- a/src/utils.py +++ b/src/utils.py @@ -3,6 +3,7 @@ import platform import re import time from enum import Enum +from typing import List, Tuple import music_tag import requests @@ -27,7 +28,7 @@ def wait(seconds: int = 3) -> None: time.sleep(1) -def split_input(selection) -> list[str]: +def split_input(selection) -> List[str]: """ Returns a list of inputted strings """ inputs = [] if '-' in selection: @@ -91,7 +92,7 @@ def set_music_thumbnail(filename, image_url) -> None: tags.save() -def regex_input_for_urls(search_input) -> tuple[str, str, str, str, str, str]: +def regex_input_for_urls(search_input) -> Tuple[str, str, str, str, str, str]: """ Since many kinds of search may be passed at the command line, process them all here. """ track_uri_search = re.search( r'^spotify:track:(?P[0-9a-zA-Z]{22})$', search_input) From 798b25f3e6e1da7a1abd58e58a7c6d04c15c5bc3 Mon Sep 17 00:00:00 2001 From: Jared Rossberg Date: Sun, 24 Oct 2021 14:25:09 -0600 Subject: [PATCH 18/31] Fix dockerfile entrypoint --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index ceb88cf2..dcba36ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,4 +16,4 @@ COPY --from=builder /install /usr/local COPY src /app COPY zs_config.json / WORKDIR /app -ENTRYPOINT ["/usr/local/bin/python", "app.py"] +ENTRYPOINT ["/usr/local/bin/python", "__main__.py"] From e5da26c81abdb9b41c7aae0d31f1d15e4686d321 Mon Sep 17 00:00:00 2001 From: Jared Rossberg Date: Sun, 24 Oct 2021 14:32:50 -0600 Subject: [PATCH 19/31] Update README with Docker usage --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index ac87b944..80cfde88 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,13 @@ Options that can be configured in zs_config.json: ANTI_BAN_WAIT_TIME Change this setting if the time waited between bulk downloads is too high or low OVERRIDE_AUTO_WAIT Change this to true if you want to completely disable the wait between songs for faster downloads with the risk of instability ``` + +``` +Docker usage: + docker build -t zspotify . Builds the docker image from the Dockerfile + docker run --name zspotify zspotify Creates and runs a container from the image +``` + ### Will my account get banned if I use this tool? Currently no user has reported their account getting banned after using ZSpotify. This isn't to say _you_ won't get banned as it is technically against Spotify's TOS. From 5c0b02b0b83794dd863a67291b4b08a928fef1f7 Mon Sep 17 00:00:00 2001 From: Logykk <35679186+logykk@users.noreply.github.com> Date: Mon, 25 Oct 2021 14:06:26 +1300 Subject: [PATCH 20/31] Fix docker run command --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 80cfde88..9e6ea1f9 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,13 @@ Options that can be configured in zs_config.json: OVERRIDE_AUTO_WAIT Change this to true if you want to completely disable the wait between songs for faster downloads with the risk of instability ``` -``` -Docker usage: - docker build -t zspotify . Builds the docker image from the Dockerfile - docker run --name zspotify zspotify Creates and runs a container from the image +### Docker Usage + +``` +Build the docker image from the Dockerfile: + docker build -t zspotify . +Create and run a container from the image: + docker run --rm -v "$PWD/ZSpotify Music:/ZSpotify Music" -v "$PWD/ZSpotify Podcasts:/ZSpotify Podcasts" -it zspotify ``` ### Will my account get banned if I use this tool? From d7351dc95c5ddb1adf7374ea8f6a1c7b3e222a75 Mon Sep 17 00:00:00 2001 From: xx-05 Date: Sun, 24 Oct 2021 21:27:33 -0400 Subject: [PATCH 21/31] fix typo --- src/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app.py b/src/app.py index 5684b994..a2433a57 100644 --- a/src/app.py +++ b/src/app.py @@ -33,7 +33,7 @@ def client() -> None: elif sys.argv[1] == '-ls' or sys.argv[1] == '--liked-songs': for song in get_saved_tracks(): if not song[TRACK][NAME]: - print('### SKIPPING: SONG DOES NOT EXISTS ON SPOTIFY ANYMORE ###') + print('### SKIPPING: SONG DOES NOT EXIST ON SPOTIFY ANYMORE ###') else: download_track(song[TRACK][ID], 'Liked Songs/') print('\n') From 03b0b108b8a6f5f25cab88f67855043e7d420957 Mon Sep 17 00:00:00 2001 From: Footsiefat <12180913+Footsiefat@users.noreply.github.com> Date: Mon, 25 Oct 2021 14:51:12 +1300 Subject: [PATCH 22/31] renamed src to zspotify --- {src => zspotify}/__main__.py | 0 {src => zspotify}/album.py | 0 {src => zspotify}/app.py | 0 {src => zspotify}/const.py | 0 {src => zspotify}/playlist.py | 0 {src => zspotify}/podcast.py | 0 {src => zspotify}/track.py | 0 {src => zspotify}/utils.py | 0 {src => zspotify}/zspotify.py | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename {src => zspotify}/__main__.py (100%) rename {src => zspotify}/album.py (100%) rename {src => zspotify}/app.py (100%) rename {src => zspotify}/const.py (100%) rename {src => zspotify}/playlist.py (100%) rename {src => zspotify}/podcast.py (100%) rename {src => zspotify}/track.py (100%) rename {src => zspotify}/utils.py (100%) rename {src => zspotify}/zspotify.py (100%) diff --git a/src/__main__.py b/zspotify/__main__.py similarity index 100% rename from src/__main__.py rename to zspotify/__main__.py diff --git a/src/album.py b/zspotify/album.py similarity index 100% rename from src/album.py rename to zspotify/album.py diff --git a/src/app.py b/zspotify/app.py similarity index 100% rename from src/app.py rename to zspotify/app.py diff --git a/src/const.py b/zspotify/const.py similarity index 100% rename from src/const.py rename to zspotify/const.py diff --git a/src/playlist.py b/zspotify/playlist.py similarity index 100% rename from src/playlist.py rename to zspotify/playlist.py diff --git a/src/podcast.py b/zspotify/podcast.py similarity index 100% rename from src/podcast.py rename to zspotify/podcast.py diff --git a/src/track.py b/zspotify/track.py similarity index 100% rename from src/track.py rename to zspotify/track.py diff --git a/src/utils.py b/zspotify/utils.py similarity index 100% rename from src/utils.py rename to zspotify/utils.py diff --git a/src/zspotify.py b/zspotify/zspotify.py similarity index 100% rename from src/zspotify.py rename to zspotify/zspotify.py From c4ff812ac3382ddc96b50bdef984d5af1d89d022 Mon Sep 17 00:00:00 2001 From: logykk Date: Mon, 25 Oct 2021 14:59:40 +1300 Subject: [PATCH 23/31] change docker dir from src to zspotify --- CONTRIBUTING.md | 1 + Dockerfile | 2 +- README.md | 10 ++++++---- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0020b813..ca78ddd6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,6 +15,7 @@ ZSpotify is a community-driven project. There are many different ways to contrib ### What we aren't looking for Please don't use the issues section to request help installing or setting up the project. It should be reserved for bugs when running the code, and feature requests. Instead use the support channel in either our Discord or Matrix server. +Please do not make a new pull request just to fix a typo or any small issue like that. We'd rather you just make an issue reporting it and we will fix it in the next commit. This helps to prevent commit spamming. # Ground rules diff --git a/Dockerfile b/Dockerfile index dcba36ca..c24ed245 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ RUN apk add gcc libc-dev zlib zlib-dev jpeg-dev \ FROM base COPY --from=builder /install /usr/local -COPY src /app +COPY zspotify /app COPY zs_config.json / WORKDIR /app ENTRYPOINT ["/usr/local/bin/python", "__main__.py"] diff --git a/README.md b/README.md index 9e6ea1f9..634a450a 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,13 @@ Python packages: \*ffmpeg can be installed via apt for Debian-based distros or by downloading the binaries from [ffmpeg.org](https://ffmpeg.org) and placing them in your %PATH% in Windows. Mac users can install it with [Homebrew](https://brew.sh) by running `brew install ffmpeg`. \*\*Git can be installed via apt for Debian-based distros or by downloading the binaries from [git-scm.com](https://git-scm.com/download/win) for Windows. + +### Command line usage: ``` -Command line usage: - python src Loads search prompt to find then download a specific track, album or playlist - python src Downloads the track, album, playlist or podcast episode specified as a command line argument - python src Downloads all albums by specified artist +Basic usage: + python zspotify Loads search prompt to find then download a specific track, album or playlist + python zspotify Downloads the track, album, playlist or podcast episode specified as a command line argument + python zspotify Downloads all albums by specified artist Extra command line options: -p, --playlist Downloads a saved playlist from your account From bcdb07593a7e7fe43540fdf9840a70773d0bc9e2 Mon Sep 17 00:00:00 2001 From: el-gringo-alto <22480930+el-gringo-alto@users.noreply.github.com> Date: Sun, 24 Oct 2021 22:14:22 -0400 Subject: [PATCH 24/31] Generate config file if it doesn't exist --- zspotify/const.py | 12 ++++++++++++ zspotify/zspotify.py | 12 +++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/zspotify/const.py b/zspotify/const.py index f44d8694..3e4d9b0f 100644 --- a/zspotify/const.py +++ b/zspotify/const.py @@ -95,3 +95,15 @@ OVERRIDE_AUTO_WAIT = 'OVERRIDE_AUTO_WAIT' CHUNK_SIZE = 'CHUNK_SIZE' SPLIT_ALBUM_DISCS = 'SPLIT_ALBUM_DISCS' + +CONFIG_DEFAULT_SETTINGS = { + 'ROOT_PATH': '../ZSpotify Music/', + 'ROOT_PODCAST_PATH': '../ZSpotify Podcasts/', + 'SKIP_EXISTING_FILES': True, + 'DOWNLOAD_FORMAT': 'mp3', + 'FORCE_PREMIUM': False, + 'ANTI_BAN_WAIT_TIME': 1, + 'OVERRIDE_AUTO_WAIT': False, + 'CHUNK_SIZE': 50000, + 'SPLIT_ALBUM_DISCS': False +} diff --git a/zspotify/zspotify.py b/zspotify/zspotify.py index f206a80d..5739dfa5 100644 --- a/zspotify/zspotify.py +++ b/zspotify/zspotify.py @@ -18,7 +18,7 @@ from librespot.core import Session from const import CREDENTIALS_JSON, TYPE, \ PREMIUM, USER_READ_EMAIL, AUTHORIZATION, OFFSET, LIMIT, CONFIG_FILE_PATH, FORCE_PREMIUM, \ - PLAYLIST_READ_PRIVATE + PLAYLIST_READ_PRIVATE, CONFIG_DEFAULT_SETTINGS from utils import MusicFormat @@ -55,8 +55,14 @@ class ZSpotify: @classmethod def load_config(cls) -> None: app_dir = os.path.dirname(__file__) - with open(os.path.join(app_dir, CONFIG_FILE_PATH), encoding='utf-8') as config_file: - cls.CONFIG = json.load(config_file) + true_config_file_path = os.path.join(app_dir, CONFIG_FILE_PATH) + if not os.path.exists(true_config_file_path): + with open(true_config_file_path, 'w', encoding='utf-8') as config_file: + json.dump(CONFIG_DEFAULT_SETTINGS, config_file, indent=4) + cls.CONFIG = CONFIG_DEFAULT_SETTINGS + else: + with open(true_config_file_path, encoding='utf-8') as config_file: + cls.CONFIG = json.load(config_file) @classmethod def get_config(cls, key) -> Any: From 23c53247f7bf28df85b11ff9d9d4c33eb8ac376a Mon Sep 17 00:00:00 2001 From: el-gringo-alto <22480930+el-gringo-alto@users.noreply.github.com> Date: Sun, 24 Oct 2021 22:24:05 -0400 Subject: [PATCH 25/31] Config file is removed from the repository --- .gitignore | 3 +++ zs_config.json | 11 ----------- 2 files changed, 3 insertions(+), 11 deletions(-) delete mode 100644 zs_config.json diff --git a/.gitignore b/.gitignore index ea24040c..65696cee 100644 --- a/.gitignore +++ b/.gitignore @@ -151,3 +151,6 @@ ZSpotify\ Podcasts/ # Intellij .idea + +# Config file +zs_config.json diff --git a/zs_config.json b/zs_config.json deleted file mode 100644 index b5dbb919..00000000 --- a/zs_config.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "ROOT_PATH": "../ZSpotify Music/", - "ROOT_PODCAST_PATH": "../ZSpotify Podcasts/", - "SKIP_EXISTING_FILES": true, - "DOWNLOAD_FORMAT": "mp3", - "FORCE_PREMIUM": false, - "ANTI_BAN_WAIT_TIME": 1, - "OVERRIDE_AUTO_WAIT": false, - "CHUNK_SIZE": 50000, - "SPLIT_ALBUM_DISCS": false -} \ No newline at end of file From 0b51951b5f73204ba6daf0263041112d8dcec63b Mon Sep 17 00:00:00 2001 From: Footsiefat <12180913+Footsiefat@users.noreply.github.com> Date: Mon, 25 Oct 2021 16:44:42 +1300 Subject: [PATCH 26/31] Fixed crash from variable reference before assignment --- .gitignore | 3 +++ zs_config.json | 20 ++++++++-------- zspotify/app.py | 64 +++++++++++++++++++++++++------------------------ 3 files changed, 46 insertions(+), 41 deletions(-) diff --git a/.gitignore b/.gitignore index ea24040c..5295e936 100644 --- a/.gitignore +++ b/.gitignore @@ -151,3 +151,6 @@ ZSpotify\ Podcasts/ # Intellij .idea + +#Configuration json file +zs_config.json diff --git a/zs_config.json b/zs_config.json index b5dbb919..5e555afa 100644 --- a/zs_config.json +++ b/zs_config.json @@ -1,11 +1,11 @@ { - "ROOT_PATH": "../ZSpotify Music/", - "ROOT_PODCAST_PATH": "../ZSpotify Podcasts/", - "SKIP_EXISTING_FILES": true, - "DOWNLOAD_FORMAT": "mp3", - "FORCE_PREMIUM": false, - "ANTI_BAN_WAIT_TIME": 1, - "OVERRIDE_AUTO_WAIT": false, - "CHUNK_SIZE": 50000, - "SPLIT_ALBUM_DISCS": false -} \ No newline at end of file + "ROOT_PATH": "../ZSpotify Music/", + "ROOT_PODCAST_PATH": "../ZSpotify Podcasts/", + "SKIP_EXISTING_FILES": true, + "DOWNLOAD_FORMAT": "mp3", + "FORCE_PREMIUM": false, + "ANTI_BAN_WAIT_TIME": 1, + "OVERRIDE_AUTO_WAIT": false, + "CHUNK_SIZE": 50000, + "SPLIT_ALBUM_DISCS": false +} diff --git a/zspotify/app.py b/zspotify/app.py index a2433a57..3ee926b1 100644 --- a/zspotify/app.py +++ b/zspotify/app.py @@ -33,12 +33,14 @@ def client() -> None: elif sys.argv[1] == '-ls' or sys.argv[1] == '--liked-songs': for song in get_saved_tracks(): if not song[TRACK][NAME]: - print('### SKIPPING: SONG DOES NOT EXIST ON SPOTIFY ANYMORE ###') + print( + '### SKIPPING: SONG DOES NOT EXIST ON SPOTIFY ANYMORE ###') else: download_track(song[TRACK][ID], 'Liked Songs/') print('\n') else: - track_id, album_id, playlist_id, episode_id, show_id, artist_id = regex_input_for_urls(sys.argv[1]) + track_id, album_id, playlist_id, episode_id, show_id, artist_id = regex_input_for_urls( + sys.argv[1]) if track_id is not None: download_track(track_id) @@ -51,7 +53,7 @@ def client() -> None: name, _ = get_playlist_info(playlist_id) for song in playlist_songs: download_track(song[TRACK][ID], - sanitize_data(name) + '/') + sanitize_data(name) + '/') print('\n') elif episode_id is not None: download_episode(episode_id) @@ -64,7 +66,8 @@ def client() -> None: while len(search_text) == 0: search_text = input('Enter search or URL: ') - track_id, album_id, playlist_id, episode_id, show_id, artist_id = regex_input_for_urls(search_text) + track_id, album_id, playlist_id, episode_id, show_id, artist_id = regex_input_for_urls( + search_text) if track_id is not None: download_track(track_id) @@ -114,7 +117,6 @@ def search(search_term): raise ValueError('Invalid limit passed. Max is 50.\n') params['limit'] = splits[index+1] - if split == '-t' or split == '-type': allowed_types = ['track', 'playlist', 'album', 'artist'] @@ -148,6 +150,7 @@ def search(search_term): counter = 1 dics = [] + total_tracks = 0 if TRACK in params['type'].split(','): tracks = resp[TRACKS][ITEMS] if len(tracks) > 0: @@ -162,20 +165,20 @@ def search(search_term): track_data.append([counter, f'{track[NAME]} {explicit}', ','.join([artist[NAME] for artist in track[ARTISTS]])]) dics.append({ - ID : track[ID], - NAME : track[NAME], - 'type' : TRACK, + ID: track[ID], + NAME: track[NAME], + 'type': TRACK, }) counter += 1 total_tracks = counter - 1 - print(tabulate(track_data, headers=['S.NO', 'Name', 'Artists'], tablefmt='pretty')) + print(tabulate(track_data, headers=[ + 'S.NO', 'Name', 'Artists'], tablefmt='pretty')) print('\n') del tracks del track_data - else: - total_tracks = 0 + total_albums = 0 if ALBUM in params['type'].split(','): albums = resp[ALBUMS][ITEMS] if len(albums) > 0: @@ -185,20 +188,20 @@ def search(search_term): album_data.append([counter, album[NAME], ','.join([artist[NAME] for artist in album[ARTISTS]])]) dics.append({ - ID : album[ID], - NAME : album[NAME], - 'type' : ALBUM, + ID: album[ID], + NAME: album[NAME], + 'type': ALBUM, }) counter += 1 total_albums = counter - total_tracks - 1 - print(tabulate(album_data, headers=['S.NO', 'Album', 'Artists'], tablefmt='pretty')) + print(tabulate(album_data, headers=[ + 'S.NO', 'Album', 'Artists'], tablefmt='pretty')) print('\n') del albums del album_data - else: - total_albums = 0 + total_artists = 0 if ARTIST in params['type'].split(','): artists = resp[ARTISTS][ITEMS] if len(artists) > 0: @@ -207,39 +210,39 @@ def search(search_term): for artist in artists: artist_data.append([counter, artist[NAME]]) dics.append({ - ID : artist[ID], - NAME : artist[NAME], - 'type' : ARTIST, + ID: artist[ID], + NAME: artist[NAME], + 'type': ARTIST, }) counter += 1 total_artists = counter - total_tracks - total_albums - 1 - print(tabulate(artist_data, headers=['S.NO', 'Name'], tablefmt='pretty')) + print(tabulate(artist_data, headers=[ + 'S.NO', 'Name'], tablefmt='pretty')) print('\n') del artists del artist_data - else: - total_artists = 0 + total_playlists = 0 if PLAYLIST in params['type'].split(','): playlists = resp[PLAYLISTS][ITEMS] if len(playlists) > 0: print('### PLAYLISTS ###') playlist_data = [] for playlist in playlists: - playlist_data.append([counter, playlist[NAME], playlist[OWNER][DISPLAY_NAME]]) + playlist_data.append( + [counter, playlist[NAME], playlist[OWNER][DISPLAY_NAME]]) dics.append({ - ID : playlist[ID], - NAME : playlist[NAME], - 'type' : PLAYLIST, + ID: playlist[ID], + NAME: playlist[NAME], + 'type': PLAYLIST, }) counter += 1 total_playlists = counter - total_artists - total_tracks - total_albums - 1 - print(tabulate(playlist_data, headers=['S.NO', 'Name', 'Owner'], tablefmt='pretty')) + print(tabulate(playlist_data, headers=[ + 'S.NO', 'Name', 'Owner'], tablefmt='pretty')) print('\n') del playlists del playlist_data - else: - total_playlists = 0 if total_tracks + total_albums + total_artists + total_playlists == 0: print('NO RESULTS FOUND - EXITING...') @@ -261,4 +264,3 @@ def search(search_term): download_artist_albums(dic[ID]) else: download_playlist(dic) - From 2f717c4bd5ee960b3f5e11a896162fb9d9ffd2e5 Mon Sep 17 00:00:00 2001 From: Footsiefat <12180913+Footsiefat@users.noreply.github.com> Date: Mon, 25 Oct 2021 16:44:58 +1300 Subject: [PATCH 27/31] Delete zs_config.json --- zs_config.json | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 zs_config.json diff --git a/zs_config.json b/zs_config.json deleted file mode 100644 index 5e555afa..00000000 --- a/zs_config.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "ROOT_PATH": "../ZSpotify Music/", - "ROOT_PODCAST_PATH": "../ZSpotify Podcasts/", - "SKIP_EXISTING_FILES": true, - "DOWNLOAD_FORMAT": "mp3", - "FORCE_PREMIUM": false, - "ANTI_BAN_WAIT_TIME": 1, - "OVERRIDE_AUTO_WAIT": false, - "CHUNK_SIZE": 50000, - "SPLIT_ALBUM_DISCS": false -} From 4576985b43a6f9b9ed1410588e748ffd09c9dbc7 Mon Sep 17 00:00:00 2001 From: Footsiefat <12180913+Footsiefat@users.noreply.github.com> Date: Mon, 25 Oct 2021 17:07:11 +1300 Subject: [PATCH 28/31] Reuploaded deleted file cos im a dumbass it would be fine if i merged a speicifc PR but I hadnt yet so this completely fucks things up :/ --- zs_config.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 zs_config.json diff --git a/zs_config.json b/zs_config.json new file mode 100644 index 00000000..5e555afa --- /dev/null +++ b/zs_config.json @@ -0,0 +1,11 @@ +{ + "ROOT_PATH": "../ZSpotify Music/", + "ROOT_PODCAST_PATH": "../ZSpotify Podcasts/", + "SKIP_EXISTING_FILES": true, + "DOWNLOAD_FORMAT": "mp3", + "FORCE_PREMIUM": false, + "ANTI_BAN_WAIT_TIME": 1, + "OVERRIDE_AUTO_WAIT": false, + "CHUNK_SIZE": 50000, + "SPLIT_ALBUM_DISCS": false +} From 1993fc05e13024dd26b481cce0821f9d553f079d Mon Sep 17 00:00:00 2001 From: Footsiefat <12180913+Footsiefat@users.noreply.github.com> Date: Mon, 25 Oct 2021 17:08:56 +1300 Subject: [PATCH 29/31] Fixed multiple issues including split disks breaking --- .gitignore | 5 +---- zspotify/track.py | 30 +++++++++++++++++++----------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 5295e936..9834e1b0 100644 --- a/.gitignore +++ b/.gitignore @@ -150,7 +150,4 @@ ZSpotify\ Music/ ZSpotify\ Podcasts/ # Intellij -.idea - -#Configuration json file -zs_config.json +.idea \ No newline at end of file diff --git a/zspotify/track.py b/zspotify/track.py index 9a928eca..423cce0c 100644 --- a/zspotify/track.py +++ b/zspotify/track.py @@ -22,7 +22,8 @@ def get_saved_tracks() -> list: limit = 50 while True: - resp = ZSpotify.invoke_url_with_params(SAVED_TRACKS_URL, limit=limit, offset=offset) + resp = ZSpotify.invoke_url_with_params( + SAVED_TRACKS_URL, limit=limit, offset=offset) offset += limit songs.extend(resp[ITEMS]) if len(resp[ITEMS]) < limit: @@ -53,24 +54,29 @@ def get_song_info(song_id) -> Tuple[List[str], str, str, Any, Any, Any, Any, Any # noinspection PyBroadException def download_track(track_id: str, extra_paths='', prefix=False, prefix_value='', disable_progressbar=False) -> None: """ Downloads raw song audio from Spotify """ - download_directory = os.path.join(os.path.dirname(__file__), ZSpotify.get_config(ROOT_PATH), extra_paths) + try: (artists, album_name, name, image_url, release_year, disc_number, track_number, scraped_song_id, is_playable) = get_song_info(track_id) + if ZSpotify.get_config(SPLIT_ALBUM_DISCS): + download_directory = os.path.join(os.path.dirname( + __file__), ZSpotify.get_config(ROOT_PATH), extra_paths, f'Disc {disc_number}') + else: + download_directory = os.path.join(os.path.dirname( + __file__), ZSpotify.get_config(ROOT_PATH), extra_paths) + song_name = artists[0] + ' - ' + name if prefix: song_name = f'{prefix_value.zfill(2)} - {song_name}' if prefix_value.isdigit( ) else f'{prefix_value} - {song_name}' - if ZSpotify.get_config(SPLIT_ALBUM_DISCS): - filename = os.path.join(download_directory, f'Disc {disc_number}', - f'{song_name}.{ZSpotify.get_config(DOWNLOAD_FORMAT)}') - else: - filename = os.path.join(download_directory, - f'{song_name}.{ZSpotify.get_config(DOWNLOAD_FORMAT)}') - except Exception: + filename = os.path.join( + download_directory, f'{song_name}.{ZSpotify.get_config(DOWNLOAD_FORMAT)}') + + except Exception as e: print('### SKIPPING SONG - FAILED TO QUERY METADATA ###') + print(e) else: try: if not is_playable: @@ -84,7 +90,8 @@ def download_track(track_id: str, extra_paths='', prefix=False, prefix_value='', if track_id != scraped_song_id: track_id = scraped_song_id track_id = TrackId.from_base62(track_id) - stream = ZSpotify.get_content_stream(track_id, ZSpotify.DOWNLOAD_QUALITY) + stream = ZSpotify.get_content_stream( + track_id, ZSpotify.DOWNLOAD_QUALITY) create_download_directory(download_directory) total_size = stream.input_stream.size @@ -125,4 +132,5 @@ def convert_audio_format(filename) -> None: bitrate = '320k' else: bitrate = '160k' - raw_audio.export(filename, format=ZSpotify.get_config(DOWNLOAD_FORMAT), bitrate=bitrate) + raw_audio.export(filename, format=ZSpotify.get_config( + DOWNLOAD_FORMAT), bitrate=bitrate) From 460ed6722003d2c0d12c68febc5226aa516dcb36 Mon Sep 17 00:00:00 2001 From: Footsiefat <12180913+Footsiefat@users.noreply.github.com> Date: Mon, 25 Oct 2021 17:20:08 +1300 Subject: [PATCH 30/31] tryna fix merge conflicts the lazy way for PR #115 --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9834e1b0..65696cee 100644 --- a/.gitignore +++ b/.gitignore @@ -150,4 +150,7 @@ ZSpotify\ Music/ ZSpotify\ Podcasts/ # Intellij -.idea \ No newline at end of file +.idea + +# Config file +zs_config.json From 179946498690d9768bb025a821699f74aca34753 Mon Sep 17 00:00:00 2001 From: Footsiefat <12180913+Footsiefat@users.noreply.github.com> Date: Mon, 25 Oct 2021 17:20:20 +1300 Subject: [PATCH 31/31] tryna fix merge conflicts the lazy way for PR #155 --- zs_config.json | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 zs_config.json diff --git a/zs_config.json b/zs_config.json deleted file mode 100644 index 5e555afa..00000000 --- a/zs_config.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "ROOT_PATH": "../ZSpotify Music/", - "ROOT_PODCAST_PATH": "../ZSpotify Podcasts/", - "SKIP_EXISTING_FILES": true, - "DOWNLOAD_FORMAT": "mp3", - "FORCE_PREMIUM": false, - "ANTI_BAN_WAIT_TIME": 1, - "OVERRIDE_AUTO_WAIT": false, - "CHUNK_SIZE": 50000, - "SPLIT_ALBUM_DISCS": false -}