typed-ffmpeg-compatible 2.6.3__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 (35) hide show
  1. typed_ffmpeg/__init__.py +24 -0
  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/streams/channel_layout.py +13 -0
  22. typed_ffmpeg/utils/escaping.py +47 -7
  23. typed_ffmpeg/utils/forzendict.py +108 -0
  24. typed_ffmpeg/utils/lazy_eval/operator.py +43 -1
  25. typed_ffmpeg/utils/lazy_eval/schema.py +122 -6
  26. typed_ffmpeg/utils/run.py +44 -7
  27. typed_ffmpeg/utils/snapshot.py +36 -1
  28. typed_ffmpeg/utils/typing.py +29 -4
  29. typed_ffmpeg/utils/view.py +46 -4
  30. {typed_ffmpeg_compatible-2.6.3.dist-info → typed_ffmpeg_compatible-2.6.4.dist-info}/METADATA +1 -1
  31. typed_ffmpeg_compatible-2.6.4.dist-info/RECORD +48 -0
  32. typed_ffmpeg_compatible-2.6.3.dist-info/RECORD +0 -47
  33. {typed_ffmpeg_compatible-2.6.3.dist-info → typed_ffmpeg_compatible-2.6.4.dist-info}/LICENSE +0 -0
  34. {typed_ffmpeg_compatible-2.6.3.dist-info → typed_ffmpeg_compatible-2.6.4.dist-info}/WHEEL +0 -0
  35. {typed_ffmpeg_compatible-2.6.3.dist-info → typed_ffmpeg_compatible-2.6.4.dist-info}/entry_points.txt +0 -0
typed_ffmpeg/dag/nodes.py CHANGED
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any
9
9
  from ..exceptions import FFMpegTypeError, FFMpegValueError
10
10
  from ..schema import Default, StreamType
11
11
  from ..utils.escaping import escape
12
+ from ..utils.forzendict import FrozenDict
12
13
  from ..utils.lazy_eval.schema import LazyValue
13
14
  from ..utils.typing import override
14
15
  from .global_runnable.runnable import GlobalRunable
@@ -28,42 +29,61 @@ logger = logging.getLogger(__name__)
28
29
  @dataclass(frozen=True, kw_only=True)
29
30
  class FilterNode(Node):
30
31
  """
31
- A filter node that can be used to apply filters to streams
32
+ A node that represents an FFmpeg filter operation in the filter graph.
33
+
34
+ FilterNode represents a single filter operation in the FFmpeg filter graph,
35
+ such as scaling, cropping, or audio mixing. It connects input streams to
36
+ output streams and defines the parameters for the filter operation.
32
37
  """
33
38
 
34
39
  name: str
35
40
  """
36
- The name of the filter
41
+ The name of the filter as used in FFmpeg (e.g., 'scale', 'overlay', 'amix')
37
42
  """
38
43
 
39
44
  inputs: tuple[FilterableStream, ...] = ()
40
45
  """
41
- The input streams
46
+ The input streams that this filter processes
42
47
  """
43
48
 
44
49
  input_typings: tuple[StreamType, ...] = ()
45
50
  """
46
- The input typings
51
+ The expected types (audio/video) for each input stream
47
52
  """
48
53
 
49
54
  output_typings: tuple[StreamType, ...] = ()
50
55
  """
51
- The output typings
56
+ The types (audio/video) of each output stream this filter produces
52
57
  """
53
58
 
54
59
  @override
55
60
  def repr(self) -> str:
61
+ """
62
+ Get a string representation of this filter node.
63
+
64
+ Returns:
65
+ The name of the filter
66
+ """
56
67
  return self.name
57
68
 
58
69
  def video(self, index: int) -> VideoStream:
59
70
  """
60
- Return the video stream at the specified index
71
+ Get a video output stream from this filter node.
72
+
73
+ This method retrieves a specific video output stream from the filter,
74
+ identified by its index among all video outputs. For example, if a filter
75
+ produces multiple video outputs (like 'split'), this method allows
76
+ accessing each one individually.
61
77
 
62
78
  Args:
63
- index: the index of the video stream
79
+ index: The index of the video stream to retrieve (0-based)
80
+ among all video outputs of this filter
64
81
 
65
82
  Returns:
66
- the video stream at the specified index
83
+ A VideoStream object representing the specified output
84
+
85
+ Raises:
86
+ FFMpegValueError: If the specified index is out of range
67
87
  """
