typed-ffmpeg-compatible 3.0.0__tar.gz → 3.0.0a0__tar.gz

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.
Files changed (93) hide show
  1. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/PKG-INFO +1 -1
  2. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/pyproject.toml +1 -1
  3. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/compile/compile_cli.py +70 -14
  4. typed_ffmpeg_compatible-3.0.0a0/src/typed_ffmpeg/compile/compile_json.py +38 -0
  5. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/compile/compile_python.py +4 -4
  6. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/dag/global_runnable/runnable.py +6 -6
  7. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/LICENSE +0 -0
  8. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/README.md +0 -0
  9. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/__init__.py +0 -0
  10. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/base.py +0 -0
  11. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/__init__.py +0 -0
  12. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/.gitignore +0 -0
  13. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/acrossover.json +0 -0
  14. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/afir.json +0 -0
  15. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/aiir.json +0 -0
  16. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/ainterleave.json +0 -0
  17. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/amerge.json +0 -0
  18. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/amix.json +0 -0
  19. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/amovie.json +0 -0
  20. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/anequalizer.json +0 -0
  21. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/aphasemeter.json +0 -0
  22. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/asegment.json +0 -0
  23. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/aselect.json +0 -0
  24. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/asplit.json +0 -0
  25. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/astreamselect.json +0 -0
  26. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/bm3d.json +0 -0
  27. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/channelsplit.json +0 -0
  28. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/concat.json +0 -0
  29. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/decimate.json +0 -0
  30. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/ebur128.json +0 -0
  31. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/extractplanes.json +0 -0
  32. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/fieldmatch.json +0 -0
  33. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/guided.json +0 -0
  34. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/headphone.json +0 -0
  35. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/hstack.json +0 -0
  36. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/interleave.json +0 -0
  37. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/join.json +0 -0
  38. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/libplacebo.json +0 -0
  39. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/limitdiff.json +0 -0
  40. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/mergeplanes.json +0 -0
  41. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/mix.json +0 -0
  42. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/movie.json +0 -0
  43. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/premultiply.json +0 -0
  44. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/segment.json +0 -0
  45. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/select.json +0 -0
  46. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/signature.json +0 -0
  47. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/split.json +0 -0
  48. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/streamselect.json +0 -0
  49. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/unpremultiply.json +0 -0
  50. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/vstack.json +0 -0
  51. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/xmedian.json +0 -0
  52. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/xstack.json +0 -0
  53. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache/list/filters.json +0 -0
  54. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/cache.py +0 -0
  55. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/schema.py +0 -0
  56. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/common/serialize.py +0 -0
  57. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/compile/__init__.py +0 -0
  58. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/compile/context.py +0 -0
  59. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/compile/validate.py +0 -0
  60. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/dag/__init__.py +0 -0
  61. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/dag/factory.py +0 -0
  62. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/dag/global_runnable/__init__.py +0 -0
  63. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/dag/global_runnable/global_args.py +0 -0
  64. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/dag/io/__init__.py +0 -0
  65. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/dag/io/_input.py +0 -0
  66. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/dag/io/_output.py +0 -0
  67. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/dag/io/output_args.py +0 -0
  68. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/dag/nodes.py +0 -0
  69. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/dag/schema.py +0 -0
  70. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/dag/utils.py +0 -0
  71. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/exceptions.py +0 -0
  72. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/filters.py +0 -0
  73. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/info.py +0 -0
  74. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/probe.py +0 -0
  75. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/py.typed +0 -0
  76. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/schema.py +0 -0
  77. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/sources.py +0 -0
  78. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/streams/__init__.py +0 -0
  79. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/streams/audio.py +0 -0
  80. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/streams/av.py +0 -0
  81. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/streams/channel_layout.py +0 -0
  82. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/streams/video.py +0 -0
  83. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/types.py +0 -0
  84. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/utils/__init__.py +0 -0
  85. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/utils/escaping.py +0 -0
  86. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/utils/forzendict.py +0 -0
  87. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/utils/lazy_eval/__init__.py +0 -0
  88. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/utils/lazy_eval/operator.py +0 -0
  89. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/utils/lazy_eval/schema.py +0 -0
  90. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/utils/run.py +0 -0
  91. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/utils/snapshot.py +0 -0
  92. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/utils/typing.py +0 -0
  93. {typed_ffmpeg_compatible-3.0.0 → typed_ffmpeg_compatible-3.0.0a0}/src/typed_ffmpeg/utils/view.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: typed-ffmpeg-compatible
