typed-ffmpeg-compatible 3.5.1__py3-none-any.whl → 3.5.2__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.5.1'
21
- __version_tuple__ = version_tuple = (3, 5, 1)
20
+ __version__ = version = '3.5.2'
21
+ __version_tuple__ = version_tuple = (3, 5, 2)
@@ -16,8 +16,10 @@ filter graph syntax, and escaping of special characters in FFmpeg commands.
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
+ import logging
19
20
  import re
20
21
  import shlex
22
+ import tempfile
21
23
  from collections import defaultdict
22
24
  from collections.abc import Mapping
23
25
  from dataclasses import replace
@@ -47,6 +49,8 @@ from ..utils.run import command_line
47
49
  from .context import DAGContext
48
50
  from .validate import validate
49
51
 
52
+ logger = logging.getLogger(__name__)
53
+
50
54
 
51
55
  def get_options_dict() -> dict[str, FFMpegOption]:
52
56
  """
@@ -502,7 +506,9 @@ def parse(cli: str) -> Stream:
502
506
  return result
503
507
 
504
508
 
505
- def compile(stream: Stream, auto_fix: bool = True) -> str:
509
+ def compile(
510
+ stream: Stream, auto_fix: bool = True, use_filter_complex_script: bool = False
511
+ ) -> str:
506
512
  """
507
513
  Compile a stream into a command-line string.
508
514
 
@@ -512,14 +518,20 @@ def compile(stream: Stream, auto_fix: bool = True) -> str:
512
518
  Args:
513
519
  stream: The Stream object to compile into a command-line string
514
520
  auto_fix: Whether to automatically fix issues in the stream
521
+ use_filter_complex_script: If True, use -filter_complex_script with a
522
+ temporary file instead of -filter_complex
515
523
 
516
524
  Returns:
517
525
  A command-line string that can be passed to FFmpeg
518
526
  """
519
- return "ffmpeg " + command_line(compile_as_list(stream, auto_fix))
527
+ return "ffmpeg " + command_line(
528
+ compile_as_list(stream, auto_fix, use_filter_complex_script)
529
+ )
520
530
 
521
531
 
522
- def compile_as_list(stream: Stream, auto_fix: bool = True) -> list[str]:
532
+ def compile_as_list(
533
+ stream: Stream, auto_fix: bool = True, use_filter_complex_script: bool = False
534
+ ) -> list[str]:
523
535
  """
524
536
  Compile a stream into a list of FFmpeg command-line arguments.
525
537
 
@@ -545,6 +557,8 @@ def compile_as_list(stream: Stream, auto_fix: bool = True) -> list[str]:
545
557
  - Properly labels all streams
546
558
  - Maintains correct filter chain order
547
559
  - Handles stream splitting and merging
560
+ - If use_filter_complex_script is True, creates a temporary file
561
+ with the filter complex content and uses -filter_complex_script
548
562
 
549
563
  5. Output Files: Processes destination files
550
564
  - File paths and output options
@@ -560,6 +574,8 @@ def compile_as_list(stream: Stream, auto_fix: bool = True) -> list[str]:
560
574
  stream: The Stream object to compile into arguments
561
575
  auto_fix: Whether to automatically fix issues in the stream
562
576
  (e.g., reconnecting disconnected nodes)
577
+ use_filter_complex_script: If True, use -filter_complex_script with a
578
+ temporary file instead of -filter_complex
563
579
 
564
580
  Returns:
565
581
  A list of strings representing FFmpeg command-line arguments
@@ -605,7 +621,19 @@ def compile_as_list(stream: Stream, auto_fix: bool = True) -> list[str]:
605
621
  vf_commands += ["".join(get_args(node, context))]
606
622
 
607
623
  if vf_commands:
608
- commands += ["-filter_complex", ";".join(vf_commands)]
624
+ filter_complex_content = ";".join(vf_commands)
625
+
626
+ if use_filter_complex_script:
627
+ # Create a temporary file with the filter complex content
628
+ with tempfile.NamedTemporaryFile(
629
+ mode="w", suffix=".txt", delete=False
630
+ ) as f:
631
+ f.write(filter_complex_content)
632
+ temp_filename = f.name
633
+
634
+ commands += ["-filter_complex_script", temp_filename]
635
+ else:
636
+ commands += ["-filter_complex", filter_complex_content]
609
637
 
610
638
  # compile the output nodes
611
639
  output_nodes = [node for node in context.all_nodes if isinstance(node, OutputNode)]
@@ -21,7 +21,7 @@ from .context import DAGContext
21
21
 
22
22
 
23
23
  def remove_split(
24
- current_stream: Stream, mapping: dict[Stream, Stream] = None
24
+ current_stream: Stream, mapping: dict[Stream, Stream] | None = None
25
25
  ) -> tuple[Stream, dict[Stream, Stream]]:
26
26
  """
27
27
  Remove all split nodes from the graph to prepare for reconstruction.
@@ -91,10 +91,10 @@ def remove_split(
91
91
 
92
92
  def add_split(
93
93
  current_stream: Stream,
94
- down_node: Node = None,
95
- down_index: int = None,
96
- context: DAGContext = None,
97
- mapping: dict[tuple[Stream, Node | None, int | None], Stream] = None,
94
+ down_node: Node | None = None,
95
+ down_index: int | None = None,
96
+ context: DAGContext | None = None,
97
+ mapping: dict[tuple[Stream, Node | None, int | None], Stream] | None = None,
98
98
  ) -> tuple[Stream, dict[tuple[Stream, Node | None, int | None], Stream]]:
99
99
  """
100
100
  Add split nodes to the graph where streams are reused.
@@ -83,6 +83,7 @@ class GlobalRunable(GlobalArgs):
83
83
  cmd: str | list[str] = "ffmpeg",
84
84
  overwrite_output: bool | None = None,
85
85
  auto_fix: bool = True,
86
+ use_filter_complex_script: bool = False,
86
87
  ) -> list[str]:
87
88
  """
88
89
  Build command-line arguments for invoking FFmpeg.
@@ -100,6 +101,8 @@ class GlobalRunable(GlobalArgs):
100
101
  If None (default), use the current settings
101
102
  auto_fix: Whether to automatically fix issues in the filter graph,
102
103
  such as adding split filters for reused streams
104
+ use_filter_complex_script: If True, use -filter_complex_script with a
105
+ temporary file instead of -filter_complex
103
106
 
104
107
  Returns:
105
108
  A list of strings representing the complete FFmpeg command
@@ -117,17 +120,30 @@ class GlobalRunable(GlobalArgs):
117
120
  cmd = [cmd]
118
121
 
119
122
  if overwrite_output is True:
120
- return self.global_args(y=True).compile(cmd, auto_fix=auto_fix)
123
+ return self.global_args(y=True).compile(
124
+ cmd,
125
+ auto_fix=auto_fix,
126
+ use_filter_complex_script=use_filter_complex_script,
127
+ )
121
128
  elif overwrite_output is False:
122
- return self.global_args(n=True).compile(cmd, auto_fix=auto_fix)
129
+ return self.global_args(n=True).compile(
130
+ cmd,
131
+ auto_fix=auto_fix,
132
+ use_filter_complex_script=use_filter_complex_script,
133
+ )
123
134
 
124
- return cmd + compile_as_list(self._global_node().stream(), auto_fix=auto_fix)
135
+ return cmd + compile_as_list(
136
+ self._global_node().stream(),
137
+ auto_fix=auto_fix,
138
+ use_filter_complex_script=use_filter_complex_script,
139
+ )
125
140
 
126
141
  def compile_line(
127
142
  self,
128
143
  cmd: str | list[str] = "ffmpeg",
129
144
  overwrite_output: bool | None = None,
130
145
  auto_fix: bool = True,
146
+ use_filter_complex_script: bool = False,
131
147
  ) -> str:
132
148
  """
133
149
  Build a command-line string for invoking FFmpeg.
@@ -143,6 +159,8 @@ class GlobalRunable(GlobalArgs):
143
159
  If False, add the -n option to never overwrite
144
160
  If None (default), use the current settings
145
161
  auto_fix: Whether to automatically fix issues in the filter graph
162
+ use_filter_complex_script: If True, use -filter_complex_script with a
163
+ temporary file instead of -filter_complex
146
164
 
147
165
  Returns:
148
166
  A string representing the complete FFmpeg command with proper escaping
