From 8a450fd1eec8465c5a8cf90e7df1909ec7a45330 Mon Sep 17 00:00:00 2001 From: jonathan2384 <93066841+jonathan2384@users.noreply.github.com> Date: Tue, 26 Oct 2021 12:58:11 +0900 Subject: [PATCH 01/17] Fix for #127 Simple change to include ?include_groups=album%2Csingle in the api request when function get_artist_albums is used. Sorry if there is something wrong with this, I have not used Python before. It seems to work as expected in my testing. --- zspotify/album.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zspotify/album.py b/zspotify/album.py index 19004d11..2070da24 100644 --- a/zspotify/album.py +++ b/zspotify/album.py @@ -33,7 +33,7 @@ def get_album_name(album_id): def get_artist_albums(artist_id): """ Returns artist's albums """ - resp = ZSpotify.invoke_url(f'{ARTIST_URL}/{artist_id}/albums') + resp = ZSpotify.invoke_url(f'{ARTIST_URL}/{artist_id}/albums?include_groups=album%2Csingle') # 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 From 3167c44d94f2c8884e1879fe37ee55652d829858 Mon Sep 17 00:00:00 2001 From: mockuser404 <53865090+mockuser404@users.noreply.github.com> Date: Wed, 27 Oct 2021 00:00:19 +0530 Subject: [PATCH 02/17] use os.path.join fixes #142 --- zspotify/podcast.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/zspotify/podcast.py b/zspotify/podcast.py index eea36c1b..4b0b3b60 100644 --- a/zspotify/podcast.py +++ b/zspotify/podcast.py @@ -50,11 +50,15 @@ def download_episode(episode_id) -> None: episode_id = EpisodeId.from_base62(episode_id) stream = ZSpotify.get_content_stream(episode_id, ZSpotify.DOWNLOAD_QUALITY) - download_directory = os.path.dirname(__file__) + ZSpotify.get_config(ROOT_PODCAST_PATH) + extra_paths + download_directory = os.path.join( + os.path.dirname(__file__), + ZSpotify.get_config(ROOT_PODCAST_PATH), + extra_paths, + ) create_download_directory(download_directory) total_size = stream.input_stream.size - with open(download_directory + filename + MusicFormat.OGG.value, + with open(os.path.join(download_directory, f"{filename}.ogg"), 'wb') as file, tqdm( desc=filename, total=total_size, @@ -67,4 +71,4 @@ def download_episode(episode_id) -> None: stream.input_stream.stream().read(ZSpotify.get_config(CHUNK_SIZE)))) # convert_audio_format(ROOT_PODCAST_PATH + - # extra_paths + filename + '.ogg') \ No newline at end of file + # extra_paths + filename + '.ogg') From b01c96bb1c4de91dfde06872b56b647c6eab1097 Mon Sep 17 00:00:00 2001 From: mockuser404 <53865090+mockuser404@users.noreply.github.com> Date: Wed, 27 Oct 2021 00:31:13 +0530 Subject: [PATCH 03/17] update ban info on readme --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 060509c2..52b64d0d 100644 --- a/README.md +++ b/README.md @@ -70,8 +70,9 @@ Create and run 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. +~~Currently no user has reported their account getting banned after using ZSpotify.~~ +**There have been 2-3 reports from users who received account bans from Spotify for using this tool**. +We are working on making it less likely for your account to get banned, please be patient. **Use ZSpotify at your own risk**, the developers of ZSpotify are not responsible if your account gets banned. ### What do I do if I see "Your session has been terminated"? From 5248995a7bf4c7df88b0479676e86a4c4eaeceb3 Mon Sep 17 00:00:00 2001 From: mockuser404 <53865090+mockuser404@users.noreply.github.com> Date: Wed, 27 Oct 2021 00:31:56 +0530 Subject: [PATCH 04/17] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 52b64d0d..bd74f7c2 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ Create and run 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.~~ + **There have been 2-3 reports from users who received account bans from Spotify for using this tool**. We are working on making it less likely for your account to get banned, please be patient. **Use ZSpotify at your own risk**, the developers of ZSpotify are not responsible if your account gets banned. From 524c6fe075cf596003091fb73e123bfbd99e9346 Mon Sep 17 00:00:00 2001 From: mockuser404 <53865090+mockuser404@users.noreply.github.com> Date: Wed, 27 Oct 2021 00:32:53 +0530 Subject: [PATCH 05/17] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bd74f7c2..95e55254 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,9 @@ Create and run a container from the image: ~~Currently no user has reported their account getting banned after using ZSpotify.~~ **There have been 2-3 reports from users who received account bans from Spotify for using this tool**. + We are working on making it less likely for your account to get banned, please be patient. + **Use ZSpotify at your own risk**, the developers of ZSpotify are not responsible if your account gets banned. ### What do I do if I see "Your session has been terminated"? From eba1ec11c78645c2bcfbb7ec04f2df4faf4cd1cf Mon Sep 17 00:00:00 2001 From: shirt <2660574+shirt-dev@users.noreply.github.com> Date: Tue, 26 Oct 2021 15:06:17 -0400 Subject: [PATCH 06/17] Use ffmpeg instead of pydub, support ogg stream copy --- requirements.txt | 6 +++--- zspotify/const.py | 23 +++++++++++++++++++++++ zspotify/track.py | 46 ++++++++++++++++++++++++++-------------------- 3 files changed, 52 insertions(+), 23 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4f4b877e..7163d55e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ +ffmpy git+https://github.com/kokarare1212/librespot-python music_tag -pydub Pillow -tqdm -tabulate \ No newline at end of file +tabulate +tqdm \ No newline at end of file diff --git a/zspotify/const.py b/zspotify/const.py index 3e4d9b0f..3708c58e 100644 --- a/zspotify/const.py +++ b/zspotify/const.py @@ -96,11 +96,34 @@ CHUNK_SIZE = 'CHUNK_SIZE' SPLIT_ALBUM_DISCS = 'SPLIT_ALBUM_DISCS' +BITRATE = 'BITRATE' + +CODEC_MAP = { + 'aac': 'aac', + 'fdk_aac': 'libfdk_aac', + 'm4a': 'aac', + 'mp3': 'libmp3lame', + 'ogg': 'copy', + 'opus': 'libopus', + 'vorbis': 'copy', +} + +EXT_MAP = { + 'aac': 'm4a', + 'fdk_aac': 'm4a', + 'm4a': 'm4a', + 'mp3': 'mp3', + 'ogg': 'ogg', + 'opus': 'ogg', + 'vorbis': 'ogg', +} + CONFIG_DEFAULT_SETTINGS = { 'ROOT_PATH': '../ZSpotify Music/', 'ROOT_PODCAST_PATH': '../ZSpotify Podcasts/', 'SKIP_EXISTING_FILES': True, 'DOWNLOAD_FORMAT': 'mp3', + 'BITRATE': '160k', 'FORCE_PREMIUM': False, 'ANTI_BAN_WAIT_TIME': 1, 'OVERRIDE_AUTO_WAIT': False, diff --git a/zspotify/track.py b/zspotify/track.py index 423cce0c..87452de2 100644 --- a/zspotify/track.py +++ b/zspotify/track.py @@ -2,16 +2,14 @@ import os import time from typing import Any, Tuple, List -from librespot.audio.decoders import AudioQuality from librespot.metadata import TrackId -from pydub import AudioSegment +from ffmpy import FFmpeg from tqdm import tqdm from const import TRACKS, ALBUM, NAME, ITEMS, DISC_NUMBER, TRACK_NUMBER, IS_PLAYABLE, ARTISTS, IMAGES, URL, \ RELEASE_DATE, ID, TRACKS_URL, SAVED_TRACKS_URL, SPLIT_ALBUM_DISCS, ROOT_PATH, DOWNLOAD_FORMAT, CHUNK_SIZE, \ - SKIP_EXISTING_FILES, ANTI_BAN_WAIT_TIME, OVERRIDE_AUTO_WAIT -from utils import sanitize_data, set_audio_tags, set_music_thumbnail, create_download_directory, \ - MusicFormat + SKIP_EXISTING_FILES, ANTI_BAN_WAIT_TIME, OVERRIDE_AUTO_WAIT, BITRATE, CODEC_MAP, EXT_MAP +from utils import sanitize_data, set_audio_tags, set_music_thumbnail, create_download_directory from zspotify import ZSpotify @@ -72,7 +70,7 @@ def download_track(track_id: str, extra_paths='', prefix=False, prefix_value='', ) else f'{prefix_value} - {song_name}' filename = os.path.join( - download_directory, f'{song_name}.{ZSpotify.get_config(DOWNLOAD_FORMAT)}') + download_directory, f'{song_name}.{EXT_MAP.get(ZSpotify.get_config(DOWNLOAD_FORMAT))}') except Exception as e: print('### SKIPPING SONG - FAILED TO QUERY METADATA ###') @@ -107,11 +105,10 @@ def download_track(track_id: str, extra_paths='', prefix=False, prefix_value='', p_bar.update(file.write( stream.input_stream.stream().read(ZSpotify.get_config(CHUNK_SIZE)))) - if ZSpotify.get_config(DOWNLOAD_FORMAT) == 'mp3': - convert_audio_format(filename) - set_audio_tags(filename, artists, name, album_name, - release_year, disc_number, track_number) - set_music_thumbnail(filename, image_url) + convert_audio_format(filename) + set_audio_tags(filename, artists, name, album_name, + release_year, disc_number, track_number) + set_music_thumbnail(filename, image_url) if not ZSpotify.get_config(OVERRIDE_AUTO_WAIT): time.sleep(ZSpotify.get_config(ANTI_BAN_WAIT_TIME)) @@ -124,13 +121,22 @@ def download_track(track_id: str, extra_paths='', prefix=False, prefix_value='', def convert_audio_format(filename) -> None: - """ Converts raw audio into playable mp3 """ - # print('### CONVERTING TO ' + MUSIC_FORMAT.upper() + ' ###') - raw_audio = AudioSegment.from_file(filename, format=MusicFormat.OGG.value, - frame_rate=44100, channels=2, sample_width=2) - if ZSpotify.DOWNLOAD_QUALITY == AudioQuality.VERY_HIGH: - bitrate = '320k' + """ Converts raw audio into playable file """ + temp_filename = f'{os.path.splitext(filename)[0]}.tmp' + os.replace(filename, temp_filename) + + download_format = ZSpotify.get_config(DOWNLOAD_FORMAT) + file_codec = CODEC_MAP.get(download_format, "copy") + if file_codec != 'copy': + bitrate = ZSpotify.get_config(BITRATE) else: - bitrate = '160k' - raw_audio.export(filename, format=ZSpotify.get_config( - DOWNLOAD_FORMAT), bitrate=bitrate) + bitrate = None + + ff_m = FFmpeg( + global_options=['-y', '-hide_banner', '-loglevel error'], + inputs={temp_filename: None}, + outputs={filename: ['-c:a', file_codec] + ['-b:a', bitrate] if bitrate else []} + ) + ff_m.run() + if os.path.exists(temp_filename): + os.remove(temp_filename) From 709a8cbfcd73cb3a7308e7650458e1c01f6eb4da Mon Sep 17 00:00:00 2001 From: mockuser404 <53865090+mockuser404@users.noreply.github.com> Date: Wed, 27 Oct 2021 00:36:23 +0530 Subject: [PATCH 07/17] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 95e55254..444581cd 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ Create and run a container from the image: **There have been 2-3 reports from users who received account bans from Spotify for using this tool**. We are working on making it less likely for your account to get banned, please be patient. +We suggest you wait till this fix has been made to avoid ban, or if you are in a hurry, make sure to not use your primary account. **Use ZSpotify at your own risk**, the developers of ZSpotify are not responsible if your account gets banned. From 48db26860f9dd7976dd06fb8eb089296da236403 Mon Sep 17 00:00:00 2001 From: Leonard Sheng Sheng Lee Date: Tue, 26 Oct 2021 21:28:43 +0200 Subject: [PATCH 08/17] feature: pylint-exit Pylint always fails. Pylint-exit displays fatal errors only. --- .github/workflows/pylint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 0b88fe7a..c742569a 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -16,8 +16,8 @@ jobs: - name: Setup Pylint run: | python -m pip install --upgrade pip - pip install pylint + pip install pylint pylint-exit pip install -r requirements.txt - name: Run Pylint run: | - pylint $(git ls-files '*.py') + pylint $(git ls-files '*.py') || pylint-exit $? From 27d6c791ce7c0d9601fe28adfac922637202749e Mon Sep 17 00:00:00 2001 From: shirt <2660574+shirt-dev@users.noreply.github.com> Date: Tue, 26 Oct 2021 16:14:31 -0400 Subject: [PATCH 09/17] Fix audio copy, set bitrate based on account type if not in config --- zspotify/const.py | 3 +-- zspotify/track.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/zspotify/const.py b/zspotify/const.py index 3708c58e..8a0df9ec 100644 --- a/zspotify/const.py +++ b/zspotify/const.py @@ -122,8 +122,7 @@ CONFIG_DEFAULT_SETTINGS = { 'ROOT_PATH': '../ZSpotify Music/', 'ROOT_PODCAST_PATH': '../ZSpotify Podcasts/', 'SKIP_EXISTING_FILES': True, - 'DOWNLOAD_FORMAT': 'mp3', - 'BITRATE': '160k', + 'DOWNLOAD_FORMAT': 'ogg', 'FORCE_PREMIUM': False, 'ANTI_BAN_WAIT_TIME': 1, 'OVERRIDE_AUTO_WAIT': False, diff --git a/zspotify/track.py b/zspotify/track.py index 87452de2..65ad590d 100644 --- a/zspotify/track.py +++ b/zspotify/track.py @@ -2,6 +2,7 @@ import os import time from typing import Any, Tuple, List +from librespot.audio.decoders import AudioQuality from librespot.metadata import TrackId from ffmpy import FFmpeg from tqdm import tqdm @@ -129,13 +130,22 @@ def convert_audio_format(filename) -> None: file_codec = CODEC_MAP.get(download_format, "copy") if file_codec != 'copy': bitrate = ZSpotify.get_config(BITRATE) + if not bitrate: + if ZSpotify.DOWNLOAD_QUALITY == AudioQuality.VERY_HIGH: + bitrate = '320k' + else: + bitrate = '160k' else: bitrate = None + output_params = ['-c:a', file_codec] + if bitrate: + output_params += ['-b:a', bitrate] + ff_m = FFmpeg( global_options=['-y', '-hide_banner', '-loglevel error'], inputs={temp_filename: None}, - outputs={filename: ['-c:a', file_codec] + ['-b:a', bitrate] if bitrate else []} + outputs={filename: output_params} ) ff_m.run() if os.path.exists(temp_filename): From 5358d6f8eeb6b4d513dab3a145a22a9cef282cc5 Mon Sep 17 00:00:00 2001 From: mockuser404 <53865090+mockuser404@users.noreply.github.com> Date: Wed, 27 Oct 2021 12:55:27 +0530 Subject: [PATCH 10/17] fix podcast existing episodes not skipping Skip if existing episodes. --- zspotify/podcast.py | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/zspotify/podcast.py b/zspotify/podcast.py index 4b0b3b60..437e769e 100644 --- a/zspotify/podcast.py +++ b/zspotify/podcast.py @@ -5,11 +5,11 @@ from librespot.audio.decoders import VorbisOnlyAudioQuality from librespot.metadata import EpisodeId from tqdm import tqdm -from const import NAME, ERROR, SHOW, ITEMS, ID, ROOT_PODCAST_PATH, CHUNK_SIZE -from utils import sanitize_data, create_download_directory, MusicFormat +from const import (CHUNK_SIZE, ERROR, ID, ITEMS, NAME, ROOT_PODCAST_PATH, SHOW, + SKIP_EXISTING_FILES) +from utils import MusicFormat, create_download_directory, sanitize_data from zspotify import ZSpotify - EPISODE_INFO_URL = 'https://api.spotify.com/v1/episodes' SHOWS_URL = 'https://api.spotify.com/v1/shows' @@ -55,16 +55,32 @@ def download_episode(episode_id) -> None: ZSpotify.get_config(ROOT_PODCAST_PATH), extra_paths, ) + download_directory = os.path.realpath(download_directory) create_download_directory(download_directory) total_size = stream.input_stream.size - with open(os.path.join(download_directory, f"{filename}.ogg"), - 'wb') as file, tqdm( - desc=filename, - total=total_size, - unit='B', - unit_scale=True, - unit_divisor=1024 + + filepath = os.path.join(download_directory, f"{filename}.ogg") + if ( + os.path.isfile(filepath) + and os.path.getsize(filepath) == total_size + and ZSpotify.get_config(SKIP_EXISTING_FILES) + ): + print( + "\n### SKIPPING:", + podcast_name, + "-", + episode_name, + "(EPISODE ALREADY EXISTS) ###", + ) + return + + with open(filepath, 'wb') as file, tqdm( + desc=filename, + total=total_size, + unit='B', + unit_scale=True, + unit_divisor=1024 ) as bar: for _ in range(int(total_size / ZSpotify.get_config(CHUNK_SIZE)) + 1): bar.update(file.write( From 8e77cea4d1361b2490369194378a44db5f31841d Mon Sep 17 00:00:00 2001 From: mockuser404 <53865090+mockuser404@users.noreply.github.com> Date: Wed, 27 Oct 2021 12:58:14 +0530 Subject: [PATCH 11/17] Update podcast.py --- zspotify/podcast.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zspotify/podcast.py b/zspotify/podcast.py index 437e769e..03bc7b35 100644 --- a/zspotify/podcast.py +++ b/zspotify/podcast.py @@ -7,7 +7,7 @@ from tqdm import tqdm from const import (CHUNK_SIZE, ERROR, ID, ITEMS, NAME, ROOT_PODCAST_PATH, SHOW, SKIP_EXISTING_FILES) -from utils import MusicFormat, create_download_directory, sanitize_data +from utils import create_download_directory, sanitize_data from zspotify import ZSpotify EPISODE_INFO_URL = 'https://api.spotify.com/v1/episodes' From fe6f266b41679492a9d20491185df7c0a05bdc85 Mon Sep 17 00:00:00 2001 From: logykk Date: Wed, 27 Oct 2021 22:49:21 +1300 Subject: [PATCH 12/17] Added realtime downloading for songs --- requirements.txt | 2 ++ zspotify/const.py | 5 ++++- zspotify/track.py | 34 +++++++++++++++++++++++++++------- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/requirements.txt b/requirements.txt index 7163d55e..b1877ecf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,7 @@ ffmpy git+https://github.com/kokarare1212/librespot-python music_tag Pillow +protobuf +pydub tabulate tqdm \ No newline at end of file diff --git a/zspotify/const.py b/zspotify/const.py index 8a0df9ec..4da5d9b7 100644 --- a/zspotify/const.py +++ b/zspotify/const.py @@ -96,6 +96,8 @@ CHUNK_SIZE = 'CHUNK_SIZE' SPLIT_ALBUM_DISCS = 'SPLIT_ALBUM_DISCS' +DOWNLOAD_REAL_TIME = 'DOWNLOAD_REAL_TIME' + BITRATE = 'BITRATE' CODEC_MAP = { @@ -127,5 +129,6 @@ CONFIG_DEFAULT_SETTINGS = { 'ANTI_BAN_WAIT_TIME': 1, 'OVERRIDE_AUTO_WAIT': False, 'CHUNK_SIZE': 50000, - 'SPLIT_ALBUM_DISCS': False + 'SPLIT_ALBUM_DISCS': False, + 'DOWNLOAD_REAL_TIME': True } diff --git a/zspotify/track.py b/zspotify/track.py index 65ad590d..57960496 100644 --- a/zspotify/track.py +++ b/zspotify/track.py @@ -5,11 +5,12 @@ from typing import Any, Tuple, List from librespot.audio.decoders import AudioQuality from librespot.metadata import TrackId from ffmpy import FFmpeg +from pydub import AudioSegment from tqdm import tqdm from const import TRACKS, ALBUM, NAME, ITEMS, DISC_NUMBER, TRACK_NUMBER, IS_PLAYABLE, ARTISTS, IMAGES, URL, \ RELEASE_DATE, ID, TRACKS_URL, SAVED_TRACKS_URL, SPLIT_ALBUM_DISCS, ROOT_PATH, DOWNLOAD_FORMAT, CHUNK_SIZE, \ - SKIP_EXISTING_FILES, ANTI_BAN_WAIT_TIME, OVERRIDE_AUTO_WAIT, BITRATE, CODEC_MAP, EXT_MAP + SKIP_EXISTING_FILES, ANTI_BAN_WAIT_TIME, OVERRIDE_AUTO_WAIT, BITRATE, CODEC_MAP, EXT_MAP, DOWNLOAD_REAL_TIME from utils import sanitize_data, set_audio_tags, set_music_thumbnail, create_download_directory from zspotify import ZSpotify @@ -80,11 +81,11 @@ def download_track(track_id: str, extra_paths='', prefix=False, prefix_value='', try: if not is_playable: print('\n### SKIPPING:', song_name, - '(SONG IS UNAVAILABLE) ###') + '(SONG IS UNAVAILABLE) ###') else: if os.path.isfile(filename) and os.path.getsize(filename) and ZSpotify.get_config(SKIP_EXISTING_FILES): print('\n### SKIPPING:', song_name, - '(SONG ALREADY EXISTS) ###') + '(SONG ALREADY EXISTS) ###') else: if track_id != scraped_song_id: track_id = scraped_song_id @@ -102,13 +103,20 @@ def download_track(track_id: str, extra_paths='', prefix=False, prefix_value='', unit_divisor=1024, disable=disable_progressbar ) as p_bar: - for _ in range(int(total_size / ZSpotify.get_config(CHUNK_SIZE)) + 1): - p_bar.update(file.write( - stream.input_stream.stream().read(ZSpotify.get_config(CHUNK_SIZE)))) + for chunk in range(int(total_size / ZSpotify.get_config(CHUNK_SIZE)) + 1): + data = stream.input_stream.stream().read(ZSpotify.get_config(CHUNK_SIZE)) + if data == b'': + break + p_bar.update(file.write(data)) + if ZSpotify.get_config(DOWNLOAD_REAL_TIME): + if chunk == 0: + pause = get_segment_duration(p_bar) + if pause: + time.sleep(pause) convert_audio_format(filename) set_audio_tags(filename, artists, name, album_name, - release_year, disc_number, track_number) + release_year, disc_number, track_number) set_music_thumbnail(filename, image_url) if not ZSpotify.get_config(OVERRIDE_AUTO_WAIT): @@ -121,6 +129,18 @@ def download_track(track_id: str, extra_paths='', prefix=False, prefix_value='', os.remove(filename) +def get_segment_duration(segment): + """ Returns playback duration of given audio segment """ + sound = AudioSegment( + data = segment, + sample_width = 2, + frame_rate = 44100, + channels = 2 + ) + duration = len(sound) / 5000 + return duration + + def convert_audio_format(filename) -> None: """ Converts raw audio into playable file """ temp_filename = f'{os.path.splitext(filename)[0]}.tmp' From 68cea35fdebcda3458f08409401ad107358e3d79 Mon Sep 17 00:00:00 2001 From: logykk Date: Wed, 27 Oct 2021 23:03:33 +1300 Subject: [PATCH 13/17] Updated documentation --- CHANGELOG.md | 6 ++++++ README.md | 5 +++-- zspotify/const.py | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f93e54e..c7001d48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ ## **Changelog:** +**v2.4 (27 Oct 20212):** +- Added realtime downloading support to avoid account suspensions. +- Fix for downloading by artist. +- Replace audio conversion method for better quality. +- Fix bug when automatically setting audio bitrate. + **v2.3 (25 Oct 2021):** - Moved changelog to seperate file. - Added argument parsing in search function (query results limit and query result types). diff --git a/README.md b/README.md index 444581cd..24bba2f5 100644 --- a/README.md +++ b/README.md @@ -74,8 +74,9 @@ Create and run a container from the image: **There have been 2-3 reports from users who received account bans from Spotify for using this tool**. -We are working on making it less likely for your account to get banned, please be patient. -We suggest you wait till this fix has been made to avoid ban, or if you are in a hurry, make sure to not use your primary account. +We recommend using ZSpotify with a burner account. +Alternatively, there is a configuration option labled ```DOWNLOAD_REAL_TIME```, this limits the download speed to the duration of the song being downloaded thus not appearing suspicious to Spotify. +This option is much slower and is only recommended for premium users who wish to download songs in 320kbps without buying premium on a burner account. **Use ZSpotify at your own risk**, the developers of ZSpotify are not responsible if your account gets banned. diff --git a/zspotify/const.py b/zspotify/const.py index 4da5d9b7..1ec7594a 100644 --- a/zspotify/const.py +++ b/zspotify/const.py @@ -130,5 +130,5 @@ CONFIG_DEFAULT_SETTINGS = { 'OVERRIDE_AUTO_WAIT': False, 'CHUNK_SIZE': 50000, 'SPLIT_ALBUM_DISCS': False, - 'DOWNLOAD_REAL_TIME': True + 'DOWNLOAD_REAL_TIME': False } From 0915741249f3ac711065eaba6f39ac7d49608a8e Mon Sep 17 00:00:00 2001 From: yiannisha Date: Wed, 27 Oct 2021 21:43:35 +0300 Subject: [PATCH 14/17] Added digit prefixes to playlist track names --- zspotify/playlist.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zspotify/playlist.py b/zspotify/playlist.py index 9a1304c2..cf333d1b 100644 --- a/zspotify/playlist.py +++ b/zspotify/playlist.py @@ -52,10 +52,12 @@ def download_playlist(playlist): 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) + enum = 1 for song in p_bar: download_track(song[TRACK][ID], sanitize_data(playlist[NAME].strip()) + '/', - disable_progressbar=True) + prefix=True, prefix_value=str(enum) ,disable_progressbar=True) p_bar.set_description(song[TRACK][NAME]) + enum += 1 def download_from_user_playlist(): From 371de63d21f6b0e4c2173dbb8eb131f7ed385b78 Mon Sep 17 00:00:00 2001 From: yiannisha Date: Wed, 27 Oct 2021 22:14:35 +0300 Subject: [PATCH 15/17] Revert "Added digit prefixes to playlist track names" This reverts commit 0915741249f3ac711065eaba6f39ac7d49608a8e. --- zspotify/playlist.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/zspotify/playlist.py b/zspotify/playlist.py index cf333d1b..9a1304c2 100644 --- a/zspotify/playlist.py +++ b/zspotify/playlist.py @@ -52,12 +52,10 @@ def download_playlist(playlist): 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) - enum = 1 for song in p_bar: download_track(song[TRACK][ID], sanitize_data(playlist[NAME].strip()) + '/', - prefix=True, prefix_value=str(enum) ,disable_progressbar=True) + disable_progressbar=True) p_bar.set_description(song[TRACK][NAME]) - enum += 1 def download_from_user_playlist(): From 9ec6c9d1b102e2ee941e9aff081bbe2a4f6d1217 Mon Sep 17 00:00:00 2001 From: yiannisha Date: Wed, 27 Oct 2021 22:18:39 +0300 Subject: [PATCH 16/17] Added digit prefixes to playlist track names fixes#138 --- zspotify/playlist.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zspotify/playlist.py b/zspotify/playlist.py index 9a1304c2..cf333d1b 100644 --- a/zspotify/playlist.py +++ b/zspotify/playlist.py @@ -52,10 +52,12 @@ def download_playlist(playlist): 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) + enum = 1 for song in p_bar: download_track(song[TRACK][ID], sanitize_data(playlist[NAME].strip()) + '/', - disable_progressbar=True) + prefix=True, prefix_value=str(enum) ,disable_progressbar=True) p_bar.set_description(song[TRACK][NAME]) + enum += 1 def download_from_user_playlist(): From 9a93544cb4fdada72183c6f71d08caa499e978f8 Mon Sep 17 00:00:00 2001 From: el-gringo-alto <22480930+el-gringo-alto@users.noreply.github.com> Date: Thu, 28 Oct 2021 02:21:40 -0400 Subject: [PATCH 17/17] Delete Dockerfile --- Dockerfile | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 83de5a5b..00000000 --- a/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM python:3.9-alpine as base - -RUN apk --update add git ffmpeg - -FROM base as builder -RUN mkdir /install -WORKDIR /install -COPY requirements.txt /requirements.txt -RUN apk add gcc libc-dev zlib zlib-dev jpeg-dev \ - && pip install --prefix="/install" -r /requirements.txt - - -FROM base - -COPY --from=builder /install /usr/local -COPY zspotify /app -COPY *zs_config.json / -WORKDIR /app -ENTRYPOINT ["/usr/local/bin/python", "app.py"]