From cdff1c7565b6db52f4a46604193c513fd11fb993 Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Sat, 8 Mar 2025 13:24:45 -0800 Subject: [PATCH] forgt what i was doing --- docs/source/config_reference/config_yaml.rst | 132 +++++++++---------- src/ytdl_sub/config/config_validator.py | 12 ++ tests/unit/docgen/test_docgen.py | 4 + tools/docgen/configuration.py | 47 ++----- tools/docgen/utils.py | 36 ++++- 5 files changed, 119 insertions(+), 112 deletions(-) diff --git a/docs/source/config_reference/config_yaml.rst b/docs/source/config_reference/config_yaml.rst index 8e4097c3..a8f12180 100644 --- a/docs/source/config_reference/config_yaml.rst +++ b/docs/source/config_reference/config_yaml.rst @@ -1,90 +1,80 @@ -================== + Configuration File ================== ------------ -config.yaml ------------ +Something -ytdl-sub is configured using a ``config.yaml`` file. +dl_aliases +---------- +.. _dl_aliases: -The ``config.yaml`` is made up of two sections: +Alias definitions to shorten ``ytdl-sub dl`` arguments. For example, .. code-block:: yaml - configuration: - presets: + configuration: + dl_aliases: + mv: "--preset music_video" + u: "--download.url" -You can jump to any section and subsection of the config using the navigation -section to the left. +Simplifies -Note for Windows users, paths can be represented with ``C:/forward/slashes/like/linux``. -If you wish to represent paths like Windows, you will need to ``C:\\double\\bashslash\\paths`` -in order to escape the backslash character. +.. code-block:: bash -configuration -~~~~~~~~~~~~~ -The ``configuration`` section contains app-wide configs applied to all presets -and subscriptions. + ytdl-sub dl --preset "Jellyfin Music Videos" --download.url "youtube.com/watch?v=a1b2c3" -.. autoclass:: ytdl_sub.config.config_validator.ConfigOptions() - :members: - :member-order: bysource - :exclude-members: subscription_value, persist_logs, experimental +to + +.. code-block:: bash + + ytdl-sub dl --mv --u "youtube.com/watch?v=a1b2c3" + +experimental +------------ +TODO(jessebannon) fill out + +``enable_update_with_info_json`` + +Enables modifying subscription files using info.json files using the argument +``--update-with-info-json``. This feature is still being tested and has the ability to +destroy files. Ensure you have a full backup before usage. You have been warned! + +ffmpeg_path +----------- +Path to ffmpeg executable. Defaults to ``/usr/bin/ffmpeg`` for Linux, and +``ffmpeg.exe`` for Windows (in the same directory as ytdl-sub). + +ffprobe_path +------------ +Path to ffprobe executable. Defaults to ``/usr/bin/ffprobe`` for Linux, and +``ffprobe.exe`` for Windows (in the same directory as ytdl-sub). + +file_name_max_bytes +------------------- +Max file name size in bytes. Most OS's typically default to 255 bytes. + +lock_directory +-------------- +The directory to temporarily store file locks, which prevents multiple instances +of ``ytdl-sub`` from running. Note that file locks do not work on network-mounted +directories. Ensure that this directory resides on the host machine. Defaults to ``/tmp``. persist_logs -"""""""""""" -Within ``configuration``, define whether logs from subscription downloads -should be persisted. +------------ +TODO(jessebannon) fill out -.. code-block:: yaml +``keep_successful_logs`` - configuration: - persist_logs: - logs_directory: "/path/to/log/directory" +Optional. Whether to store logs when downloading is successful. Defaults to True. -Log files are stored as -``YYYY-mm-dd-HHMMSS.subscription_name.(success|error).log``. +``logs_directory`` -.. autoclass:: ytdl_sub.config.config_validator.PersistLogsValidator() - :members: - :member-order: bysource +Required. The directory to store the logs in. -presets -~~~~~~~ -``presets`` define a `formula` for how to format downloaded media and metadata. +umask +----- +Umask (octal format) to apply to every created file. Defaults to "022". -This section is work-in-progress! - -preset -"""""" -Presets support inheritance by defining a parent preset: - -.. code-block:: yaml - - presets: - custom_preset: - ... - parent_preset: - ... - child_preset: - preset: "parent_preset" - -In the example above, ``child_preset`` inherits all fields defined in ``parent_preset``. -It is advantageous to use parent presets where possible to reduce duplicate yaml -definitions. - -Presets also support inheritance from multiple presets: - -.. code-block:: yaml - - child_preset: - preset: - - "custom_preset" - - "parent_preset" - -In this example, ``child_preset`` will inherit all fields from ``custom_preset`` -and ``parent_preset`` in that order. The bottom-most preset has the highest -priority. - -If you are only inheriting from one preset, the syntax ``preset: "parent_preset"`` is -valid YAML. Inheriting from multiple presets require use of a list. \ No newline at end of file +working_directory +----------------- +The directory to temporarily store downloaded files before moving them into their final +directory. Defaults to .ytdl-sub-working-directory diff --git a/src/ytdl_sub/config/config_validator.py b/src/ytdl_sub/config/config_validator.py index 55a91f55..f1290c51 100644 --- a/src/ytdl_sub/config/config_validator.py +++ b/src/ytdl_sub/config/config_validator.py @@ -22,6 +22,10 @@ from ytdl_sub.validators.validators import StringValidator class ExperimentalValidator(StrictDictValidator): + """ + TODO(jessebannon) fill out + """ + _optional_keys = {"enable_update_with_info_json"} _allow_extra_keys = True @@ -43,6 +47,10 @@ class ExperimentalValidator(StrictDictValidator): class PersistLogsValidator(StrictDictValidator): + """ + TODO(jessebannon) fill out + """ + _required_keys = {"logs_directory"} _optional_keys = {"keep_logs_after", "keep_successful_logs"} @@ -98,6 +106,10 @@ class PersistLogsValidator(StrictDictValidator): class ConfigOptions(StrictDictValidator): + """ + Something + """ + _optional_keys = { "working_directory", "umask", diff --git a/tests/unit/docgen/test_docgen.py b/tests/unit/docgen/test_docgen.py index 5d326839..5cfb8f92 100644 --- a/tests/unit/docgen/test_docgen.py +++ b/tests/unit/docgen/test_docgen.py @@ -1,5 +1,6 @@ from typing import Type +from tools.docgen.configuration import ConfigurationDocGen from tools.docgen.docgen import DocGen from tools.docgen.entry_variables import EntryVariablesDocGen from tools.docgen.plugins import PluginsDocGen @@ -28,3 +29,6 @@ class TestDocGen: def test_plugins_generated(self): _test_doc_gen(PluginsDocGen) + + def test_configuration_generated(self): + _test_doc_gen(ConfigurationDocGen) diff --git a/tools/docgen/configuration.py b/tools/docgen/configuration.py index 721efcec..7d9cb203 100644 --- a/tools/docgen/configuration.py +++ b/tools/docgen/configuration.py @@ -1,26 +1,8 @@ from pathlib import Path -from typing import Any -from typing import Dict -from typing import Type from tools.docgen.docgen import DocGen -from tools.docgen.utils import cached_properties -from tools.docgen.utils import camel_case_to_human -from tools.docgen.utils import get_function_docs -from tools.docgen.utils import line_section -from tools.docgen.utils import section +from tools.docgen.utils import generate_options_validator_docs from ytdl_sub.config.config_validator import ConfigOptions -from ytdl_sub.entries.script.variable_definitions import VARIABLES -from ytdl_sub.entries.script.variable_definitions import VariableDefinitions - - -def _variable_class_to_name(obj: Type[Any]) -> str: - assert "VariableDefinitions" in obj.__name__, f"{obj.__name__} doesnt have VariableDefinitions" - return ( - camel_case_to_human(obj.__name__) - .replace("Variable Definitions", "Variables") - .replace("Ytdl Sub", "Ytdl-Sub") - ) class ConfigurationDocGen(DocGen): @@ -28,23 +10,14 @@ class ConfigurationDocGen(DocGen): LOCATION = Path("docs/source/config_reference/config_yaml.rst") @classmethod - def generate(cls) -> str: - docs = section("Configuration File", level=0) - - parent_objs: Dict[str, Type[Any]] = { - _variable_class_to_name(obj): obj for obj in ConfigOptions.__bases__ - } - - for idx, name in enumerate(sorted(parent_objs.keys())): - docs += line_section(section_idx=idx) - docs += section(name, level=1) - - for variable_function_name in cached_properties(parent_objs[name]): - docs += get_function_docs( - function_name=variable_function_name, - obj=parent_objs[name], - pre_docstring=f":type: ``{getattr(VARIABLES, variable_function_name).human_readable_type()}``\n", - level=2, - ) + def generate(cls): + docs = generate_options_validator_docs( + name="Configuration File", + options=ConfigOptions, + offset=0, + skip_properties=False, + recurse_property_options=True, + property_sections=True + ) return docs diff --git a/tools/docgen/utils.py b/tools/docgen/utils.py index a870b40c..ced0da07 100644 --- a/tools/docgen/utils.py +++ b/tools/docgen/utils.py @@ -4,10 +4,10 @@ from typing import Any from typing import Dict from typing import List from typing import Optional -from typing import Set from typing import Type -from ytdl_sub.config.validators.options import OptionsValidator +from ytdl_sub.script.utils.type_checking import is_optional, get_optional_type +from ytdl_sub.validators.validators import Validator LEVEL_CHARS: Dict[int, str] = {0: "=", 1: "-", 2: "~", 3: "^"} @@ -25,6 +25,19 @@ def _should_filter_property(property_name: str) -> bool: "unresolvable", ) +def _is_validator_property(options: Type[Validator], property_name: str) -> Optional[Type[Validator]]: + property_return_type = inspect.getfullargspec(getattr(options, property_name).fget).annotations['return'] + if is_optional(property_return_type): + property_return_type = get_optional_type(property_return_type) + + try: + if issubclass(property_return_type, Validator): + return property_return_type + except TypeError: + return None + + return None + def section(name: str, level: Optional[int]) -> str: if level is None: @@ -85,7 +98,7 @@ def get_function_docs( def generate_options_validator_docs( - name: str, options: Type[OptionsValidator], offset: int, skip_properties: bool + name: str, options: Type[Validator], offset: int, skip_properties: bool, recurse_property_options: bool = False, property_sections: bool = False ) -> str: docs = "" docs += section(name, level=offset + 0) @@ -98,6 +111,21 @@ def generate_options_validator_docs( property_names = [prop for prop in properties(options) if not _should_filter_property(prop)] for property_name in sorted(property_names): - docs += get_function_docs(function_name=property_name, obj=options, level=None, display_function_name=f"``{property_name}``") + maybe_validator_property = _is_validator_property(options, property_name) if recurse_property_options else None + if maybe_validator_property: + docs += generate_options_validator_docs( + name=property_name, + options=maybe_validator_property, + offset=offset + 1, + skip_properties=False, + recurse_property_options=False + ) + else: + docs += get_function_docs( + function_name=property_name, + obj=options, + level=(offset + 1) if property_sections else None, + display_function_name=f"``{property_name}``" if not property_sections else None, + ) return docs