@@ -155,7 +173,12 @@ class GlobalRunable(GlobalArgs):
155
173
  ```
156
174
  """
157
175
  return command_line(
158
- self.compile(cmd, overwrite_output=overwrite_output, auto_fix=auto_fix)
176
+ self.compile(
177
+ cmd,
178
+ overwrite_output=overwrite_output,
179
+ auto_fix=auto_fix,
180
+ use_filter_complex_script=use_filter_complex_script,
181
+ )
159
182
  )
160
183
 
161
184
  def run_async(
@@ -167,6 +190,7 @@ class GlobalRunable(GlobalArgs):
167
190
  quiet: bool = False,
168
191
  overwrite_output: bool | None = None,
169
192
  auto_fix: bool = True,
193
+ use_filter_complex_script: bool = False,
170
194
  ) -> subprocess.Popen[bytes]:
171
195
  """
172
196
  Run FFmpeg asynchronously as a subprocess.
@@ -186,6 +210,8 @@ class GlobalRunable(GlobalArgs):
186
210
  If False, add the -n option to never overwrite
187
211
  If None (default), use the current settings
188
212
  auto_fix: Whether to automatically fix issues in the filter graph
213
+ use_filter_complex_script: If True, use -filter_complex_script with a
214
+ temporary file instead of -filter_complex
189
215
 
190
216
  Returns:
191
217
  A subprocess.Popen object representing the running FFmpeg process
@@ -199,14 +225,17 @@ class GlobalRunable(GlobalArgs):
199
225
  ```
200
226
  """
201
227
 
202
- args = self.compile(cmd, overwrite_output=overwrite_output, auto_fix=auto_fix)
228
+ args = self.compile(
229
+ cmd,
230
+ overwrite_output=overwrite_output,
231
+ auto_fix=auto_fix,
232
+ use_filter_complex_script=use_filter_complex_script,
233
+ )
203
234
  stdin_stream = subprocess.PIPE if pipe_stdin else None
204
235
  stdout_stream = subprocess.PIPE if pipe_stdout or quiet else None
205
236
  stderr_stream = subprocess.PIPE if pipe_stderr or quiet else None
206
237
 
207
- logger.info(
208
- f"Running command: {self.compile_line(cmd, overwrite_output=overwrite_output, auto_fix=auto_fix)}"
209
- )
238
+ logger.info(f"Running command: {' '.join(args)}")
210
239
 
211
240
  return subprocess.Popen(
212
241
  args,
@@ -224,6 +253,7 @@ class GlobalRunable(GlobalArgs):
224
253
  quiet: bool = False,
225
254
  overwrite_output: bool | None = None,
226
255
  auto_fix: bool = True,
256
+ use_filter_complex_script: bool = False,
227
257
  ) -> tuple[bytes, bytes]:
228
258
  """
229
259
  Run FFmpeg synchronously and wait for completion.
@@ -243,6 +273,8 @@ class GlobalRunable(GlobalArgs):
243
273
  If False, add the -n option to never overwrite
244
274
  If None (default), use the current settings
245
275
  auto_fix: Whether to automatically fix issues in the filter graph
276
+ use_filter_complex_script: If True, use -filter_complex_script with a
277
+ temporary file instead of -filter_complex
246
278
 
247
279
  Returns:
248
280
  A tuple of (stdout_bytes, stderr_bytes), which will be empty bytes
@@ -272,6 +304,7 @@ class GlobalRunable(GlobalArgs):
272
304
  quiet=quiet,
273
305
  overwrite_output=overwrite_output,
274
306
  auto_fix=auto_fix,
307
+ use_filter_complex_script=use_filter_complex_script,
275
308
  )
276
309
  stdout, stderr = process.communicate(input)
277
310
  retcode = process.poll()
@@ -280,7 +313,10 @@ class GlobalRunable(GlobalArgs):
280
313
  raise FFMpegExecuteError(
281
314
  retcode=retcode,
282
315
  cmd=self.compile_line(
283
- cmd, overwrite_output=overwrite_output, auto_fix=auto_fix
316
+ cmd,
317
+ overwrite_output=overwrite_output,
318
+ auto_fix=auto_fix,
319
+ use_filter_complex_script=use_filter_complex_script,
284
320
  ),
285
321
  stdout=stdout,
286
322
  stderr=stderr,
@@ -12,8 +12,8 @@ from typing import Optional
12
12
 
13
13
  from syrupy.extensions.json import JSONSnapshotExtension
14
14
  from syrupy.types import (
15
- PropertyFilter,
16
- PropertyMatcher,
15
+ PropertyFilter, # ty: ignore[possibly-unbound-import]
16
+ PropertyMatcher, # ty: ignore[possibly-unbound-import]
17
17
  SerializableData,
18
18
  SerializedData,
19
19
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typed-ffmpeg-compatible
3
- Version: 3.5.1
3
+ Version: 3.5.2
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=6ULyAwFhDsIb03SeKRKArqYWlxfeOvRTGEI6vUUj774,1676
2
- typed_ffmpeg/_version.py,sha256=Ds9Dz3aeXTarOqzWPCE0NEHSkHj_416LzolkDmAzO60,511
2
+ typed_ffmpeg/_version.py,sha256=Os7iM-cbnpNkMbuTfkjwZkN72w6VSpuD0K5OQvMq66s,511
3
3
  typed_ffmpeg/base.py,sha256=JNNNnN-1A50i9zviR6uOjAs-bClCDx5qx9A5iSePBeI,6309
4
4
  typed_ffmpeg/exceptions.py,sha256=D4SID6WOwkjVV8O8mAjrEDHWn-8BRDnK_jteaDof1SY,2474
5
5
  typed_ffmpeg/filters.py,sha256=XY1LqXY6Ch1wOX-1XW3SzgwH3NQaAtSEQew6f3JoTeY,144001
@@ -17,11 +17,11 @@ typed_ffmpeg/common/cache.py,sha256=j0JvfX7jewLpdJWxgo7Pwze0BkUJdYGHX2uGR8BZ-9M,
17
17
  typed_ffmpeg/common/schema.py,sha256=qM8yfMX9UU3EAQSNsTrr-SAmyqKx8eQCXTtu3RJWkEk,19673
18
18
  typed_ffmpeg/common/serialize.py,sha256=ECgnown2I6BpKK2xYxYKzBUE4tnWxDEyUiSvsymoG1I,7719
19
19
  typed_ffmpeg/compile/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- typed_ffmpeg/compile/compile_cli.py,sha256=FyFcFN2qgpi57M3fERzXlSdEs4Z7XwD0zKicXbl7_4M,32980
20
+ typed_ffmpeg/compile/compile_cli.py,sha256=Gs1jJqQ2DUufOI220S4uPpREs2RWy7DWP2uhEEwnBW4,34094
21
21
  typed_ffmpeg/compile/compile_json.py,sha256=YCiTyfAnUVSbFr7BiQpmJYs13K5sa-xo77Iih33mb6I,992
22
22
  typed_ffmpeg/compile/compile_python.py,sha256=YnnRRHE8TEUiqFF9DsqkYOwIcA2ejCYw12k-O5n825A,11506
23
23
  typed_ffmpeg/compile/context.py,sha256=macQ3HhEJ73j_WbWYtU9GCQCzcB_KQGAPimcuU-WOac,10946
24
- typed_ffmpeg/compile/validate.py,sha256=QsWksdvlRwWw6hnatFo-ABakms1qDXRbEmvMQGRLrD8,9579
24
+ typed_ffmpeg/compile/validate.py,sha256=BraermYPsQEHbrCPEriMDFVDr2Bo2KNxHgaGrpaUngg,9614
25
25
  typed_ffmpeg/dag/__init__.py,sha256=qAApSNqjbZ1DtUaV5bSku9RwG7MpMPa1HJO764cSBt4,849
26
26
  typed_ffmpeg/dag/factory.py,sha256=2IMVKP_2UaTrlGXBg8YDx5KXBqhpScJiJQ87PRrppzY,3147
27
27
  typed_ffmpeg/dag/nodes.py,sha256=TEBlG2299_sTjvt0eTbsULK6Ln8ndo1Z38ftNYUC5Lc,20526
@@ -29,7 +29,7 @@ typed_ffmpeg/dag/schema.py,sha256=ZIW2RTdSa484akezWnREEZC5FgdTZHvVLYC-Vl6-dF8,59
29
29
  typed_ffmpeg/dag/utils.py,sha256=hydh7_kjpOCw8WEGhXMxIXR4Ek-3DeoOt6esInuK2Xw,1941
30
30
  typed_ffmpeg/dag/global_runnable/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
31
  typed_ffmpeg/dag/global_runnable/global_args.py,sha256=PINtqw5QNDcZ9OC4w0frt-fWV8Yg6_We6w68dnXia94,7801
32
- typed_ffmpeg/dag/global_runnable/runnable.py,sha256=0QGBm3ghM4LFT4H9km9-h_N0w5BR3kTaESRapb-qTv0,11098
32
+ typed_ffmpeg/dag/global_runnable/runnable.py,sha256=jvXMs8MOBrv29Jeg6Fmf0T5r8Jx493YxxWsgZQtW6dk,12613
33
33
  typed_ffmpeg/dag/io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  typed_ffmpeg/dag/io/_input.py,sha256=NFr4leQWCQ6f1bj_o5jPli4UMRFfwcEZufU5BztFpm4,7826
35
35
  typed_ffmpeg/dag/io/_output.py,sha256=SfQCEtmGJKFmxbXXDlc7DaQpeNFNYVG9x6vDNVaFmhE,13232
@@ -53,15 +53,15 @@ typed_ffmpeg/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
53
53
  typed_ffmpeg/utils/escaping.py,sha256=m6CTEBwWZTFdtZHTHW-3pQCgkpdZb9f9ynoO-gsD7uM,2937
54
54
  typed_ffmpeg/utils/frozendict.py,sha256=oRlG0hQWIHhnYCrMeGQ6JE37yge_dB1gY3lDdoiJ0hw,4569
55
55
  typed_ffmpeg/utils/run.py,sha256=R3WSxEUdY-QmJnHT03VrvdUVQDyyI1QEiMw6Nz8AH1s,2178
56
- typed_ffmpeg/utils/snapshot.py,sha256=SHdfM1OXxR0cReNs4snDdAASjW45unz2KjF_6jMwq7c,2180
56
+ typed_ffmpeg/utils/snapshot.py,sha256=jqjG_Jgp6tL-gPislqpaLBM0b31D-ul9AI-LQ9EBeqw,2258
57
57
  typed_ffmpeg/utils/typing.py,sha256=DBQn_gCF8C_DTwsfMHeCgfnNUROwAjlIcHrQ7lNDOoE,1187
58
58
  typed_ffmpeg/utils/view.py,sha256=jiCKJnx-KVJh-uvhkADBQNlWH1uHPqyYgmSZ_iSEj0c,3484
59
59
  typed_ffmpeg/utils/lazy_eval/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
60
  typed_ffmpeg/utils/lazy_eval/operator.py,sha256=QWybd-UH3VdDa8kgWkqAMi3WV0b0WF1d1JixQr6is2E,4136
61
61
  typed_ffmpeg/utils/lazy_eval/schema.py,sha256=WSg-E3MS3itN1AT6Dq4Z9btnRHEReuN3o6zruXou7h4,9623
62
- typed_ffmpeg_compatible-3.5.1.dist-info/licenses/LICENSE,sha256=8Aaya5i_09Cou2i3QMxTwz6uHGzi_fGA4uhkco07-A4,1066
63
- typed_ffmpeg_compatible-3.5.1.dist-info/METADATA,sha256=1xN_TPTfNYKO3649Cc6ViRv4K02gMWL9GGBTUbEakTY,8385
64
- typed_ffmpeg_compatible-3.5.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
65
- typed_ffmpeg_compatible-3.5.1.dist-info/entry_points.txt,sha256=kUQvZ27paV-07qtkIFV-emKsYtjFOTw9kknBRSXPs04,45
66
- typed_ffmpeg_compatible-3.5.1.dist-info/top_level.txt,sha256=vuASJGVRQiNmhWY1pt0RXESWSNkknWXqWLIRAU7H_L4,13
67
- typed_ffmpeg_compatible-3.5.1.dist-info/RECORD,,
62
+ typed_ffmpeg_compatible-3.5.2.dist-info/licenses/LICENSE,sha256=8Aaya5i_09Cou2i3QMxTwz6uHGzi_fGA4uhkco07-A4,1066
63
+ typed_ffmpeg_compatible-3.5.2.dist-info/METADATA,sha256=-CodS0or09ivYNKLFawPFb4AVsi-FXjv89Wrk2wfWfg,8385
64
+ typed_ffmpeg_compatible-3.5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
65
+ typed_ffmpeg_compatible-3.5.2.dist-info/entry_points.txt,sha256=kUQvZ27paV-07qtkIFV-emKsYtjFOTw9kknBRSXPs04,45
66
+ typed_ffmpeg_compatible-3.5.2.dist-info/top_level.txt,sha256=vuASJGVRQiNmhWY1pt0RXESWSNkknWXqWLIRAU7H_L4,13
67
+ typed_ffmpeg_compatible-3.5.2.dist-info/RECORD,,