typed-ffmpeg-compatible 3.5.2__py3-none-any.whl → 3.6__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/__init__.py +4 -1
- typed_ffmpeg/_version.py +2 -2
- typed_ffmpeg/base.py +4 -1
- typed_ffmpeg/codecs/__init__.py +2 -0
- typed_ffmpeg/codecs/decoders.py +1852 -1853
- typed_ffmpeg/codecs/encoders.py +2001 -1782
- typed_ffmpeg/codecs/schema.py +6 -12
- typed_ffmpeg/common/__init__.py +1 -0
- typed_ffmpeg/common/cache.py +9 -6
- typed_ffmpeg/common/schema.py +11 -0
- typed_ffmpeg/common/serialize.py +13 -7
- typed_ffmpeg/compile/__init__.py +1 -0
- typed_ffmpeg/compile/compile_cli.py +23 -4
- typed_ffmpeg/compile/compile_json.py +4 -0
- typed_ffmpeg/compile/compile_python.py +15 -0
- typed_ffmpeg/compile/context.py +15 -4
- typed_ffmpeg/compile/validate.py +4 -3
- typed_ffmpeg/dag/factory.py +2 -0
- typed_ffmpeg/dag/global_runnable/__init__.py +1 -0
- typed_ffmpeg/dag/global_runnable/global_args.py +2 -2
- typed_ffmpeg/dag/global_runnable/runnable.py +6 -2
- typed_ffmpeg/dag/io/__init__.py +1 -0
- typed_ffmpeg/dag/io/_input.py +20 -5
- typed_ffmpeg/dag/io/_output.py +24 -9
- typed_ffmpeg/dag/io/output_args.py +21 -7
- typed_ffmpeg/dag/nodes.py +20 -0
- typed_ffmpeg/dag/schema.py +19 -6
- typed_ffmpeg/dag/utils.py +2 -2
- typed_ffmpeg/exceptions.py +2 -1
- typed_ffmpeg/expressions.py +884 -0
- typed_ffmpeg/ffprobe/__init__.py +1 -0
- typed_ffmpeg/ffprobe/parse.py +7 -1
- typed_ffmpeg/ffprobe/probe.py +3 -1
- typed_ffmpeg/ffprobe/schema.py +83 -1
- typed_ffmpeg/ffprobe/xml2json.py +8 -2
- typed_ffmpeg/filters.py +540 -631
- typed_ffmpeg/formats/__init__.py +2 -0
- typed_ffmpeg/formats/demuxers.py +1869 -1921
- typed_ffmpeg/formats/muxers.py +1382 -1107
- typed_ffmpeg/formats/schema.py +6 -12
- typed_ffmpeg/info.py +8 -0
- typed_ffmpeg/options/__init__.py +15 -0
- typed_ffmpeg/options/codec.py +711 -0
- typed_ffmpeg/options/format.py +196 -0
- typed_ffmpeg/options/framesync.py +43 -0
- typed_ffmpeg/options/timeline.py +22 -0
- typed_ffmpeg/schema.py +15 -0
- typed_ffmpeg/sources.py +392 -381
- typed_ffmpeg/streams/__init__.py +2 -0
- typed_ffmpeg/streams/audio.py +1071 -882
- typed_ffmpeg/streams/av.py +9 -3
- typed_ffmpeg/streams/subtitle.py +3 -3
- typed_ffmpeg/streams/video.py +1873 -1725
- typed_ffmpeg/types.py +3 -2
- typed_ffmpeg/utils/__init__.py +1 -0
- typed_ffmpeg/utils/escaping.py +8 -4
- typed_ffmpeg/utils/frozendict.py +31 -1
- typed_ffmpeg/utils/lazy_eval/__init__.py +1 -0
- typed_ffmpeg/utils/lazy_eval/operator.py +75 -27
- typed_ffmpeg/utils/lazy_eval/schema.py +176 -4
- typed_ffmpeg/utils/run.py +2 -0
- typed_ffmpeg/utils/snapshot.py +1 -0
- typed_ffmpeg/utils/typing.py +2 -1
- typed_ffmpeg/utils/view.py +2 -1
- {typed_ffmpeg_compatible-3.5.2.dist-info → typed_ffmpeg_compatible-3.6.dist-info}/METADATA +1 -1
- typed_ffmpeg_compatible-3.6.dist-info/RECORD +73 -0
- typed_ffmpeg_compatible-3.5.2.dist-info/RECORD +0 -67
- {typed_ffmpeg_compatible-3.5.2.dist-info → typed_ffmpeg_compatible-3.6.dist-info}/WHEEL +0 -0
- {typed_ffmpeg_compatible-3.5.2.dist-info → typed_ffmpeg_compatible-3.6.dist-info}/entry_points.txt +0 -0
- {typed_ffmpeg_compatible-3.5.2.dist-info → typed_ffmpeg_compatible-3.6.dist-info}/licenses/LICENSE +0 -0
- {typed_ffmpeg_compatible-3.5.2.dist-info → typed_ffmpeg_compatible-3.6.dist-info}/top_level.txt +0 -0
@@ -56,6 +56,7 @@ class GlobalRunable(GlobalArgs):
|
|
56
56
|
merged = output1.merge_outputs(output2)
|
57
57
|
merged.run() # Creates both output files with one FFmpeg command
|
58
58
|
```
|
59
|
+
|
59
60
|
"""
|
60
61
|
return self._global_node(*streams).stream()
|
61
62
|
|
@@ -75,6 +76,7 @@ class GlobalRunable(GlobalArgs):
|
|
75
76
|
# Overwrite output file if it already exists
|
76
77
|
ffmpeg.input("input.mp4").output("output.mp4").overwrite_output().run()
|
77
78
|
```
|
79
|
+
|
78
80
|
"""
|
79
81
|
return self._global_node(y=True).stream()
|
80
82
|
|
@@ -113,6 +115,7 @@ class GlobalRunable(GlobalArgs):
|
|
113
115
|
args = ffmpeg.input("input.mp4").output("output.mp4").compile()
|
114
116
|
# Result: ['ffmpeg', '-i', 'input.mp4', 'output.mp4']
|
115
117
|
```
|
118
|
+
|
116
119
|
"""
|
117
120
|
from ...compile.compile_cli import compile_as_list
|
118
121
|
|
@@ -171,6 +174,7 @@ class GlobalRunable(GlobalArgs):
|
|
171
174
|
cmd_str = ffmpeg.input("input.mp4").output("output.mp4").compile_line()
|
172
175
|
# Result: 'ffmpeg -i input.mp4 output.mp4'
|
173
176
|
```
|
177
|
+
|
174
178
|
"""
|
175
179
|
return command_line(
|
176
180
|
self.compile(
|
@@ -223,8 +227,8 @@ class GlobalRunable(GlobalArgs):
|
|
223
227
|
# Do something while FFmpeg is running
|
224
228
|
process.wait() # Wait for completion
|
225
229
|
```
|
226
|
-
"""
|
227
230
|
|
231
|
+
"""
|
228
232
|
args = self.compile(
|
229
233
|
cmd,
|
230
234
|
overwrite_output=overwrite_output,
|
@@ -294,8 +298,8 @@ class GlobalRunable(GlobalArgs):
|
|
294
298
|
)
|
295
299
|
print(stderr.decode()) # Print FFmpeg's progress information
|
296
300
|
```
|
297
|
-
"""
|
298
301
|
|
302
|
+
"""
|
299
303
|
process = self.run_async(
|
300
304
|
cmd,
|
301
305
|
pipe_stdin=input is not None,
|
typed_ffmpeg/dag/io/__init__.py
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
"""Input/output utilities for FFmpeg DAG operations."""
|
typed_ffmpeg/dag/io/_input.py
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
# NOTE: this file is auto-generated, do not modify
|
2
|
-
|
2
|
+
"""Input node."""
|
3
3
|
|
4
4
|
from pathlib import Path
|
5
5
|
from typing import Any
|
6
6
|
|
7
7
|
from ...codecs.schema import FFMpegDecoderOption
|
8
8
|
from ...formats.schema import FFMpegDemuxerOption
|
9
|
+
from ...options.codec import (
|
10
|
+
FFMpegAVCodecContextDecoderOption,
|
11
|
+
)
|
12
|
+
from ...options.format import (
|
13
|
+
FFMpegAVFormatContextDecoderOption,
|
14
|
+
)
|
9
15
|
from ...streams.av import AVStream
|
10
16
|
from ...types import (
|
11
17
|
Boolean,
|
@@ -16,7 +22,9 @@ from ...types import (
|
|
16
22
|
Time,
|
17
23
|
)
|
18
24
|
from ...utils.frozendict import merge
|
19
|
-
from ..nodes import
|
25
|
+
from ..nodes import (
|
26
|
+
InputNode,
|
27
|
+
)
|
20
28
|
|
21
29
|
|
22
30
|
def input(
|
@@ -76,10 +84,12 @@ def input(
|
|
76
84
|
top: Int = None,
|
77
85
|
decoder_options: FFMpegDecoderOption | None = None,
|
78
86
|
demuxer_options: FFMpegDemuxerOption | None = None,
|
87
|
+
format_options: FFMpegAVFormatContextDecoderOption | None = None,
|
88
|
+
codec_options: FFMpegAVCodecContextDecoderOption | None = None,
|
79
89
|
extra_options: dict[str, Any] | None = None,
|
80
90
|
) -> AVStream:
|
81
91
|
"""
|
82
|
-
Input file URL (ffmpeg ``-i`` option)
|
92
|
+
Input file URL (ffmpeg ``-i`` option).
|
83
93
|
|
84
94
|
Args:
|
85
95
|
filename: Input file URL
|
@@ -137,6 +147,8 @@ def input(
|
|
137
147
|
top: deprecated, use the setfield video filter
|
138
148
|
decoder_options: ffmpeg's decoder options
|
139
149
|
demuxer_options: ffmpeg's demuxer options
|
150
|
+
format_options: ffmpeg's AVFormatContext options
|
151
|
+
codec_options: ffmpeg's AVCodecContext options
|
140
152
|
extra_options: ffmpeg's input file options
|
141
153
|
|
142
154
|
Returns:
|
@@ -147,6 +159,7 @@ def input(
|
|
147
159
|
>>> input('input.mp4')
|
148
160
|
<AVStream:input.mp4:0>
|
149
161
|
```
|
162
|
+
|
150
163
|
"""
|
151
164
|
return InputNode(
|
152
165
|
filename=str(filename),
|
@@ -205,8 +218,10 @@ def input(
|
|
205
218
|
"dn": dn,
|
206
219
|
"top": top,
|
207
220
|
},
|
208
|
-
decoder_options
|
209
|
-
demuxer_options
|
221
|
+
decoder_options,
|
222
|
+
demuxer_options,
|
223
|
+
format_options,
|
224
|
+
codec_options,
|
210
225
|
extra_options,
|
211
226
|
),
|
212
227
|
).stream()
|
typed_ffmpeg/dag/io/_output.py
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
# NOTE: this file is auto-generated, do not modify
|
2
|
-
|
2
|
+
"""Output node."""
|
3
3
|
|
4
4
|
from pathlib import Path
|
5
5
|
from typing import Any
|
6
6
|
|
7
7
|
from ...codecs.schema import FFMpegEncoderOption
|
8
8
|
from ...formats.schema import FFMpegMuxerOption
|
9
|
+
from ...options.codec import (
|
10
|
+
FFMpegAVCodecContextEncoderOption,
|
11
|
+
)
|
12
|
+
from ...options.format import (
|
13
|
+
FFMpegAVFormatContextEncoderOption,
|
14
|
+
)
|
9
15
|
from ...types import (
|
10
16
|
Boolean,
|
11
17
|
Float,
|
@@ -16,7 +22,11 @@ from ...types import (
|
|
16
22
|
Time,
|
17
23
|
)
|
18
24
|
from ...utils.frozendict import merge
|
19
|
-
from ..nodes import
|
25
|
+
from ..nodes import (
|
26
|
+
FilterableStream,
|
27
|
+
OutputNode,
|
28
|
+
OutputStream,
|
29
|
+
)
|
20
30
|
|
21
31
|
|
22
32
|
def output(
|
@@ -118,10 +128,12 @@ def output(
|
|
118
128
|
top: Int = None,
|
119
129
|
encoder_options: FFMpegEncoderOption | None = None,
|
120
130
|
muxer_options: FFMpegMuxerOption | None = None,
|
131
|
+
format_options: FFMpegAVFormatContextEncoderOption | None = None,
|
132
|
+
codec_options: FFMpegAVCodecContextEncoderOption | None = None,
|
121
133
|
extra_options: dict[str, Any] | None = None,
|
122
134
|
) -> OutputStream:
|
123
|
-
"""
|
124
|
-
Output file URL
|
135
|
+
r"""
|
136
|
+
Output file URL.
|
125
137
|
|
126
138
|
Args:
|
127
139
|
*streams: the streams to output
|
@@ -142,8 +154,7 @@ def output(
|
|
142
154
|
program: add program with specified streams
|
143
155
|
stream_group: add stream group with specified streams and group type-specific arguments
|
144
156
|
dframes: set the number of data frames to output
|
145
|
-
target: specify target file type (\"vcd\", \"svcd\", \"dvd\", \"dv\" or \"dv50\
|
146
|
-
"with optional prefixes \"pal-\", \"ntsc-\" or \"film-\")
|
157
|
+
target: specify target file type (\"vcd\", \"svcd\", \"dvd\", \"dv\" or \"dv50\ "with optional prefixes \"pal-\", \"ntsc-\" or \"film-\")
|
147
158
|
shortest: finish encoding within shortest input
|
148
159
|
shortest_buf_duration: maximum buffering duration (in seconds) for the -shortest option
|
149
160
|
bitexact: bitexact mode
|
@@ -223,12 +234,14 @@ def output(
|
|
223
234
|
top: deprecated, use the setfield video filter
|
224
235
|
encoder_options: ffmpeg's encoder options
|
225
236
|
muxer_options: ffmpeg's muxer options
|
237
|
+
format_options: ffmpeg's AVFormatContext options
|
238
|
+
codec_options: ffmpeg's AVCodecContext options
|
226
239
|
extra_options: the arguments for the output
|
227
240
|
|
228
241
|
Returns:
|
229
242
|
the output stream
|
230
|
-
"""
|
231
243
|
|
244
|
+
"""
|
232
245
|
return OutputNode(
|
233
246
|
inputs=streams,
|
234
247
|
filename=str(filename),
|
@@ -329,8 +342,10 @@ def output(
|
|
329
342
|
"dn": dn,
|
330
343
|
"top": top,
|
331
344
|
},
|
332
|
-
encoder_options
|
333
|
-
muxer_options
|
345
|
+
encoder_options,
|
346
|
+
muxer_options,
|
347
|
+
format_options,
|
348
|
+
codec_options,
|
334
349
|
extra_options,
|
335
350
|
),
|
336
351
|
).stream()
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# NOTE: this file is auto-generated, do not modify
|
2
|
-
|
2
|
+
"""Output arguments."""
|
3
3
|
|
4
4
|
from __future__ import annotations
|
5
5
|
|
@@ -9,6 +9,12 @@ from typing import TYPE_CHECKING, Any
|
|
9
9
|
|
10
10
|
from ...codecs.schema import FFMpegEncoderOption
|
11
11
|
from ...formats.schema import FFMpegMuxerOption
|
12
|
+
from ...options.codec import (
|
13
|
+
FFMpegAVCodecContextEncoderOption,
|
14
|
+
)
|
15
|
+
from ...options.format import (
|
16
|
+
FFMpegAVFormatContextEncoderOption,
|
17
|
+
)
|
12
18
|
from ...types import (
|
13
19
|
Boolean,
|
14
20
|
Float,
|
@@ -25,6 +31,8 @@ if TYPE_CHECKING:
|
|
25
31
|
|
26
32
|
|
27
33
|
class OutputArgs(ABC):
|
34
|
+
"""Output arguments interface."""
|
35
|
+
|
28
36
|
@abstractmethod
|
29
37
|
def _output_node(
|
30
38
|
self, *streams: FilterableStream, filename: str | Path, **kwargs: Any
|
@@ -130,10 +138,12 @@ class OutputArgs(ABC):
|
|
130
138
|
top: Int = None,
|
131
139
|
encoder_options: FFMpegEncoderOption | None = None,
|
132
140
|
muxer_options: FFMpegMuxerOption | None = None,
|
141
|
+
format_options: FFMpegAVFormatContextEncoderOption | None = None,
|
142
|
+
codec_options: FFMpegAVCodecContextEncoderOption | None = None,
|
133
143
|
extra_options: dict[str, Any] | None = None,
|
134
144
|
) -> OutputStream:
|
135
|
-
"""
|
136
|
-
Output file URL
|
145
|
+
r"""
|
146
|
+
Output file URL.
|
137
147
|
|
138
148
|
Args:
|
139
149
|
*streams: the streams to output
|
@@ -234,13 +244,15 @@ class OutputArgs(ABC):
|
|
234
244
|
dn: disable data
|
235
245
|
top: deprecated, use the setfield video filter
|
236
246
|
encoder_options: ffmpeg's encoder options
|
237
|
-
muxer_options: FFMpegMuxerOption
|
247
|
+
muxer_options: FFMpegMuxerOption
|
248
|
+
format_options: FFMpegAVFormatContextEncoderOption
|
249
|
+
codec_options: FFMpegAVCodecContextEncoderOption
|
238
250
|
extra_options: the arguments for the output
|
239
251
|
|
240
252
|
Returns:
|
241
253
|
the output stream
|
242
|
-
"""
|
243
254
|
|
255
|
+
"""
|
244
256
|
return self._output_node(
|
245
257
|
*streams,
|
246
258
|
filename=filename,
|
@@ -341,8 +353,10 @@ class OutputArgs(ABC):
|
|
341
353
|
"dn": dn,
|
342
354
|
"top": top,
|
343
355
|
},
|
344
|
-
encoder_options
|
345
|
-
muxer_options
|
356
|
+
encoder_options,
|
357
|
+
muxer_options,
|
358
|
+
format_options,
|
359
|
+
codec_options,
|
346
360
|
extra_options,
|
347
361
|
),
|
348
362
|
).stream()
|
typed_ffmpeg/dag/nodes.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
"""DAG node definitions for FFmpeg filter graphs."""
|
2
|
+
|
1
3
|
from __future__ import annotations
|
2
4
|
|
3
5
|
import logging
|
@@ -60,6 +62,7 @@ class FilterNode(Node):
|
|
60
62
|
|
61
63
|
Returns:
|
62
64
|
The name of the filter
|
65
|
+
|
63
66
|
"""
|
64
67
|
return self.name
|
65
68
|
|
@@ -81,6 +84,7 @@ class FilterNode(Node):
|
|
81
84
|
|
82
85
|
Raises:
|
83
86
|
FFMpegValueError: If the specified index is out of range
|
87
|
+
|
84
88
|
"""
|
85
89
|
from ..streams.video import VideoStream
|
86
90
|
|
@@ -111,6 +115,7 @@ class FilterNode(Node):
|
|
111
115
|
|
112
116
|
Raises:
|
113
117
|
FFMpegValueError: If the specified index is out of range
|
118
|
+
|
114
119
|
"""
|
115
120
|
from ..streams.audio import AudioStream
|
116
121
|
|
@@ -136,6 +141,7 @@ class FilterNode(Node):
|
|
136
141
|
Raises:
|
137
142
|
FFMpegValueError: If the number of inputs doesn't match input_typings
|
138
143
|
FFMpegTypeError: If an input stream doesn't match its expected type
|
144
|
+
|
139
145
|
"""
|
140
146
|
from ..streams.audio import AudioStream
|
141
147
|
from ..streams.video import VideoStream
|
@@ -207,6 +213,7 @@ class FilterableStream(Stream, OutputArgs):
|
|
207
213
|
filename="output.mp4", c="libx264", crf=23
|
208
214
|
)
|
209
215
|
```
|
216
|
+
|
210
217
|
"""
|
211
218
|
return OutputNode(
|
212
219
|
inputs=(self, *streams),
|
@@ -244,6 +251,7 @@ class FilterableStream(Stream, OutputArgs):
|
|
244
251
|
# Apply a blur filter to a video stream
|
245
252
|
blurred = stream.vfilter(name="boxblur", luma_radius=2)
|
246
253
|
```
|
254
|
+
|
247
255
|
"""
|
248
256
|
return self.filter_multi_output(
|
249
257
|
*streams,
|
@@ -283,6 +291,7 @@ class FilterableStream(Stream, OutputArgs):
|
|
283
291
|
# Apply a volume filter to an audio stream
|
284
292
|
louder = stream.afilter(name="volume", volume=2.0)
|
285
293
|
```
|
294
|
+
|
286
295
|
"""
|
287
296
|
return self.filter_multi_output(
|
288
297
|
*streams,
|
@@ -327,6 +336,7 @@ class FilterableStream(Stream, OutputArgs):
|
|
327
336
|
stream1 = split_node.video(0)
|
328
337
|
stream2 = split_node.video(1)
|
329
338
|
```
|
339
|
+
|
330
340
|
"""
|
331
341
|
return FilterNode(
|
332
342
|
name=name,
|
@@ -337,6 +347,7 @@ class FilterableStream(Stream, OutputArgs):
|
|
337
347
|
)
|
338
348
|
|
339
349
|
def __post_init__(self) -> None:
|
350
|
+
"""Validate that filter streams have an index."""
|
340
351
|
if not isinstance(self.node, InputNode):
|
341
352
|
assert self.index is not None, "Filter streams must have an index"
|
342
353
|
|
@@ -368,6 +379,7 @@ class InputNode(Node):
|
|
368
379
|
|
369
380
|
Returns:
|
370
381
|
The basename of the input file
|
382
|
+
|
371
383
|
"""
|
372
384
|
return os.path.basename(self.filename)
|
373
385
|
|
@@ -390,6 +402,7 @@ class InputNode(Node):
|
|
390
402
|
# Apply a filter to the video stream
|
391
403
|
scaled = video.scale(width=1280, height=720)
|
392
404
|
```
|
405
|
+
|
393
406
|
"""
|
394
407
|
from ..streams.video import VideoStream
|
395
408
|
|
@@ -414,6 +427,7 @@ class InputNode(Node):
|
|
414
427
|
# Apply a filter to the audio stream
|
415
428
|
volume_adjusted = audio.volume(volume=2.0)
|
416
429
|
```
|
430
|
+
|
417
431
|
"""
|
418
432
|
from ..streams.audio import AudioStream
|
419
433
|
|
@@ -438,6 +452,7 @@ class InputNode(Node):
|
|
438
452
|
# Output both audio and video to a new file
|
439
453
|
output = av_stream.output("output.mp4")
|
440
454
|
```
|
455
|
+
|
441
456
|
"""
|
442
457
|
from ..streams.av import AVStream
|
443
458
|
|
@@ -471,6 +486,7 @@ class OutputNode(Node):
|
|
471
486
|
|
472
487
|
Returns:
|
473
488
|
The basename of the output file
|
489
|
+
|
474
490
|
"""
|
475
491
|
return os.path.basename(self.filename)
|
476
492
|
|
@@ -492,6 +508,7 @@ class OutputNode(Node):
|
|
492
508
|
output_stream = output_node.stream()
|
493
509
|
with_global_opts = output_stream.global_args(y=True)
|
494
510
|
```
|
511
|
+
|
495
512
|
"""
|
496
513
|
return OutputStream(node=self)
|
497
514
|
|
@@ -531,6 +548,7 @@ class OutputStream(Stream, GlobalRunable):
|
|
531
548
|
# Add global options to an output stream
|
532
549
|
global_node = output_stream._global_node(y=True, loglevel="quiet")
|
533
550
|
```
|
551
|
+
|
534
552
|
"""
|
535
553
|
return GlobalNode(inputs=(self, *streams), kwargs=FrozenDict(kwargs))
|
536
554
|
|
@@ -567,6 +585,7 @@ class GlobalNode(Node):
|
|
567
585
|
# Execute the command
|
568
586
|
global_stream.run()
|
569
587
|
```
|
588
|
+
|
570
589
|
"""
|
571
590
|
return GlobalStream(node=self)
|
572
591
|
|
@@ -607,6 +626,7 @@ class GlobalStream(Stream, GlobalRunable):
|
|
607
626
|
global_stream = ffmpeg.output("output.mp4").global_args(y=True)
|
608
627
|
enhanced = global_stream._global_node(loglevel="quiet")
|
609
628
|
```
|
629
|
+
|
610
630
|
"""
|
611
631
|
inputs = (*self.node.inputs, *streams)
|
612
632
|
kwargs = dict(self.node.kwargs) | kwargs
|
typed_ffmpeg/dag/schema.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
"""DAG schema definitions for FFmpeg filter graphs."""
|
2
|
+
|
1
3
|
from __future__ import annotations
|
2
4
|
|
3
5
|
from dataclasses import dataclass, replace
|
@@ -12,15 +14,11 @@ from .utils import is_dag
|
|
12
14
|
|
13
15
|
@dataclass(frozen=True, kw_only=True)
|
14
16
|
class HashableBaseModel(Serializable):
|
15
|
-
"""
|
16
|
-
A base class for hashable dataclasses.
|
17
|
-
"""
|
17
|
+
"""A base class for hashable dataclasses."""
|
18
18
|
|
19
19
|
@cached_property
|
20
20
|
def hex(self) -> str:
|
21
|
-
"""
|
22
|
-
Get the hexadecimal hash of the object.
|
23
|
-
"""
|
21
|
+
"""Get the hexadecimal hash of the object."""
|
24
22
|
return hex(abs(hash(self)))[2:]
|
25
23
|
|
26
24
|
|
@@ -31,6 +29,7 @@ class Stream(HashableBaseModel):
|
|
31
29
|
|
32
30
|
Note:
|
33
31
|
Each stream in the DAG is a sequence of operations that transforms the data from its input form to its output form. The stream is an essential component of the DAG, as it defines the order and the nature of the operations that are performed on the data.
|
32
|
+
|
34
33
|
"""
|
35
34
|
|
36
35
|
node: Node
|
@@ -66,6 +65,7 @@ class Stream(HashableBaseModel):
|
|
66
65
|
|
67
66
|
Returns:
|
68
67
|
The file path of the visualization.
|
68
|
+
|
69
69
|
"""
|
70
70
|
from ..utils.view import view
|
71
71
|
|
@@ -87,6 +87,7 @@ class Node(HashableBaseModel):
|
|
87
87
|
|
88
88
|
Note:
|
89
89
|
Each node in the DAG represents a single operation that transforms the data from its input form to its output form. The node is an essential component of the DAG, as it defines the nature of the operations that are performed on the data.
|
90
|
+
|
90
91
|
"""
|
91
92
|
|
92
93
|
# Filter_Node_Option_Type
|
@@ -102,6 +103,13 @@ class Node(HashableBaseModel):
|
|
102
103
|
"""
|
103
104
|
|
104
105
|
def __post_init__(self) -> None:
|
106
|
+
"""
|
107
|
+
Validate that the graph is a DAG (Directed Acyclic Graph).
|
108
|
+
|
109
|
+
Raises:
|
110
|
+
ValueError: If the graph is not a DAG
|
111
|
+
|
112
|
+
"""
|
105
113
|
# Validate the DAG
|
106
114
|
passed = set()
|
107
115
|
nodes = [self]
|
@@ -127,6 +135,7 @@ class Node(HashableBaseModel):
|
|
127
135
|
|
128
136
|
Returns:
|
129
137
|
The representation of the node.
|
138
|
+
|
130
139
|
"""
|
131
140
|
return repr(self)
|
132
141
|
|
@@ -140,6 +149,7 @@ class Node(HashableBaseModel):
|
|
140
149
|
|
141
150
|
Returns:
|
142
151
|
The new graph with the replaced node.
|
152
|
+
|
143
153
|
"""
|
144
154
|
if self == old_node:
|
145
155
|
return new_node
|
@@ -165,6 +175,7 @@ class Node(HashableBaseModel):
|
|
165
175
|
|
166
176
|
Returns:
|
167
177
|
The maximum depth of the node.
|
178
|
+
|
168
179
|
"""
|
169
180
|
return max((i.node.max_depth for i in self.inputs), default=0) + 1
|
170
181
|
|
@@ -175,6 +186,7 @@ class Node(HashableBaseModel):
|
|
175
186
|
|
176
187
|
Returns:
|
177
188
|
The upstream nodes of the node.
|
189
|
+
|
178
190
|
"""
|
179
191
|
output = {self}
|
180
192
|
for input in self.inputs:
|
@@ -191,6 +203,7 @@ class Node(HashableBaseModel):
|
|
191
203
|
|
192
204
|
Returns:
|
193
205
|
The file path of the visualization.
|
206
|
+
|
194
207
|
"""
|
195
208
|
from ..utils.view import view
|
196
209
|
|
typed_ffmpeg/dag/utils.py
CHANGED
@@ -38,9 +38,9 @@ def is_dag(graph: dict[str, set[str]]) -> bool:
|
|
38
38
|
graph = {"A": {"B"}, "B": {"C"}, "C": {"A"}}
|
39
39
|
assert is_dag(graph) == False
|
40
40
|
```
|
41
|
-
"""
|
42
41
|
|
43
|
-
|
42
|
+
"""
|
43
|
+
in_degree = dict.fromkeys(graph, 0) # Initialize in-degree of each node to 0
|
44
44
|
|
45
45
|
# Calculate in-degree of each node
|
46
46
|
for u in graph:
|
typed_ffmpeg/exceptions.py
CHANGED
@@ -54,6 +54,7 @@ class FFMpegExecuteError(FFMpegError):
|
|
54
54
|
stderr: The standard error output of the failed command
|
55
55
|
cmd: The command string that was executed
|
56
56
|
retcode: The process return code
|
57
|
+
|
57
58
|
"""
|
58
59
|
|
59
60
|
def __init__(self, retcode: int | None, cmd: str, stdout: bytes, stderr: bytes):
|
@@ -65,8 +66,8 @@ class FFMpegExecuteError(FFMpegError):
|
|
65
66
|
cmd: The FFmpeg command string that was executed
|
66
67
|
stdout: The captured standard output from the process
|
67
68
|
stderr: The captured standard error from the process
|
68
|
-
"""
|
69
69
|
|
70
|
+
"""
|
70
71
|
self.stdout = stdout
|
71
72
|
self.stderr = stderr
|
72
73
|
self.cmd = cmd
|