typed-ffmpeg-compatible 3.2.2__py3-none-any.whl → 3.2.3__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.
typed_ffmpeg/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '3.2.2'
21
- __version_tuple__ = version_tuple = (3, 2, 2)
20
+ __version__ = version = '3.2.3'
21
+ __version_tuple__ = version_tuple = (3, 2, 3)
@@ -47,11 +47,23 @@ from .validate import validate
47
47
 
48
48
 
49
49
  def get_options_dict() -> dict[str, FFMpegOption]:
50
+ """
51
+ Load and index FFmpeg options from the cache.
52
+
53
+ Returns:
54
+ Dictionary mapping option names to their FFMpegOption definitions
55
+ """
50
56
  options = load(list[FFMpegOption], "options")
51
57
  return {option.name: option for option in options}
52
58
 
53
59
 
54
60
  def get_filter_dict() -> dict[str, FFMpegFilter]:
61
+ """
62
+ Load and index FFmpeg filters from the cache.
63
+
64
+ Returns:
65
+ Dictionary mapping filter names to their FFMpegFilter definitions
66
+ """
55
67
  filters = load(list[FFMpegFilter], "filters")
56
68
  return {filter.name: filter for filter in filters}
57
69
 
@@ -99,6 +111,24 @@ def parse_options(tokens: list[str]) -> dict[str, list[str | None | bool]]:
99
111
  def parse_stream_selector(
100
112
  selector: str, mapping: Mapping[str, FilterableStream]
101
113
  ) -> FilterableStream:
114
+ """
115
+ Parse a stream selector string and return the corresponding stream.
116
+
117
+ This function handles FFmpeg's stream selector syntax:
118
+ - Simple selectors: "[0]" (first input)
119
+ - Type selectors: "[0:v]" (first input, video stream)
120
+ - Index selectors: "[0:v:0]" (first input, first video stream)
121
+
122
+ Args:
123
+ selector: Stream selector string to parse
124
+ mapping: Dictionary of available streams
125
+
126
+ Returns:
127
+ The selected FilterableStream
128
+
129
+ Raises:
130
+ AssertionError: If the stream label is not found in the mapping
131
+ """
102
132
  selector = selector.strip("[]")
103
133
 
104
134
  if ":" in selector:
@@ -126,19 +156,48 @@ def parse_stream_selector(
126
156
  return stream
127
157
 
128
158
 
159
+ def _is_filename(token: str) -> bool:
160
+ """
161
+ Check if a token is a filename.
162
+
163
+ Args:
164
+ token: The token to check
165
+
166
+ Returns:
167
+ True if the token is a filename, False otherwise
168
+ """
169
+ # not start with - and has ext
170
+ return not token.startswith("-") and len(token.split(".")) > 1
171
+
172
+
129
173
  def parse_output(
130
174
  source: list[str],
131
175
  in_streams: Mapping[str, FilterableStream],
132
176
  ffmpeg_options: dict[str, FFMpegOption],
133
177
  ) -> list[OutputStream]:
178
+ """
179
+ Parse output file specifications and their options.
180
+
181
+ This function processes the output portion of an FFmpeg command line,
182
+ handling output file paths, stream mapping, and output-specific options.
183
+
184
+ Args:
185
+ source: List of command-line tokens for output specifications
186
+ in_streams: Dictionary of available input streams
187
+ ffmpeg_options: Dictionary of valid FFmpeg options
188
+
189
+ Returns:
190
+ List of OutputStream objects representing the output specifications
191
+ """
134
192
  tokens = source.copy()
135
193
 
136
194
  export: list[OutputStream] = []
137
-
138
195
  buffer: list[str] = []
196
+
139
197
  while tokens:
140
198
  token = tokens.pop(0)
141
- if token.startswith("-") or len(buffer) % 2 == 1:
199
+
200
+ if not _is_filename(token):
142
201
  buffer.append(token)
143
202
  continue
144
203
 
@@ -161,8 +220,21 @@ def parse_output(
161
220
  if isinstance(in_streams[k], AVStream)
162
221
  ]
163
222
 
164
- assert inputs, f"No inputs found for output {filename}"
165
- export.append(output(*inputs, filename=filename, extra_options=options))
223
+ parameters: dict[str, str | bool] = {}
224
+
225
+ for key, value in options.items():
226
+ key_base = key.split(":")[0]
227
+ if key_base in ffmpeg_options:
228
+ option = ffmpeg_options[key_base]
229
+
230
+ if option.is_output_option:
231
+ # just ignore not input options
232
+ if value[-1] is None:
233
+ parameters[key] = True
234
+ else:
235
+ parameters[key] = value[-1]
236
+
237
+ export.append(output(*inputs, filename=filename, extra_options=parameters))
166
238
  buffer = []
167
239
 
168
240
  return export
@@ -171,6 +243,19 @@ def parse_output(
171
243
  def parse_input(
172
244
  tokens: list[str], ffmpeg_options: dict[str, FFMpegOption]
173
245
  ) -> dict[str, FilterableStream]:
246
+ """
247
+ Parse input file specifications and their options.
248
+
249
+ This function processes the input portion of an FFmpeg command line,
250
+ handling input file paths and input-specific options.
251
+
252
+ Args:
253
+ tokens: List of command-line tokens for input specifications
254
+ ffmpeg_options: Dictionary of valid FFmpeg options
255
+
256
+ Returns:
257
+ Dictionary mapping input indices to their FilterableStream objects
258
+ """
174
259
  output: list[AVStream] = []
175
260
 
176
261
  while "-i" in tokens:
@@ -184,15 +269,15 @@ def parse_input(
184
269
  parameters: dict[str, str | bool] = {}
185
270
 
186
271
  for key, value in options.items():
187
- assert key in ffmpeg_options, f"Unknown option: {key}"
188
- option = ffmpeg_options[key]
272
+ if key in ffmpeg_options:
273
+ option = ffmpeg_options[key]
189
274
 
190
- if option.is_input_option:
191
- # just ignore not input options
192
- if value[-1] is None:
193
- parameters[key] = True
194
- else:
195
- parameters[key] = value[-1]
275
+ if option.is_input_option:
276
+ # just ignore not input options
277
+ if value[-1] is None:
278
+ parameters[key] = True
279
+ else:
280
+ parameters[key] = value[-1]
196
281
 
197
282
  output.append(input(filename=filename, extra_options=parameters))
198
283
 
@@ -322,19 +407,42 @@ def parse_global(
322
407
  parameters: dict[str, str | bool] = {}
323
408
 
324
409
  for key, value in options.items():
325
- assert key in ffmpeg_options, f"Unknown option: {key}"
326
- option = ffmpeg_options[key]
410
+ if key in ffmpeg_options:
411
+ option = ffmpeg_options[key]
327
412
 
328
- if option.is_global_option:
329
- # Process only recognized global options
330
- if value[-1] is None:
331
- parameters[key] = True
332
- else:
333
- parameters[key] = value[-1]
413
+ if option.is_global_option:
414
+ # Process only recognized global options
415
+ if value[-1] is None:
416
+ parameters[key] = True
417
+ else:
418
+ parameters[key] = value[-1]
334
419
  return parameters, remaining_tokens
335
420
 
336
421
 
337
422
  def parse(cli: str) -> Stream:
423
+ """
424
+ Parse a complete FFmpeg command line into a Stream object.
425
+
426
+ This function takes a full FFmpeg command line string and converts it into
427
+ a Stream object representing the filter graph. It handles all components:
428
+ - Global options
429
+ - Input files and their options
430
+ - Filter complex
431
+ - Output files and their options
432
+
433
+ Args:
434
+ cli: Complete FFmpeg command line string
435
+
436
+ Returns:
437
+ Stream object representing the parsed command line
438
+
439
+ Example:
440
+ ```python
441
+ stream = parse(
442
+ "ffmpeg -i input.mp4 -filter_complex '[0:v]scale=1280:720[v]' -map '[v]' output.mp4"
443
+ )
444
+ ```
445
+ """
338
446
  # ffmpeg [global_options] {[input_file_options] -i input_url} ... {[output_file_options] output_url} ...
339
447
  ffmpeg_options = get_options_dict()
340
448
  ffmpeg_filters = get_filter_dict()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typed-ffmpeg-compatible
3
- Version: 3.2.2
3
+ Version: 3.2.3
4
4
  Summary: Modern Python FFmpeg wrappers offer comprehensive support for complex filters, complete with detailed typing and documentation.
5
5
  Author-email: lucemia <lucemia@gmail.com>
6
6
  License-Expression: MIT
@@ -1,5 +1,5 @@
1
1
  typed_ffmpeg/__init__.py,sha256=n1SZfENc9xhZ0eA3ZzkBNPuIY-Pt3-rOwB8-uUj5olU,1592
2
- typed_ffmpeg/_version.py,sha256=VfQFwh2Vyb8ry8WT1y6VfXFKIN0E2GC1aed16irkCFQ,511
2
+ typed_ffmpeg/_version.py,sha256=P9n86yqDQj4uxDqvcIKGlJWsA9YZ7G1BSuEXNqVACeI,511
3
3
  typed_ffmpeg/base.py,sha256=C5Tqbx2I0c-09D7aXKZoGkspu-lAAeAhuOns5zr3PXQ,6304
4
4
  typed_ffmpeg/exceptions.py,sha256=D4SID6WOwkjVV8O8mAjrEDHWn-8BRDnK_jteaDof1SY,2474
5
5
  typed_ffmpeg/filters.py,sha256=_lBpGZgPHK3KgGVrw-TCdQEsBRuEXVIgwggYNGd80MU,110076
@@ -13,7 +13,7 @@ typed_ffmpeg/common/cache.py,sha256=j0JvfX7jewLpdJWxgo7Pwze0BkUJdYGHX2uGR8BZ-9M,
13
13
  typed_ffmpeg/common/schema.py,sha256=qM8yfMX9UU3EAQSNsTrr-SAmyqKx8eQCXTtu3RJWkEk,19673
14
14
  typed_ffmpeg/common/serialize.py,sha256=dLim0DBP5CdJ1JiMV9xEmmh1XMSIhBOWs61EopAL15s,7719
15
15
  typed_ffmpeg/compile/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- typed_ffmpeg/compile/compile_cli.py,sha256=nLrTV5nuC1ETff8C7_ZgZDKPtvXJoQfZsAbm_oq1WA8,28397
16
+ typed_ffmpeg/compile/compile_cli.py,sha256=--2j1QPPOB2pEQzqFM4VpINokbJilcegtRqTRF2_Jfg,31588
17
17
  typed_ffmpeg/compile/compile_json.py,sha256=YCiTyfAnUVSbFr7BiQpmJYs13K5sa-xo77Iih33mb6I,992
18
18
  typed_ffmpeg/compile/compile_python.py,sha256=oo4e8Ldwk0OkrZtHucfuGR5JDFF8xY8omNKPMDyUpQ8,11506
19
19
  typed_ffmpeg/compile/context.py,sha256=macQ3HhEJ73j_WbWYtU9GCQCzcB_KQGAPimcuU-WOac,10946
@@ -50,9 +50,9 @@ typed_ffmpeg/utils/view.py,sha256=QCSlQoQkRBI-T0sWjiywGgM9DlKd8Te3CB2ZYX-pEVU,34
50
50
  typed_ffmpeg/utils/lazy_eval/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
51
  typed_ffmpeg/utils/lazy_eval/operator.py,sha256=QWybd-UH3VdDa8kgWkqAMi3WV0b0WF1d1JixQr6is2E,4136
52
52
  typed_ffmpeg/utils/lazy_eval/schema.py,sha256=WSg-E3MS3itN1AT6Dq4Z9btnRHEReuN3o6zruXou7h4,9623
53
- typed_ffmpeg_compatible-3.2.2.dist-info/licenses/LICENSE,sha256=8Aaya5i_09Cou2i3QMxTwz6uHGzi_fGA4uhkco07-A4,1066
54
- typed_ffmpeg_compatible-3.2.2.dist-info/METADATA,sha256=eJv1JAMT41NxhuCYDvTZOBNyU5n2XoiFfyDTVtohI28,8385
55
- typed_ffmpeg_compatible-3.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
56
- typed_ffmpeg_compatible-3.2.2.dist-info/entry_points.txt,sha256=kUQvZ27paV-07qtkIFV-emKsYtjFOTw9kknBRSXPs04,45
57
- typed_ffmpeg_compatible-3.2.2.dist-info/top_level.txt,sha256=vuASJGVRQiNmhWY1pt0RXESWSNkknWXqWLIRAU7H_L4,13
58
- typed_ffmpeg_compatible-3.2.2.dist-info/RECORD,,
53
+ typed_ffmpeg_compatible-3.2.3.dist-info/licenses/LICENSE,sha256=8Aaya5i_09Cou2i3QMxTwz6uHGzi_fGA4uhkco07-A4,1066
54
+ typed_ffmpeg_compatible-3.2.3.dist-info/METADATA,sha256=HwRj91ZONWRkUUACimtXw8TNwhZveMe358-cYoBYaUU,8385
55
+ typed_ffmpeg_compatible-3.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
56
+ typed_ffmpeg_compatible-3.2.3.dist-info/entry_points.txt,sha256=kUQvZ27paV-07qtkIFV-emKsYtjFOTw9kknBRSXPs04,45
57
+ typed_ffmpeg_compatible-3.2.3.dist-info/top_level.txt,sha256=vuASJGVRQiNmhWY1pt0RXESWSNkknWXqWLIRAU7H_L4,13
58
+ typed_ffmpeg_compatible-3.2.3.dist-info/RECORD,,