diff --git a/src/ytdl_sub/downloaders/url/downloader.py b/src/ytdl_sub/downloaders/url/downloader.py index 6b03cf3d..a4f45209 100644 --- a/src/ytdl_sub/downloaders/url/downloader.py +++ b/src/ytdl_sub/downloaders/url/downloader.py @@ -22,6 +22,7 @@ from ytdl_sub.downloaders.ytdl_options_builder import YTDLOptionsBuilder from ytdl_sub.downloaders.ytdlp import YTDLP from ytdl_sub.entries.entry import Entry from ytdl_sub.entries.entry_parent import EntryParent +from ytdl_sub.entries.script.variable_definitions import VARIABLES as v from ytdl_sub.entries.variables.kwargs import COLLECTION_URL from ytdl_sub.entries.variables.kwargs import COMMENTS from ytdl_sub.entries.variables.kwargs import DOWNLOAD_INDEX @@ -181,7 +182,7 @@ class UrlDownloaderCollectionVariablePlugin(SourcePluginExtension): """ # COLLECTION_URL is a recent variable that may not exist for old entries when updating. # Try to use source_webpage_url if it does not exist - entry_collection_url = entry.kwargs_get(COLLECTION_URL, entry.source_webpage_url) + entry_collection_url = entry.kwargs_get(COLLECTION_URL, entry.get(v.source_webpage_url)) # If the collection URL cannot find its mapping, use the last URL collection_url = ( @@ -189,7 +190,7 @@ class UrlDownloaderCollectionVariablePlugin(SourcePluginExtension): or list(self._collection_url_mapping.values())[-1] ) - entry.add_variables(variables_to_add=collection_url.variables.dict_with_format_strings) + entry.add(collection_url.variables.dict_with_format_strings) return entry @@ -504,7 +505,7 @@ class MultiUrlDownloader(SourcePlugin[MultiUrlValidator]): return None upload_date_idx = self._enhanced_download_archive.mapping.get_num_entries_with_upload_date( - upload_date_standardized=entry.upload_date_standardized + upload_date_standardized=entry.get(v.upload_date_standardized) ) download_idx = self._enhanced_download_archive.num_entries diff --git a/src/ytdl_sub/entries/base_entry.py b/src/ytdl_sub/entries/base_entry.py index 697bb1f6..cb7cad4c 100644 --- a/src/ytdl_sub/entries/base_entry.py +++ b/src/ytdl_sub/entries/base_entry.py @@ -304,31 +304,6 @@ class BaseEntry(BaseEntryVariables, ABC): self._kwargs = dict(self._kwargs, **variables_to_add) return self - def add_variables(self, variables_to_add: Dict[str, str]) -> "BaseEntry": - """ - Parameters - ---------- - variables_to_add - Variables to add to this entry - - Returns - ------- - self - - Raises - ------ - ValueError - If a variable trying to be added already exists as a source variable - """ - for variable_name in variables_to_add.keys(): - if self.kwargs_contains(variable_name): - raise ValueError( - f"Cannot add variable '{variable_name}': already exists in the kwargs" - ) - - self._additional_variables = dict(self._additional_variables, **variables_to_add) - return self - def get_download_info_json_name(self) -> str: """ Returns diff --git a/src/ytdl_sub/entries/entry.py b/src/ytdl_sub/entries/entry.py index 751e719d..7fe44e32 100644 --- a/src/ytdl_sub/entries/entry.py +++ b/src/ytdl_sub/entries/entry.py @@ -8,6 +8,7 @@ from typing import final from ytdl_sub.entries.base_entry import BaseEntry from ytdl_sub.entries.script.variable_definitions import VARIABLES +from ytdl_sub.entries.script.variable_definitions import Variable 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 @@ -24,10 +25,13 @@ class Entry(BaseEntry, Scriptable): 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({VARIABLES.entry_metadata.variable_name: f"{{{json.dumps(self._kwargs)}}}"}) self.script.add(override_variables) self.script.resolve(update=True) + def get(self, variable: Variable) -> str: + return self.script.resolve(unresolvable=self.unresolvable).get_str(variable.variable_name) + @property def ext(self) -> str: """ @@ -35,12 +39,13 @@ class Entry(BaseEntry, Scriptable): This is not reflected in the entry. See if the mkv file exists and return "mkv" if so, otherwise, return the original extension. """ - for possible_ext in [super().ext, "mkv"]: + ext = self.get(VARIABLES.ext) + for possible_ext in [ext, "mkv"]: file_path = str(Path(self.working_directory()) / f"{self.uid}.{possible_ext}") if os.path.isfile(file_path): return possible_ext - return super().ext + return ext def get_download_file_name(self) -> str: """ @@ -60,7 +65,7 @@ class Entry(BaseEntry, Scriptable): ------- The download thumbnail's file name """ - return f"{self.uid}.{self.thumbnail_ext}" + return f"{self.get(VARIABLES.uid)}.{self.get(VARIABLES.thumbnail_ext)}" def get_download_thumbnail_path(self) -> str: """Returns the entry's thumbnail's file path to where it was downloaded""" diff --git a/src/ytdl_sub/entries/script/variable_definitions.py b/src/ytdl_sub/entries/script/variable_definitions.py index 78215e40..4cc744cd 100644 --- a/src/ytdl_sub/entries/script/variable_definitions.py +++ b/src/ytdl_sub/entries/script/variable_definitions.py @@ -11,6 +11,11 @@ class Variable: variable_name: str +@dataclass(frozen=True) +class InternalVariable(Variable): + pass + + @dataclass(frozen=True) class Metadata(Variable): pass diff --git a/src/ytdl_sub/utils/scriptable.py b/src/ytdl_sub/utils/scriptable.py index 70b5b6aa..88bfa54c 100644 --- a/src/ytdl_sub/utils/scriptable.py +++ b/src/ytdl_sub/utils/scriptable.py @@ -1,4 +1,6 @@ +import json from abc import ABC +from typing import Any from typing import Dict from typing import Set @@ -14,9 +16,29 @@ class Scriptable(ABC): } return dict(variables, **sanitized_variables) + @classmethod + def to_script(cls, value: Any) -> str: + if isinstance(value, str): + return value + if isinstance(value, int): + return f"{{%int({value})}}" + if isinstance(value, float): + return f"{{%float({value})}}" + if isinstance(value, bool): + return f"{{%bool({value})}}" + return f"{{{json.dumps(value)}}}" + 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) + + def add(self, values: Dict[str, Any]) -> None: + self.script.add( + self.add_sanitized_variables( + {name: self.to_script(value) for name, value in values.items()} + ) + ) + self.script.resolve(unresolvable=self.unresolvable, update=True) diff --git a/src/ytdl_sub/ytdl_additions/enhanced_download_archive.py b/src/ytdl_sub/ytdl_additions/enhanced_download_archive.py index e614176f..a03a3233 100644 --- a/src/ytdl_sub/ytdl_additions/enhanced_download_archive.py +++ b/src/ytdl_sub/ytdl_additions/enhanced_download_archive.py @@ -14,6 +14,7 @@ from yt_dlp import DateRange from yt_dlp.utils import make_archive_id from ytdl_sub.entries.entry import Entry +from ytdl_sub.entries.script.variable_definitions import VARIABLES as v from ytdl_sub.entries.variables.kwargs import SPLIT_BY_CHAPTERS_PARENT_ENTRY from ytdl_sub.utils.file_handler import FileHandler from ytdl_sub.utils.file_handler import FileHandlerTransactionLog @@ -71,7 +72,7 @@ class DownloadMapping: DownloadMapping for the entry """ return DownloadMapping( - upload_date=entry.upload_date_standardized, + upload_date=entry.get(v.upload_date_standardized), extractor=entry.extractor, file_names=set(), ) diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index f25845a7..de4ef21d 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -16,6 +16,7 @@ from ytdl_sub.entries.variables.kwargs import DESCRIPTION from ytdl_sub.entries.variables.kwargs import EPOCH from ytdl_sub.entries.variables.kwargs import EXT from ytdl_sub.entries.variables.kwargs import EXTRACTOR +from ytdl_sub.entries.variables.kwargs import IE_KEY from ytdl_sub.entries.variables.kwargs import PLAYLIST_COUNT from ytdl_sub.entries.variables.kwargs import PLAYLIST_ENTRY from ytdl_sub.entries.variables.kwargs import PLAYLIST_INDEX @@ -68,6 +69,7 @@ def mock_entry_dict_factory(mock_downloaded_file_path) -> Callable: PLAYLIST_INDEX: playlist_index, PLAYLIST_COUNT: playlist_count, EXTRACTOR: "mock-entry-dict", + IE_KEY: "mock-ie-key", TITLE: f"Mock Entry {uid}", EXT: "mp4", UPLOAD_DATE: upload_date, diff --git a/tests/unit/entries/test_variable_scripts.py b/tests/unit/entries/test_variable_scripts.py index 305baeb9..ca596602 100644 --- a/tests/unit/entries/test_variable_scripts.py +++ b/tests/unit/entries/test_variable_scripts.py @@ -1,18 +1,9 @@ -import json -from typing import Dict - from ytdl_sub.entries.script.variable_definitions import VARIABLES -from ytdl_sub.entries.script.variable_scripts import VARIABLE_SCRIPTS -from ytdl_sub.script.script import Script class TestEntry(object): def test_entry_to_dict(self, mock_entry, mock_entry_to_dict): - entry_metadata: Dict[str, str] = { - VARIABLES.entry_metadata.variable_name: f"{{{json.dumps(mock_entry._kwargs)}}}" - } - script = Script(dict(entry_metadata, **VARIABLE_SCRIPTS)) - output = {var_name: var_output.native for var_name, var_output in script.resolve().items()} + output = mock_entry.script.resolve().as_native() del output[VARIABLES.entry_metadata.variable_name] del output[VARIABLES.ie_key.variable_name] assert output == mock_entry_to_dict