diff --git a/Makefile b/Makefile index f5ec2086..8d53d610 100644 --- a/Makefile +++ b/Makefile @@ -4,5 +4,7 @@ wheel: docker: wheel cp dist/*.whl docker/root/ sudo docker build --no-cache -t ytdl-sub:0.1 docker/ +docs: + sphinx-build -a -b html docs docs/_html -.PHONY: wheel docker +.PHONY: wheel docker docs diff --git a/docs/config.rst b/docs/config.rst index 785c33da..b47fec48 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -22,6 +22,11 @@ configuration The ``configuration`` section contains app-wide configs applied to all presets and subscriptions. +.. autoclass:: ytdl_sub.config.config_file.ConfigOptions() + :members: + :member-order: bysource + + presets ^^^^^^^ ``presets`` define a `formula` for how to format downloaded media and metadata. diff --git a/src/ytdl_sub/config/config_file.py b/src/ytdl_sub/config/config_file.py index 7d9d5528..9b6d1ca1 100644 --- a/src/ytdl_sub/config/config_file.py +++ b/src/ytdl_sub/config/config_file.py @@ -1,4 +1,6 @@ +import os from typing import Any +from typing import Optional from ytdl_sub.utils.yaml import load_yaml from ytdl_sub.validators.strict_dict_validator import StrictDictValidator @@ -7,16 +9,33 @@ from ytdl_sub.validators.validators import StringValidator class ConfigOptions(StrictDictValidator): - """Validation for global config options""" - _required_keys = {"working_directory"} + _optional_keys = {"umask"} def __init__(self, name: str, value: Any): super().__init__(name, value) - self.working_directory = self._validate_key( + self._working_directory = self._validate_key( key="working_directory", validator=StringValidator ) + self._umask = self._validate_key_if_present( + key="umask", validator=StringValidator, default="022" + ) + + @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 class ConfigFile(StrictDictValidator): @@ -29,6 +48,19 @@ class ConfigFile(StrictDictValidator): # Make sure presets is a dictionary. Will be validated in `PresetValidator` self.presets = self._validate_key("presets", LiteralDictValidator) + def initialize(self): + """ + Configures things (umask, pgid) prior to any downloading + + Returns + ------- + self + """ + if self.config_options.umask: + os.umask(int(self.config_options.umask, 8)) + + return self + @classmethod def from_dict(cls, config_dict: dict) -> "ConfigFile": """ diff --git a/src/ytdl_sub/config/preset.py b/src/ytdl_sub/config/preset.py index f1c9c76d..80c902e7 100644 --- a/src/ytdl_sub/config/preset.py +++ b/src/ytdl_sub/config/preset.py @@ -157,14 +157,17 @@ class Preset(StrictDictValidator): for variable_name in formatter_validator.format_variables: if variable_name not in resolvable_override_variables: + # pylint: disable=protected-access raise StringFormattingVariableNotFoundException( - f"This variable can only use override variables that resolve without needing " - f"variables from a downloaded file. The only override variables defined that " - f"meet this condition are: {', '.join(sorted(resolvable_override_variables))}" + f"Validation error in {formatter_validator._name}: " + f"This can only use override variables. Available override variables are: " + f"{', '.join(sorted(resolvable_override_variables))}", ) + # pylint: enable=protected-access def __recursive_preset_validate( - self, validator_dict: Optional[Dict[str, Validator]] = None + self, + validator_dict: Optional[Dict[str, Validator]] = None, ) -> None: """ Ensure all OverridesStringFormatterValidator's only contain variables from the overrides @@ -175,9 +178,9 @@ class Preset(StrictDictValidator): for validator in validator_dict.values(): if isinstance(validator, DictValidator): + # pylint: disable=protected-access # Usage of protected variables in other validators is fine. The reason to keep them # protected is for readability when using them in subscriptions. - # pylint: disable=protected-access self.__recursive_preset_validate(validator._validator_dict) # pylint: enable=protected-access diff --git a/src/ytdl_sub/main.py b/src/ytdl_sub/main.py index 0bcb023d..256528b0 100644 --- a/src/ytdl_sub/main.py +++ b/src/ytdl_sub/main.py @@ -69,7 +69,7 @@ def _main(): args, extra_args = parser.parse_known_args() - config: ConfigFile = ConfigFile.from_file_path(args.config) + config: ConfigFile = ConfigFile.from_file_path(args.config).initialize() if args.subparser == "sub": _download_subscriptions_from_yaml_files(config=config, args=args) logger.info("Subscription download complete!") diff --git a/src/ytdl_sub/subscriptions/subscription.py b/src/ytdl_sub/subscriptions/subscription.py index 1c9f9a80..e461c89a 100644 --- a/src/ytdl_sub/subscriptions/subscription.py +++ b/src/ytdl_sub/subscriptions/subscription.py @@ -120,7 +120,7 @@ class Subscription: ------- The directory that the downloader saves files to """ - return str(Path(self.__config_options.working_directory.value) / Path(self.name)) + return str(Path(self.__config_options.working_directory) / Path(self.name)) @property def output_directory(self) -> str: