diff --git a/src/ytdl_sub/downloaders/info_json/info_json_downloader.py b/src/ytdl_sub/downloaders/info_json/info_json_downloader.py index bbdfe1d9..0ba65354 100644 --- a/src/ytdl_sub/downloaders/info_json/info_json_downloader.py +++ b/src/ytdl_sub/downloaders/info_json/info_json_downloader.py @@ -10,7 +10,12 @@ from ytdl_sub.config.overrides import Overrides from ytdl_sub.config.preset_options import OptionsDictValidator from ytdl_sub.downloaders.source_plugin import SourcePlugin from ytdl_sub.downloaders.ytdl_options_builder import YTDLOptionsBuilder +from ytdl_sub.entries.entry import YTDL_SUB_ENTRY_VARIABLES_KWARG_KEY from ytdl_sub.entries.entry import Entry +from ytdl_sub.entries.script.variable_definitions import VARIABLES as v +from ytdl_sub.entries.script.variable_scripts import VARIABLE_SCRIPTS +from ytdl_sub.entries.variables.kwargs import DOWNLOAD_INDEX +from ytdl_sub.entries.variables.kwargs import UPLOAD_DATE_INDEX from ytdl_sub.utils.exceptions import ValidationException from ytdl_sub.utils.file_handler import FileHandler from ytdl_sub.utils.file_handler import get_file_extension @@ -104,8 +109,22 @@ class InfoJsonDownloader(SourcePlugin[InfoJsonDownloaderOptions]): # unless it is filtered for entry in entries: self._enhanced_download_archive.mapping.remove_entry(entry.uid) + prior_variables = entry.kwargs_get(YTDL_SUB_ENTRY_VARIABLES_KWARG_KEY, {}) - for entry in sorted(entries, key=lambda ent: ent.download_index): + entry.add( + { + v.download_index.variable_name: prior_variables.get( + v.download_index.variable_name, + VARIABLE_SCRIPTS[v.download_index.variable_name], + ), + v.upload_date_index.variable_name: prior_variables.get( + v.upload_date_index.variable_name, + VARIABLE_SCRIPTS[v.upload_date_index.variable_name], + ), + } + ) + + for entry in sorted(entries, key=lambda ent: ent.get(v.download_index)): yield entry # If the original entry file_path is no longer maintained in the new mapping, then diff --git a/src/ytdl_sub/entries/base_entry.py b/src/ytdl_sub/entries/base_entry.py index cb7cad4c..c05da40a 100644 --- a/src/ytdl_sub/entries/base_entry.py +++ b/src/ytdl_sub/entries/base_entry.py @@ -320,36 +320,6 @@ class BaseEntry(BaseEntryVariables, ABC): """ return str(Path(self.working_directory()) / self.get_download_info_json_name()) - def _added_variables(self) -> Dict[str, str]: - """ - Returns - ------- - Dict of variables added to this entry - """ - return self._additional_variables - - @classmethod - def source_variables(cls) -> List[str]: - """ - Returns - ------- - List of all source variables - """ - property_names = [prop for prop in dir(cls) if isinstance(getattr(cls, prop), property)] - return property_names - - @final - def to_dict(self) -> Dict[str, str]: - """ - Returns - ------- - Dictionary containing all variables - """ - source_variable_dict = { - source_var: getattr(self, source_var) for source_var in self.source_variables() - } - return dict(source_variable_dict, **self._added_variables()) - @final def to_type(self, entry_type: Type[TBaseEntry]) -> TBaseEntry: """ diff --git a/src/ytdl_sub/entries/entry.py b/src/ytdl_sub/entries/entry.py index 9679e511..bb2503e1 100644 --- a/src/ytdl_sub/entries/entry.py +++ b/src/ytdl_sub/entries/entry.py @@ -13,6 +13,8 @@ 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 +YTDL_SUB_ENTRY_VARIABLES_KWARG_KEY: str = "ytdl_sub_entry_variables" + class Entry(BaseEntry, Scriptable): """ @@ -136,3 +138,12 @@ class Entry(BaseEntry, Scriptable): break return file_exists + + @final + def to_dict(self) -> Dict[str, str]: + """ + Returns + ------- + Dictionary containing all variables + """ + return self.script.resolve().as_native() diff --git a/src/ytdl_sub/entries/script/variable_scripts.py b/src/ytdl_sub/entries/script/variable_scripts.py index ed8bb252..57b58c09 100644 --- a/src/ytdl_sub/entries/script/variable_scripts.py +++ b/src/ytdl_sub/entries/script/variable_scripts.py @@ -1,5 +1,6 @@ from typing import Dict from typing import Optional +from typing import Set import mergedeep @@ -132,6 +133,7 @@ ENTRY_DEFAULT_VARIABLES: Dict[MetadataVariable, str] = { ENTRY_INJECTED_VARIABLES: Dict[Variable, str] = { v.download_index: "{%int(1)}", v.upload_date_index: "{%int(1)}", + v.playlist_max_upload_year: f"{{{v.upload_year.variable_name}}}", } ENTRY_DERIVED_VARIABLES: Dict[Variable, str] = { @@ -202,10 +204,6 @@ PLAYLIST_VARIABLES: Dict[Variable, str] = { v.playlist_uploader_url: playlist_get(v.playlist_uploader_url, v.playlist_webpage_url), } -PLAYLIST_INJECTED_VARIABLES: Dict[Variable, str] = { - v.playlist_max_upload_year: playlist_get_int(v.playlist_max_upload_year, v.upload_year) -} - PLAYLIST_DERIVED_VARIABLES: Dict[Variable, str] = { v.playlist_title_sanitized: sanitized(v.playlist_title), v.playlist_index_reversed: f"{{%sub({v.playlist_count.variable_name}, {v.playlist_index.variable_name}, -1)}}", @@ -247,7 +245,6 @@ mergedeep.merge( ENTRY_UPLOAD_DATE_VARIABLES, ENTRY_RELEASE_DATE_VARIABLES, PLAYLIST_VARIABLES, - PLAYLIST_INJECTED_VARIABLES, PLAYLIST_DERIVED_VARIABLES, SOURCE_VARIABLES, SOURCE_DERIVED_VARIABLES, @@ -256,5 +253,6 @@ mergedeep.merge( VARIABLE_SCRIPTS: Dict[str, str] = { var.variable_name: script for var, script in _VARIABLE_SCRIPTS.items() } +UNRESOLVED_VARIABLES: Set[str] = {var.variable_name for var in ENTRY_INJECTED_VARIABLES} CustomFunctions.register() diff --git a/src/ytdl_sub/plugins/regex.py b/src/ytdl_sub/plugins/regex.py index e8aea15f..014b8487 100644 --- a/src/ytdl_sub/plugins/regex.py +++ b/src/ytdl_sub/plugins/regex.py @@ -22,6 +22,7 @@ from ytdl_sub.validators.strict_dict_validator import StrictDictValidator from ytdl_sub.validators.string_formatter_validators import ListFormatterValidator from ytdl_sub.validators.string_formatter_validators import StringFormatterValidator from ytdl_sub.validators.validators import BoolValidator +from ytdl_sub.validators.validators import DictValidator logger = Logger.get(name="regex") @@ -112,11 +113,7 @@ class VariableRegex(StrictDictValidator): return self._capture_group_defaults is not None -class FromSourceVariablesRegex(StrictDictValidator): - - _optional_keys = Entry.source_variables() - _allow_extra_keys = True - +class FromSourceVariablesRegex(DictValidator): def __init__(self, name, value): super().__init__(name, value) self.variable_capture_dict: Dict[str, VariableRegex] = { diff --git a/src/ytdl_sub/utils/scriptable.py b/src/ytdl_sub/utils/scriptable.py index 88bfa54c..50bd9891 100644 --- a/src/ytdl_sub/utils/scriptable.py +++ b/src/ytdl_sub/utils/scriptable.py @@ -1,9 +1,12 @@ +import copy import json from abc import ABC from typing import Any from typing import Dict from typing import Set +from ytdl_sub.entries.script.variable_scripts import ENTRY_INJECTED_VARIABLES +from ytdl_sub.entries.script.variable_scripts import UNRESOLVED_VARIABLES from ytdl_sub.entries.script.variable_scripts import VARIABLE_SCRIPTS from ytdl_sub.script.script import Script @@ -29,8 +32,8 @@ class Scriptable(ABC): return f"{{{json.dumps(value)}}}" def __init__(self): - self.script = Script(VARIABLE_SCRIPTS) - self.unresolvable: Set[str] = set() + self.script = Script(copy.deepcopy(VARIABLE_SCRIPTS)) + self.unresolvable: Set[str] = copy.deepcopy(UNRESOLVED_VARIABLES) def update_script(self) -> None: self.script.resolve(unresolvable=self.unresolvable, update=True) @@ -41,4 +44,6 @@ class Scriptable(ABC): {name: self.to_script(value) for name, value in values.items()} ) ) + + self.unresolvable -= set(list(values.keys())) self.script.resolve(unresolvable=self.unresolvable, update=True)