3
- Version: 3.0.0
3
+ Version: 3.0.0a0
4
4
  Summary: Modern Python FFmpeg wrappers offer comprehensive support for complex filters, complete with detailed typing and documentation.
5
5
  Home-page: https://livingbio.github.io/typed-ffmpeg/
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "typed-ffmpeg-compatible"
3
- version = "3.0.0"
3
+ version = "3.0.0a0"
4
4
  description = "Modern Python FFmpeg wrappers offer comprehensive support for complex filters, complete with detailed typing and documentation."
5
5
  authors = ["lucemia <lucemia@gmail.com>"]
6
6
  readme = "README.md"
@@ -3,8 +3,15 @@ Compiles FFmpeg filter graphs into command-line arguments.
3
3
 
4
4
  This module provides functionality to convert the internal DAG (Directed Acyclic Graph)
5
5
  representation of an FFmpeg filter chain into the actual command-line arguments
6
- that would be passed to FFmpeg. It traverses the graph in the correct order,
7
- handling global options, inputs, complex filtergraphs, and outputs.
6
+ that would be passed to FFmpeg. It handles the following components:
7
+
8
+ 1. Global Options: General FFmpeg settings like log level, overwrite flags
9
+ 2. Input Files: Source media files with their specific options
10
+ 3. Filter Graphs: Complex filter chains with proper stream labeling
11
+ 4. Output Files: Destination files with codec and format settings
12
+
13
+ The module ensures proper ordering of arguments and handles stream mapping,
14
+ filter graph syntax, and escaping of special characters in FFmpeg commands.
8
15
  """
9
16
 
10
17
  from __future__ import annotations
@@ -15,11 +22,29 @@ from ..exceptions import FFMpegValueError
15
22
  from ..schema import Default
16
23
  from ..utils.escaping import escape
17
24
  from ..utils.lazy_eval.schema import LazyValue
25
+ from ..utils.run import command_line
18
26
  from .context import DAGContext
19
27
  from .validate import validate
20
28
 
21
29
 
22
- def compile(stream: Stream, auto_fix: bool = True) -> list[str]:
30
+ def compile(stream: Stream, auto_fix: bool = True) -> str:
31
+ """
32
+ Compile a stream into a command-line string.
33
+
34
+ This function takes a Stream object representing an FFmpeg filter graph
35
+ and converts it into a command-line string that can be passed to FFmpeg.
36
+
37
+ Args:
38
+ stream: The Stream object to compile into a command-line string
39
+ auto_fix: Whether to automatically fix issues in the stream
40
+
41
+ Returns:
42
+ A command-line string that can be passed to FFmpeg
43
+ """
44
+ return command_line(compile_as_list(stream, auto_fix))
45
+
46
+
47
+ def compile_as_list(stream: Stream, auto_fix: bool = True) -> list[str]:
23
48
  """
24
49
  Compile a stream into a list of FFmpeg command-line arguments.
25
50
 
@@ -32,7 +57,8 @@ def compile(stream: Stream, auto_fix: bool = True) -> list[str]:
32
57
  4. Output nodes (output files and their options)
33
58
 
34
59
  The function validates the graph before compilation to ensure it's properly
35
- formed. If auto_fix is enabled, it will attempt to fix common issues.
60
+ formed. If auto_fix is enabled, it will attempt to fix common issues like
61
+ disconnected nodes or invalid stream mappings.
36
62
 
37
63
  Args:
38
64
  stream: The Stream object to compile into arguments
@@ -42,6 +68,9 @@ def compile(stream: Stream, auto_fix: bool = True) -> list[str]:
42
68
  Returns:
43
69
  A list of strings representing FFmpeg command-line arguments
44
70
 
71
+ Raises:
72
+ FFMpegValueError: If the stream contains invalid configurations that cannot be fixed
73
+
45
74
  Example:
46
75
  ```python
47
76
  # Create a simple video scaling filter graph
@@ -108,6 +137,7 @@ def get_stream_label(stream: Stream, context: DAGContext | None = None) -> str:
108
137
  - Multi-output filters: "filterlabel#index"
109
138
 
110
139
  Args:
140
+ stream: The stream to generate a label for
111
141
  context: Optional DAG context for resolving node labels.
112
142
  If not provided, a new context will be built.
113
143
 
@@ -156,9 +186,12 @@ def get_args_filter_node(node: FilterNode, context: DAGContext) -> list[str]:
156
186
  FFmpeg's syntax where input labels are followed by the filter name
157
187
  and parameters, and then output labels.
158
188
 
189
+ The filter string format is:
190
+ [input_label]filter_name=param1=value1:param2=value2[output_label]
191
+
159
192
  Args:
160
- context: Optional DAG context for resolving stream labels.
161
- If not provided, a new context will be built.
193
+ node: The FilterNode to generate arguments for
194
+ context: DAG context for resolving stream labels
162
195
 
163
196
  Returns:
164
197
  A list of strings that, when joined, form the filter string
@@ -207,9 +240,12 @@ def get_args_input_node(node: InputNode, context: DAGContext) -> list[str]:
207
240
 
208
241
  This method creates the command-line arguments needed to specify
209
242
  this input file to FFmpeg, including any input-specific options.
243
+ Options are converted to FFmpeg's command-line format, with boolean
244
+ options using -option or -nooption syntax.
210
245
 
211
246
  Args:
212
- context: Optional DAG context (not used for input nodes)
247
+ node: The InputNode to generate arguments for
248
+ context: DAG context (not used for input nodes)
213
249
 
214
250
  Returns:
215
251
  A list of strings representing FFmpeg command-line arguments
@@ -237,11 +273,12 @@ def get_args_output_node(node: OutputNode, context: DAGContext) -> list[str]:
237
273
 
238
274
  This method creates the command-line arguments needed to specify
239
275
  this output file to FFmpeg, including stream mapping and output-specific
240
- options like codecs and formats.
276
+ options like codecs and formats. It handles both direct input streams
277
+ and filter output streams appropriately.
241
278
 
242
279
  Args:
243
- context: Optional DAG context for resolving stream labels.
244
- If not provided, a new context will be built.
280
+ node: The OutputNode to generate arguments for
281
+ context: DAG context for resolving stream labels
245
282
 
246
283
  Returns:
247
284
  A list of strings representing FFmpeg command-line arguments
@@ -278,10 +315,12 @@ def get_args_global_node(node: GlobalNode, context: DAGContext) -> list[str]:
278
315
 
279
316
  This method creates the command-line arguments needed to specify
280
317
  global options to FFmpeg, such as -y for overwrite or -loglevel for
281
- controlling log output.
318
+ controlling log output. Boolean options are converted to -option or
319
+ -nooption syntax.
282
320
 
283
321
  Args:
284
- context: Optional DAG context (not used for global options)
322
+ node: The GlobalNode to generate arguments for
323
+ context: DAG context (not used for global options)
285
324
 
286
325
  Returns:
287
326
  A list of strings representing FFmpeg command-line arguments
@@ -304,7 +343,22 @@ def get_args_global_node(node: GlobalNode, context: DAGContext) -> list[str]:
304
343
 
305
344
  def get_args(node: Node, context: DAGContext | None = None) -> list[str]:
306
345
  """
307
- Get the arguments for a node.
346
+ Get the FFmpeg command-line arguments for a specific node.
347
+
348
+ This function dispatches to the appropriate argument generation function
349
+ based on the node type. It handles all node types in the FFmpeg DAG:
350
+ FilterNode, InputNode, OutputNode, and GlobalNode.
351
+
352
+ Args:
353
+ node: The node to generate arguments for
354
+ context: Optional DAG context for resolving stream labels.
355
+ If not provided, a new context will be built.
356
+
357
+ Returns:
358
+ A list of strings representing FFmpeg command-line arguments
359
+
360
+ Raises:
361
+ FFMpegValueError: If the node type is not recognized
308
362
  """
309
363
 
310
364
  context = context or DAGContext.build(node)
@@ -330,9 +384,11 @@ def get_node_label(node: Node, context: DAGContext) -> str:
330
384
  filter graph notation. The label format depends on the node type:
331
385
  - Input nodes: sequential numbers (0, 1, 2...)
332
386
  - Filter nodes: 's' prefix followed by a number (s0, s1, s2...)
387
+ - Output nodes: 'out'
333
388
 
334
389
  Args:
335
- node: The node to get the label for (must be an InputNode or FilterNode)
390
+ node: The node to get the label for
391
+ context: DAG context containing node ID mappings
336
392
 
337
393
  Returns:
338
394
  The string label for the node
@@ -0,0 +1,38 @@
1
+ from ..common.serialize import dumps, loads
2
+ from ..dag.schema import Stream
3
+ from .validate import validate
4
+
5
+
6
+ def compile(stream: Stream, auto_fix: bool = True) -> str:
7
+ """
8
+ Compile a stream into a JSON string.
9
+
10
+ This function takes a Stream object representing an FFmpeg filter graph
11
+ and converts it into a JSON string that can be passed to FFmpeg.
12
+
13
+ Args:
14
+ stream: The Stream object to compile into a JSON string
15
+ auto_fix: Whether to automatically fix issues in the stream
16
+
17
+ Returns:
18
+ A JSON string that can be passed to FFmpeg
19
+ """
20
+ stream = validate(stream, auto_fix=auto_fix)
21
+
22
+ return dumps(stream)
23
+
24
+
25
+ def parse(json: str) -> Stream:
26
+ """
27
+ Parse a JSON string into a Stream object.
28
+
29
+ This function takes a JSON string that can be passed to FFmpeg
30
+ and converts it into a Stream object.
31
+
32
+ Args:
33
+ json: The JSON string to parse into a Stream object
34
+
35
+ Returns:
36
+ A Stream object
37
+ """
38
+ return loads(json)
@@ -191,7 +191,7 @@ def compile_fluent(code: list[str]) -> list[str]:
191
191
  return [f"{k.strip()} = {v.strip()}" for k, v in buffer]
192
192
 
193
193
 
194
- def compile(stream: Stream, auto_fix: bool = True, fluent: bool = True) -> list[str]:
194
+ def compile(stream: Stream, auto_fix: bool = True, fluent: bool = True) -> str:
195
195
  """
196
196
  Compile the python code.
197
197
 
@@ -295,10 +295,10 @@ def compile(stream: Stream, auto_fix: bool = True, fluent: bool = True) -> list[
295
295
  if fluent:
296
296
  code = compile_fluent(code)
297
297
 
298
- return ["import ffmpeg", *code]
298
+ return "\n".join(["import ffmpeg", *code])
299
299
 
300
300
 
301
- def parse(code: list[str]) -> Stream:
301
+ def parse(code: str) -> Stream:
302
302
  """
303
303
  Parse the python code.
304
304
 
@@ -312,7 +312,7 @@ def parse(code: list[str]) -> Stream:
312
312
  The parsed stream.
313
313
  """
314
314
  local_vars: dict[str, Any] = {}
315
- exec("\n".join(code), {}, local_vars)
315
+ exec(code, {}, local_vars)
316
316
  result = local_vars["result"]
317
317
 
318
318
  assert isinstance(result, Stream)
@@ -81,7 +81,7 @@ class GlobalRunable(GlobalArgs):
81
81
  def compile(
82
82
  self,
83
83
  cmd: str | list[str] = "ffmpeg",
84
- overwrite_output: bool = None,
84
+ overwrite_output: bool | None = None,
85
85
  auto_fix: bool = True,
86
86
  ) -> list[str]:
87
87
  """
@@ -111,7 +111,7 @@ class GlobalRunable(GlobalArgs):
111
111
  # Result: ['ffmpeg', '-i', 'input.mp4', 'output.mp4']
112
112
  ```
113
113
  """
114
- from ...compile.compile_cli import compile
114
+ from ...compile.compile_cli import compile_as_list
115
115
 
116
116
  if isinstance(cmd, str):
117
117
  cmd = [cmd]
@@ -121,12 +121,12 @@ class GlobalRunable(GlobalArgs):
121
121
  elif overwrite_output is False:
122
122
  return self.global_args(n=True).compile(cmd, auto_fix=auto_fix)
123
123
 
124
- return cmd + compile(self._global_node().stream(), auto_fix=auto_fix)
124
+ return cmd + compile_as_list(self._global_node().stream(), auto_fix=auto_fix)
125
125
 
126
126
  def compile_line(
127
127
  self,
128
128
  cmd: str | list[str] = "ffmpeg",
129
- overwrite_output: bool = None,
129
+ overwrite_output: bool | None = None,
130
130
  auto_fix: bool = True,
131
131
  ) -> str:
132
132
  """
@@ -165,7 +165,7 @@ class GlobalRunable(GlobalArgs):
165
165
  pipe_stdout: bool = False,
166
166
  pipe_stderr: bool = False,
167
167
  quiet: bool = False,
168
- overwrite_output: bool = None,
168
+ overwrite_output: bool | None = None,
169
169
  auto_fix: bool = True,
170
170
  ) -> subprocess.Popen[bytes]:
171
171
  """
@@ -222,7 +222,7 @@ class GlobalRunable(GlobalArgs):
222
222
  capture_stderr: bool = False,
223
223
  input: bytes | None = None,
224
224
  quiet: bool = False,
225
- overwrite_output: bool = None,
225
+ overwrite_output: bool | None = None,
226
226
  auto_fix: bool = True,
227
227
  ) -> tuple[bytes, bytes]:
228
228
  """