diff --git a/CHANGES.md b/CHANGES.md index 1a6aedc..161b588 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,10 +8,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [2.0.0] - ### Migrating from v1.2.6 to v2.0.0 -It is highly recommended to remove your previous `config.yml` due to breaking changes -in v2.0.0 (marked as **[Breaking]** in the below sections), new options being added, and old -ones being removed. You may want to first backup your old configuration for reference. -You can then install spotdl v2.0.0 and remove your current configuration by running: +For v2.0.0 to work correctly, you need to remove your previous `config.yml` due to +breaking changes in v2.0.0 (marked as **[Breaking]** in the below sections), new options being +added, and old ones being removed. You may want to first backup your old configuration for +reference. You can then install spotdl v2.0.0 and remove your current configuration by +running: ``` $ spotdl --remove-config ``` @@ -42,6 +43,9 @@ All the below changes were made as a part of #690. Such as `-o .mp3` is now written as `-o mp3`. - **[Breaking]** Search format now uses hyphen for word break instead of underscore. Such as `-sf "{artist} - {track_name}"` is now written as `-sf "{artist} - {track-name}"`. +- **[Breaking]** `--write-sucessful` and `--skip` is renamed to `--write-succesful-file` and + `--skip-file` respectively. + Such as `-o .mp3` is now written as `-o mp3`. - Partial re-write and internal API refactor. - Enhance debug log output readability. - Internally adapt to latest changes made in Spotipy library. @@ -51,6 +55,8 @@ All the below changes were made as a part of #690. for the already downloaded track to determine whether to overwrite the already downloaded track, which caused unexpected behvaiours at times. - Codebase is now more modular making it easier to use spotdl in python scripts. +- `config.yml` now uses underscores for separating between argument words instead of + hyphens for better compatibility with `argparse`. ### Optimized - Track download and encoding now happen parallely instead of sequentially making spotdl diff --git a/spotdl/command_line/__main__.py b/spotdl/command_line/__main__.py index ab3f0b5..3b734e6 100644 --- a/spotdl/command_line/__main__.py +++ b/spotdl/command_line/__main__.py @@ -31,10 +31,16 @@ def set_logger(level): def main(): - argument_handler = get_arguments() - logging_level = argument_handler.get_logging_level() - logger = set_logger(logging_level) + try: + argument_handler = get_arguments() + except ArgumentError as e: + logger = set_logger(logging.INFO) + logger.info(e.args[0]) + sys.exit(5) + logging_level = argument_handler.get_logging_level() + print(logging_level) + logger = set_logger(logging_level) try: spotdl = Spotdl(argument_handler) except ArgumentError as e: diff --git a/spotdl/command_line/arguments.py b/spotdl/command_line/arguments.py index 7d22b00..625595c 100644 --- a/spotdl/command_line/arguments.py +++ b/spotdl/command_line/arguments.py @@ -25,7 +25,7 @@ _LOG_LEVELS = { if os.path.isfile(spotdl.config.DEFAULT_CONFIG_FILE): saved_config = spotdl.config.read_config(spotdl.config.DEFAULT_CONFIG_FILE) else: - saved_config = {} + saved_config = {"spotify-downloader": {}} _CONFIG_BASE = spotdl.util.merge( spotdl.config.DEFAULT_CONFIGURATION, @@ -34,15 +34,33 @@ _CONFIG_BASE = spotdl.util.merge( def get_arguments(config_base=_CONFIG_BASE): - defaults = config_base["spotify-downloader"] parser = argparse.ArgumentParser( - description="Download and convert tracks from Spotify, Youtube etc.", + description="Download and convert tracks from Spotify, Youtube, etc.", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) + defaults = config_base["spotify-downloader"] + + to_remove_config = "--remove-config" in sys.argv[1:] + if not to_remove_config and "download-only-metadata" in defaults: + raise ArgumentError( + "The default configuration file currently set is not suitable for spotdl>=2.0.0.\n" + "You need to remove your previous `config.yml` due to breaking changes\n" + "introduced in v2.0.0, new options being added, and old ones being removed\n" + "You may want to first backup your old configuration for reference. You can\n" + "then remove your current configuration by running:\n" + "```\n" + "$ spotdl --remove-config\n" + "```\n" + "spotdl will automatically generate a new configuration file on the next run.\n" + "You can then replace the appropriate fields in the newly generated\n" + "configuration file by referring to your old configuration file.\n\n" + "For the list of OTHER BREAKING CHANGES and release notes check out:\n" + "https://github.com/ritiek/spotify-downloader/releases/tag/v2.0.0" + ) + # `--remove-config` does not require the any of the group arguments to be passed. - require_group_args = not "--remove-config" in sys.argv[1:] - group = parser.add_mutually_exclusive_group(required=require_group_args) + group = parser.add_mutually_exclusive_group(required=not to_remove_config) group.add_argument( "-s", @@ -188,24 +206,16 @@ def get_arguments(config_base=_CONFIG_BASE): "generating filenames", action="store_true", ) - parser.add_argument( - "-ll", - "--log-level", - default=defaults["log_level"], - choices=_LOG_LEVELS.keys(), - type=str.upper, - help="set log verbosity", - ) parser.add_argument( "-sk", - "--skip", - default=defaults["skip"], + "--skip-file", + default=defaults["skip_file"], help="path to file containing tracks to skip", ) parser.add_argument( "-w", - "--write-successful", - default=defaults["write_successful"], + "--write-successful-file", + default=defaults["write_successful_file"], help="path to file to write successful tracks to", ) parser.add_argument( @@ -218,6 +228,14 @@ def get_arguments(config_base=_CONFIG_BASE): default=defaults["spotify_client_secret"], help=argparse.SUPPRESS, ) + parser.add_argument( + "-ll", + "--log-level", + default=defaults["log_level"], + choices=_LOG_LEVELS.keys(), + type=str.upper, + help="set log verbosity", + ) parser.add_argument( "-c", "--config", @@ -268,7 +286,7 @@ class ArgumentHandler: return self.configured_args def get_logging_level(self): - return _LOG_LEVELS[self.args["log_level"]] + return _LOG_LEVELS[self.configured_args["log_level"]] def run_errands(self): args = self.get_configured_args() diff --git a/spotdl/command_line/lib.py b/spotdl/command_line/lib.py index 204d0bb..d813107 100644 --- a/spotdl/command_line/lib.py +++ b/spotdl/command_line/lib.py @@ -326,8 +326,8 @@ class Spotdl: except (NoYouTubeVideoFoundError, NoYouTubeVideoMatchError) as e: logger.error("{err}".format(err=e.args[0])) else: - if self.arguments["write_successful"]: - with open(self.arguments["write_successful"], "a") as fout: + if self.arguments["write_sucessful_file"]: + with open(self.arguments["write_sucessful_file"], "a") as fout: fout.write(track) finally: spotdl.util.writelines_to_nonbinary_file(path, tracks[position:]) @@ -397,8 +397,8 @@ class Spotdl: tracks.append(current_track) else: tracks_count -= 1 - if self.arguments["write_successful"]: - with open(self.arguments["write_successful"], "a") as fout: + if self.arguments["write_sucessful_file"]: + with open(self.arguments["write_sucessful_file"], "a") as fout: fout.write(current_track) finally: current_iteration += 1 diff --git a/spotdl/config.py b/spotdl/config.py index c979292..727584c 100644 --- a/spotdl/config.py +++ b/spotdl/config.py @@ -10,7 +10,6 @@ DEFAULT_CONFIGURATION = { "spotify-downloader": { "manual": False, "no_metadata": False, - "no_fallback_metadata": False, "no_encode": False, "overwrite": "prompt", "quality": "best", @@ -18,19 +17,16 @@ DEFAULT_CONFIGURATION = { "output_ext": "mp3", "write_to": None, "trim_silence": False, - "download_only_metadata": False, + "search_format": "{artist} - {track-name} lyrics", "dry_run": False, - "music_videos_only": False, "no_spaces": False, # "processor": "synchronous", "output_file": "{artist} - {track-name}.{output-ext}", - "search_format": "{artist} - {track-name} lyrics", - "youtube_api_key": None, - "skip": None, - "write_successful": None, - "log_level": "INFO", + "skip_file": None, + "write_successful_file": None, "spotify_client_id": "4fe3fecfe5334023a1472516cc99d805", "spotify_client_secret": "0f02b7c483c04257984695007a4a8d5c", + "log_level": "INFO", } } diff --git a/spotdl/util.py b/spotdl/util.py index bab2234..dc4c8f9 100644 --- a/spotdl/util.py +++ b/spotdl/util.py @@ -44,10 +44,15 @@ class ThreadWithReturnValue(threading.Thread): def merge(base, overrider): - """ Override default dict with config dict. """ - merger = base.copy() - merger.update(overrider) - return merger + """ Override base dict with an overrider dict, recursively. """ + for key, value in base.items(): + if isinstance(value, dict): + subitem = overrider.setdefault(key, {}) + merge(value, subitem) + else: + overrider[key] = value + + return overrider def prompt_user_for_selection(items):