wip
This commit is contained in:
parent
a71577e703
commit
d3c2c14452
9 changed files with 120 additions and 97 deletions
|
|
@ -8,7 +8,7 @@ from typing import Tuple
|
||||||
from mergedeep import mergedeep
|
from mergedeep import mergedeep
|
||||||
|
|
||||||
from ytdl_sub.cli.main_args_parser import MainArgs
|
from ytdl_sub.cli.main_args_parser import MainArgs
|
||||||
from ytdl_sub.config.config_file import ConfigOptions
|
from ytdl_sub.config.config_validator import ConfigOptions
|
||||||
from ytdl_sub.utils.exceptions import InvalidDlArguments
|
from ytdl_sub.utils.exceptions import InvalidDlArguments
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,99 +1,19 @@
|
||||||
import os
|
import os
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Dict
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
import mergedeep
|
from ytdl_sub.config.config_validator import ConfigValidator
|
||||||
|
from ytdl_sub.config.preset import Preset
|
||||||
from ytdl_sub.prebuilt_presets import PREBUILT_PRESETS
|
|
||||||
from ytdl_sub.utils.yaml import load_yaml
|
from ytdl_sub.utils.yaml import load_yaml
|
||||||
from ytdl_sub.validators.strict_dict_validator import StrictDictValidator
|
|
||||||
from ytdl_sub.validators.validators import LiteralDictValidator
|
|
||||||
from ytdl_sub.validators.validators import StringValidator
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigOptions(StrictDictValidator):
|
class ConfigFile(ConfigValidator):
|
||||||
_required_keys = {"working_directory"}
|
|
||||||
_optional_keys = {"umask", "dl_aliases"}
|
|
||||||
|
|
||||||
def __init__(self, name: str, value: Any):
|
|
||||||
super().__init__(name, value)
|
|
||||||
|
|
||||||
self._working_directory = self._validate_key(
|
|
||||||
key="working_directory", validator=StringValidator
|
|
||||||
)
|
|
||||||
self._umask = self._validate_key_if_present(
|
|
||||||
key="umask", validator=StringValidator, default="022"
|
|
||||||
)
|
|
||||||
self._dl_aliases = self._validate_key_if_present(
|
|
||||||
key="dl_aliases", validator=LiteralDictValidator
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def working_directory(self) -> str:
|
|
||||||
"""
|
|
||||||
The directory to temporarily store downloaded files before moving them into their final
|
|
||||||
directory.
|
|
||||||
"""
|
|
||||||
return self._working_directory.value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def umask(self) -> Optional[str]:
|
|
||||||
"""
|
|
||||||
Umask (octal format) to apply to every created file. Defaults to "022".
|
|
||||||
"""
|
|
||||||
return self._umask.value
|
|
||||||
|
|
||||||
@property
|
|
||||||
def dl_aliases(self) -> Optional[Dict[str, str]]:
|
|
||||||
"""
|
|
||||||
Alias definitions to shorten ``ytdl-sub dl`` arguments. For example,
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
configuration:
|
|
||||||
dl_aliases:
|
|
||||||
mv: "--preset yt_music_video"
|
|
||||||
v: "--youtube.video_url"
|
|
||||||
|
|
||||||
Simplifies
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
ytdl-sub dl --preset "yt_music_video" --youtube.video_url "youtube.com/watch?v=a1b2c3"
|
|
||||||
|
|
||||||
to
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
ytdl-sub dl --mv --v "youtube.com/watch?v=a1b2c3"
|
|
||||||
"""
|
|
||||||
if self._dl_aliases:
|
|
||||||
return self._dl_aliases.dict
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigFile(StrictDictValidator):
|
|
||||||
_required_keys = {"configuration", "presets"}
|
_required_keys = {"configuration", "presets"}
|
||||||
|
|
||||||
def __init__(self, name: str, value: Any):
|
def __init__(self, name: str, value: Any):
|
||||||
super().__init__(name, value)
|
super().__init__(name, value)
|
||||||
self.config_options = self._validate_key("configuration", ConfigOptions)
|
|
||||||
|
|
||||||
prebuilt_presets = PREBUILT_PRESETS
|
for preset_name, preset_dict in self.presets.dict.items():
|
||||||
|
Preset.preset_partial_validate(config=self, name=preset_name, value=preset_dict)
|
||||||
# Make sure presets is a dictionary. Will be validated in `PresetValidator`
|
|
||||||
self.presets = self._validate_key("presets", LiteralDictValidator)
|
|
||||||
|
|
||||||
# Ensure custom presets do not collide with prebuilt presets
|
|
||||||
for preset_name in self.presets.keys:
|
|
||||||
if preset_name in prebuilt_presets:
|
|
||||||
raise self._validation_exception(
|
|
||||||
f"preset name '{preset_name}' conflicts with a prebuilt preset"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Merge prebuilt presets into the config so custom presets can use them
|
|
||||||
mergedeep.merge(self.presets._value, prebuilt_presets)
|
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
92
src/ytdl_sub/config/config_validator.py
Normal file
92
src/ytdl_sub/config/config_validator.py
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
from typing import Any
|
||||||
|
from typing import Dict
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from mergedeep import mergedeep
|
||||||
|
|
||||||
|
from ytdl_sub.prebuilt_presets import PREBUILT_PRESETS
|
||||||
|
from ytdl_sub.validators.strict_dict_validator import StrictDictValidator
|
||||||
|
from ytdl_sub.validators.validators import LiteralDictValidator
|
||||||
|
from ytdl_sub.validators.validators import StringValidator
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigOptions(StrictDictValidator):
|
||||||
|
_required_keys = {"working_directory"}
|
||||||
|
_optional_keys = {"umask", "dl_aliases"}
|
||||||
|
|
||||||
|
def __init__(self, name: str, value: Any):
|
||||||
|
super().__init__(name, value)
|
||||||
|
|
||||||
|
self._working_directory = self._validate_key(
|
||||||
|
key="working_directory", validator=StringValidator
|
||||||
|
)
|
||||||
|
self._umask = self._validate_key_if_present(
|
||||||
|
key="umask", validator=StringValidator, default="022"
|
||||||
|
)
|
||||||
|
self._dl_aliases = self._validate_key_if_present(
|
||||||
|
key="dl_aliases", validator=LiteralDictValidator
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def working_directory(self) -> str:
|
||||||
|
"""
|
||||||
|
The directory to temporarily store downloaded files before moving them into their final
|
||||||
|
directory.
|
||||||
|
"""
|
||||||
|
return self._working_directory.value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def umask(self) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
Umask (octal format) to apply to every created file. Defaults to "022".
|
||||||
|
"""
|
||||||
|
return self._umask.value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dl_aliases(self) -> Optional[Dict[str, str]]:
|
||||||
|
"""
|
||||||
|
Alias definitions to shorten ``ytdl-sub dl`` arguments. For example,
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
configuration:
|
||||||
|
dl_aliases:
|
||||||
|
mv: "--preset yt_music_video"
|
||||||
|
v: "--youtube.video_url"
|
||||||
|
|
||||||
|
Simplifies
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
ytdl-sub dl --preset "yt_music_video" --youtube.video_url "youtube.com/watch?v=a1b2c3"
|
||||||
|
|
||||||
|
to
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
ytdl-sub dl --mv --v "youtube.com/watch?v=a1b2c3"
|
||||||
|
"""
|
||||||
|
if self._dl_aliases:
|
||||||
|
return self._dl_aliases.dict
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigValidator(StrictDictValidator):
|
||||||
|
_required_keys = {"configuration", "presets"}
|
||||||
|
|
||||||
|
def __init__(self, name: str, value: Any):
|
||||||
|
super().__init__(name, value)
|
||||||
|
self.config_options = self._validate_key("configuration", ConfigOptions)
|
||||||
|
|
||||||
|
# Make sure presets is a dictionary. Will be validated in `PresetValidator`
|
||||||
|
self.presets = self._validate_key("presets", LiteralDictValidator)
|
||||||
|
|
||||||
|
# Ensure custom presets do not collide with prebuilt presets
|
||||||
|
for preset_name in self.presets.keys:
|
||||||
|
if preset_name in PREBUILT_PRESETS:
|
||||||
|
raise self._validation_exception(
|
||||||
|
f"preset name '{preset_name}' conflicts with a prebuilt preset"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Merge prebuilt presets into the config so custom presets can use them
|
||||||
|
mergedeep.merge(self.presets._value, PREBUILT_PRESETS)
|
||||||
|
|
@ -11,7 +11,7 @@ from typing import Union
|
||||||
|
|
||||||
from mergedeep import mergedeep
|
from mergedeep import mergedeep
|
||||||
|
|
||||||
from ytdl_sub.config.config_file import ConfigFile
|
from ytdl_sub.config.config_validator import ConfigValidator
|
||||||
from ytdl_sub.config.preset_class_mappings import DownloadStrategyMapping
|
from ytdl_sub.config.preset_class_mappings import DownloadStrategyMapping
|
||||||
from ytdl_sub.config.preset_class_mappings import PluginMapping
|
from ytdl_sub.config.preset_class_mappings import PluginMapping
|
||||||
from ytdl_sub.config.preset_options import OutputOptions
|
from ytdl_sub.config.preset_options import OutputOptions
|
||||||
|
|
@ -33,6 +33,7 @@ from ytdl_sub.validators.validators import ListValidator
|
||||||
from ytdl_sub.validators.validators import StringListValidator
|
from ytdl_sub.validators.validators import StringListValidator
|
||||||
from ytdl_sub.validators.validators import StringValidator
|
from ytdl_sub.validators.validators import StringValidator
|
||||||
from ytdl_sub.validators.validators import Validator
|
from ytdl_sub.validators.validators import Validator
|
||||||
|
from ytdl_sub.validators.validators import validation_exception
|
||||||
|
|
||||||
PRESET_KEYS = {
|
PRESET_KEYS = {
|
||||||
"preset",
|
"preset",
|
||||||
|
|
@ -129,7 +130,11 @@ class Preset(StrictDictValidator):
|
||||||
_optional_keys = PRESET_KEYS
|
_optional_keys = PRESET_KEYS
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def preset_partial_validate(cls, config: ConfigFile, name: str, value: Any) -> None:
|
def preset_partial_validate(cls, config: ConfigValidator, name: str, value: Any) -> None:
|
||||||
|
# Ensure value is a dict
|
||||||
|
_ = DictValidator(name=name, value=value)
|
||||||
|
assert isinstance(value, dict)
|
||||||
|
|
||||||
cls._partial_validate_key(name, value, "output_options", OutputOptions)
|
cls._partial_validate_key(name, value, "output_options", OutputOptions)
|
||||||
cls._partial_validate_key(name, value, "ytdl_options", YTDLOptions)
|
cls._partial_validate_key(name, value, "ytdl_options", YTDLOptions)
|
||||||
cls._partial_validate_key(name, value, "overrides", Overrides)
|
cls._partial_validate_key(name, value, "overrides", Overrides)
|
||||||
|
|
@ -141,6 +146,15 @@ class Preset(StrictDictValidator):
|
||||||
validator=PluginMapping.get(plugin_name).plugin_options_type,
|
validator=PluginMapping.get(plugin_name).plugin_options_type,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parent_presets = StringListValidator(name=f"{name}.preset", value=value.get("preset", []))
|
||||||
|
for parent_preset_name in parent_presets.list:
|
||||||
|
if parent_preset_name.value not in config.presets.keys:
|
||||||
|
raise validation_exception(
|
||||||
|
name=f"{name}.preset",
|
||||||
|
error_message=f"preset '{parent_preset_name.value}' does not exist in the "
|
||||||
|
f"provided config. Available presets: {', '.join(config.presets.keys)}",
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _source_variables(self) -> List[str]:
|
def _source_variables(self) -> List[str]:
|
||||||
return Entry.source_variables()
|
return Entry.source_variables()
|
||||||
|
|
@ -291,7 +305,7 @@ class Preset(StrictDictValidator):
|
||||||
self.__validate_override_string_formatter_validator(validator_value)
|
self.__validate_override_string_formatter_validator(validator_value)
|
||||||
|
|
||||||
def _get_presets_to_merge(
|
def _get_presets_to_merge(
|
||||||
self, parent_presets: str | List[str], seen_presets: List[str], config: ConfigFile
|
self, parent_presets: str | List[str], seen_presets: List[str], config: ConfigValidator
|
||||||
) -> List[Dict]:
|
) -> List[Dict]:
|
||||||
presets_to_merge: List[Dict] = []
|
presets_to_merge: List[Dict] = []
|
||||||
|
|
||||||
|
|
@ -326,7 +340,7 @@ class Preset(StrictDictValidator):
|
||||||
|
|
||||||
return presets_to_merge
|
return presets_to_merge
|
||||||
|
|
||||||
def __merge_parent_preset_dicts_if_present(self, config: ConfigFile):
|
def __merge_parent_preset_dicts_if_present(self, config: ConfigValidator):
|
||||||
parent_preset_validator = self._validate_key_if_present(
|
parent_preset_validator = self._validate_key_if_present(
|
||||||
key="preset", validator=StringListValidator
|
key="preset", validator=StringListValidator
|
||||||
)
|
)
|
||||||
|
|
@ -345,7 +359,7 @@ class Preset(StrictDictValidator):
|
||||||
mergedeep.merge({}, *reversed(presets_to_merge), strategy=mergedeep.Strategy.ADDITIVE)
|
mergedeep.merge({}, *reversed(presets_to_merge), strategy=mergedeep.Strategy.ADDITIVE)
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, config: ConfigFile, name: str, value: Any):
|
def __init__(self, config: ConfigValidator, name: str, value: Any):
|
||||||
super().__init__(name=name, value=value)
|
super().__init__(name=name, value=value)
|
||||||
|
|
||||||
# Perform the merge of parent presets before validating any keys
|
# Perform the merge of parent presets before validating any keys
|
||||||
|
|
@ -380,7 +394,7 @@ class Preset(StrictDictValidator):
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, config: ConfigFile, preset_name: str, preset_dict: Dict) -> "Preset":
|
def from_dict(cls, config: ConfigValidator, preset_name: str, preset_dict: Dict) -> "Preset":
|
||||||
"""
|
"""
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ from ytdl_sub.validators.string_formatter_validators import DictFormatterValidat
|
||||||
from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator
|
from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator
|
||||||
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
|
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
|
||||||
from ytdl_sub.validators.validators import BoolValidator
|
from ytdl_sub.validators.validators import BoolValidator
|
||||||
from ytdl_sub.validators.validators import DictValidator
|
|
||||||
from ytdl_sub.validators.validators import LiteralDictValidator
|
from ytdl_sub.validators.validators import LiteralDictValidator
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from abc import ABC
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
from ytdl_sub.config.config_file import ConfigOptions
|
from ytdl_sub.config.config_validator import ConfigOptions
|
||||||
from ytdl_sub.config.preset import Preset
|
from ytdl_sub.config.preset import Preset
|
||||||
from ytdl_sub.config.preset import PresetPlugins
|
from ytdl_sub.config.preset import PresetPlugins
|
||||||
from ytdl_sub.config.preset_options import OutputOptions
|
from ytdl_sub.config.preset_options import OutputOptions
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Any
|
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
from typing import Any
|
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Set
|
from typing import Set
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import pytest
|
||||||
from ytdl_sub.cli.download_args_parser import DownloadArgsParser
|
from ytdl_sub.cli.download_args_parser import DownloadArgsParser
|
||||||
from ytdl_sub.cli.main_args_parser import MainArgs
|
from ytdl_sub.cli.main_args_parser import MainArgs
|
||||||
from ytdl_sub.cli.main_args_parser import parser
|
from ytdl_sub.cli.main_args_parser import parser
|
||||||
from ytdl_sub.config.config_file import ConfigOptions
|
from ytdl_sub.config.config_validator import ConfigOptions
|
||||||
from ytdl_sub.utils.exceptions import InvalidDlArguments
|
from ytdl_sub.utils.exceptions import InvalidDlArguments
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue