typed-ffmpeg-compatible 2.6.2__py3-none-any.whl → 2.6.4__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.
Files changed (36) hide show
  1. typed_ffmpeg/__init__.py +26 -1
  2. typed_ffmpeg/base.py +87 -29
  3. typed_ffmpeg/common/schema.py +281 -3
  4. typed_ffmpeg/common/serialize.py +118 -21
  5. typed_ffmpeg/dag/__init__.py +13 -0
  6. typed_ffmpeg/dag/compile.py +39 -4
  7. typed_ffmpeg/dag/context.py +137 -29
  8. typed_ffmpeg/dag/factory.py +27 -0
  9. typed_ffmpeg/dag/global_runnable/global_args.py +11 -0
  10. typed_ffmpeg/dag/global_runnable/runnable.py +143 -34
  11. typed_ffmpeg/dag/io/_input.py +2 -1
  12. typed_ffmpeg/dag/io/_output.py +2 -1
  13. typed_ffmpeg/dag/nodes.py +402 -67
  14. typed_ffmpeg/dag/schema.py +3 -1
  15. typed_ffmpeg/dag/utils.py +29 -8
  16. typed_ffmpeg/dag/validate.py +83 -20
  17. typed_ffmpeg/exceptions.py +42 -9
  18. typed_ffmpeg/info.py +137 -16
  19. typed_ffmpeg/probe.py +31 -6
  20. typed_ffmpeg/schema.py +32 -5
  21. typed_ffmpeg/sources.py +2825 -0
  22. typed_ffmpeg/streams/channel_layout.py +13 -0
  23. typed_ffmpeg/utils/escaping.py +47 -7
  24. typed_ffmpeg/utils/forzendict.py +108 -0
  25. typed_ffmpeg/utils/lazy_eval/operator.py +43 -1
  26. typed_ffmpeg/utils/lazy_eval/schema.py +122 -6
  27. typed_ffmpeg/utils/run.py +44 -7
  28. typed_ffmpeg/utils/snapshot.py +36 -1
  29. typed_ffmpeg/utils/typing.py +29 -4
  30. typed_ffmpeg/utils/view.py +46 -4
  31. {typed_ffmpeg_compatible-2.6.2.dist-info → typed_ffmpeg_compatible-2.6.4.dist-info}/METADATA +1 -1
  32. typed_ffmpeg_compatible-2.6.4.dist-info/RECORD +48 -0
  33. typed_ffmpeg_compatible-2.6.2.dist-info/RECORD +0 -46
  34. {typed_ffmpeg_compatible-2.6.2.dist-info → typed_ffmpeg_compatible-2.6.4.dist-info}/LICENSE +0 -0
  35. {typed_ffmpeg_compatible-2.6.2.dist-info → typed_ffmpeg_compatible-2.6.4.dist-info}/WHEEL +0 -0
  36. {typed_ffmpeg_compatible-2.6.2.dist-info → typed_ffmpeg_compatible-2.6.4.dist-info}/entry_points.txt +0 -0
@@ -1,3 +1,12 @@
1
+ """
2
+ Serialization utilities for FFmpeg filter graphs and components.
3
+
4
+ This module provides functions for serializing and deserializing FFmpeg filter
5
+ graph components to and from JSON. It handles dataclasses, enums, and other
6
+ custom types used in the typed-ffmpeg library, enabling filter graphs to be
7
+ saved to disk and loaded back.
8
+ """
9
+
1
10
  from __future__ import annotations
2
11
 
3
12
  import importlib
@@ -8,17 +17,36 @@ from functools import partial
8
17
  from pathlib import Path
9
18
  from typing import Any
10
19
 
20
+ from ..utils.forzendict import FrozenDict
21
+
11
22
 
12
23
  def load_class(path: str, strict: bool = True) -> Any:
13
24
  """
14
- Load a class from a string path
25
+ Load a class from a string path.
26
+
27
+ This function dynamically imports a class based on its fully qualified
28
+ path (e.g., 'ffmpeg.dag.nodes.FilterNode'). It's used during deserialization
29
+ to reconstruct objects from their class names.
15
30
 
16
31
  Args:
17
- path: The path to the class.
18
- strict: If True, raise an error if the class is not in ffmpeg package.
32
+ path: The fully qualified path to the class (module.submodule.ClassName)
33
+ strict: If True, only allow loading classes from the ffmpeg package
34
+ as a security measure
19
35
 
20
36
  Returns:
21
- The class.
37
+ The class object that can be instantiated
38
+
39
+ Raises:
40
+ AssertionError: If strict is True and the path doesn't start with 'ffmpeg.'
41
+ ImportError: If the module or class cannot be found
42
+
43
+ Example:
44
+ ```python
45
+ # Load the FilterNode class
46
+ FilterNode = load_class('ffmpeg.dag.nodes.FilterNode')
47
+ # Create an instance
48
+ node = FilterNode(name='scale', ...)
49
+ ```
22
50
  """
23
51
  if strict:
24
52
  assert path.startswith("ffmpeg."), (
@@ -32,32 +60,62 @@ def load_class(path: str, strict: bool = True) -> Any:
32
60
 
33
61
  def frozen(v: Any) -> Any:
34
62
  """
35
- Convert the instance to a frozen instance
63
+ Convert mutable data structures to immutable (frozen) equivalents.
64
+
65
+ This function recursively converts lists to tuples and dictionaries to
66
+ FrozenDict instances, ensuring that the resulting data structure is
67
+ completely immutable. This is important for dataclasses that are marked
68
+ as frozen, as they can only contain immutable data.
36
69
 
37
70
  Args:
38
- v: The instance to convert.
71
+ v: The value to convert, which may be a list, dict, or any other type
39
72
 
40
73
  Returns:
41
- The frozen instance.
74
+ An immutable version of the input value
75
+
76
+ Example:
77
+ ```python
78
+ # Convert a nested structure to immutable form
79
+ frozen_data = frozen(
80
+ {"options": ["option1", "option2"], "settings": {"key": "value"}}
81
+ )
82
+ # Result: FrozenDict with tuple instead of list and nested FrozenDict
83
+ ```
42
84
  """
43
85
  if isinstance(v, list):
44
86
  return tuple(frozen(i) for i in v)
45
87
 
46
88
  if isinstance(v, dict):
47
- return tuple((key, frozen(value)) for key, value in v.items())
89
+ return FrozenDict({k: frozen(v) for k, v in v.items()})
48
90
 
49
91
  return v
50
92
 
51
93
 
52
94
  def object_hook(obj: Any, strict: bool = True) -> Any:
53
95
  """
54
- Convert the dictionary to an instance
96
+ Custom JSON object hook for deserializing FFmpeg objects.
97
+
98
+ This function is used by the JSON decoder to convert dictionaries into
99
+ appropriate Python objects during deserialization. It looks for a special
100
+ '__class__' key that indicates the type of object to create.
55
101
 
56
102
  Args:
57
- obj: The dictionary to convert.
103
+ obj: A dictionary from the JSON parser
104
+ strict: If True, only allow loading classes from the ffmpeg package
58
105
 
59
106
  Returns:
60
- The instance.
107
+ Either the original dictionary or an instance of the specified class
108
+
109
+ Example:
110
+ ```python
111
+ # A JSON object with class information
112
+ json_obj = {
113
+ "__class__": "ffmpeg.dag.nodes.FilterNode",
114
+ "name": "scale",
115
+ "kwargs": {"width": 1280, "height": 720},
116
+ }
117
+ # Will be converted to a FilterNode instance
118
+ ```
61
119
  """
62
120
  if isinstance(obj, dict):
63
121
  if obj.get("__class__"):
@@ -74,13 +132,26 @@ def object_hook(obj: Any, strict: bool = True) -> Any:
74
132
 
75
133
  def loads(raw: str, strict: bool = True) -> Any:
76
134
  """
77
- Deserialize the JSON string to an instance
135
+ Deserialize a JSON string into Python objects with proper class types.
136
+
137
+ This function parses a JSON string and reconstructs the original Python
138
+ objects, including dataclasses and enums, based on class information
139
+ embedded in the JSON.
78
140
 
79
141
  Args:
80
- raw: The JSON string to deserialize.
142
+ raw: The JSON string to deserialize
143
+ strict: If True, only allow loading classes from the ffmpeg package
81
144
 
82
145
  Returns:
83
- The deserialized instance.
146
+ The deserialized Python object with proper types
147
+
148
+ Example:
149
+ ```python
150
+ # Deserialize a filter graph from JSON
151
+ json_str = '{"__class__": "ffmpeg.dag.nodes.FilterNode", "name": "scale", ...}'
152
+ filter_node = loads(json_str)
153
+ # filter_node is now a FilterNode instance
154
+ ```
84
155
  """
85
156
  object_hook_strict = partial(object_hook, strict=strict)
86
157
 
@@ -89,16 +160,29 @@ def loads(raw: str, strict: bool = True) -> Any:
89
160
 
90
161
  def to_dict_with_class_info(instance: Any) -> Any:
91
162
  """
92
- Convert the instance to a dictionary with class information
163
+ Convert Python objects to dictionaries with embedded class information.
164
+
165
+ This function recursively converts Python objects to dictionaries, lists,
166
+ and primitive types suitable for JSON serialization. For dataclasses and
167
+ enums, it adds a '__class__' key with the fully qualified class name,
168
+ allowing them to be reconstructed during deserialization.
93
169
 
94
170
  Args:
95
- instance: The instance to convert.
171
+ instance: The Python object to convert
96
172
 
97
173
  Returns:
98
- The dictionary with class information
174
+ A JSON-serializable representation with embedded class information
175
+
176
+ Example:
177
+ ```python
178
+ # Convert a FilterNode to a serializable dict
179
+ filter_node = FilterNode(name='scale', ...)
180
+ serializable = to_dict_with_class_info(filter_node)
181
+ # serializable now contains class information and all attributes
182
+ ```
99
183
  """
100
184
 
101
- if isinstance(instance, dict):
185
+ if isinstance(instance, dict | FrozenDict):
102
186
  return {k: to_dict_with_class_info(v) for k, v in instance.items()}
103
187
  elif isinstance(instance, list):
104
188
  return [to_dict_with_class_info(v) for v in instance]
@@ -125,13 +209,26 @@ def to_dict_with_class_info(instance: Any) -> Any:
125
209
  # Serialization
126
210
  def dumps(instance: Any) -> str:
127
211
  """
128
- Serialize the instance to a JSON string
212
+ Serialize a Python object to a JSON string with class information.
213
+
214
+ This function converts a Python object (including dataclasses, enums,
215
+ and other custom types) to a JSON string that includes class information,
216
+ allowing it to be deserialized back into the original object types.
129
217
 
130
218
  Args:
131
- instance: The instance to serialize.
219
+ instance: The Python object to serialize
132
220
 
133
221
  Returns:
134
- The serialized instance.
222
+ A JSON string representation of the object with class information
223
+
224
+ Example:
225
+ ```python
226
+ # Serialize a filter graph to JSON
227
+ filter_node = FilterNode(name='scale', ...)
228
+ json_str = dumps(filter_node)
229
+ # json_str can be saved to a file and later deserialized
230
+ # with loads() to reconstruct the original object
231
+ ```
135
232
  """
136
233
  obj = to_dict_with_class_info(instance)
137
234
  return json.dumps(obj, indent=2)
@@ -1,3 +1,16 @@
1
+ """
2
+ Directed Acyclic Graph (DAG) implementation for FFmpeg filter chains.
3
+
4
+ This package provides the core components for representing FFmpeg filter chains
5
+ as directed acyclic graphs. It includes classes for different types of nodes
6
+ (inputs, filters, outputs) and streams, as well as utilities for validating,
7
+ compiling, and manipulating these graphs.
8
+
9
+ The DAG structure enables type-safe construction and validation of complex
10
+ FFmpeg filter chains, ensuring that the resulting FFmpeg commands are valid
11
+ and correctly structured.
12
+ """
13
+
1
14
  from .nodes import (
2
15
  FilterableStream,
3
16
  FilterNode,
@@ -1,3 +1,12 @@
1
+ """
2
+ Compiles FFmpeg filter graphs into command-line arguments.
3
+
4
+ This module provides functionality to convert the internal DAG (Directed Acyclic Graph)
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.
8
+ """
9
+
1
10
  from __future__ import annotations
2
11
 
3
12
  from .context import DAGContext
@@ -8,14 +17,40 @@ from .validate import validate
8
17
 
9
18
  def compile(stream: Stream, auto_fix: bool = True) -> list[str]:
10
19
  """
11
- Compile the stream into a list of arguments.
20
+ Compile a stream into a list of FFmpeg command-line arguments.
21
+
22
+ This function takes a Stream object representing an FFmpeg filter graph
23
+ and converts it into a list of command-line arguments that can be passed
24
+ to FFmpeg. It processes the graph in the correct order:
25
+ 1. Global nodes (general FFmpeg options)
26
+ 2. Input nodes (input files and their options)
27
+ 3. Filter nodes (combined into a -filter_complex argument)
28
+ 4. Output nodes (output files and their options)
29
+
30
+ The function validates the graph before compilation to ensure it's properly
31
+ formed. If auto_fix is enabled, it will attempt to fix common issues.
12
32
 
13
33
  Args:
14
- stream: The stream to compile.
15
- auto_fix: Whether to automatically fix the stream.
34
+ stream: The Stream object to compile into arguments
35
+ auto_fix: Whether to automatically fix issues in the stream
36
+ (e.g., reconnecting disconnected nodes)
16
37
 
17
38
  Returns:
18
- The list of arguments.
39
+ A list of strings representing FFmpeg command-line arguments
40
+
41
+ Example:
42
+ ```python
43
+ # Create a simple video scaling filter graph
44
+ input_stream = ffmpeg.input("input.mp4")
45
+ scaled = input_stream.filter("scale", 1280, 720)
46
+ output_stream = scaled.output("output.mp4")
47
+
48
+ # Compile to FFmpeg arguments
49
+ args = ffmpeg.dag.compile(output_stream)
50
+ print(
51
+ args
52
+ ) # ['ffmpeg', '-i', 'input.mp4', '-filter_complex', '...', 'output.mp4']
53
+ ```
19
54
  """
20
55
 
21
56
  stream = validate(stream, auto_fix=auto_fix)
@@ -1,3 +1,12 @@
1
+ """
2
+ Context management for FFmpeg filter graph traversal and manipulation.
3
+
4
+ This module provides the DAGContext class, which represents the context
5
+ of a Directed Acyclic Graph (DAG) of FFmpeg filter nodes. It provides methods
6
+ for traversing, manipulating, and rendering the graph structure, and is used
7
+ during graph validation and command-line compilation.
8
+ """
9
+
1
10
  from __future__ import annotations
2
11
 
3
12
  from collections import defaultdict
@@ -14,13 +23,17 @@ T = TypeVar("T")
14
23
 
15
24
  def _remove_duplicates(seq: list[T]) -> list[T]:
16
25
  """
17
- Remove duplicates from a list while preserving order.
26
+ Remove duplicates from a list while preserving the original order.
27
+
28
+ This helper function processes a list and removes any duplicate elements
29
+ while maintaining the relative ordering of elements. The first occurrence
30
+ of each element is kept, subsequent duplicates are removed.
18
31
 
19
32
  Args:
20
- seq: The list to remove duplicates from.
33
+ seq: The list to remove duplicates from
21
34
 
22
35
  Returns:
23
- The list with duplicates removed.
36
+ A new list with duplicates removed, preserving the original order
24
37
  """
25
38
  seen = set()
26
39
  output = []
@@ -35,13 +48,19 @@ def _remove_duplicates(seq: list[T]) -> list[T]:
35
48
 
36
49
  def _collect(node: Node) -> tuple[list[Node], list[Stream]]:
37
50
  """
38
- Collect all nodes and streams that are upstreamed to the given node
51
+ Recursively collect all nodes and streams in the upstream path of a given node.
52
+
53
+ This function traverses the graph starting from the given node and collects
54
+ all nodes and streams that are upstream (input sources) to the node. The
55
+ traversal is performed recursively to ensure all dependencies are captured.
39
56
 
40
57
  Args:
41
- node: The node to collect from.
58
+ node: The starting node to collect dependencies from
42
59
 
43
60
  Returns:
44
- A tuple of all nodes and streams that are upstreamed to the given node.
61
+ A tuple containing two lists:
62
+ - A list of all nodes in the upstream path (including the starting node)
63
+ - A list of all streams connecting these nodes
45
64
  """
46
65
  nodes, streams = [node], [*node.inputs]
47
66
 
@@ -56,34 +75,53 @@ def _collect(node: Node) -> tuple[list[Node], list[Stream]]:
56
75
  @dataclass(frozen=True, kw_only=True)
57
76
  class DAGContext:
58
77
  """
59
- A context for a directed acyclic graph (DAG).
78
+ Context class for working with a Directed Acyclic Graph (DAG) of FFmpeg filter nodes.
79
+
80
+ This immutable class provides methods and properties for analyzing, traversing,
81
+ and manipulating a filter graph. It maintains information about nodes and streams
82
+ in the graph, their relationships, and provides efficient lookups for graph operations.
83
+
84
+ The context is built from a "root" node (typically an output node) and captures all
85
+ upstream dependencies (input nodes, filter nodes, and connecting streams).
60
86
  """
61
87
 
62
88
  node: Node
63
89
  """
64
90
  The root node (the destination) of the DAG.
91
+
92
+ This is typically an output node where the graph traversal begins.
93
+ All nodes collected in the context are upstream from this node.
65
94
  """
66
95
 
67
96
  nodes: tuple[Node, ...]
68
97
  """
69
- All nodes in the graph.
98
+ All nodes in the graph as an immutable tuple.
99
+
100
+ This includes the root node and all upstream nodes (inputs, filters)
101
+ that contribute to the filter graph.
70
102
  """
71
103
 
72
104
  streams: tuple[Stream, ...]
73
105
  """
74
- All streams in the graph.
106
+ All streams in the graph as an immutable tuple.
107
+
108
+ These streams represent the connections between nodes in the filter graph.
75
109
  """
76
110
 
77
111
  @classmethod
78
112
  def build(cls, node: Node) -> DAGContext:
79
113
  """
80
- create a DAG context based on the given node
114
+ Create a DAG context by traversing the graph from the specified root node.
115
+
116
+ This factory method builds a complete DAGContext by recursively collecting
117
+ all nodes and streams that are upstream from the specified node. It removes
118
+ duplicates to ensure each node and stream is represented only once in the context.
81
119
 
82
120
  Args:
83
- node: The root node of the DAG.
121
+ node: The root node to build the context from (typically an output node)
84
122
 
85
123
  Returns:
86
- A DAG context based on the given node.
124
+ A fully initialized DAGContext containing all nodes and streams in the graph
87
125
  """
88
126
  nodes, streams = _collect(node)
89
127
 
@@ -96,14 +134,29 @@ class DAGContext:
96
134
  @cached_property
97
135
  def all_nodes(self) -> list[Node]:
98
136
  """
99
- All nodes in the graph sorted by the number of upstream nodes.
137
+ Get all nodes in the graph sorted by their position in the processing chain.
138
+
139
+ This property returns a list of all nodes in the graph, sorted by the number
140
+ of upstream nodes. This ensures that nodes earlier in the processing chain
141
+ (closer to inputs) come before nodes later in the chain (closer to outputs).
142
+
143
+ Returns:
144
+ A sorted list of all nodes in the graph
100
145
  """
101
146
  return sorted(self.nodes, key=lambda node: len(node.upstream_nodes))
102
147
 
103
148
  @cached_property
104
149
  def all_streams(self) -> list[Stream]:
105
150
  """
106
- All streams in the graph sorted by the number of upstream nodes and the index of the stream.
151
+ Get all streams in the graph sorted by their position in the processing chain.
152
+
153
+ This property returns a list of all streams in the graph, sorted first by the
154
+ number of upstream nodes of the source node, and then by the stream index.
155
+ This ensures a consistent and logical ordering of streams based on their
156
+ position in the processing pipeline.
157
+
158
+ Returns:
159
+ A sorted list of all streams in the graph
107
160
  """
108
161
  return sorted(
109
162
  self.streams,
@@ -113,7 +166,15 @@ class DAGContext:
113
166
  @cached_property
114
167
  def outgoing_nodes(self) -> dict[Stream, list[tuple[Node, int]]]:
115
168
  """
116
- A dictionary of outgoing nodes for each stream.
169
+ Get a mapping of streams to the nodes they connect to.
170
+
171
+ This property builds a dictionary that maps each stream to a list of
172
+ tuples containing (node, input_index) pairs. Each tuple represents a node
173
+ that receives this stream as input, along with the index position where
174
+ the stream connects to that node.
175
+
176
+ Returns:
177
+ A dictionary mapping streams to their destination nodes and connection indices
117
178
  """
118
179
  outgoing_nodes: dict[Stream, list[tuple[Node, int]]] = defaultdict(list)
119
180
 
@@ -126,7 +187,14 @@ class DAGContext:
126
187
  @cached_property
127
188
  def outgoing_streams(self) -> dict[Node, list[Stream]]:
128
189
  """
129
- A dictionary of outgoing streams for each node.
190
+ Get a mapping of nodes to the streams they output.
191
+
192
+ This property builds a dictionary that maps each node to a list of streams
193
+ that originate from it. This is particularly useful for determining all the
194
+ outputs from a specific filter or input node.
195
+
196
+ Returns:
197
+ A dictionary mapping nodes to their output streams
130
198
  """
131
199
 
132
200
  outgoing_streams: dict[Node, list[Stream]] = defaultdict(list)
@@ -139,7 +207,18 @@ class DAGContext:
139
207
  @cached_property
140
208
  def node_labels(self) -> dict[Node, str]:
141
209
  """
142
- A dictionary of outgoing streams for each node.
210
+ Get a mapping of nodes to their string labels used in FFmpeg filter graphs.
211
+
212
+ This property assigns a unique label to each node in the graph, following
213
+ the FFmpeg filter graph labeling conventions:
214
+ - Input nodes are labeled with sequential numbers (0, 1, 2...)
215
+ - Filter nodes are labeled with 's' followed by a number (s0, s1, s2...)
216
+ - Output nodes are labeled as 'out'
217
+
218
+ These labels are used when generating the filter_complex argument for FFmpeg.
219
+
220
+ Returns:
221
+ A dictionary mapping nodes to their string labels
143
222
  """
144
223
 
145
224
  input_node_index = 0
@@ -161,26 +240,38 @@ class DAGContext:
161
240
  @override
162
241
  def get_outgoing_nodes(self, stream: Stream) -> list[tuple[Node, int]]:
163
242
  """
164
- Get all outgoing nodes of the stream.
243
+ Get all nodes that receive a specific stream as input.
244
+
245
+ This method returns a list of (node, index) tuples representing the nodes
246
+ that receive the given stream as input, along with the input index position
247
+ where the stream connects to each node.
165
248
 
166
249
  Args:
167
- stream: The stream to get the outgoing nodes of.
250
+ stream: The stream to get the destination nodes for
168
251
 
169
252
  Returns:
170
- The outgoing nodes of the stream.
253
+ A list of (node, input_index) tuples for nodes that receive this stream
171
254
  """
172
255
  return self.outgoing_nodes[stream]
173
256
 
174
257
  @override
175
258
  def get_node_label(self, node: Node) -> str:
176
259
  """
177
- Get the label of the node.
260
+ Get the string label for a specific node in the filter graph.
261
+
262
+ This method returns the label assigned to the node, which is used in FFmpeg
263
+ filter graph notation. The label format depends on the node type:
264
+ - Input nodes: sequential numbers (0, 1, 2...)
265
+ - Filter nodes: 's' prefix followed by a number (s0, s1, s2...)
178
266
 
179
267
  Args:
180
- node: The node to get the label of.
268
+ node: The node to get the label for (must be an InputNode or FilterNode)
181
269
 
182
270
  Returns:
183
- The label of the node.
271
+ The string label for the node
272
+
273
+ Raises:
274
+ AssertionError: If the node is not an InputNode or FilterNode
184
275
  """
185
276
 
186
277
  assert isinstance(node, (InputNode, FilterNode)), (
@@ -191,25 +282,42 @@ class DAGContext:
191
282
  @override
192
283
  def get_outgoing_streams(self, node: Node) -> list[Stream]:
193
284
  """
194
- Extract all node's outgoing streams from the given set of streams, Because a node only know its incoming streams.
285
+ Get all streams that originate from a specific node.
286
+
287
+ This method returns all streams where the given node is the source.
288
+ It's particularly useful because nodes natively only track their inputs,
289
+ not their outputs, so this context method provides a way to look up
290
+ a node's outputs.
195
291
 
196
292
  Args:
197
- node: The node to get the outgoing streams of.
293
+ node: The node to get the output streams for
198
294
 
199
295
  Returns:
200
- The outgoing streams of the node.
296
+ A list of streams that originate from this node
201
297
  """
202
298
  return self.outgoing_streams[node]
203
299
 
204
300
  def render(self, obj: Any) -> Any:
205
301
  """
206
- Render the object to a string.
302
+ Recursively convert graph objects to a human-readable representation.
303
+
304
+ This method processes arbitrary objects, with special handling for graph
305
+ elements like nodes and streams. It converts them to a readable string format
306
+ that includes node labels. It recursively handles nested structures like
307
+ lists, tuples, and dictionaries.
308
+
309
+ This is primarily used for debugging, logging, and visualization purposes.
207
310
 
208
311
  Args:
209
- obj: The object to render.
312
+ obj: The object to render, which may be a Node, Stream, or a container
313
+ with these objects nested inside
210
314
 
211
315
  Returns:
212
- The rendered object.
316
+ The rendered representation of the object:
317
+ - For nodes: "Node(repr#label)"
318
+ - For streams: "Stream(node_repr#label#index)"
319
+ - For containers: recursively rendered contents
320
+ - For other objects: the original object unchanged
213
321
  """
214
322
 
215
323
  if isinstance(obj, (list, tuple)):
@@ -1,3 +1,11 @@
1
+ """
2
+ Factory functions for creating FFmpeg filter nodes.
3
+
4
+ This module provides factory functions that create filter nodes based on
5
+ FFmpeg filter definitions. These factories handle the evaluation of automatic
6
+ parameters and the conversion of input/output typing specifications.
7
+ """
8
+
1
9
  import re
2
10
  from typing import Any
3
11
 
@@ -10,6 +18,25 @@ from .nodes import FilterableStream, FilterNode
10
18
  def filter_node_factory(
11
19
  ffmpeg_filter_def: FFMpegFilterDef, *inputs: FilterableStream, **kwargs: Any
12
20
  ) -> FilterNode:
21
+ """
22
+ Create a FilterNode from an FFmpeg filter definition.
23
+
24
+ This function creates a FilterNode based on the provided FFmpeg filter definition.
25
+ It handles the evaluation of Auto parameters and the conversion of input/output
26
+ typing specifications from the filter definition.
27
+
28
+ Args:
29
+ ffmpeg_filter_def: The FFmpeg filter definition to create a node from
30
+ *inputs: The input streams to connect to the filter
31
+ **kwargs: Filter-specific parameters as keyword arguments
32
+
33
+ Returns:
34
+ A FilterNode configured according to the filter definition
35
+
36
+ Note:
37
+ This function is primarily used internally by the filter generation system
38
+ to create filter nodes from the FFmpeg filter definitions.
39
+ """
13
40
  for k, v in kwargs.items():
14
41
  if isinstance(v, Auto):
15
42
  kwargs[k] = eval(
@@ -11,6 +11,17 @@ if TYPE_CHECKING:
11
11
 
12
12
 
13
13
  class GlobalArgs(ABC):
14
+ """
15
+ Abstract base class for providing global FFmpeg command-line arguments.
16
+
17
+ This class defines an interface for setting global options that apply to the entire
18
+ FFmpeg command. These options control the general behavior of FFmpeg such as
19
+ logging levels, overwrite behavior, thread usage, and hardware acceleration.
20
+
21
+ Implementers must define the _global_node abstract method to apply these arguments
22
+ to actual FFmpeg command execution.
23
+ """
24
+
14
25
  @abstractmethod
15
26
  def _global_node(self, *streams: OutputStream, **kwargs: Any) -> GlobalNode: ...
16
27