ytdl-sub 2025.11.27__py3-none-any.whl → 2025.11.28__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ytdl_sub/__init__.py CHANGED
@@ -1 +1 @@
1
- __pypi_version__ = "2025.11.27";__local_version__ = "2025.11.27+24e71ce"
1
+ __pypi_version__ = "2025.11.28";__local_version__ = "2025.11.28+19f47cd"
@@ -158,10 +158,15 @@ class Overrides(UnstructuredDictFormatterValidator, Scriptable):
158
158
  script = entry.script
159
159
  unresolvable = entry.unresolvable
160
160
 
161
+ # Update the script internally so long as we are not supplying overrides
162
+ # that could alter the script with one-off state
163
+ update = function_overrides is None
164
+
161
165
  try:
162
166
  return script.resolve_once(
163
167
  dict({"tmp_var": formatter.format_string}, **(function_overrides or {})),
164
168
  unresolvable=unresolvable,
169
+ update=update,
165
170
  )["tmp_var"]
166
171
  except ScriptVariableNotResolved as exc:
167
172
  raise StringFormattingException(
ytdl_sub/script/parser.py CHANGED
@@ -605,7 +605,7 @@ def parse(
605
605
  name=name,
606
606
  custom_function_names=custom_function_names,
607
607
  variable_names=variable_names,
608
- ).ast
608
+ ).ast.maybe_resolvable_casted()
609
609
 
610
610
 
611
611
  # pylint: enable=invalid-name
ytdl_sub/script/script.py CHANGED
@@ -10,6 +10,7 @@ from ytdl_sub.script.script_output import ScriptOutput
10
10
  from ytdl_sub.script.types.resolvable import BuiltInFunctionType
11
11
  from ytdl_sub.script.types.resolvable import Lambda
12
12
  from ytdl_sub.script.types.resolvable import Resolvable
13
+ from ytdl_sub.script.types.syntax_tree import ResolvedSyntaxTree
13
14
  from ytdl_sub.script.types.syntax_tree import SyntaxTree
14
15
  from ytdl_sub.script.types.variable import FunctionArgument
15
16
  from ytdl_sub.script.types.variable import Variable
@@ -273,7 +274,7 @@ class Script:
273
274
 
274
275
  def _update_internally(self, resolved_variables: Dict[str, Resolvable]) -> None:
275
276
  for variable_name, resolved in resolved_variables.items():
276
- self._variables[variable_name] = SyntaxTree(ast=[resolved])
277
+ self._variables[variable_name] = ResolvedSyntaxTree(ast=[resolved])
277
278
 