68
88
  from ..streams.video import VideoStream
69
89
 
@@ -78,13 +98,22 @@ class FilterNode(Node):
78
98
 
79
99
  def audio(self, index: int) -> AudioStream:
80
100
  """
81
- Return the audio stream at the specified index
101
+ Get an audio output stream from this filter node.
102
+
103
+ This method retrieves a specific audio output stream from the filter,
104
+ identified by its index among all audio outputs. For example, if a filter
105
+ produces multiple audio outputs (like 'asplit'), this method allows
106
+ accessing each one individually.
82
107
 
83
108
  Args:
84
- index: the index of the audio stream
109
+ index: The index of the audio stream to retrieve (0-based)
110
+ among all audio outputs of this filter
85
111
 
86
112
  Returns:
87
- the audio stream at the specified index
113
+ An AudioStream object representing the specified output
114
+
115
+ Raises:
116
+ FFMpegValueError: If the specified index is out of range
88
117
  """
89
118
  from ..streams.audio import AudioStream
90
119
 
@@ -99,6 +128,18 @@ class FilterNode(Node):
99
128
  return AudioStream(node=self, index=audio_outputs[index])
100
129
 
101
130
  def __post_init__(self) -> None:
131
+ """
132
+ Validate the filter node after initialization.
133
+
134
+ This method performs type checking to ensure that the input streams
135
+ match the expected types (audio/video) specified in input_typings.
136
+ It also verifies that the number of inputs matches the number of
137
+ input type specifications.
138
+
139
+ Raises:
140
+ FFMpegValueError: If the number of inputs doesn't match input_typings
141
+ FFMpegTypeError: If an input stream doesn't match its expected type
142
+ """
102
143
  from ..streams.audio import AudioStream
103
144
  from ..streams.video import VideoStream
104
145
 
@@ -128,6 +169,26 @@ class FilterNode(Node):
128
169
 
129
170
  @override
130
171
  def get_args(self, context: DAGContext = None) -> list[str]:
172
+ """
173
+ Generate the FFmpeg filter string for this filter node.
174
+
175
+ This method creates the filter string that will be used in the
176
+ filter_complex argument of the FFmpeg command. The format follows
177
+ FFmpeg's syntax where input labels are followed by the filter name
178
+ and parameters, and then output labels.
179
+
180
+ Args:
181
+ context: Optional DAG context for resolving stream labels.
182
+ If not provided, a new context will be built.
183
+
184
+ Returns:
185
+ A list of strings that, when joined, form the filter string
186
+ for this node in the filter_complex argument
187
+
188
+ Example:
189
+ For a scale filter with width=1280 and height=720, this might return:
190
+ ['[0:v]', 'scale=', 'width=1280:height=720', '[s0]']
191
+ """
131
192
  from .context import DAGContext
132
193
 
133
194
  if not context:
@@ -143,7 +204,7 @@ class FilterNode(Node):
143
204
  outgoing_labels += f"[{output.label(context)}]"
144
205
 
145
206
  commands = []
146
- for key, value in self.kwargs:
207
+ for key, value in self.kwargs.items():
147
208
  assert not isinstance(value, LazyValue), (
148
209
  f"LazyValue should have been evaluated: {key}={value}"
149
210
  )
@@ -168,7 +229,14 @@ class FilterNode(Node):
168
229
  @dataclass(frozen=True, kw_only=True)
169
230
  class FilterableStream(Stream, OutputArgs):
170
231
  """
171
- A stream that can be used as input to a filter
232
+ A stream that can be used as input to an FFmpeg filter.
233
+
234
+ FilterableStream represents a media stream (audio or video) that can be
235
+ processed by FFmpeg filters. It provides methods for applying various
236
+ filters to the stream and for outputting the stream to a file.
237
+
238
+ This class serves as a base for specific stream types like VideoStream
239
+ and AudioStream, providing common functionality for filter operations.
172
240
  """
173
241
 
174
242
  node: FilterNode | InputNode
@@ -178,20 +246,33 @@ class FilterableStream(Stream, OutputArgs):
178
246
  self, *streams: FilterableStream, filename: str | Path, **kwargs: Any
179
247
  ) -> OutputNode:
180
248
  """
181
- Output the streams to a file URL
249
+ Create an output node that writes this stream (and optionally others) to a file.
250
+
251
+ This method creates an OutputNode that represents writing one or more
252
+ streams to a file. The resulting node can be used to generate the
253
+ FFmpeg command-line arguments for the output file.
182
254
 
183
255
  Args:
184
- *streams: the other streams to output
185
- filename: the filename to output to
186
- **kwargs: the arguments for the output
256
+ *streams: Additional streams to include in the same output file
257
+ filename: Path to the output file
258
+ **kwargs: FFmpeg output options (e.g., codec, bitrate, format)
259
+ as keyword arguments
187
260
 
188
261
  Returns:
189
- the output stream
262
+ An OutputNode representing the file output operation
263
+
264
+ Example:
265
+ ```python
266
+ # Output a video stream to an MP4 file with H.264 codec
267
+ output_node = video_stream._output_node(
268
+ filename="output.mp4", c="libx264", crf=23
269
+ )
270
+ ```
190
271
  """
191
272
  return OutputNode(
192
273
  inputs=(self, *streams),
193
274
  filename=str(filename),
194
- kwargs=tuple(kwargs.items()),
275
+ kwargs=FrozenDict(kwargs),
195
276
  )
196
277
 
197
278
  def vfilter(
@@ -202,16 +283,28 @@ class FilterableStream(Stream, OutputArgs):
202
283
  **kwargs: Any,
203
284
  ) -> VideoStream:
204
285
  """
205
- Apply a custom video filter which has only one output to this stream
286
+ Apply a custom video filter to this stream.
287
+
288
+ This method applies a custom FFmpeg video filter to this stream and
289
+ returns the resulting video stream. It's a convenience wrapper around
290
+ filter_multi_output that handles the case of filters with a single
291
+ video output.
206
292
 
207
293
  Args:
208
- *streams (FilterableStream): the streams to apply the filter to
209
- name: the name of the filter
210
- input_typings: the input typings
211
- **kwargs: the arguments for the filter
294
+ *streams: Additional input streams for the filter
295
+ name: The name of the FFmpeg filter to apply
296
+ input_typings: The expected types of the input streams
297
+ (defaults to all video)
298
+ **kwargs: Filter-specific parameters as keyword arguments
212
299
 
213
300
  Returns:
214
- the output stream
301
+ A VideoStream representing the filter's output
302
+
303
+ Example:
304
+ ```python
305
+ # Apply a blur filter to a video stream
306
+ blurred = stream.vfilter(name="boxblur", luma_radius=2)
307
+ ```
215
308
  """
216
309
  return self.filter_multi_output(
217
310
  *streams,
@@ -229,16 +322,28 @@ class FilterableStream(Stream, OutputArgs):
229
322
  **kwargs: Any,
230
323
  ) -> AudioStream:
231
324
  """
232
- Apply a custom audio filter which has only one output to this stream
325
+ Apply a custom audio filter to this stream.
326
+
327
+ This method applies a custom FFmpeg audio filter to this stream and
328
+ returns the resulting audio stream. It's a convenience wrapper around
329
+ filter_multi_output that handles the case of filters with a single
330
+ audio output.
233
331
 
234
332
  Args:
235
- *streams (FilterableStream): the streams to apply the filter to
236
- name: the name of the filter
237
- input_typings: the input typings
238
- **kwargs: the arguments for the filter
333
+ *streams: Additional input streams for the filter
334
+ name: The name of the FFmpeg filter to apply
335
+ input_typings: The expected types of the input streams
336
+ (defaults to all audio)
337
+ **kwargs: Filter-specific parameters as keyword arguments
239
338
 
240
339
  Returns:
241
- the output stream
340
+ An AudioStream representing the filter's output
341
+
342
+ Example:
343
+ ```python
344
+ # Apply a volume filter to an audio stream
345
+ louder = stream.afilter(name="volume", volume=2.0)
346
+ ```
242
347
  """
243
348
  return self.filter_multi_output(
244
349
  *streams,
@@ -257,21 +362,36 @@ class FilterableStream(Stream, OutputArgs):
257
362
  **kwargs: Any,
258
363
  ) -> FilterNode:
