diff --git a/src/ytdl_sub/entries/script/variable_definitions.py b/src/ytdl_sub/entries/script/variable_definitions.py index 4cc744cd..110780a9 100644 --- a/src/ytdl_sub/entries/script/variable_definitions.py +++ b/src/ytdl_sub/entries/script/variable_definitions.py @@ -74,14 +74,14 @@ class _Variables: return Variable("uid_sanitized_plex") @property - def ie_key(self) -> MetadataVariable: + def extractor_key(self) -> MetadataVariable: """ Returns ------- str The info-extractor key """ - return MetadataVariable(metadata_key="ie_key", variable_name="ie_key") + return MetadataVariable(metadata_key="extractor_key", variable_name="extractor_key") @property def extractor(self) -> MetadataVariable: diff --git a/src/ytdl_sub/entries/script/variable_scripts.py b/src/ytdl_sub/entries/script/variable_scripts.py index 14990456..a9c891f7 100644 --- a/src/ytdl_sub/entries/script/variable_scripts.py +++ b/src/ytdl_sub/entries/script/variable_scripts.py @@ -40,11 +40,11 @@ def _get_internal( # TODO: assert with good error message if key DNE out = f"%map_get({metadata.variable_name}, '{key.metadata_key}')" elif isinstance(default, Variable): - out = f"%map_get({metadata.variable_name}, '{key.metadata_key}', {default.variable_name})" + out = f"%map_get_non_empty({metadata.variable_name}, '{key.metadata_key}', {default.variable_name})" elif isinstance(default, str): - out = f"%map_get({metadata.variable_name}, '{key.metadata_key}', '{default}')" + out = f"%map_get_non_empty({metadata.variable_name}, '{key.metadata_key}', '{default}')" else: - out = f"%map_get({metadata.variable_name}, '{key.metadata_key}', {default})" + out = f"%map_get_non_empty({metadata.variable_name}, '{key.metadata_key}', {default})" return f"%legacy_bracket_safety({out})" @@ -107,7 +107,7 @@ ENTRY_HARDCODED_VARIABLES: Dict[Variable, str] = { ENTRY_REQUIRED_VARIABLES: Dict[MetadataVariable, str] = { v.uid: entry_get(v.uid), - v.ie_key: entry_get(v.ie_key), + v.extractor_key: entry_get(v.extractor_key), v.epoch: entry_get_int(v.epoch), v.webpage_url: entry_get(v.webpage_url), v.ext: entry_get(v.ext), @@ -116,7 +116,7 @@ ENTRY_REQUIRED_VARIABLES: Dict[MetadataVariable, str] = { ENTRY_DEFAULT_VARIABLES: Dict[MetadataVariable, str] = { v.playlist_index: playlist_get_int(v.playlist_index, 1), v.title: entry_get(v.title, v.uid), - v.extractor: entry_get(v.extractor, v.ie_key), + v.extractor: entry_get(v.extractor, v.extractor_key), v.description: entry_get(v.description, ""), v.uploader_id: entry_get(v.uploader_id, v.uid), v.uploader: entry_get(v.uploader, v.uploader_id), diff --git a/src/ytdl_sub/script/functions/map_functions.py b/src/ytdl_sub/script/functions/map_functions.py index 51f8ebee..10f7bca2 100644 --- a/src/ytdl_sub/script/functions/map_functions.py +++ b/src/ytdl_sub/script/functions/map_functions.py @@ -8,6 +8,7 @@ from ytdl_sub.script.types.resolvable import Hashable from ytdl_sub.script.types.resolvable import Integer from ytdl_sub.script.types.resolvable import LambdaThree from ytdl_sub.script.types.resolvable import LambdaTwo +from ytdl_sub.script.types.resolvable import String from ytdl_sub.script.utils.exceptions import KeyDoesNotExistRuntimeException @@ -27,6 +28,17 @@ class MapFunctions: ) return mapping.value[key] + @staticmethod + def map_get_non_empty(mapping: Map, key: Hashable, default: AnyArgument) -> AnyArgument: + """ + Return ``key``'s value within the Map. If ``key`` does not exist or is an empty string, + return ``default``. Otherwise, will error. + """ + output = MapFunctions.map_get(mapping, key, default) + if isinstance(output, String) and output.value == "": + return default + return output + @staticmethod def map_contains(mapping: Map, key: Hashable) -> Boolean: """ diff --git a/src/ytdl_sub/script/parser.py b/src/ytdl_sub/script/parser.py index 4e7356ab..1028bf2b 100644 --- a/src/ytdl_sub/script/parser.py +++ b/src/ytdl_sub/script/parser.py @@ -113,11 +113,15 @@ def _is_breakable(char: str) -> bool: def _is_boolean_true(string: Optional[str]) -> bool: - return string == "True" + return string and string.lower() == "true" def _is_boolean_false(string: Optional[str]) -> bool: - return string == "False" + return string and string.lower() == "false" + + +def _is_null(string: Optional[str]) -> bool: + return string and string.lower() == "null" def _is_custom_function_argument_start(char: str) -> bool: @@ -306,6 +310,9 @@ class _Parser: if _is_boolean_false(self._read(increment_pos=False, length=5)): self._pos += 5 return Boolean(value=False) + if _is_null(self._read(increment_pos=False, length=4)): + self._pos += 4 + return String(value="") if _is_string_start(self._read(increment_pos=False)): return self._parse_string() if self._read(increment_pos=False) == "[": @@ -528,7 +535,7 @@ class _Parser: self._ast.append(self._parse_variable()) elif _is_numeric_start(ch1): raise NUMERICS_ONLY_ARGS - elif _is_string_start(ch1): + elif _is_string_start(ch1) or _is_null(self._read(increment_pos=False, length=4)): raise STRINGS_ONLY_ARGS elif _is_boolean_true( self._read(increment_pos=False, length=4) diff --git a/tests/unit/entries/test_variable_scripts.py b/tests/unit/entries/test_variable_scripts.py index ca596602..955dd5aa 100644 --- a/tests/unit/entries/test_variable_scripts.py +++ b/tests/unit/entries/test_variable_scripts.py @@ -5,5 +5,5 @@ class TestEntry(object): def test_entry_to_dict(self, mock_entry, mock_entry_to_dict): output = mock_entry.script.resolve().as_native() del output[VARIABLES.entry_metadata.variable_name] - del output[VARIABLES.ie_key.variable_name] + del output[VARIABLES.extractor_key.variable_name] assert output == mock_entry_to_dict