From d6fe2505f6f19ecac5dba9eff0c5860dc13aee03 Mon Sep 17 00:00:00 2001 From: jbannon Date: Wed, 13 Apr 2022 05:30:29 +0000 Subject: [PATCH] one-off downloader working --- config.yaml | 1 + subscriptions.yaml | 2 - .../cli/subscription_args_parser.py | 80 ++++++++++++++++++- ytdl_subscribe/main.py | 31 ++++--- ytdl_subscribe/subscriptions/soundcloud.py | 1 - ytdl_subscribe/subscriptions/youtube.py | 1 - .../config/subscription_validator.py | 13 ++- 7 files changed, 103 insertions(+), 26 deletions(-) diff --git a/config.yaml b/config.yaml index a2e8fb53..7b058726 100644 --- a/config.yaml +++ b/config.yaml @@ -10,6 +10,7 @@ presets: ytdl_options: format: 'bestaudio[ext=mp3]' output_options: + output_directory: "/tmp/{sanitized_artist}" file_name: "[{album_year}] {sanitized_album}/{track_number_padded} - {sanitized_title}.{ext}" convert_thumbnail: "jpeg" thumbnail_name: "[{album_year}] {sanitized_album}/folder.jpeg" diff --git a/subscriptions.yaml b/subscriptions.yaml index b8c9e13f..397fe554 100644 --- a/subscriptions.yaml +++ b/subscriptions.yaml @@ -22,8 +22,6 @@ bl00dwave: preset: "soundcloud_with_id3_tags" soundcloud: username: bl00dwave - output_options: - output_directory: "/tmp/bl00dwave" overrides: artist: "bl00dwave" genre: "Synthwave / Electronic" diff --git a/ytdl_subscribe/cli/subscription_args_parser.py b/ytdl_subscribe/cli/subscription_args_parser.py index 38e686e3..d821fe63 100644 --- a/ytdl_subscribe/cli/subscription_args_parser.py +++ b/ytdl_subscribe/cli/subscription_args_parser.py @@ -1,12 +1,84 @@ +from argparse import ArgumentError +from typing import Dict from typing import List +from mergedeep import mergedeep + class SubscriptionArgsParser: """ - 'Unknown' arguments can be given to `ytdl-sub dl` which are meant to represent fields - in a subscription yaml. This class will convert those extra args into a + 'Extra' arguments can be given to `ytdl-sub dl` which are meant to represent fields + in a subscription yaml. This class will convert those extra args into a dict that can be + passed in to instantiate a :class:`~ytdl_subscribe.validators.config.preset_validator.PresetValidator` """ - def __init__(self, unknown_arguments: List[str]): - self._unknown_arguments = unknown_arguments + def __init__(self, extra_arguments: List[str]): + """ + :param extra_arguments: List of extra arguments from argparse + """ + self._unknown_arguments = extra_arguments + + @property + def _argument_exception(self) -> ArgumentError: + """ + :return: Exception to raise if a parsing error occurs. + """ + return ArgumentError( + argument=None, + message="dl arguments must be in the form of --subscription.option.name 'value' " + "proceeding the url", + ) + + @classmethod + def _is_argument_name(cls, arg: str) -> bool: + """ + :param arg: Arg value from the unknown args list + :return: True if it's an argument name, denoted by starting with '--'. False otherwise. + """ + return arg.startswith("--") + + @classmethod + def _argument_name_and_value_to_dict(cls, arg_name: str, arg_value: str) -> Dict: + """ + :param arg_name: Argument name in the form of 'key1.key2.key3' + :param arg_value: Argument value + :return: dict containing {'key1':{'key2':{'key3': value}}} + """ + argument_dict = {} + + # Remove the argument --'s, then split on period + arg_name_split = arg_name.replace("--", "", 1).split(".") + + next_arg_name = arg_name_split[0] + next_dict = argument_dict + for next_arg_name in arg_name_split[:-1]: + next_dict[next_arg_name] = {} + next_dict = next_dict[next_arg_name] + + next_dict[arg_name_split[-1]] = arg_value + + return argument_dict + + def to_subscription_dict(self) -> Dict: + """ + Converts the extra arguments into a dict equivalent to a subscription yaml. + :return: dict containing argument names and values + """ + subscription_dict = {} + if len(self._unknown_arguments) % 2 != 0: + raise self._argument_exception + + for idx in range(0, len(self._unknown_arguments), 2): + argument_name = self._unknown_arguments[idx] + argument_value = self._unknown_arguments[idx + 1] + + if not self._is_argument_name(arg=argument_name): + raise self._argument_exception + + argument_dict = self._argument_name_and_value_to_dict( + arg_name=argument_name, arg_value=argument_value + ) + mergedeep.merge(subscription_dict, argument_dict, strategy=mergedeep.Strategy.REPLACE) + + return subscription_dict diff --git a/ytdl_subscribe/main.py b/ytdl_subscribe/main.py index 49e3ba62..77653513 100644 --- a/ytdl_subscribe/main.py +++ b/ytdl_subscribe/main.py @@ -1,9 +1,11 @@ import argparse import sys +from random import randint from typing import List ################################################################################################### # GLOBAL PARSER +from ytdl_subscribe.cli.subscription_args_parser import SubscriptionArgsParser from ytdl_subscribe.validators.config.config_file_validator import ConfigFileValidator from ytdl_subscribe.validators.config.subscription_validator import SubscriptionValidator @@ -30,15 +32,11 @@ subscription_parser.add_argument( default="config.yaml", ) ################################################################################################### -# INTERACTIVE DOWNLOAD PARSER +# DOWNLOAD PARSER download_parser = subparsers.add_parser("dl") -download_parser.add_argument( - "url", - help="url to the desired content to download, will prompt for further arguments needed", -) if __name__ == "__main__": - args, unknown = parser.parse_known_args() + args, extra_args = parser.parse_known_args() config: ConfigFileValidator = ConfigFileValidator.from_file_path(args.config) if args.subparser == "sub": @@ -53,18 +51,19 @@ if __name__ == "__main__": for subscription in subscriptions: subscription.to_subscription().download() - print("hi") - # for subscription_path in subscription_paths: - # - # - # subscriptions = parse_subscriptions( - # yaml_dict, presets, subscriptions=args.subscriptions - # ) - # for s in subscriptions: - # s.extract_info() + print("Subscription download complete!") # One-off download if args.subparser == "dl": - print("Interactive download is not supported yet. Stay tuned!") + subscription_name = f"cli-dl-{randint(0, 999)}" + subscription_args_dict = SubscriptionArgsParser( + extra_arguments=extra_args + ).to_subscription_dict() + SubscriptionValidator.from_dict( + config=config, + subscription_name=subscription_name, + subscription_dict=subscription_args_dict, + ).to_subscription().download() + print("Download complete!") sys.exit(0) diff --git a/ytdl_subscribe/subscriptions/soundcloud.py b/ytdl_subscribe/subscriptions/soundcloud.py index 129cde94..9bfdc73f 100644 --- a/ytdl_subscribe/subscriptions/soundcloud.py +++ b/ytdl_subscribe/subscriptions/soundcloud.py @@ -1,4 +1,3 @@ -from abc import ABC from typing import List from ytdl_subscribe.downloaders.soundcloud_downloader import SoundcloudDownloader diff --git a/ytdl_subscribe/subscriptions/youtube.py b/ytdl_subscribe/subscriptions/youtube.py index 354f7c5d..6cf080a3 100644 --- a/ytdl_subscribe/subscriptions/youtube.py +++ b/ytdl_subscribe/subscriptions/youtube.py @@ -1,4 +1,3 @@ -from abc import ABC from typing import List from ytdl_subscribe.downloaders.youtube_downloader import YoutubeDownloader diff --git a/ytdl_subscribe/validators/config/subscription_validator.py b/ytdl_subscribe/validators/config/subscription_validator.py index 50cf77ce..725ab9f9 100644 --- a/ytdl_subscribe/validators/config/subscription_validator.py +++ b/ytdl_subscribe/validators/config/subscription_validator.py @@ -1,5 +1,6 @@ import copy from typing import Any +from typing import Dict from typing import List from typing import Optional from typing import Type @@ -93,6 +94,12 @@ class SubscriptionValidator(StrictDictValidator): preset_options=self.preset, ) + @classmethod + def from_dict( + cls, config: ConfigFileValidator, subscription_name, subscription_dict: Dict + ) -> "SubscriptionValidator": + return SubscriptionValidator(config=config, name=subscription_name, value=subscription_dict) + @classmethod def from_file_path( cls, config: ConfigFileValidator, subscription_path: str @@ -104,8 +111,10 @@ class SubscriptionValidator(StrictDictValidator): subscriptions: List["SubscriptionValidator"] = [] for subscription_key, subscription_object in subscription_dict.items(): subscriptions.append( - SubscriptionValidator( - config=config, name=subscription_key, value=subscription_object + SubscriptionValidator.from_dict( + config=config, + subscription_name=subscription_key, + subscription_dict=subscription_object, ) )