259
364
  """
260
- Apply a custom filter which has multiple outputs to this stream
365
+ Apply a custom filter with multiple outputs to this stream.
366
+
367
+ This method creates a FilterNode that applies a custom FFmpeg filter
368
+ to this stream (and optionally additional streams). Unlike vfilter and
369
+ afilter which return a single stream, this method returns the FilterNode
370
+ itself, allowing access to multiple output streams.
261
371
 
262
372
  Args:
263
- *streams (FilterableStream): the streams to apply the filter to
264
- name: the name of the filter
265
- input_typings: the input typings
266
- output_typings: the output typings
267
- **kwargs: the arguments for the filter
373
+ *streams: Additional input streams for the filter
374
+ name: The name of the FFmpeg filter to apply
375
+ input_typings: The expected types of the input streams
376
+ output_typings: The types of output streams this filter produces
377
+ **kwargs: Filter-specific parameters as keyword arguments
268
378
 
269
379
  Returns:
270
- the FilterNode
380
+ A FilterNode that can be used to access the filter's outputs
381
+
382
+ Example:
383
+ ```python
384
+ # Split a video into two identical streams
385
+ split_node = stream.filter_multi_output(
386
+ name="split", output_typings=(StreamType.video, StreamType.video)
387
+ )
388
+ stream1 = split_node.video(0)
389
+ stream2 = split_node.video(1)
390
+ ```
271
391
  """