278
279
  def _recursive_get_unresolved_output_filter_variables(
279
280
  self, current_var: SyntaxTree, subset_to_resolve: Set[str], unresolvable: Set[Variable]
@@ -398,8 +399,8 @@ class Script:
398
399
 
399
400
  # Otherwise, if it has dependencies that are all resolved, then
400
401
  # resolve the definition
401
- elif not definition.is_subset_of(
402
- variables=resolved.keys(), custom_function_definitions=self._functions
402
+ elif definition.is_subset_of(
403
+ variables=resolved, custom_function_definitions=self._functions
403
404
  ):
404
405
  resolved[variable] = unresolved[variable].resolve(
405
406
  resolved_variables=resolved,
@@ -522,6 +523,7 @@ class Script:
522
523
  variable_definitions: Dict[str, str],
523
524
  resolved: Optional[Dict[str, Resolvable]] = None,
524
525
  unresolvable: Optional[Set[str]] = None,
526
+ update: bool = False,
525
527
  ) -> Dict[str, Resolvable]:
526
528
  """
527
529
  Given a new set of variable definitions, resolve them using the Script, but do not
@@ -536,6 +538,8 @@ class Script:
536
538
  unresolvable
537
539
  Optional. Unresolvable variables that will be ignored in resolution, including all
538
540
  variables with a dependency to them.
541
+ update
542
+ Whether to update the script's state with resolved variables. Defaults to False.
539
543
 
540
544
  Returns
541
545
  -------
@@ -548,6 +552,7 @@ class Script:
548
552
  pre_resolved=resolved,
549
553
  unresolvable=unresolvable,
550
554
  output_filter=set(list(variable_definitions.keys())),
555
+ update=update,
551
556
  ).output
552
557
  finally:
553
558
  for name in variable_definitions.keys():
@@ -1,4 +1,3 @@
1
- import copy
2
1
  import functools
3
2
  from abc import ABC
4
3
  from dataclasses import dataclass
@@ -58,23 +57,29 @@ class CustomFunction(Function, NamedCustomFunction):
58
57
  # Should be validated in the Script
59
58
  raise UNREACHABLE
60
59
 
61
- resolved_variables_with_args = copy.deepcopy(resolved_variables)
60
+ function_args: List[FunctionArgument] = []
62
61
  for i, arg in enumerate(resolved_args):
63
62
  function_arg = FunctionArgument.from_idx(idx=i, custom_function_name=self.name)
64
63
 
65
- if function_arg in resolved_variables_with_args:
64
+ if function_arg in resolved_variables:
66
65
  # function args should always be unique since they are only defined once
67
66
  # in the custom function as %custom_function_name___idx
68
67
  # and returned as a set from each custom function.
69
68
  raise UNREACHABLE
70
69
 
71
- resolved_variables_with_args[function_arg] = arg
70
+ resolved_variables[function_arg] = arg
71
+ function_args.append(function_arg)
72
72
 
73
- return custom_functions[self.name].resolve(
74
- resolved_variables=resolved_variables_with_args,
73
+ out = custom_functions[self.name].resolve(
74
+ resolved_variables=resolved_variables,
75
75
  custom_functions=custom_functions,
76
76
  )
77
77
 
78
+ for function_arg in function_args:
79
+ del resolved_variables[function_arg]
80
+
81
+ return out
82
+
78
83
  # Implies the custom function does not exist. This should have
79
84
  # been checked in the parser with
80
85
  raise UNREACHABLE
@@ -47,6 +47,32 @@ class SyntaxTree(VariableDependency):
47
47
  -------
48
48
  A resolvable if the AST contains a single type that is resolvable. None otherwise.
49
49
  """
50
- if len(self.ast) == 1 and isinstance(self.ast[0], Resolvable):
51
- return self.ast[0]
52
50
  return None
51
+
52
+ def maybe_resolvable_casted(self) -> "SyntaxTree":
53
+ """
54
+ Returns
55
+ -------
56
+ Optimized SyntaxTree if its deemed resolvable
57
+ """
58
+ if len(self.ast) == 1 and isinstance(self.ast[0], Resolvable):
59
+ return ResolvedSyntaxTree(self.ast)
60
+ return self
61
+
62
+
63
+ @dataclass(frozen=True)
64
+ class ResolvedSyntaxTree(SyntaxTree):
65
+ """
66
+ SyntaxTree with optimized helper functions if it's known to be resolved.
67
+ """
68
+
69
+ def resolve(
70
+ self,
71
+ resolved_variables: Dict[Variable, Resolvable],
72
+ custom_functions: Dict[str, VariableDependency],
73
+ ) -> Resolvable:
74
+ return self.ast[0]
75
+
76
+ @property
77
+ def maybe_resolvable(self) -> Optional[Resolvable]:
78
+ return self.ast[0]
@@ -162,7 +162,7 @@ class VariableDependency(ABC):
162
162
  @final
163
163
  def is_subset_of(
164
164
  self,
165
- variables: Iterable[Variable],
165
+ variables: Dict[Variable, Resolvable],
166
166
  custom_function_definitions: Dict[str, "VariableDependency"],
167
167
  ) -> bool:
168
168
  """
@@ -171,12 +171,12 @@ class VariableDependency(ABC):
171
171
  True if it contains all input variables as a dependency. False otherwise.
172
172
  """
173
173
  for custom_function in self.custom_functions:
174
- if custom_function_definitions[custom_function.name].is_subset_of(
174
+ if not custom_function_definitions[custom_function.name].is_subset_of(
175
175
  variables=variables, custom_function_definitions=custom_function_definitions
176
176
  ):
177
- return True
177
+ return False
178
178
 
179
- return not self.variables.issubset(variables)
179
+ return all(var in variables for var in self.variables)
180
180
 
181
181
  @final
182
182
  def contains(
@@ -246,6 +246,7 @@ def _validate_formatter(
246
246
  )
247
247
  },
248
248
  unresolvable=unresolvable,
249
+ update=True,
249
250
  )
250
251
  except RuntimeException as exc:
251
252
  if isinstance(exc, ScriptVariableNotResolved) and is_static_formatter:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ytdl-sub
3
- Version: 2025.11.27
3
+ Version: 2025.11.28
4
4
  Summary: Automate downloading metadata generation with YoutubeDL
5
5
  Author: Jesse Bannon
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -1,4 +1,4 @@
1
- ytdl_sub/__init__.py,sha256=HL0ZbBhknqTueVCBTSlnKc8VUboa8vFIEliivkqhdos,73
1
+ ytdl_sub/__init__.py,sha256=MJqqnTBKwqx_Q4TIhY-bdO9nLWwN9iCJZdwTSSHvWqU,73
2
2
  ytdl_sub/main.py,sha256=4Rf9wXxSKW7IPnWqG5YtTZ814PjP1n9WtoFDivaainE,1004
3
3
  ytdl_sub/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  ytdl_sub/cli/entrypoint.py,sha256=XXjUH4HiOP_BB2ZA_bNcyt5-o6YLAdZmj0EP3xtOtD8,9496
@@ -12,7 +12,7 @@ ytdl_sub/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  ytdl_sub/config/config_file.py,sha256=SQtVrMIUq2z3WwJVOed4y84JBMQ8aa4pbiBB0Y-YY4M,2781
13
13
  ytdl_sub/config/config_validator.py,sha256=NWPoCb6qjOom-YQamjho4UAIMSC43LRIT28W0rriXds,9424
14
14
  ytdl_sub/config/defaults.py,sha256=NTwzlKDkks1LDGNjFMxh91fw5E6T6d_zGsCwODNYJxo,1152
15
- ytdl_sub/config/overrides.py,sha256=WjfKnT8z5dth4uqMC53KrVzitGAPbKGaNN_25C35dUM,8704
15
+ ytdl_sub/config/overrides.py,sha256=0L6WaVtgqGqLvSuUkn4lbll9E8IboJlA7iJteIfN4Mo,8918
16
16
  ytdl_sub/config/preset.py,sha256=cp_oCyR78pHqYIakUNvNnwuFfFUq1YzitvIav5QW3Jw,8517
17
17
  ytdl_sub/config/preset_options.py,sha256=RgwnpIQqe_17YiaqaI-qlQ12RnkVGTdawXR_e6gYhuQ,13523
18
18
  ytdl_sub/config/plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -92,8 +92,8 @@ ytdl_sub/prebuilt_presets/tv_show/tv_show.yaml,sha256=qmAB3jsXY1VsqmtLnLXXte5rz_
92
92
  ytdl_sub/prebuilt_presets/tv_show/tv_show_by_date.yaml,sha256=0OgIOzxSPNVqwcZnL6qT9_azplBhDXePcf0ASNTQJ5Q,7223
93
93
  ytdl_sub/prebuilt_presets/tv_show/tv_show_collection.yaml,sha256=d76uoka4NdQhaJiBE_93hFIwgYYDPjvlWzNuzf0_dyc,37722
94
94
  ytdl_sub/script/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
95
- ytdl_sub/script/parser.py,sha256=s-tGfZ-yRjY8AscItt2g81oq--bLaNw7Vt-RpD8782Y,21872
96
- ytdl_sub/script/script.py,sha256=qsa3N8z8ZSSo3Yp4-GfdEdXPN0PwXu0o733BjogWohw,23860
95
+ ytdl_sub/script/parser.py,sha256=30SOHkEFi0rxiW_IrC1w6WzPKuyIf766zYEVHDsndtg,21898
96
+ ytdl_sub/script/script.py,sha256=cLmQ07yIffk-blomLWKdH-dTRiDroLYjgnvidc5-L80,24091
97
97
  ytdl_sub/script/script_output.py,sha256=5SIamnI-1D3xMA0qQzjf9xrIy8j6BVhGCKrl_Q1d2M8,1381
98
98
  ytdl_sub/script/functions/__init__.py,sha256=rzl6O5G0IEqFYHQECIM9bRvcuQj-ytC_p-Xl2TTa6j0,2932
99
99
  ytdl_sub/script/functions/array_functions.py,sha256=yg9rcZP67-aVzhu4oZZzUu3-AHiHltbQDWEs8GW1DJ4,7193
@@ -109,12 +109,12 @@ ytdl_sub/script/functions/regex_functions.py,sha256=d6omjhD9FjkP0BVjUaMsJfXVt-re
109
109
  ytdl_sub/script/functions/string_functions.py,sha256=rZbOuP2V9FvoKzMG94R7vsvj-GxHK_hwgVDMa_Yeftw,6095
110
110
  ytdl_sub/script/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
111
111
  ytdl_sub/script/types/array.py,sha256=1yEJEWtwS6T4W8KZN2IdNAGpi3SUa1qqzCXQfjo-P2M,1711
112
- ytdl_sub/script/types/function.py,sha256=s4s_x6ShNXq2iD1vmSSgH-X0J0qcF_ECK_fTvPTQVDA,12230
112
+ ytdl_sub/script/types/function.py,sha256=rnKb5L8frQ6sG95lMFj_AS6qQw7tt88z26tyWrCWPhY,12341
113
113
  ytdl_sub/script/types/map.py,sha256=Y3zlm__IHcnycGFv0xbU_P7wgiQgtgddatPLQypjCvU,2254
114
114
  ytdl_sub/script/types/resolvable.py,sha256=OinvpfKsVjgU_mnE0uYONQftUxNLa1zY2jA74bywwBQ,5302
115
- ytdl_sub/script/types/syntax_tree.py,sha256=U9Ox7g6L5VXtXoA_tvHPnUIWEGQY32NFuq-lQc5SQgw,1685
115
+ ytdl_sub/script/types/syntax_tree.py,sha256=4xWluTMPJqA6yYuQ4XCcncSFt5cDZ9ZIT2AIOOH4bT0,2336
116
116
  ytdl_sub/script/types/variable.py,sha256=aVJ3ocUr3WpDoolOq6y3NV71b3EQQYPAGrIT0FtIqc4,813
117
- ytdl_sub/script/types/variable_dependency.py,sha256=hs6pDBar11cONqgCPXNbQzdJIxy7mCT0hr2en59sVPc,6259
117
+ ytdl_sub/script/types/variable_dependency.py,sha256=wgNSdAjntziVdlwqxCT7QKwvm43XCA7qxIoFlmD_Tdg,6281
118
118
  ytdl_sub/script/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
119
119
  ytdl_sub/script/utils/exception_formatters.py,sha256=hZDX2w90vU4lY2xiEM-qxQofQz2dmqPwhkSeWaJRsQ4,4645
120
120
  ytdl_sub/script/utils/exceptions.py,sha256=cag5ZLM6as1w-RsrOwO-oe4YFpwlFu_8U0QNGv_jQ68,2774
@@ -153,14 +153,14 @@ ytdl_sub/validators/regex_validator.py,sha256=jS8No927eg3zcpYEOv5g0gykePV0DCZaIr
153
153
  ytdl_sub/validators/source_variable_validator.py,sha256=ziG4PVIyzj5ky-Okle0FB2d2P5DWs3-jYF81hMvBTEQ,922
154
154
  ytdl_sub/validators/strict_dict_validator.py,sha256=RduK_3pOEbEQQuugAUeKKqM0Tv5x2vxSSb7vROyo2vQ,1661
155
155
  ytdl_sub/validators/string_datetime.py,sha256=GpbBiZH1FHTMeo0Zk134-uMdTMk2JD3Re3TnvuPx2OU,1125
156
- ytdl_sub/validators/string_formatter_validators.py,sha256=D7wuYFuHlbOYMCkKoy9ILLGPCmvoDDfm7QOrs05SCHE,10600
156
+ ytdl_sub/validators/string_formatter_validators.py,sha256=CRwZH_fnA5LabSNf973ELSMtWt2q1vQD9ZSPVGnd4Es,10625
157
157
  ytdl_sub/validators/string_select_validator.py,sha256=KFXNKWX2J80WGt08m5gVYphPMHYxhHlgfcoXAQMq6zw,1086
158
158
  ytdl_sub/validators/validators.py,sha256=X9AkECdngEXcME_C25njHaOjtqbfEJMED2eG9Qy3UPs,9352
159
159
  ytdl_sub/ytdl_additions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
160
160
  ytdl_sub/ytdl_additions/enhanced_download_archive.py,sha256=Lsc0wjHdx9d8dYJCskZYAUGDAQ_QzQ-_xbQlyrBSzfk,24884
161
- ytdl_sub-2025.11.27.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
162
- ytdl_sub-2025.11.27.dist-info/METADATA,sha256=6gz2E3LSUIoKRcdFTShxj4TIzBaF6SwcGF0LtQg9_IY,51421
163
- ytdl_sub-2025.11.27.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
164
- ytdl_sub-2025.11.27.dist-info/entry_points.txt,sha256=K3T5235NlAI-WLmHCg5tzLZHqc33OLN5IY5fOGc9t10,48
165
- ytdl_sub-2025.11.27.dist-info/top_level.txt,sha256=6z-JWazl6jXspC2DNyxOnGnEqYyGzVbgcBDoXfbkUhI,9
166
- ytdl_sub-2025.11.27.dist-info/RECORD,,
161
+ ytdl_sub-2025.11.28.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
162
+ ytdl_sub-2025.11.28.dist-info/METADATA,sha256=R80_Nm3ywXjJ4e6s1FtFHkcsF08OLEhex7ausuia_Wc,51421
163
+ ytdl_sub-2025.11.28.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
164
+ ytdl_sub-2025.11.28.dist-info/entry_points.txt,sha256=K3T5235NlAI-WLmHCg5tzLZHqc33OLN5IY5fOGc9t10,48
165
+ ytdl_sub-2025.11.28.dist-info/top_level.txt,sha256=6z-JWazl6jXspC2DNyxOnGnEqYyGzVbgcBDoXfbkUhI,9
166
+ ytdl_sub-2025.11.28.dist-info/RECORD,,