diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index a368df701..e8e7057d4 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -110,4 +110,4 @@ jobs: uses: softprops/action-gh-release@v1 with: files: | - dist/spotdl* + dist/spotdl*-aarch64 diff --git a/README.md b/README.md index bc5412993..ffe9f1ec3 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Refer to our [Installation Guide](https://spotdl.rtfd.io/en/latest/installation/ ### Other options - Prebuilt Executable - - You can download the latest version from from the [Releases Tab](https://github.com/spotDL/spotify-downloader/releases) + - You can download the latest version from the [Releases Tab](https://github.com/spotDL/spotify-downloader/releases) - On Termux - `curl -L https://raw.githubusercontent.com/spotDL/spotify-downloader/master/scripts/termux.sh | sh` - Arch @@ -70,7 +70,7 @@ Refer to our [Installation Guide](https://spotdl.rtfd.io/en/latest/installation/ If using FFmpeg only for spotDL, you can install FFmpeg to your local directory. `spotdl --download-ffmpeg` will download FFmpeg to your spotDL installation directory. -We reccomend the above option, but if you want to install FFmpeg system-wide, +We recommend the above option, but if you want to install FFmpeg system-wide, - [Windows Tutorial](https://windowsloop.com/install-ffmpeg-windows-10/) - OSX - `brew install ffmpeg` diff --git a/docs/usage.md b/docs/usage.md index 0a7321559..be16465fd 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -216,6 +216,7 @@ If you don't want config to load automatically change `load_config` option in co "overwrite": "skip", "client_id": "5f573c9620494bae87890c0f08a60293", "client_secret": "212476d9b0f3472eaa762d90b19b0ba8", + "auth_token": null, "user_auth": false, "search_query": null, "filter_results": true, @@ -263,6 +264,8 @@ Spotify options: The client id to use when logging in to Spotify. --client-secret CLIENT_SECRET The client secret to use when logging in to Spotify. + --auth-token AUTH_TOKEN + The authorisation token to use directly to log in to Spotify. --cache-path CACHE_PATH The path where spotipy cache file will be stored. --no-cache Disable caching (both requests and token). diff --git a/pyproject.toml b/pyproject.toml index 0d52a3f6f..109d08040 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "spotdl" -version = "4.0.0-rc.2" +version = "4.0.0-rc.3" description = "Download your Spotify playlists and songs along with album art and metadata" license = "MIT" authors = ["spotDL Team "] diff --git a/spotdl/_version.py b/spotdl/_version.py index b7e9dcb62..2f1d72731 100644 --- a/spotdl/_version.py +++ b/spotdl/_version.py @@ -2,4 +2,4 @@ Version module for spotdl. """ -__version__ = "4.0.0-rc.2" +__version__ = "4.0.0-rc.3" diff --git a/spotdl/console/download.py b/spotdl/console/download.py index 8f5c327be..a480810bf 100644 --- a/spotdl/console/download.py +++ b/spotdl/console/download.py @@ -43,4 +43,6 @@ def download( if save_path: # Save the songs to a file with open(save_path, "w", encoding="utf-8") as save_file: - json.dump(songs, save_file, indent=4, ensure_ascii=False) + json.dump( + [song.json for song in songs], save_file, indent=4, ensure_ascii=False + ) diff --git a/spotdl/console/entry_point.py b/spotdl/console/entry_point.py index 17130ce34..8d58a5bc6 100644 --- a/spotdl/console/entry_point.py +++ b/spotdl/console/entry_point.py @@ -144,21 +144,16 @@ def entry_point(): config = get_config() # Create settings dict - # If the setting in config file is the same as - # the default setting, it will be ignored - # and argument value will be used + # Argument value has always the priority, then the config file + # value, and if neither are set, use default value settings = {} for key, default_value in DEFAULT_CONFIG.items(): argument_val = arguments.__dict__.get(key) config_val = config.get(key) - if argument_val is not None and config_val == default_value: + if argument_val is not None: settings[key] = argument_val - elif ( - argument_val is None - and config_val != default_value - and config_val is not None - ): + elif config_val is not None: settings[key] = config_val else: settings[key] = default_value @@ -183,6 +178,7 @@ def entry_point(): SpotifyClient.init( client_id=settings["client_id"], client_secret=settings["client_secret"], + auth_token=settings["auth_token"], user_auth=settings["user_auth"], cache_path=settings["cache_path"], no_cache=settings["no_cache"], diff --git a/spotdl/download/progress_handler.py b/spotdl/download/progress_handler.py index a7c8594b3..e21b15407 100644 --- a/spotdl/download/progress_handler.py +++ b/spotdl/download/progress_handler.py @@ -463,7 +463,7 @@ def notify_error(self, message: str, traceback: Exception) -> None: self.parent.debug(message) self.parent.error(f"{traceback.__class__.__name__}: {traceback}") - def notify_download_complete(self, status="Embeding metadata") -> None: + def notify_download_complete(self, status="Embedding metadata") -> None: """ Notifies the progress handler that the song has been downloaded. diff --git a/spotdl/utils/arguments.py b/spotdl/utils/arguments.py index 2c4281884..607729e8f 100644 --- a/spotdl/utils/arguments.py +++ b/spotdl/utils/arguments.py @@ -12,7 +12,6 @@ from spotdl import _version from spotdl.download.progress_handler import NAME_TO_LEVEL from spotdl.utils.ffmpeg import FFMPEG_FORMATS -from spotdl.utils.config import DEFAULT_CONFIG from spotdl.utils.formatter import VARS from spotdl.download.downloader import ( AUDIO_PROVIDERS, @@ -145,7 +144,6 @@ def parse_main_options(parser: _ArgumentGroup): dest="audio_providers", nargs="*", choices=AUDIO_PROVIDERS, - default=DEFAULT_CONFIG["audio_providers"], help="The audio provider to use. You can provide more than one for fallback.", ) @@ -155,7 +153,6 @@ def parse_main_options(parser: _ArgumentGroup): dest="lyrics_providers", nargs="*", choices=LYRICS_PROVIDERS.keys(), - default=DEFAULT_CONFIG["lyrics_providers"], help="The lyrics provider to use. You can provide more than one for fallback.", ) @@ -173,16 +170,13 @@ def parse_main_options(parser: _ArgumentGroup): # Add search query argument parser.add_argument( "--search-query", - default=DEFAULT_CONFIG["search_query"], help=f"The search query to use, available variables: {', '.join(VARS)}", ) # Add don't filter results argument parser.add_argument( "--dont-filter-results", - action="store_false", dest="filter_results", - default=DEFAULT_CONFIG["filter_results"], help="Disable filtering results.", ) @@ -198,45 +192,45 @@ def parse_spotify_options(parser: _ArgumentGroup): # Add login argument parser.add_argument( "--user-auth", - action="store_true", - default=DEFAULT_CONFIG["user_auth"], help="Login to Spotify using OAuth.", ) # Add client id argument parser.add_argument( "--client-id", - default=DEFAULT_CONFIG["client_id"], help="The client id to use when logging in to Spotify.", ) # Add client secret argument parser.add_argument( "--client-secret", - default=DEFAULT_CONFIG["client_secret"], help="The client secret to use when logging in to Spotify.", ) + # Add auth token argument + parser.add_argument( + "--auth-token", + help="The authorisation token to use directly to log in to Spotify.", + ) + # Add cache path argument parser.add_argument( "--cache-path", type=str, - default=DEFAULT_CONFIG["cache_path"], help="The path where spotipy cache file will be stored.", ) # Add no cache argument parser.add_argument( "--no-cache", - action="store_true", - default=DEFAULT_CONFIG["no_cache"], + action="store_const", + const=True, help="Disable caching (both requests and token).", ) # Add cookie file argument parser.add_argument( "--cookie-file", - default=DEFAULT_CONFIG["cookie_file"], help="Path to cookies file.", ) @@ -252,14 +246,12 @@ def parse_ffmpeg_options(parser: _ArgumentGroup): # Add ffmpeg executable argument parser.add_argument( "--ffmpeg", - default=DEFAULT_CONFIG["ffmpeg"], help="The ffmpeg executable to use.", ) # Add search threads argument parser.add_argument( "--threads", - default=DEFAULT_CONFIG["threads"], type=int, help="The number of threads to use when downloading songs.", ) @@ -284,7 +276,6 @@ def parse_ffmpeg_options(parser: _ArgumentGroup): "256k", "320k", ], - default=DEFAULT_CONFIG["bitrate"], type=str.lower, help="The constant bitrate to use for the output file.", ) @@ -293,7 +284,6 @@ def parse_ffmpeg_options(parser: _ArgumentGroup): parser.add_argument( "--ffmpeg-args", type=str, - default=DEFAULT_CONFIG["ffmpeg_args"], help="Additional ffmpeg arguments passed as a string.", ) @@ -310,7 +300,6 @@ def parse_output_options(parser: _ArgumentGroup): parser.add_argument( "--format", choices=FFMPEG_FORMATS.keys(), - default=DEFAULT_CONFIG["format"], help="The format to download the song in.", ) @@ -318,7 +307,6 @@ def parse_output_options(parser: _ArgumentGroup): parser.add_argument( "--save-file", type=str, - default=DEFAULT_CONFIG["save_file"], help=( "The file to save/load the songs data from/to. " "It has to end with .spotdl. " @@ -331,8 +319,8 @@ def parse_output_options(parser: _ArgumentGroup): # Add preload argument parser.add_argument( "--preload", - action="store_true", - default=DEFAULT_CONFIG["preload"], + action="store_const", + const=True, help="Preload the download url to speed up the download process.", ) @@ -340,7 +328,6 @@ def parse_output_options(parser: _ArgumentGroup): parser.add_argument( "--output", type=str, - default=DEFAULT_CONFIG["output"], help=f"Specify the downloaded file name format, available variables: {', '.join(VARS)}", ) @@ -348,7 +335,6 @@ def parse_output_options(parser: _ArgumentGroup): parser.add_argument( "--m3u", type=str, - default=DEFAULT_CONFIG["m3u"], help="Name of the m3u file to save the songs to.", ) @@ -356,32 +342,31 @@ def parse_output_options(parser: _ArgumentGroup): parser.add_argument( "--overwrite", choices={"force", "skip"}, - default=DEFAULT_CONFIG["overwrite"], help="Overwrite existing files.", ) # Option to restrict filenames for easier handling in the shell parser.add_argument( "--restrict", - default=DEFAULT_CONFIG["restrict"], + action="store_const", + const=True, help="Restrict filenames to ASCII only", - action="store_true", ) # Option to print errors on exit, useful for long playlist parser.add_argument( "--print-errors", - default=DEFAULT_CONFIG["print_errors"], + action="store_const", + const=True, help="Print errors (wrong songs, failed downloads etc) on exit, useful for long playlist", - action="store_true", ) # Option to use sponsor block parser.add_argument( "--sponsor-block", - default=DEFAULT_CONFIG["sponsor_block"], + action="store_const", + const=True, help="Use the sponsor block to download songs from yt/ytm.", - action="store_true", ) @@ -403,16 +388,16 @@ def parse_misc_options(parser: _ArgumentGroup): # Add simple tui argument parser.add_argument( "--simple-tui", - action="store_true", - default=DEFAULT_CONFIG["simple_tui"], + action="store_const", + const=True, help="Use a simple tui.", ) # Add headless argument parser.add_argument( "--headless", - action="store_true", - default=DEFAULT_CONFIG["headless"], + action="store_const", + const=True, help="Run in headless mode.", ) diff --git a/spotdl/utils/config.py b/spotdl/utils/config.py index d080f1971..90fcb7d50 100644 --- a/spotdl/utils/config.py +++ b/spotdl/utils/config.py @@ -131,6 +131,7 @@ def get_config() -> Dict[str, Any]: "overwrite": "skip", "client_id": "5f573c9620494bae87890c0f08a60293", "client_secret": "212476d9b0f3472eaa762d90b19b0ba8", + "auth_token": None, "user_auth": False, "search_query": None, "filter_results": True, diff --git a/spotdl/utils/spotify.py b/spotdl/utils/spotify.py index ded55b4f4..1e2bc7491 100644 --- a/spotdl/utils/spotify.py +++ b/spotdl/utils/spotify.py @@ -53,6 +53,7 @@ def init( # pylint: disable=bad-mcs-method-argument self, client_id: str, client_secret: str, + auth_token: Optional[str] = None, user_auth: bool = False, cache_path: Optional[str] = None, no_cache: bool = False, @@ -64,6 +65,7 @@ def init( # pylint: disable=bad-mcs-method-argument ### Arguments - client_id: The client ID of the application. - client_secret: The client secret of the application. + - auth_token: The access token to use. - user_auth: Whether or not to use user authentication. - cache_path: The path to the cache file. - no_cache: Whether or not to use the cache. @@ -101,12 +103,15 @@ def init( # pylint: disable=bad-mcs-method-argument client_secret=client_secret, cache_handler=cache_handler, ) + if auth_token is not None: + credential_manager = None self.user_auth = user_auth self.no_cache = no_cache # Create instance self._instance = super().__call__( + auth=auth_token, auth_manager=credential_manager, status_forcelist=(429, 500, 502, 503, 504, 404), ) @@ -129,6 +134,7 @@ def __init__(self, *args, **kwargs): Initializes the SpotifyClient. ### Arguments + - auth: The access token to use. - auth_manager: The auth manager to use. """ diff --git a/tests/conftest.py b/tests/conftest.py index 6f9226c7d..78a5dac51 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -39,6 +39,7 @@ def returncode(self): def new_initialize( client_id, client_secret, + auth_token=None, user_auth=False, cache_path=None, no_cache=True, @@ -51,6 +52,7 @@ def new_initialize( return ORIGINAL_INITIALIZE( client_id=client_id, client_secret=client_secret, + auth_token=auth_token, user_auth=user_auth, cache_path=cache_path, no_cache=True, diff --git a/tests/utils/test_arguments.py b/tests/utils/test_arguments.py index 998bc91cf..2c31604ad 100644 --- a/tests/utils/test_arguments.py +++ b/tests/utils/test_arguments.py @@ -1,6 +1,6 @@ import pytest -from spotdl.utils.arguments import parse_arguments, DEFAULT_CONFIG +from spotdl.utils.arguments import parse_arguments def test_parse_arguments():