272
392
  return FilterNode(
273
393
  name=name,
274
- kwargs=tuple(kwargs.items()),
394
+ kwargs=FrozenDict(kwargs),
275
395
  inputs=(self, *streams),
276
396
  input_typings=input_typings,
277
397
  output_typings=output_typings,
@@ -279,13 +399,30 @@ class FilterableStream(Stream, OutputArgs):
279
399
 
280
400
  def label(self, context: DAGContext = None) -> str:
281
401
  """
282
- Return the label for this stream
402
+ Generate the FFmpeg label for this stream in filter graphs.
403
+
404
+ This method creates the label string used to identify this stream in
405
+ FFmpeg filter graphs. The format of the label depends on the stream's
406
+ source (input file or filter) and type (video or audio).
407
+
408
+ For input streams, labels follow FFmpeg's stream specifier syntax:
409
+ - Video streams: "0:v" (first input, video stream)
410
+ - Audio streams: "0:a" (first input, audio stream)
411
+ - AV streams: "0" (first input, all streams)
412
+
413
+ For filter outputs, labels use the filter's label:
414
+ - Single output filters: "filterlabel"
415
+ - Multi-output filters: "filterlabel#index"
283
416
 
284
417
  Args:
285
- context: the DAG context
418
+ context: Optional DAG context for resolving node labels.
419
+ If not provided, a new context will be built.
286
420
 
287
421
  Returns:
288
- the label for this stream
422
+ A string label for this stream in FFmpeg filter syntax
423
+
424
+ Raises:
425
+ FFMpegValueError: If the stream has an unknown type or node type
289
426
  """
290
427
  from ..streams.audio import AudioStream
291
428
  from ..streams.av import AVStream
@@ -324,27 +461,52 @@ class FilterableStream(Stream, OutputArgs):
324
461
  @dataclass(frozen=True, kw_only=True)
325
462
  class InputNode(Node):
326
463
  """
327
- A node that can be used to read from files
464
+ A node that represents an input file in the FFmpeg filter graph.
465
+
466
+ InputNode represents a media file that serves as input to the FFmpeg
467
+ command. It provides access to the video and audio streams contained
468
+ in the file, which can then be processed by filters.
328
469
  """
329
470
 
330
471
  filename: str
331
472
  """
332
- The filename to read from
473
+ The path to the input media file
333
474
  """
334
475
 
335
476
  inputs: tuple[()] = ()
477
+ """
478
+ Input nodes have no inputs themselves (they are source nodes)
479
+ """
336
480
 
337
481
  @override
338
482
  def repr(self) -> str:
483
+ """
484
+ Get a string representation of this input node.
485
+
486
+ Returns:
487
+ The basename of the input file
488
+ """
339
489
  return os.path.basename(self.filename)
340
490
 
341
491
  @property
342
492
  def video(self) -> VideoStream:
343
493
  """
344
- Return the video stream of this node
494
+ Get the video stream from this input file.
495
+
496
+ This property provides access to the video component of the input file.
497
+ The resulting VideoStream can be used as input to video filters.
345
498
 
346
499
  Returns:
347
- the video stream
500
+ A VideoStream representing the video content of this input file
501
+
502
+ Example:
503
+ ```python
504
+ # Access the video stream from an input file
505
+ input_node = ffmpeg.input("input.mp4")
506
+ video = input_node.video
507
+ # Apply a filter to the video stream
508
+ scaled = video.scale(width=1280, height=720)
509
+ ```
348
510
  """
349
511
  from ..streams.video import VideoStream
350
512
 
@@ -353,10 +515,22 @@ class InputNode(Node):
353
515
  @property
354
516
  def audio(self) -> AudioStream:
355
517
  """
356
- Return the audio stream of this node
518
+ Get the audio stream from this input file.
519
+
520
+ This property provides access to the audio component of the input file.
521
+ The resulting AudioStream can be used as input to audio filters.
357
522
 
358
523
  Returns:
359
- the audio stream
524
+ An AudioStream representing the audio content of this input file
525
+
526
+ Example:
527
+ ```python
528
+ # Access the audio stream from an input file
529
+ input_node = ffmpeg.input("input.mp4")
530
+ audio = input_node.audio
531
+ # Apply a filter to the audio stream
532
+ volume_adjusted = audio.volume(volume=2.0)
533
+ ```
360
534
  """
361
535
  from ..streams.audio import AudioStream
362
536
 
@@ -364,10 +538,23 @@ class InputNode(Node):
364
538
 
365
539
  def stream(self) -> AVStream:
366
540
  """
367
- Return the output stream of this node
541
+ Get a combined audio-video stream from this input file.
542
+
543
+ This method provides access to both the audio and video components
544
+ of the input file as a single AVStream. This is useful when you need
545
+ to work with both components together.
368
546
 
369
547
  Returns:
370
- the output stream
548
+ An AVStream representing both audio and video content
549
+
550
+ Example:
551
+ ```python
552
+ # Access both audio and video from an input file
553
+ input_node = ffmpeg.input("input.mp4")
554
+ av_stream = input_node.stream()
555
+ # Output both audio and video to a new file
556
+ output = av_stream.output("output.mp4")
557
+ ```
371
558
  """
372
559
  from ..streams.av import AVStream
373
560
 
@@ -375,8 +562,24 @@ class InputNode(Node):
375
562
 
376
563
  @override
377
564
  def get_args(self, context: DAGContext = None) -> list[str]:
565
+ """
566
+ Generate the FFmpeg command-line arguments for this input file.
567
+
568
+ This method creates the command-line arguments needed to specify
569
+ this input file to FFmpeg, including any input-specific options.
570
+
571
+ Args:
572
+ context: Optional DAG context (not used for input nodes)
573
+
574
+ Returns:
575
+ A list of strings representing FFmpeg command-line arguments
576
+
577
+ Example:
578
+ For an input file "input.mp4" with options like seeking to 10 seconds:
579
+ ['-ss', '10', '-i', 'input.mp4']
580
+ """
378
581
  commands = []
379
- for key, value in self.kwargs:
582
+ for key, value in self.kwargs.items():
380
583
  if isinstance(value, bool):
381
584
  if value is True:
382
585
  commands += [f"-{key}"]
@@ -390,27 +593,75 @@ class InputNode(Node):
390
593
 
391
594
  @dataclass(frozen=True, kw_only=True)
392
595
  class OutputNode(Node):
596
+ """
597
+ A node that represents an output file in the FFmpeg filter graph.
598
+
599
+ OutputNode represents a destination file where processed media streams
600
+ will be written. It connects one or more streams (video, audio, or both)
601
+ to an output file and specifies output options like codecs and formats.
602
+ """
603
+
393
604
  filename: str
394
605
  """
395
- The filename to output to
606
+ The path to the output media file
396
607
  """
608
+
397
609
  inputs: tuple[FilterableStream, ...]
610
+ """
611
+ The streams to be written to this output file
612
+ """
398
613
 
399
614
  @override
400
615
  def repr(self) -> str:
616
+ """
617
+ Get a string representation of this output node.
618
+
619
+ Returns:
620
+ The basename of the output file
621
+ """
401
622
  return os.path.basename(self.filename)
402
623
 
403
624
  def stream(self) -> OutputStream:
404
625
  """
405
- Return the output stream of this node
626
+ Get an output stream representing this output file.
627
+
628
+ This method creates an OutputStream object that wraps this OutputNode,
629
+ allowing it to be used in operations that require an output stream,
630
+ such as adding global options.
406
631
 
407
632
  Returns:
408
- the output stream
633
+ An OutputStream representing this output file
634
+
635
+ Example:
636
+ ```python
637
+ # Create an output file and add global options
638
+ output_node = video.output("output.mp4")
639
+ output_stream = output_node.stream()
640
+ with_global_opts = output_stream.global_args(y=True)
641
+ ```
409
642
  """
410
643
  return OutputStream(node=self)
411
644
 
412
645
  @override
413
646
  def get_args(self, context: DAGContext = None) -> list[str]:
647
+ """
648
+ Generate the FFmpeg command-line arguments for this output file.
649
+
650
+ This method creates the command-line arguments needed to specify
651
+ this output file to FFmpeg, including stream mapping and output-specific
652
+ options like codecs and formats.
653
+
654
+ Args:
655
+ context: Optional DAG context for resolving stream labels.
656
+ If not provided, a new context will be built.
657
+
658
+ Returns:
659
+ A list of strings representing FFmpeg command-line arguments
660
+
661
+ Example:
662
+ For an output file "output.mp4" with H.264 video codec:
663
+ ['-map', '[v0]', '-c:v', 'libx264', 'output.mp4']
664
+ """
414
665
  # !handle mapping
415
666
  commands = []
416
667
 
@@ -424,7 +675,7 @@ class OutputNode(Node):
424
675
  else:
425
676
  commands += ["-map", f"[{input.label(context)}]"]
426
677
 
427
- for key, value in self.kwargs:
678
+ for key, value in self.kwargs.items():
428
679
  if isinstance(value, bool):
429
680
  if value is True:
430
681
  commands += [f"-{key}"]
@@ -438,47 +689,109 @@ class OutputNode(Node):
438
689
 
439
690
  @dataclass(frozen=True, kw_only=True)
440
691
  class OutputStream(Stream, GlobalRunable):
692
+ """
693
+ A stream representing an output file with additional capabilities.
694
+
695
+ OutputStream wraps an OutputNode and provides additional functionality,
696
+ particularly the ability to add global FFmpeg options. This class serves
697
+ as an intermediate step between creating an output file and executing
698
+ the FFmpeg command.
699
+ """
700
+
441
701
  node: OutputNode
702
+ """The output node this stream represents"""
442
703
 
443
704
  @override
444
705
  def _global_node(self, *streams: OutputStream, **kwargs: Any) -> GlobalNode:
445
706
  """
446
- Add extra global command-line argument
707
+ Create a GlobalNode with additional global FFmpeg options.
708
+
709
+ This method creates a GlobalNode that applies global options to the
710
+ FFmpeg command. These options affect the entire command rather than
711
+ specific inputs or outputs.
447
712
 
448
713
  Args:
449
- **kwargs: the extra arguments
714
+ *streams: Additional output streams to include in the same command
715
+ **kwargs: Global FFmpeg options as keyword arguments
450
716
 
451
717
  Returns:
452
- the output stream
718
+ A GlobalNode with the specified options
719
+
720
+ Example:
721
+ ```python
722
+ # Add global options to an output stream
723
+ global_node = output_stream._global_node(y=True, loglevel="quiet")
724
+ ```
453
725
  """
454
- return GlobalNode(inputs=(self, *streams), kwargs=tuple(kwargs.items()))
726
+ return GlobalNode(inputs=(self, *streams), kwargs=FrozenDict(kwargs))
455
727
 
456
728
 
457
729
  @dataclass(frozen=True, kw_only=True)
458
730
  class GlobalNode(Node):
459
731
  """
460
- A node that can be used to set global options
732
+ A node that represents global FFmpeg options.
733
+
734
+ GlobalNode represents options that apply to the entire FFmpeg command
735
+ rather than to specific inputs or outputs. These include options like
736
+ overwrite (-y), log level, and other general FFmpeg settings.
461
737
  """
462
738
 
463
739
  inputs: tuple[OutputStream, ...]
740
+ """The output streams this node applies to"""
464
741
 
465
742
  @override
466
743
  def repr(self) -> str:
744
+ """
745
+ Get a string representation of this global node.
746
+
747
+ Returns:
748
+ A space-separated string of the global options
749
+ """
467
750
  return " ".join(self.get_args())
468
751
 
469
752
  def stream(self) -> GlobalStream:
470
753
  """
471
- Return the output stream of this node
754
+ Get a global stream representing this global node.
755
+
756
+ This method creates a GlobalStream object that wraps this GlobalNode,
757
+ allowing it to be used in operations that require a global stream,
758
+ such as adding more global options or executing the command.
472
759
 
473
760
  Returns:
474
- the output stream
761
+ A GlobalStream representing this global node
762
+
763
+ Example:
764
+ ```python
765
+ # Create a global node and get its stream
766
+ global_node = ffmpeg.global_args(y=True)
767
+ global_stream = global_node.stream()
768
+ # Execute the command
769
+ global_stream.run()
770
+ ```
475
771
  """
476
772
  return GlobalStream(node=self)
477
773
 
478
774
  @override
479
775
  def get_args(self, context: DAGContext = None) -> list[str]:
776
+ """
777
+ Generate the FFmpeg command-line arguments for these global options.
778
+
779
+ This method creates the command-line arguments needed to specify
780
+ global options to FFmpeg, such as -y for overwrite or -loglevel for
781
+ controlling log output.
782
+
783
+ Args:
784
+ context: Optional DAG context (not used for global options)
785
+
786
+ Returns:
787
+ A list of strings representing FFmpeg command-line arguments
788
+
789
+ Example:
790
+ For global options like overwrite and quiet logging:
791
+ ['-y', '-loglevel', 'quiet']
792
+ """
480
793
  commands = []
481
- for key, value in self.kwargs:
794
+ for key, value in self.kwargs.items():
482
795
  if isinstance(value, bool):
483
796
  if value is True:
484
797
  commands += [f"-{key}"]
@@ -491,21 +804,43 @@ class GlobalNode(Node):
491
804
 
492
805
  @dataclass(frozen=True, kw_only=True)
493
806
  class GlobalStream(Stream, GlobalRunable):
807
+ """
808
+ A stream representing a set of global FFmpeg options.
809
+
810
+ GlobalStream wraps a GlobalNode and provides additional functionality,
811
+ particularly the ability to add more global options or execute the
812
+ FFmpeg command. This class is typically the final step in the FFmpeg
813
+ command construction process.
814
+ """
815
+
494
816
  node: GlobalNode
817
+ """The global node this stream represents"""
495
818
 
496
819
  @override
497
820
  def _global_node(self, *streams: OutputStream, **kwargs: Any) -> GlobalNode:
498
821
  """
499
- Add extra global command-line argument
822
+ Add additional global FFmpeg options to this stream.
823
+
824
+ This method creates a new GlobalNode that combines the existing global
825
+ options with new ones. It also allows adding more output streams to
826
+ the command.
500
827
 
501
828
  Args:
502
- **kwargs: the extra arguments
829
+ *streams: Additional output streams to include in the command
830
+ **kwargs: Additional global FFmpeg options as keyword arguments
503
831
 
504
832
  Returns:
505
- the output stream
833
+ A new GlobalNode with the combined options and streams
834
+
835
+ Example:
836
+ ```python
837
+ # Add more global options to an existing global stream
838
+ global_stream = ffmpeg.output("output.mp4").global_args(y=True)
839
+ enhanced = global_stream._global_node(loglevel="quiet")
840
+ ```
506
841
  """
507
842
  inputs = (*self.node.inputs, *streams)
508
843
  kwargs = dict(self.node.kwargs) | kwargs
509
844
 
510
- new_node = replace(self.node, inputs=inputs, kwargs=tuple(kwargs.items()))
845
+ new_node = replace(self.node, inputs=inputs, kwargs=FrozenDict(kwargs))
511
846
  return new_node