really need partial script
This commit is contained in:
parent
7dbeb7a4ff
commit
6ad5c47068
23 changed files with 271 additions and 272 deletions
134
src/ytdl_sub/config/overrides.py
Normal file
134
src/ytdl_sub/config/overrides.py
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
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"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
@ -7,7 +7,7 @@ from typing import Optional
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
from ytdl_sub.config.preset_options import Overrides
|
from ytdl_sub.config.overrides import Overrides
|
||||||
from ytdl_sub.config.preset_options import TOptionsValidator
|
from ytdl_sub.config.preset_options import TOptionsValidator
|
||||||
from ytdl_sub.entries.entry import Entry
|
from ytdl_sub.entries.entry import Entry
|
||||||
from ytdl_sub.utils.file_handler import FileMetadata
|
from ytdl_sub.utils.file_handler import FileMetadata
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import copy
|
import copy
|
||||||
|
import functools
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from typing import Set
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from typing import Type
|
from typing import Type
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
@ -11,18 +13,22 @@ from typing import Union
|
||||||
from mergedeep import mergedeep
|
from mergedeep import mergedeep
|
||||||
|
|
||||||
from ytdl_sub.config.config_validator import ConfigValidator
|
from ytdl_sub.config.config_validator import ConfigValidator
|
||||||
|
from ytdl_sub.config.overrides import Overrides
|
||||||
from ytdl_sub.config.plugin import Plugin
|
from ytdl_sub.config.plugin import Plugin
|
||||||
from ytdl_sub.config.plugin_mapping import PluginMapping
|
from ytdl_sub.config.plugin_mapping import PluginMapping
|
||||||
from ytdl_sub.config.preset_options import OptionsValidator
|
from ytdl_sub.config.preset_options import OptionsValidator
|
||||||
from ytdl_sub.config.preset_options import OutputOptions
|
from ytdl_sub.config.preset_options import OutputOptions
|
||||||
from ytdl_sub.config.preset_options import Overrides
|
|
||||||
from ytdl_sub.config.preset_options import TOptionsValidator
|
from ytdl_sub.config.preset_options import TOptionsValidator
|
||||||
from ytdl_sub.config.preset_options import YTDLOptions
|
from ytdl_sub.config.preset_options import YTDLOptions
|
||||||
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
|
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
|
||||||
from ytdl_sub.entries.entry import Entry
|
from ytdl_sub.entries.entry import Entry
|
||||||
|
from ytdl_sub.entries.script.variable_definitions import VARIABLES
|
||||||
from ytdl_sub.entries.script.variable_scripts import VARIABLE_SCRIPTS
|
from ytdl_sub.entries.script.variable_scripts import VARIABLE_SCRIPTS
|
||||||
from ytdl_sub.prebuilt_presets import PREBUILT_PRESET_NAMES
|
from ytdl_sub.prebuilt_presets import PREBUILT_PRESET_NAMES
|
||||||
from ytdl_sub.prebuilt_presets import PUBLISHED_PRESET_NAMES
|
from ytdl_sub.prebuilt_presets import PUBLISHED_PRESET_NAMES
|
||||||
|
from ytdl_sub.script.script import Script
|
||||||
|
from ytdl_sub.script.utils.exceptions import VariableDoesNotExist
|
||||||
|
from ytdl_sub.utils.exceptions import StringFormattingVariableNotFoundException
|
||||||
from ytdl_sub.utils.exceptions import ValidationException
|
from ytdl_sub.utils.exceptions import ValidationException
|
||||||
from ytdl_sub.utils.logger import Logger
|
from ytdl_sub.utils.logger import Logger
|
||||||
from ytdl_sub.utils.yaml import dump_yaml
|
from ytdl_sub.utils.yaml import dump_yaml
|
||||||
|
|
@ -159,6 +165,35 @@ class Preset(_PresetShell):
|
||||||
def _source_variables(self) -> List[str]:
|
def _source_variables(self) -> List[str]:
|
||||||
return list(VARIABLE_SCRIPTS.keys())
|
return list(VARIABLE_SCRIPTS.keys())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _added_variables(self) -> Dict[str, str]:
|
||||||
|
added_variables: Dict[str, str] = {
|
||||||
|
var_name: "dummy_string"
|
||||||
|
for var_name in self.downloader_options.added_source_variables()
|
||||||
|
}
|
||||||
|
|
||||||
|
for plugin_options in self.plugins.plugin_options:
|
||||||
|
for source_var in plugin_options.added_source_variables():
|
||||||
|
added_variables[source_var] = "dummy_string"
|
||||||
|
return added_variables
|
||||||
|
|
||||||
|
@functools.cached_property
|
||||||
|
def _mock_script(self) -> Script:
|
||||||
|
# Set the formatter variables to be the overrides
|
||||||
|
variable_dict = copy.deepcopy(self.overrides.dict_with_format_strings)
|
||||||
|
|
||||||
|
source_variables = {
|
||||||
|
source_var: "dummy_string"
|
||||||
|
for source_var in self._source_variables
|
||||||
|
+ self.downloader_options.added_source_variables()
|
||||||
|
}
|
||||||
|
variable_dict = dict(source_variables, **variable_dict)
|
||||||
|
variable_dict = dict(variable_dict, **self._added_variables)
|
||||||
|
|
||||||
|
script = Script(variable_dict)
|
||||||
|
script.resolve(update=True)
|
||||||
|
return script
|
||||||
|
|
||||||
def __validate_and_get_plugins(self) -> PresetPlugins:
|
def __validate_and_get_plugins(self) -> PresetPlugins:
|
||||||
preset_plugins = PresetPlugins()
|
preset_plugins = PresetPlugins()
|
||||||
|
|
||||||
|
|
@ -174,54 +209,30 @@ class Preset(_PresetShell):
|
||||||
return preset_plugins
|
return preset_plugins
|
||||||
|
|
||||||
def __validate_added_variables(self):
|
def __validate_added_variables(self):
|
||||||
source_variables = copy.deepcopy(self._source_variables)
|
self.downloader_options.validate_with_variables(script=copy.deepcopy(self._mock_script))
|
||||||
|
|
||||||
# Validate added download option variables here since plugins could subsequently use them
|
|
||||||
self.downloader_options.validate_with_variables(
|
|
||||||
source_variables=source_variables,
|
|
||||||
override_variables=self.overrides.dict_with_format_strings,
|
|
||||||
)
|
|
||||||
source_variables.extend(self.downloader_options.added_source_variables())
|
|
||||||
|
|
||||||
for _, plugin_options in sorted(
|
for _, plugin_options in sorted(
|
||||||
self.plugins.zipped(), key=lambda pl: pl[0].priority.modify_entry
|
self.plugins.zipped(), key=lambda pl: pl[0].priority.modify_entry
|
||||||
):
|
):
|
||||||
# Validate current plugin using source + added plugin variables
|
# Validate current plugin using source + added plugin variables
|
||||||
plugin_options.validate_with_variables(
|
plugin_options.validate_with_variables(script=copy.deepcopy(self._mock_script))
|
||||||
source_variables=source_variables,
|
|
||||||
override_variables=self.overrides.dict_with_format_strings,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Extend existing source variables with ones created from this plugin
|
|
||||||
source_variables.extend(plugin_options.added_source_variables())
|
|
||||||
|
|
||||||
def __validate_override_string_formatter_validator(
|
def __validate_override_string_formatter_validator(
|
||||||
self,
|
self,
|
||||||
formatter_validator: Union[StringFormatterValidator, OverridesStringFormatterValidator],
|
formatter_validator: Union[StringFormatterValidator, OverridesStringFormatterValidator],
|
||||||
):
|
):
|
||||||
# Set the formatter variables to be the overrides
|
script = copy.deepcopy(self._mock_script)
|
||||||
variable_dict = copy.deepcopy(self.overrides.dict_with_format_strings)
|
try:
|
||||||
|
script.add({"tmp_var": formatter_validator.format_string})
|
||||||
|
except VariableDoesNotExist as exc:
|
||||||
|
raise StringFormattingVariableNotFoundException(exc) from exc
|
||||||
|
|
||||||
source_variables = {
|
unresolved: Optional[Set[str]] = (
|
||||||
source_var: "dummy_string"
|
{VARIABLES.entry_metadata.variable_name}
|
||||||
for source_var in self._source_variables
|
if isinstance(formatter_validator, OverridesStringFormatterValidator)
|
||||||
+ self.downloader_options.added_source_variables()
|
else None
|
||||||
}
|
)
|
||||||
variable_dict = dict(source_variables, **variable_dict)
|
_ = script.resolve(unresolvable=unresolved)["tmp_var"] # TODO: error if not present
|
||||||
|
|
||||||
# For all plugins, add in any extra added source variables
|
|
||||||
# TODO: Check in order variables are added
|
|
||||||
for plugin_options in self.plugins.plugin_options:
|
|
||||||
added_plugin_variables = {
|
|
||||||
source_var: "dummy_string" for source_var in plugin_options.added_source_variables()
|
|
||||||
}
|
|
||||||
# sanity check plugin variables do not override source variables
|
|
||||||
expected_len = len(variable_dict) + len(added_plugin_variables)
|
|
||||||
variable_dict = dict(variable_dict, **added_plugin_variables)
|
|
||||||
|
|
||||||
assert len(variable_dict) == expected_len, "plugin variables overwrote source variables"
|
|
||||||
|
|
||||||
_ = formatter_validator.apply_formatter(variable_dict=variable_dict)
|
|
||||||
|
|
||||||
def __recursive_preset_validate(
|
def __recursive_preset_validate(
|
||||||
self,
|
self,
|
||||||
|
|
@ -333,6 +344,13 @@ class Preset(_PresetShell):
|
||||||
# values from multiple validators
|
# values from multiple validators
|
||||||
self.__recursive_preset_validate()
|
self.__recursive_preset_validate()
|
||||||
|
|
||||||
|
self.overrides.initialize_script(
|
||||||
|
unresolved_variables={
|
||||||
|
var_name: f"{{%throw('Plugin variable {var_name} has not been created yet')}}"
|
||||||
|
for var_name in self._added_variables
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,16 @@
|
||||||
import copy
|
|
||||||
import json
|
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Dict
|
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
|
|
||||||
import mergedeep
|
|
||||||
from yt_dlp.utils import sanitize_filename
|
|
||||||
|
|
||||||
from ytdl_sub.config.defaults import DEFAULT_DOWNLOAD_ARCHIVE_NAME
|
from ytdl_sub.config.defaults import DEFAULT_DOWNLOAD_ARCHIVE_NAME
|
||||||
from ytdl_sub.entries.entry import Entry
|
from ytdl_sub.script.script import Script
|
||||||
from ytdl_sub.entries.script.variable_definitions import VARIABLES
|
|
||||||
from ytdl_sub.entries.script.variable_scripts import VARIABLE_SCRIPTS
|
|
||||||
from ytdl_sub.entries.variables.override_variables import SUBSCRIPTION_NAME
|
|
||||||
from ytdl_sub.utils.exceptions import ValidationException
|
from ytdl_sub.utils.exceptions import ValidationException
|
||||||
from ytdl_sub.validators.file_path_validators import OverridesStringFormatterFilePathValidator
|
from ytdl_sub.validators.file_path_validators import OverridesStringFormatterFilePathValidator
|
||||||
from ytdl_sub.validators.file_path_validators import StringFormatterFileNameValidator
|
from ytdl_sub.validators.file_path_validators import StringFormatterFileNameValidator
|
||||||
from ytdl_sub.validators.strict_dict_validator import StrictDictValidator
|
from ytdl_sub.validators.strict_dict_validator import StrictDictValidator
|
||||||
from ytdl_sub.validators.string_datetime import StringDatetimeValidator
|
from ytdl_sub.validators.string_datetime import StringDatetimeValidator
|
||||||
from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator
|
|
||||||
from ytdl_sub.validators.string_formatter_validators import OverridesIntegerFormatterValidator
|
from ytdl_sub.validators.string_formatter_validators import OverridesIntegerFormatterValidator
|
||||||
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
|
||||||
|
|
@ -63,18 +53,14 @@ class OptionsValidator(Validator, ABC):
|
||||||
"""
|
"""
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def validate_with_variables(
|
def validate_with_variables(self, script: Script) -> None:
|
||||||
self, source_variables: List[str], override_variables: Dict[str, str]
|
|
||||||
) -> None:
|
|
||||||
"""
|
"""
|
||||||
Optional validation after init with the session's source and override variables.
|
Optional validation after init with the session's source and override variables.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
source_variables
|
script
|
||||||
Available source variables when running the plugin
|
Script containing all current variables
|
||||||
override_variables
|
|
||||||
Available override variables when running the plugin
|
|
||||||
"""
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
@ -129,103 +115,6 @@ class YTDLOptions(LiteralDictValidator):
|
||||||
|
|
||||||
# Disable for proper docstring formatting
|
# Disable for proper docstring formatting
|
||||||
# pylint: disable=line-too-long
|
# pylint: disable=line-too-long
|
||||||
class Overrides(DictFormatterValidator):
|
|
||||||
"""
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 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):
|
|
||||||
super().__init__(name, value)
|
|
||||||
|
|
||||||
# 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,
|
|
||||||
)
|
|
||||||
|
|
||||||
@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
|
|
||||||
"""
|
|
||||||
variable_dict = copy.deepcopy(VARIABLE_SCRIPTS)
|
|
||||||
mergedeep.merge(variable_dict, self.dict_with_format_strings)
|
|
||||||
|
|
||||||
if entry:
|
|
||||||
mergedeep.merge(variable_dict, {VARIABLES.entry_metadata.variable_name: json.dumps(entry._kwargs)})
|
|
||||||
if function_overrides:
|
|
||||||
mergedeep.merge(variable_dict, function_overrides)
|
|
||||||
|
|
||||||
return formatter.apply_formatter(variable_dict)
|
|
||||||
|
|
||||||
|
|
||||||
class OutputOptions(StrictDictValidator):
|
class OutputOptions(StrictDictValidator):
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ from typing import Iterable
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from ytdl_sub.config.overrides import Overrides
|
||||||
from ytdl_sub.config.preset_options import OptionsDictValidator
|
from ytdl_sub.config.preset_options import OptionsDictValidator
|
||||||
from ytdl_sub.config.preset_options import Overrides
|
|
||||||
from ytdl_sub.downloaders.source_plugin import SourcePlugin
|
from ytdl_sub.downloaders.source_plugin import SourcePlugin
|
||||||
from ytdl_sub.downloaders.ytdl_options_builder import YTDLOptionsBuilder
|
from ytdl_sub.downloaders.ytdl_options_builder import YTDLOptionsBuilder
|
||||||
from ytdl_sub.entries.entry import Entry
|
from ytdl_sub.entries.entry import Entry
|
||||||
|
|
@ -82,6 +82,7 @@ class InfoJsonDownloader(SourcePlugin[InfoJsonDownloaderOptions]):
|
||||||
return Entry(
|
return Entry(
|
||||||
entry_dict=entry_dict,
|
entry_dict=entry_dict,
|
||||||
working_directory=self.working_directory,
|
working_directory=self.working_directory,
|
||||||
|
override_variables=self.overrides.dict_with_format_strings,
|
||||||
)
|
)
|
||||||
|
|
||||||
raise ValidationException(
|
raise ValidationException(
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@ from typing import Optional
|
||||||
from typing import Type
|
from typing import Type
|
||||||
from typing import final
|
from typing import final
|
||||||
|
|
||||||
|
from ytdl_sub.config.overrides import Overrides
|
||||||
from ytdl_sub.config.plugin import BasePlugin
|
from ytdl_sub.config.plugin import BasePlugin
|
||||||
from ytdl_sub.config.plugin import Plugin
|
from ytdl_sub.config.plugin import Plugin
|
||||||
from ytdl_sub.config.preset_options import Overrides
|
|
||||||
from ytdl_sub.config.preset_options import TOptionsValidator
|
from ytdl_sub.config.preset_options import TOptionsValidator
|
||||||
from ytdl_sub.downloaders.ytdl_options_builder import YTDLOptionsBuilder
|
from ytdl_sub.downloaders.ytdl_options_builder import YTDLOptionsBuilder
|
||||||
from ytdl_sub.entries.entry import Entry
|
from ytdl_sub.entries.entry import Entry
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ from typing import Tuple
|
||||||
|
|
||||||
from yt_dlp.utils import RejectedVideoReached
|
from yt_dlp.utils import RejectedVideoReached
|
||||||
|
|
||||||
|
from ytdl_sub.config.overrides import Overrides
|
||||||
from ytdl_sub.config.plugin import PluginPriority
|
from ytdl_sub.config.plugin import PluginPriority
|
||||||
from ytdl_sub.config.preset_options import Overrides
|
|
||||||
from ytdl_sub.downloaders.source_plugin import SourcePlugin
|
from ytdl_sub.downloaders.source_plugin import SourcePlugin
|
||||||
from ytdl_sub.downloaders.source_plugin import SourcePluginExtension
|
from ytdl_sub.downloaders.source_plugin import SourcePluginExtension
|
||||||
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
|
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
|
||||||
|
|
@ -356,7 +356,11 @@ class MultiUrlDownloader(SourcePlugin[MultiUrlValidator]):
|
||||||
else entry.is_thumbnail_downloaded_via_ytdlp,
|
else entry.is_thumbnail_downloaded_via_ytdlp,
|
||||||
url=entry.webpage_url,
|
url=entry.webpage_url,
|
||||||
)
|
)
|
||||||
return Entry(download_entry_dict, working_directory=self.working_directory)
|
return Entry(
|
||||||
|
download_entry_dict,
|
||||||
|
working_directory=self.working_directory,
|
||||||
|
override_variables=self.overrides.dict_with_format_strings,
|
||||||
|
)
|
||||||
|
|
||||||
def _iterate_child_entries(
|
def _iterate_child_entries(
|
||||||
self, url_validator: UrlValidator, entries: List[Entry]
|
self, url_validator: UrlValidator, entries: List[Entry]
|
||||||
|
|
@ -413,7 +417,10 @@ class MultiUrlDownloader(SourcePlugin[MultiUrlValidator]):
|
||||||
working_directory=self.working_directory,
|
working_directory=self.working_directory,
|
||||||
)
|
)
|
||||||
orphans = EntryParent.from_entry_dicts_with_no_parents(
|
orphans = EntryParent.from_entry_dicts_with_no_parents(
|
||||||
parents=parents, entry_dicts=entry_dicts, working_directory=self.working_directory
|
parents=parents,
|
||||||
|
entry_dicts=entry_dicts,
|
||||||
|
working_directory=self.working_directory,
|
||||||
|
override_variables=self.overrides.dict_with_format_strings,
|
||||||
)
|
)
|
||||||
|
|
||||||
return parents, orphans
|
return parents, orphans
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from ytdl_sub.config.preset_options import OptionsValidator
|
from ytdl_sub.config.preset_options import OptionsValidator
|
||||||
|
from ytdl_sub.script.script import Script
|
||||||
from ytdl_sub.validators.strict_dict_validator import StrictDictValidator
|
from ytdl_sub.validators.strict_dict_validator import StrictDictValidator
|
||||||
from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator
|
from ytdl_sub.validators.string_formatter_validators import DictFormatterValidator
|
||||||
from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator
|
from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator
|
||||||
|
|
@ -251,37 +252,20 @@ class MultiUrlValidator(OptionsValidator):
|
||||||
"""
|
"""
|
||||||
return list(self._urls.list[0].variables.keys)
|
return list(self._urls.list[0].variables.keys)
|
||||||
|
|
||||||
def validate_with_variables(
|
def validate_with_variables(self, script: Script) -> None:
|
||||||
self, source_variables: List[str], override_variables: Dict[str, str]
|
|
||||||
) -> None:
|
|
||||||
"""
|
"""
|
||||||
Ensures new variables added are not existing variables
|
Ensures new variables added are not existing variables
|
||||||
"""
|
"""
|
||||||
for source_var_name in self.added_source_variables():
|
|
||||||
if source_var_name in source_variables:
|
|
||||||
raise self._validation_exception(
|
|
||||||
f"'{source_var_name}' cannot be used as a variable name because it "
|
|
||||||
f"is an existing source variable"
|
|
||||||
)
|
|
||||||
|
|
||||||
base_variables = dict(
|
|
||||||
override_variables, **{source_var: "dummy_string" for source_var in source_variables}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Apply formatting to each new source variable, ensure it resolves
|
# Apply formatting to each new source variable, ensure it resolves
|
||||||
for collection_url in self.urls.list:
|
for collection_url in self.urls.list:
|
||||||
for (
|
script.add(collection_url.variables.dict_with_format_strings)
|
||||||
source_var_name,
|
script.resolve(update=True)
|
||||||
source_var_formatter_str,
|
|
||||||
) in collection_url.variables.dict_with_format_strings.items():
|
|
||||||
_ = StringFormatterValidator(
|
|
||||||
name=f"{self._name}.{source_var_name}", value=source_var_formatter_str
|
|
||||||
).apply_formatter(base_variables)
|
|
||||||
|
|
||||||
# Ensure at least URL is non-empty
|
# Ensure at least URL is non-empty
|
||||||
has_non_empty_url = False
|
has_non_empty_url = False
|
||||||
for url_validator in self.urls.list:
|
for url_validator in self.urls.list:
|
||||||
has_non_empty_url |= bool(url_validator.url.apply_formatter(base_variables))
|
script.add({"tmp_var_url": url_validator.url.format_string})
|
||||||
|
has_non_empty_url |= bool(str(script.resolve().get("tmp_var_url")))
|
||||||
|
|
||||||
if not has_non_empty_url:
|
if not has_non_empty_url:
|
||||||
raise self._validation_exception("Must contain at least one url that is non-empty")
|
raise self._validation_exception("Must contain at least one url that is non-empty")
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,32 @@ import copy
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Dict
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import final
|
from typing import final
|
||||||
|
|
||||||
from ytdl_sub.entries.base_entry import BaseEntry
|
from ytdl_sub.entries.base_entry import BaseEntry
|
||||||
|
from ytdl_sub.entries.script.variable_definitions import VARIABLES
|
||||||
|
from ytdl_sub.utils.scriptable import Scriptable
|
||||||
from ytdl_sub.validators.audo_codec_validator import AUDIO_CODEC_EXTS
|
from ytdl_sub.validators.audo_codec_validator import AUDIO_CODEC_EXTS
|
||||||
from ytdl_sub.validators.audo_codec_validator import VIDEO_CODEC_EXTS
|
from ytdl_sub.validators.audo_codec_validator import VIDEO_CODEC_EXTS
|
||||||
|
|
||||||
|
|
||||||
class Entry(BaseEntry):
|
class Entry(BaseEntry, Scriptable):
|
||||||
"""
|
"""
|
||||||
Entry object to represent a single media object returned from yt-dlp.
|
Entry object to represent a single media object returned from yt-dlp.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, entry_dict: Dict, working_directory: str, override_variables: Dict[str, str]
|
||||||
|
):
|
||||||
|
BaseEntry.__init__(self, entry_dict=entry_dict, working_directory=working_directory)
|
||||||
|
Scriptable.__init__(self)
|
||||||
|
|
||||||
|
self.script.add({VARIABLES.entry_metadata.variable_name: json.dumps(self._kwargs)})
|
||||||
|
self.script.add(override_variables)
|
||||||
|
self.script.resolve(update=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ext(self) -> str:
|
def ext(self) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -279,7 +279,11 @@ class EntryParent(BaseEntry):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_entry_dicts_with_no_parents(
|
def from_entry_dicts_with_no_parents(
|
||||||
cls, parents: List["EntryParent"], entry_dicts: List[Dict], working_directory: str
|
cls,
|
||||||
|
parents: List["EntryParent"],
|
||||||
|
entry_dicts: List[Dict],
|
||||||
|
working_directory: str,
|
||||||
|
override_variables: Dict[str, str],
|
||||||
) -> List[Entry]:
|
) -> List[Entry]:
|
||||||
"""
|
"""
|
||||||
Reads all entries that do not have any parents
|
Reads all entries that do not have any parents
|
||||||
|
|
@ -289,7 +293,11 @@ class EntryParent(BaseEntry):
|
||||||
return any(entry_dict in parent for parent in parents)
|
return any(entry_dict in parent for parent in parents)
|
||||||
|
|
||||||
return [
|
return [
|
||||||
Entry(entry_dict=entry_dict, working_directory=working_directory)
|
Entry(
|
||||||
|
entry_dict=entry_dict,
|
||||||
|
working_directory=working_directory,
|
||||||
|
override_variables=override_variables,
|
||||||
|
)
|
||||||
for entry_dict in entry_dicts
|
for entry_dict in entry_dicts
|
||||||
if cls.is_entry(entry_dict) and not _in_any_parents(entry_dict)
|
if cls.is_entry(entry_dict) and not _in_any_parents(entry_dict)
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -98,9 +98,7 @@ def source_get_int(key: MetadataVariable, default: Optional[Variable | int] = No
|
||||||
###############################################################################################
|
###############################################################################################
|
||||||
# Scripts
|
# Scripts
|
||||||
|
|
||||||
ENTRY_EMPTY_METADATA: Dict[Variable, str] = {
|
ENTRY_EMPTY_METADATA: Dict[Variable, str] = {v.entry_metadata: "{ {} }"}
|
||||||
v.entry_metadata: "{ {} }"
|
|
||||||
}
|
|
||||||
|
|
||||||
ENTRY_HARDCODED_VARIABLES: Dict[Variable, str] = {
|
ENTRY_HARDCODED_VARIABLES: Dict[Variable, str] = {
|
||||||
v.info_json_ext: "info.json",
|
v.info_json_ext: "info.json",
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import copy
|
import copy
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from ytdl_sub.config.overrides import Overrides
|
||||||
from ytdl_sub.config.plugin import Plugin
|
from ytdl_sub.config.plugin import Plugin
|
||||||
from ytdl_sub.config.plugin import PluginPriority
|
from ytdl_sub.config.plugin import PluginPriority
|
||||||
from ytdl_sub.config.preset_options import OptionsDictValidator
|
from ytdl_sub.config.preset_options import OptionsDictValidator
|
||||||
from ytdl_sub.config.preset_options import Overrides
|
|
||||||
from ytdl_sub.entries.entry import Entry
|
from ytdl_sub.entries.entry import Entry
|
||||||
from ytdl_sub.utils.file_handler import FileMetadata
|
from ytdl_sub.utils.file_handler import FileMetadata
|
||||||
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
|
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from ytdl_sub.config.preset_options import Overrides
|
from ytdl_sub.config.overrides import Overrides
|
||||||
from ytdl_sub.entries.entry import Entry
|
from ytdl_sub.entries.entry import Entry
|
||||||
from ytdl_sub.plugins.nfo_tags import NfoTagsValidator
|
from ytdl_sub.plugins.nfo_tags import NfoTagsValidator
|
||||||
from ytdl_sub.plugins.nfo_tags import SharedNfoTagsOptions
|
from ytdl_sub.plugins.nfo_tags import SharedNfoTagsOptions
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ from ytdl_sub.config.plugin import Plugin
|
||||||
from ytdl_sub.config.plugin import PluginPriority
|
from ytdl_sub.config.plugin import PluginPriority
|
||||||
from ytdl_sub.config.preset_options import OptionsDictValidator
|
from ytdl_sub.config.preset_options import OptionsDictValidator
|
||||||
from ytdl_sub.entries.entry import Entry
|
from ytdl_sub.entries.entry import Entry
|
||||||
|
from ytdl_sub.entries.script.variable_scripts import VARIABLE_SCRIPTS
|
||||||
from ytdl_sub.entries.variables.kwargs import YTDL_SUB_REGEX_SOURCE_VARS
|
from ytdl_sub.entries.variables.kwargs import YTDL_SUB_REGEX_SOURCE_VARS
|
||||||
|
from ytdl_sub.script.script import Script
|
||||||
from ytdl_sub.utils.exceptions import RegexNoMatchException
|
from ytdl_sub.utils.exceptions import RegexNoMatchException
|
||||||
from ytdl_sub.utils.exceptions import StringFormattingVariableNotFoundException
|
from ytdl_sub.utils.exceptions import StringFormattingVariableNotFoundException
|
||||||
from ytdl_sub.utils.logger import Logger
|
from ytdl_sub.utils.logger import Logger
|
||||||
|
|
@ -212,34 +214,22 @@ class RegexOptions(OptionsDictValidator):
|
||||||
"""
|
"""
|
||||||
return self._skip_if_match_fails
|
return self._skip_if_match_fails
|
||||||
|
|
||||||
def validate_with_variables(
|
def validate_with_variables(self, script: Script) -> None:
|
||||||
self, source_variables: List[str], override_variables: Dict[str, str]
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Ensures each source variable capture group is valid
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
source_variables
|
|
||||||
Available source variables when running the plugin
|
|
||||||
override_variables
|
|
||||||
Available override variables when running the plugin
|
|
||||||
"""
|
|
||||||
for key, regex_options in self.source_variable_capture_dict.items():
|
for key, regex_options in self.source_variable_capture_dict.items():
|
||||||
# Ensure each variable getting captured is a source variable
|
# Ensure each variable getting captured is a source variable
|
||||||
if key not in source_variables and key not in override_variables:
|
if key not in script._variables:
|
||||||
raise self._validation_exception(
|
raise self._validation_exception(
|
||||||
f"cannot regex capture '{key}' because it is not a source or override variable"
|
f"cannot regex capture '{key}' because it is not a source or override variable"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Ensure the capture group names are not existing source/override variables
|
# Ensure the capture group names are not existing source/override variables
|
||||||
for capture_group_name in regex_options.capture_group_names:
|
for capture_group_name in regex_options.capture_group_names:
|
||||||
if capture_group_name in source_variables:
|
if capture_group_name in VARIABLE_SCRIPTS:
|
||||||
raise self._validation_exception(
|
raise self._validation_exception(
|
||||||
f"'{capture_group_name}' cannot be used as a capture group name because it "
|
f"'{capture_group_name}' cannot be used as a capture group name because it "
|
||||||
f"is a source variable"
|
f"is a source variable"
|
||||||
)
|
)
|
||||||
if capture_group_name in override_variables:
|
if capture_group_name in script._variables:
|
||||||
raise self._validation_exception(
|
raise self._validation_exception(
|
||||||
f"'{capture_group_name}' cannot be used as a capture group name because it "
|
f"'{capture_group_name}' cannot be used as a capture group name because it "
|
||||||
f"is an override variable"
|
f"is an override variable"
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
|
from ytdl_sub.config.overrides import Overrides
|
||||||
from ytdl_sub.config.plugin import Plugin
|
from ytdl_sub.config.plugin import Plugin
|
||||||
from ytdl_sub.config.preset_options import OptionsDictValidator
|
from ytdl_sub.config.preset_options import OptionsDictValidator
|
||||||
from ytdl_sub.config.preset_options import Overrides
|
|
||||||
from ytdl_sub.entries.entry import Entry
|
from ytdl_sub.entries.entry import Entry
|
||||||
from ytdl_sub.utils.file_handler import FileMetadata
|
from ytdl_sub.utils.file_handler import FileMetadata
|
||||||
from ytdl_sub.utils.logger import Logger
|
from ytdl_sub.utils.logger import Logger
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from ytdl_sub.config.config_validator import ConfigOptions
|
from ytdl_sub.config.config_validator import ConfigOptions
|
||||||
|
from ytdl_sub.config.overrides import Overrides
|
||||||
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
|
||||||
from ytdl_sub.config.preset_options import Overrides
|
|
||||||
from ytdl_sub.config.preset_options import YTDLOptions
|
from ytdl_sub.config.preset_options import YTDLOptions
|
||||||
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
|
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
|
||||||
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from typing import Optional
|
||||||
from typing import final
|
from typing import final
|
||||||
|
|
||||||
from ytdl_sub.config.config_file import ConfigFile
|
from ytdl_sub.config.config_file import ConfigFile
|
||||||
from ytdl_sub.config.preset_options import Overrides
|
from ytdl_sub.config.overrides import Overrides
|
||||||
from ytdl_sub.entries.variables.override_variables import SUBSCRIPTION_NAME
|
from ytdl_sub.entries.variables.override_variables import SUBSCRIPTION_NAME
|
||||||
from ytdl_sub.entries.variables.override_variables import SUBSCRIPTION_VALUE
|
from ytdl_sub.entries.variables.override_variables import SUBSCRIPTION_VALUE
|
||||||
from ytdl_sub.entries.variables.override_variables import OverrideVariables
|
from ytdl_sub.entries.variables.override_variables import OverrideVariables
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from typing import Optional
|
||||||
from yt_dlp import DateRange
|
from yt_dlp import DateRange
|
||||||
from yt_dlp.utils import datetime_from_str
|
from yt_dlp.utils import datetime_from_str
|
||||||
|
|
||||||
from ytdl_sub.config.preset_options import Overrides
|
from ytdl_sub.config.overrides import Overrides
|
||||||
from ytdl_sub.validators.string_datetime import StringDatetimeValidator
|
from ytdl_sub.validators.string_datetime import StringDatetimeValidator
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
14
src/ytdl_sub/utils/scriptable.py
Normal file
14
src/ytdl_sub/utils/scriptable.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
from abc import ABC
|
||||||
|
from typing import Set
|
||||||
|
|
||||||
|
from ytdl_sub.entries.script.variable_scripts import VARIABLE_SCRIPTS
|
||||||
|
from ytdl_sub.script.script import Script
|
||||||
|
|
||||||
|
|
||||||
|
class Scriptable(ABC):
|
||||||
|
def __init__(self):
|
||||||
|
self.script = Script(VARIABLE_SCRIPTS)
|
||||||
|
self.unresolvable: Set[str] = set()
|
||||||
|
|
||||||
|
def update_script(self) -> None:
|
||||||
|
self.script.resolve(unresolvable=self.unresolvable, update=True)
|
||||||
|
|
@ -93,33 +93,6 @@ class StringFormatterValidator(StringValidator):
|
||||||
"""
|
"""
|
||||||
return self._value
|
return self._value
|
||||||
|
|
||||||
def _variable_dict(self, variable_dict: Dict[str, str]) -> Dict[str, str]:
|
|
||||||
sanitized_variables = {
|
|
||||||
f"{var_name}_sanitized": f"{{%sanitize({var_name})}}"
|
|
||||||
for var_name in variable_dict.keys()
|
|
||||||
}
|
|
||||||
return dict(variable_dict, **sanitized_variables)
|
|
||||||
|
|
||||||
def apply_formatter(self, variable_dict: Dict[str, str]) -> str:
|
|
||||||
"""
|
|
||||||
Calls `format` on the format string using the variable_dict as input kwargs
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
variable_dict
|
|
||||||
kwargs to pass to the format string
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Format string formatted
|
|
||||||
"""
|
|
||||||
out = (
|
|
||||||
Script(self._variable_dict(variable_dict))
|
|
||||||
.add({"tmp_var": self.format_string})
|
|
||||||
.resolve()["tmp_var"]
|
|
||||||
)
|
|
||||||
return str(out)
|
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=line-too-long
|
# pylint: disable=line-too-long
|
||||||
class OverridesStringFormatterValidator(StringFormatterValidator):
|
class OverridesStringFormatterValidator(StringFormatterValidator):
|
||||||
|
|
@ -133,37 +106,6 @@ class OverridesStringFormatterValidator(StringFormatterValidator):
|
||||||
:class:`nfo_output_directory <ytdl_sub.plugins.output_directory_nfo_tags.OutputDirectoryNfoTagsOptions>`
|
:class:`nfo_output_directory <ytdl_sub.plugins.output_directory_nfo_tags.OutputDirectoryNfoTagsOptions>`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_variable_not_found_error_msg_formatter = (
|
|
||||||
"Override variable '{variable_name}' does not exist. For this field, ensure your override "
|
|
||||||
"variable does not contain any source variables - it is a requirement that this be a "
|
|
||||||
"static string. Available override variables: {available_fields}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def apply_formatter(self, variable_dict: Dict[str, str]) -> str:
|
|
||||||
"""
|
|
||||||
Calls `format` on the format string using the variable_dict as input kwargs
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
variable_dict
|
|
||||||
kwargs to pass to the format string
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Format string formatted
|
|
||||||
"""
|
|
||||||
output = (
|
|
||||||
Script(self._variable_dict(variable_dict))
|
|
||||||
.add({"tmp_var": self.format_string})
|
|
||||||
.resolve(unresolvable={VARIABLES.entry_metadata.variable_name})
|
|
||||||
)
|
|
||||||
if "tmp_var" not in output:
|
|
||||||
raise self._validation_exception(
|
|
||||||
"Has a dependency on entry variables when it is not allowed",
|
|
||||||
exception_class=StringFormattingVariableNotFoundException,
|
|
||||||
)
|
|
||||||
return str(output["tmp_var"])
|
|
||||||
|
|
||||||
|
|
||||||
# pylint: enable=line-too-long
|
# pylint: enable=line-too-long
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -652,6 +652,7 @@ class EnhancedDownloadArchive:
|
||||||
parent_entry = Entry(
|
parent_entry = Entry(
|
||||||
entry_dict=entry.kwargs(SPLIT_BY_CHAPTERS_PARENT_ENTRY),
|
entry_dict=entry.kwargs(SPLIT_BY_CHAPTERS_PARENT_ENTRY),
|
||||||
working_directory=entry.working_directory(),
|
working_directory=entry.working_directory(),
|
||||||
|
override_variables={},
|
||||||
)
|
)
|
||||||
self.mapping.add_entry(parent_entry, entry_file_path=output_file_name)
|
self.mapping.add_entry(parent_entry, entry_file_path=output_file_name)
|
||||||
elif entry:
|
elif entry:
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ class TestPreset:
|
||||||
):
|
):
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
StringFormattingVariableNotFoundException,
|
StringFormattingVariableNotFoundException,
|
||||||
match="Format variable 'dne_var' does not exist",
|
match="Variable dne_var does not exist.",
|
||||||
):
|
):
|
||||||
_ = Preset(
|
_ = Preset(
|
||||||
config=config_file,
|
config=config_file,
|
||||||
|
|
@ -145,7 +145,7 @@ class TestPreset:
|
||||||
):
|
):
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
StringFormattingVariableNotFoundException,
|
StringFormattingVariableNotFoundException,
|
||||||
match="Override variable 'dne_var' does not exist",
|
match="Variable dne_var does not exist",
|
||||||
):
|
):
|
||||||
_ = Preset(
|
_ = Preset(
|
||||||
config=config_file,
|
config=config_file,
|
||||||
|
|
@ -161,7 +161,7 @@ class TestPreset:
|
||||||
):
|
):
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
StringFormattingVariableNotFoundException,
|
StringFormattingVariableNotFoundException,
|
||||||
match="Format variable 'dne_var' does not exist",
|
match="Variable dne_var does not exist",
|
||||||
):
|
):
|
||||||
_ = Preset(
|
_ = Preset(
|
||||||
config=config_file,
|
config=config_file,
|
||||||
|
|
@ -182,7 +182,7 @@ class TestPreset:
|
||||||
):
|
):
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
StringFormattingVariableNotFoundException,
|
StringFormattingVariableNotFoundException,
|
||||||
match="Format variable 'dne_var' does not exist",
|
match="Variable dne_var does not exist",
|
||||||
):
|
):
|
||||||
_ = Preset(
|
_ = Preset(
|
||||||
config=config_file,
|
config=config_file,
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ def mock_entry_kwargs(
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_entry(mock_entry_kwargs):
|
def mock_entry(mock_entry_kwargs):
|
||||||
return Entry(entry_dict=mock_entry_kwargs, working_directory=".")
|
return Entry(entry_dict=mock_entry_kwargs, working_directory=".", override_variables={})
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue