ytdl-sub/src/ytdl_sub/config/overrides.py
2023-12-13 14:23:30 -08:00

134 lines
4.6 KiB
Python

import copy
from typing import Any
from typing import Dict
from typing import Optional
from typing import Set
from yt_dlp.utils import sanitize_filename
from ytdl_sub.entries.entry import Entry
from ytdl_sub.entries.script.variable_definitions import VARIABLES
from ytdl_sub.entries.variables.override_variables import SUBSCRIPTION_NAME
from ytdl_sub.script.parser import parse
from ytdl_sub.utils.scriptable import Scriptable
from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
class Overrides(DictFormatterValidator, Scriptable):
"""
Optional. This section allows you to define variables that can be used in any string formatter.
For example, if you want your file and thumbnail files to match without copy-pasting a large
format string, you can define something like:
.. code-block:: yaml
presets:
my_example_preset:
overrides:
output_directory: "/path/to/media"
custom_file_name: "{upload_year}.{upload_month_padded}.{upload_day_padded}.{title_sanitized}"
# Then use the override variables in the output options
output_options:
output_directory: "{output_directory}"
file_name: "{custom_file_name}.{ext}"
thumbnail_name: "{custom_file_name}.{thumbnail_ext}"
Override variables can contain explicit values and other variables, including both override
and source variables.
In addition, any override variable defined will automatically create a ``sanitized`` variable
for use. In the example above, ``output_directory_sanitized`` will exist and perform
sanitization on the value when used.
"""
@classmethod
def partial_validate(cls, name: str, value: Any) -> None:
dict_formatter = DictFormatterValidator(name=name, value=value)
_ = [parse(format_string) for format_string in dict_formatter.dict_with_format_strings]
# pylint: enable=line-too-long
def _add_override_variable(self, key_name: str, format_string: str, sanitize: bool = False):
if sanitize:
key_name = f"{key_name}_sanitized"
format_string = sanitize_filename(format_string)
self._value[key_name] = StringFormatterValidator(
name="__should_never_fail__",
value=format_string,
)
def __init__(self, name, value):
DictFormatterValidator.__init__(self, name, value)
Scriptable.__init__(self)
# Add sanitized overrides
for key in self._keys:
self._add_override_variable(
key_name=key,
format_string=self._value[key].format_string,
sanitize=True,
)
if SUBSCRIPTION_NAME not in self._value:
for sanitized in [True, False]:
self._add_override_variable(
key_name=SUBSCRIPTION_NAME,
format_string=self.subscription_name,
sanitize=sanitized,
)
self.unresolvable.add(VARIABLES.entry_metadata.variable_name)
def initialize_script(self, unresolved_variables: Dict[str, str]) -> None:
self.script.add(dict(self.dict_with_format_strings, **unresolved_variables))
self.unresolvable.update(set(unresolved_variables.keys()))
self.update_script()
@property
def subscription_name(self) -> str:
"""
Returns
-------
Name of the subscription
"""
return self._root_name
def apply_formatter(
self,
formatter: StringFormatterValidator,
entry: Optional[Entry] = None,
function_overrides: Dict[str, str] = None,
) -> str:
"""
Parameters
----------
formatter
Formatter to apply
entry
Optional. Entry to add source variables to the formatter
function_overrides
Optional. Explicit values to override the overrides themselves and source variables
Returns
-------
The format_string after .format has been called
"""
if entry:
script = copy.deepcopy(entry.script)
unresolvable = entry.unresolvable
else:
script = copy.deepcopy(self.script)
unresolvable = self.unresolvable
if function_overrides:
script.add(function_overrides)
return str(
script.add({"tmp_var": formatter.format_string}).resolve(unresolvable=unresolvable)[
"tmp_var"
]
)