really need partial script

This commit is contained in:
Jesse Bannon 2023-12-07 12:24:37 -08:00
parent 7dbeb7a4ff
commit 6ad5c47068
23 changed files with 271 additions and 272 deletions

View 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"
]
)

View file

@ -7,7 +7,7 @@ from typing import Optional
from typing import Tuple
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.entries.entry import Entry
from ytdl_sub.utils.file_handler import FileMetadata

View file

@ -1,9 +1,11 @@
import copy
import functools
from typing import Any
from typing import Dict
from typing import Iterable
from typing import List
from typing import Optional
from typing import Set
from typing import Tuple
from typing import Type
from typing import Union
@ -11,18 +13,22 @@ from typing import Union
from mergedeep import mergedeep
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_mapping import PluginMapping
from ytdl_sub.config.preset_options import OptionsValidator
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 YTDLOptions
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
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.prebuilt_presets import PREBUILT_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.logger import Logger
from ytdl_sub.utils.yaml import dump_yaml
@ -159,6 +165,35 @@ class Preset(_PresetShell):
def _source_variables(self) -> List[str]:
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:
preset_plugins = PresetPlugins()
@ -174,54 +209,30 @@ class Preset(_PresetShell):
return preset_plugins
def __validate_added_variables(self):
source_variables = copy.deepcopy(self._source_variables)
# 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())
self.downloader_options.validate_with_variables(script=copy.deepcopy(self._mock_script))
for _, plugin_options in sorted(
self.plugins.zipped(), key=lambda pl: pl[0].priority.modify_entry
):
# Validate current plugin using source + added plugin variables
plugin_options.validate_with_variables(
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())
plugin_options.validate_with_variables(script=copy.deepcopy(self._mock_script))
def __validate_override_string_formatter_validator(
self,
formatter_validator: Union[StringFormatterValidator, OverridesStringFormatterValidator],
):
# Set the formatter variables to be the overrides
variable_dict = copy.deepcopy(self.overrides.dict_with_format_strings)
script = copy.deepcopy(self._mock_script)
try:
script.add({"tmp_var": formatter_validator.format_string})
except VariableDoesNotExist as exc:
raise StringFormattingVariableNotFoundException(exc) from exc
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)
# 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)
unresolved: Optional[Set[str]] = (
{VARIABLES.entry_metadata.variable_name}
if isinstance(formatter_validator, OverridesStringFormatterValidator)
else None
)
_ = script.resolve(unresolvable=unresolved)["tmp_var"] # TODO: error if not present
def __recursive_preset_validate(
self,
@ -333,6 +344,13 @@ class Preset(_PresetShell):
# values from multiple validators
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
def name(self) -> str:
"""

View file

@ -1,26 +1,16 @@
import copy
import json
from abc import ABC
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
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.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.variables.override_variables import SUBSCRIPTION_NAME
from ytdl_sub.script.script import Script
from ytdl_sub.utils.exceptions import ValidationException
from ytdl_sub.validators.file_path_validators import OverridesStringFormatterFilePathValidator
from ytdl_sub.validators.file_path_validators import StringFormatterFileNameValidator
from ytdl_sub.validators.strict_dict_validator import StrictDictValidator
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 OverridesStringFormatterValidator
from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator
@ -63,18 +53,14 @@ class OptionsValidator(Validator, ABC):
"""
return []
def validate_with_variables(
self, source_variables: List[str], override_variables: Dict[str, str]
) -> None:
def validate_with_variables(self, script: Script) -> None:
"""
Optional validation after init with the session's source and override variables.
Parameters
----------
source_variables
Available source variables when running the plugin
override_variables
Available override variables when running the plugin
script
Script containing all current variables
"""
return None
@ -129,103 +115,6 @@ class YTDLOptions(LiteralDictValidator):
# Disable for proper docstring formatting
# 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):

View file

@ -6,8 +6,8 @@ from typing import Iterable
from typing import List
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 Overrides
from ytdl_sub.downloaders.source_plugin import SourcePlugin
from ytdl_sub.downloaders.ytdl_options_builder import YTDLOptionsBuilder
from ytdl_sub.entries.entry import Entry
@ -82,6 +82,7 @@ class InfoJsonDownloader(SourcePlugin[InfoJsonDownloaderOptions]):
return Entry(
entry_dict=entry_dict,
working_directory=self.working_directory,
override_variables=self.overrides.dict_with_format_strings,
)
raise ValidationException(

View file

@ -8,9 +8,9 @@ from typing import Optional
from typing import Type
from typing import final
from ytdl_sub.config.overrides import Overrides
from ytdl_sub.config.plugin import BasePlugin
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.downloaders.ytdl_options_builder import YTDLOptionsBuilder
from ytdl_sub.entries.entry import Entry

View file

@ -11,8 +11,8 @@ from typing import Tuple
from yt_dlp.utils import RejectedVideoReached
from ytdl_sub.config.overrides import Overrides
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 SourcePluginExtension
from ytdl_sub.downloaders.url.validators import MultiUrlValidator
@ -356,7 +356,11 @@ class MultiUrlDownloader(SourcePlugin[MultiUrlValidator]):
else entry.is_thumbnail_downloaded_via_ytdlp,
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(
self, url_validator: UrlValidator, entries: List[Entry]
@ -413,7 +417,10 @@ class MultiUrlDownloader(SourcePlugin[MultiUrlValidator]):
working_directory=self.working_directory,
)
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

View file

@ -5,6 +5,7 @@ from typing import List
from typing import Optional
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.string_formatter_validators import DictFormatterValidator
from ytdl_sub.validators.string_formatter_validators import OverridesStringFormatterValidator
@ -251,37 +252,20 @@ class MultiUrlValidator(OptionsValidator):
"""
return list(self._urls.list[0].variables.keys)
def validate_with_variables(
self, source_variables: List[str], override_variables: Dict[str, str]
) -> None:
def validate_with_variables(self, script: Script) -> None:
"""
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
for collection_url in self.urls.list:
for (
source_var_name,
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)
script.add(collection_url.variables.dict_with_format_strings)
script.resolve(update=True)
# Ensure at least URL is non-empty
has_non_empty_url = False
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:
raise self._validation_exception("Must contain at least one url that is non-empty")

View file

@ -2,19 +2,32 @@ import copy
import json
import os
from pathlib import Path
from typing import Dict
from typing import Optional
from typing import final
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 VIDEO_CODEC_EXTS
class Entry(BaseEntry):
class Entry(BaseEntry, Scriptable):
"""
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
def ext(self) -> str:
"""

View file

@ -279,7 +279,11 @@ class EntryParent(BaseEntry):
@classmethod
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]:
"""
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 [
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
if cls.is_entry(entry_dict) and not _in_any_parents(entry_dict)
]

View file

@ -98,9 +98,7 @@ def source_get_int(key: MetadataVariable, default: Optional[Variable | int] = No
###############################################################################################
# Scripts
ENTRY_EMPTY_METADATA: Dict[Variable, str] = {
v.entry_metadata: "{ {} }"
}
ENTRY_EMPTY_METADATA: Dict[Variable, str] = {v.entry_metadata: "{ {} }"}
ENTRY_HARDCODED_VARIABLES: Dict[Variable, str] = {
v.info_json_ext: "info.json",

View file

@ -1,10 +1,10 @@
import copy
from typing import Optional
from ytdl_sub.config.overrides import Overrides
from ytdl_sub.config.plugin import Plugin
from ytdl_sub.config.plugin import PluginPriority
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.utils.file_handler import FileMetadata
from ytdl_sub.ytdl_additions.enhanced_download_archive import EnhancedDownloadArchive

View file

@ -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.plugins.nfo_tags import NfoTagsValidator
from ytdl_sub.plugins.nfo_tags import SharedNfoTagsOptions

View file

@ -9,7 +9,9 @@ from ytdl_sub.config.plugin import Plugin
from ytdl_sub.config.plugin import PluginPriority
from ytdl_sub.config.preset_options import OptionsDictValidator
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.script.script import Script
from ytdl_sub.utils.exceptions import RegexNoMatchException
from ytdl_sub.utils.exceptions import StringFormattingVariableNotFoundException
from ytdl_sub.utils.logger import Logger
@ -212,34 +214,22 @@ class RegexOptions(OptionsDictValidator):
"""
return self._skip_if_match_fails
def validate_with_variables(
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
"""
def validate_with_variables(self, script: Script) -> None:
for key, regex_options in self.source_variable_capture_dict.items():
# 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(
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
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(
f"'{capture_group_name}' cannot be used as a capture group name because it "
f"is a source variable"
)
if capture_group_name in override_variables:
if capture_group_name in script._variables:
raise self._validation_exception(
f"'{capture_group_name}' cannot be used as a capture group name because it "
f"is an override variable"

View file

@ -4,9 +4,9 @@ from typing import List
from typing import Optional
from typing import Tuple
from ytdl_sub.config.overrides import Overrides
from ytdl_sub.config.plugin import Plugin
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.utils.file_handler import FileMetadata
from ytdl_sub.utils.logger import Logger

View file

@ -3,10 +3,10 @@ from pathlib import Path
from typing import Optional
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 PresetPlugins
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.downloaders.url.validators import MultiUrlValidator
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog

View file

@ -8,7 +8,7 @@ from typing import Optional
from typing import final
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_VALUE
from ytdl_sub.entries.variables.override_variables import OverrideVariables

View file

@ -3,7 +3,7 @@ from typing import Optional
from yt_dlp import DateRange
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

View 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)

View file

@ -93,33 +93,6 @@ class StringFormatterValidator(StringValidator):
"""
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
class OverridesStringFormatterValidator(StringFormatterValidator):
@ -133,37 +106,6 @@ class OverridesStringFormatterValidator(StringFormatterValidator):
: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

View file

@ -652,6 +652,7 @@ class EnhancedDownloadArchive:
parent_entry = Entry(
entry_dict=entry.kwargs(SPLIT_BY_CHAPTERS_PARENT_ENTRY),
working_directory=entry.working_directory(),
override_variables={},
)
self.mapping.add_entry(parent_entry, entry_file_path=output_file_name)
elif entry:

View file

@ -129,7 +129,7 @@ class TestPreset:
):
with pytest.raises(
StringFormattingVariableNotFoundException,
match="Format variable 'dne_var' does not exist",
match="Variable dne_var does not exist.",
):
_ = Preset(
config=config_file,
@ -145,7 +145,7 @@ class TestPreset:
):
with pytest.raises(
StringFormattingVariableNotFoundException,
match="Override variable 'dne_var' does not exist",
match="Variable dne_var does not exist",
):
_ = Preset(
config=config_file,
@ -161,7 +161,7 @@ class TestPreset:
):
with pytest.raises(
StringFormattingVariableNotFoundException,
match="Format variable 'dne_var' does not exist",
match="Variable dne_var does not exist",
):
_ = Preset(
config=config_file,
@ -182,7 +182,7 @@ class TestPreset:
):
with pytest.raises(
StringFormattingVariableNotFoundException,
match="Format variable 'dne_var' does not exist",
match="Variable dne_var does not exist",
):
_ = Preset(
config=config_file,

View file

@ -174,7 +174,7 @@ def mock_entry_kwargs(
@pytest.fixture
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