diff --git a/ytdl_subscribe/validators/base/strict_dict_validator.py b/ytdl_subscribe/validators/base/strict_dict_validator.py index 96e237ca..8ef504cc 100644 --- a/ytdl_subscribe/validators/base/strict_dict_validator.py +++ b/ytdl_subscribe/validators/base/strict_dict_validator.py @@ -17,19 +17,25 @@ class StrictDictValidator(DictValidator): def __init__(self, name, value): super().__init__(name, value) - if len(self.required_keys) == 0: + if len(self.allowed_keys) == 0: raise ValueError( - "No required fields when using a StrictDictValidator. " + "No required or optional keys defined when using a StrictDictValidator. " "Should be using DictValidator instead." ) # Ensure all required keys are present for required_key in self.required_keys: if required_key not in self.value: - error_msg = ( - f"'{self.name}' is missing the required field '{required_key}'" + raise self._validation_exception( + f"missing the required field '{required_key}'" ) - raise ValidationException(error_msg) + + # Ensure an empty dict was not passed as the value + if not self.dict: + raise self._validation_exception( + f"at least one of the following fields must be defined: " + f"{', '.join(self.optional_keys)}'" + ) # Ensure all keys are either required or optional keys if no extra field are allowed if not self.allow_extra_keys: diff --git a/ytdl_subscribe/validators/config/config_validator.py b/ytdl_subscribe/validators/config/config_validator.py index 34923aaf..60da0bc4 100644 --- a/ytdl_subscribe/validators/config/config_validator.py +++ b/ytdl_subscribe/validators/config/config_validator.py @@ -15,10 +15,13 @@ class ConfigValidator(StrictDictValidator): self.working_directory = self.validate_key("working_directory", StringValidator) self.presets = self.validate_key("presets", DictValidator) + @classmethod + def from_dict(cls, config_dict) -> "ConfigValidator": + return ConfigValidator(name="config", value=config_dict) + @classmethod def from_file_path(cls, config_path) -> "ConfigValidator": # TODO: Create separate yaml file loader class with open(config_path, "r", encoding="utf-8") as file: config_dict = yaml.safe_load(file) - - return ConfigValidator(name="config", value=config_dict) + return ConfigValidator.from_dict(config_dict) diff --git a/ytdl_subscribe/validators/config/metadata_options_validator.py b/ytdl_subscribe/validators/config/metadata_options_validator.py new file mode 100644 index 00000000..cd4ef624 --- /dev/null +++ b/ytdl_subscribe/validators/config/metadata_options_validator.py @@ -0,0 +1,34 @@ +from typing import Optional + +from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator +from ytdl_subscribe.validators.base.string_formatter_validator import ( + StringFormatterValidator, +) +from ytdl_subscribe.validators.base.string_select_validator import StringSelectValidator + + +class MetadataOptionsValidator(StrictDictValidator): + optional_keys = {"id3", "nfo"} + + def __init__(self, name, value): + super().__init__(name, value) + + self.output_directory: StringFormatterValidator = self.validate_key( + key="output_directory", validator=StringFormatterValidator + ) + + self.file_name: StringFormatterValidator = self.validate_key( + key="file_name", validator=StringFormatterValidator + ) + + self.convert_thumbnail: Optional[str] = None + if "convert_thumbnail" in self.dict: + self.convert_thumbnail = self.validate_key( + key="convert_thumbnail", validator=ConvertThumbnailValidator + ).value + + self.thumbnail_name: Optional[StringFormatterValidator] = None + if "thumbnail_name" in self.dict: + self.thumbnail_name = self.validate_key( + key="thumbnail_name", validator=StringFormatterValidator + ) diff --git a/ytdl_subscribe/validators/config/output_options_validator.py b/ytdl_subscribe/validators/config/output_options_validator.py new file mode 100644 index 00000000..b293cdbd --- /dev/null +++ b/ytdl_subscribe/validators/config/output_options_validator.py @@ -0,0 +1,39 @@ +from typing import Optional + +from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator +from ytdl_subscribe.validators.base.string_formatter_validator import ( + StringFormatterValidator, +) +from ytdl_subscribe.validators.base.string_select_validator import StringSelectValidator + + +class ConvertThumbnailValidator(StringSelectValidator): + select_values = {"jpeg"} + + +class OutputOptionsValidator(StrictDictValidator): + required_keys = {"output_directory", "file_name"} + optional_keys = {"convert_thumbnail", "thumbnail_name"} + + def __init__(self, name, value): + super().__init__(name, value) + + self.output_directory: StringFormatterValidator = self.validate_key( + key="output_directory", validator=StringFormatterValidator + ) + + self.file_name: StringFormatterValidator = self.validate_key( + key="file_name", validator=StringFormatterValidator + ) + + self.convert_thumbnail: Optional[str] = None + if "convert_thumbnail" in self.dict: + self.convert_thumbnail = self.validate_key( + key="convert_thumbnail", validator=ConvertThumbnailValidator + ).value + + self.thumbnail_name: Optional[StringFormatterValidator] = None + if "thumbnail_name" in self.dict: + self.thumbnail_name = self.validate_key( + key="thumbnail_name", validator=StringFormatterValidator + ) diff --git a/ytdl_subscribe/validators/config/preset_validator.py b/ytdl_subscribe/validators/config/preset_validator.py index 869590ab..49f0a887 100644 --- a/ytdl_subscribe/validators/config/preset_validator.py +++ b/ytdl_subscribe/validators/config/preset_validator.py @@ -1,8 +1,12 @@ from typing import Any from typing import Optional +from typing import Tuple from ytdl_subscribe.utils.enums import SubscriptionSourceName from ytdl_subscribe.validators.base.strict_dict_validator import StrictDictValidator +from ytdl_subscribe.validators.config.output_options_validator import ( + OutputOptionsValidator, +) from ytdl_subscribe.validators.config.sources.soundcloud_validators import ( SoundcloudSourceValidator, ) @@ -14,40 +18,51 @@ from ytdl_subscribe.validators.exceptions import ValidationException class PresetValidator(StrictDictValidator): - required_keys = {"post_process"} + required_keys = {"output_options, metadata_options"} optional_keys = { "ytdl_options", - "output_path", "overrides", *SubscriptionSourceName.all(), } - def __init__(self, name: str, value: Any): - super().__init__(name=name, value=value) - self.subscription_source: Optional[SourceValidator] = None - self.subscription_source_name: Optional[str] = None + def __validate_and_get_subscription_source(self) -> Tuple[str, SourceValidator]: + subscription_source: Optional[SourceValidator] = None + subscription_source_name: Optional[str] = None for key in self.keys: - if key in SubscriptionSourceName.all() and self.subscription_source: + if key in SubscriptionSourceName.all() and subscription_source: raise ValidationException( f"'{self.name}' can only have one of the following sources: {SubscriptionSourceName.pretty_all()}" ) if key == SubscriptionSourceName.SOUNDCLOUD: - self.subscription_source_name = SubscriptionSourceName.SOUNDCLOUD - self.subscription_source = self.validate_key( + subscription_source_name = SubscriptionSourceName.SOUNDCLOUD + subscription_source = self.validate_key( key=key, validator=SoundcloudSourceValidator, ) elif key == SubscriptionSourceName.YOUTUBE: - self.subscription_source_name = SubscriptionSourceName.YOUTUBE - self.subscription_source = self.validate_key( + subscription_source_name = SubscriptionSourceName.YOUTUBE + subscription_source = self.validate_key( key=key, validator=YoutubeSourceValidator, ) # If subscription source was not set, error - if not self.subscription_source: + if not subscription_source: raise ValidationException( f"'{self.name} must have one of the following sources: {SubscriptionSourceName.pretty_all()}" ) + + return subscription_source_name, subscription_source + + def __init__(self, name: str, value: Any): + super().__init__(name=name, value=value) + ( + self.subscription_source_name, + self.subscription_source, + ) = self.__validate_and_get_subscription_source() + self.output_options = self.validate_key( + key="output_options", + validator=OutputOptionsValidator, + )