forgt what i was doing

This commit is contained in:
Jesse Bannon 2025-03-08 13:24:45 -08:00
parent 9af03eea9b
commit cdff1c7565
5 changed files with 119 additions and 112 deletions

View file

@ -1,90 +1,80 @@
==================
Configuration File Configuration File
================== ==================
----------- Something
config.yaml
-----------
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 .. code-block:: yaml
configuration: configuration:
presets: dl_aliases:
mv: "--preset music_video"
u: "--download.url"
You can jump to any section and subsection of the config using the navigation Simplifies
section to the left.
Note for Windows users, paths can be represented with ``C:/forward/slashes/like/linux``. .. code-block:: bash
If you wish to represent paths like Windows, you will need to ``C:\\double\\bashslash\\paths``
in order to escape the backslash character.
configuration ytdl-sub dl --preset "Jellyfin Music Videos" --download.url "youtube.com/watch?v=a1b2c3"
~~~~~~~~~~~~~
The ``configuration`` section contains app-wide configs applied to all presets
and subscriptions.
.. autoclass:: ytdl_sub.config.config_validator.ConfigOptions() to
:members:
:member-order: bysource .. code-block:: bash
:exclude-members: subscription_value, persist_logs, experimental
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 persist_logs
"""""""""""" ------------
Within ``configuration``, define whether logs from subscription downloads TODO(jessebannon) fill out
should be persisted.
.. code-block:: yaml ``keep_successful_logs``
configuration: Optional. Whether to store logs when downloading is successful. Defaults to True.
persist_logs:
logs_directory: "/path/to/log/directory"
Log files are stored as ``logs_directory``
``YYYY-mm-dd-HHMMSS.subscription_name.(success|error).log``.
.. autoclass:: ytdl_sub.config.config_validator.PersistLogsValidator() Required. The directory to store the logs in.
:members:
:member-order: bysource
presets umask
~~~~~~~ -----
``presets`` define a `formula` for how to format downloaded media and metadata. Umask (octal format) to apply to every created file. Defaults to "022".
This section is work-in-progress! working_directory
-----------------
preset The directory to temporarily store downloaded files before moving them into their final
"""""" directory. Defaults to .ytdl-sub-working-directory
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.

View file

@ -22,6 +22,10 @@ from ytdl_sub.validators.validators import StringValidator
class ExperimentalValidator(StrictDictValidator): class ExperimentalValidator(StrictDictValidator):
"""
TODO(jessebannon) fill out
"""
_optional_keys = {"enable_update_with_info_json"} _optional_keys = {"enable_update_with_info_json"}
_allow_extra_keys = True _allow_extra_keys = True
@ -43,6 +47,10 @@ class ExperimentalValidator(StrictDictValidator):
class PersistLogsValidator(StrictDictValidator): class PersistLogsValidator(StrictDictValidator):
"""
TODO(jessebannon) fill out
"""
_required_keys = {"logs_directory"} _required_keys = {"logs_directory"}
_optional_keys = {"keep_logs_after", "keep_successful_logs"} _optional_keys = {"keep_logs_after", "keep_successful_logs"}
@ -98,6 +106,10 @@ class PersistLogsValidator(StrictDictValidator):
class ConfigOptions(StrictDictValidator): class ConfigOptions(StrictDictValidator):
"""
Something
"""
_optional_keys = { _optional_keys = {
"working_directory", "working_directory",
"umask", "umask",

View file

@ -1,5 +1,6 @@
from typing import Type from typing import Type
from tools.docgen.configuration import ConfigurationDocGen
from tools.docgen.docgen import DocGen from tools.docgen.docgen import DocGen
from tools.docgen.entry_variables import EntryVariablesDocGen from tools.docgen.entry_variables import EntryVariablesDocGen
from tools.docgen.plugins import PluginsDocGen from tools.docgen.plugins import PluginsDocGen
@ -28,3 +29,6 @@ class TestDocGen:
def test_plugins_generated(self): def test_plugins_generated(self):
_test_doc_gen(PluginsDocGen) _test_doc_gen(PluginsDocGen)
def test_configuration_generated(self):
_test_doc_gen(ConfigurationDocGen)

View file

@ -1,26 +1,8 @@
from pathlib import Path 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.docgen import DocGen
from tools.docgen.utils import cached_properties from tools.docgen.utils import generate_options_validator_docs
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 ytdl_sub.config.config_validator import ConfigOptions 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): class ConfigurationDocGen(DocGen):
@ -28,23 +10,14 @@ class ConfigurationDocGen(DocGen):
LOCATION = Path("docs/source/config_reference/config_yaml.rst") LOCATION = Path("docs/source/config_reference/config_yaml.rst")
@classmethod @classmethod
def generate(cls) -> str: def generate(cls):
docs = section("Configuration File", level=0) docs = generate_options_validator_docs(
name="Configuration File",
parent_objs: Dict[str, Type[Any]] = { options=ConfigOptions,
_variable_class_to_name(obj): obj for obj in ConfigOptions.__bases__ offset=0,
} skip_properties=False,
recurse_property_options=True,
for idx, name in enumerate(sorted(parent_objs.keys())): property_sections=True
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,
)
return docs return docs

View file

@ -4,10 +4,10 @@ from typing import Any
from typing import Dict from typing import Dict
from typing import List from typing import List
from typing import Optional from typing import Optional
from typing import Set
from typing import Type 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: "^"} LEVEL_CHARS: Dict[int, str] = {0: "=", 1: "-", 2: "~", 3: "^"}
@ -25,6 +25,19 @@ def _should_filter_property(property_name: str) -> bool:
"unresolvable", "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: def section(name: str, level: Optional[int]) -> str:
if level is None: if level is None:
@ -85,7 +98,7 @@ def get_function_docs(
def generate_options_validator_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: ) -> str:
docs = "" docs = ""
docs += section(name, level=offset + 0) 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)] property_names = [prop for prop in properties(options) if not _should_filter_property(prop)]
for property_name in sorted(property_names): 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 return docs