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
@@ -5,6 +5,7 @@ from dataclasses import dataclass, replace
5
5
  from functools import cached_property
6
6
  from typing import TYPE_CHECKING, Literal
7
7
 
8
+ from ..utils.forzendict import FrozenDict
8
9
  from ..utils.lazy_eval.schema import LazyValue
9
10
  from .utils import is_dag
10
11
 
@@ -84,7 +85,8 @@ class Node(HashableBaseModel, ABC):
84
85
  """
85
86
 
86
87
  # Filter_Node_Option_Type
87
- kwargs: tuple[tuple[str, str | int | float | bool | LazyValue], ...] = ()
88
+ # kwargs: tuple[tuple[str, str | int | float | bool | LazyValue], ...] = ()
89
+ kwargs: FrozenDict[str, str | int | float | bool | LazyValue] = FrozenDict({})
88
90
  """
89
91
  Represents the keyword arguments of the node.
90
92
  """
typed_ffmpeg/dag/utils.py CHANGED
@@ -1,22 +1,43 @@
1
+ """
2
+ Utility functions for working with directed acyclic graphs (DAGs).
3
+
4
+ This module provides functions for validating and analyzing graph structures,
5
+ particularly for ensuring that filter graphs are properly formed as DAGs
6
+ without cycles, which is a requirement for FFmpeg filter chains.
7
+ """
8
+
1
9
  from __future__ import annotations
2
10
 
3
11
  from collections import deque
4
12
 
5
- # Another approach to determine if a graph is a DAG is to try to perform a topological sort.
6
- # If the topological sort is successful (i.e., all vertices are visited exactly once),
7
- # the graph is a DAG. If the topological sort cannot include all vertices (i.e., the graph has a cycle),
8
- # it is not a DAG. Here is a basic implementation using Kahn's Algorithm:
9
-
10
13
 
11
14
  def is_dag(graph: dict[str, set[str]]) -> bool:
12
15
  """
13
- Determine if a graph is a directed acyclic graph (DAG).
16
+ Determine if a graph is a directed acyclic graph (DAG) using topological sorting.
17
+
18
+ This function implements Kahn's algorithm for topological sorting to check
19
+ if the given graph is a DAG. A graph is a DAG if it has no directed cycles.
20
+ The algorithm works by repeatedly removing nodes with no incoming edges
21
+ and their outgoing edges. If all nodes can be removed this way, the graph
22
+ is a DAG; otherwise, it contains at least one cycle.
14
23
 
15
24
  Args:
16
- graph: The graph to check.
25
+ graph: A dictionary representing the graph, where keys are node IDs and
26
+ values are sets of node IDs that the key node points to
17
27
 
18
28
  Returns:
19
- Whether the graph is a DAG.
29
+ True if the graph is a DAG (has no cycles), False otherwise
30
+
31
+ Example:
32
+ ```python
33
+ # A simple linear graph (A -> B -> C)
34
+ graph = {"A": {"B"}, "B": {"C"}, "C": set()}
35
+ assert is_dag(graph) == True
36
+
37
+ # A graph with a cycle (A -> B -> C -> A)
38
+ graph = {"A": {"B"}, "B": {"C"}, "C": {"A"}}
39
+ assert is_dag(graph) == False
40
+ ```
20
41
  """
21
42
 
22
43
  in_degree = {u: 0 for u in graph} # Initialize in-degree of each node to 0
@@ -1,3 +1,13 @@
1
+ """
2
+ Graph validation and transformation for FFmpeg filter chains.
3
+
4
+ This module provides functionality to validate and fix FFmpeg filter graphs,
5
+ particularly handling the case of stream reuse. In FFmpeg, a stream cannot
6
+ be used as input to multiple filters without explicit split/asplit filters.
7
+ This module detects such cases and automatically inserts the necessary split
8
+ filters to ensure the graph is valid for FFmpeg processing.
9
+ """
10
+
1
11
  from __future__ import annotations
2
12
 
3
13
  from dataclasses import replace
@@ -14,14 +24,25 @@ def remove_split(
14
24
  current_stream: Stream, mapping: dict[Stream, Stream] = None
15
25
  ) -> tuple[Stream, dict[Stream, Stream]]:
16
26
  """
17
- Rebuild the graph with the given mapping.
27
+ Remove all split nodes from the graph to prepare for reconstruction.
28
+
29
+ This function performs the first step of graph repair by recursively traversing
30
+ the graph and removing all existing split/asplit nodes. This creates a clean
31
+ graph without any stream splitting, which will then be reconstructed with proper
32
+ split nodes where needed.
33
+
34
+ The function works recursively, processing each node's inputs and creating a
35
+ new graph structure with the split nodes removed.
18
36
 
19
37
  Args:
20
- current_stream: The stream to rebuild the graph with.
21
- mapping: The mapping to rebuild the graph with.
38
+ current_stream: The starting stream to process
39
+ mapping: Dictionary mapping original streams to their new versions without splits
40
+ (used for recursion, pass None for initial call)
22
41
 
23
42
  Returns:
24
- A tuple of the new node and the new mapping.
43
+ A tuple containing:
44
+ - The new stream corresponding to the input stream but with splits removed
45
+ - A mapping dictionary relating original streams to their new versions
25
46
  """
26
47
 
27
48
  # remove all split nodes
@@ -76,17 +97,31 @@ def add_split(
76
97
  mapping: dict[tuple[Stream, Node | None, int | None], Stream] = None,
77
98
  ) -> tuple[Stream, dict[tuple[Stream, Node | None, int | None], Stream]]:
78
99
  """
79
- Add split nodes to the graph.
100
+ Add split nodes to the graph where streams are reused.
101
+
102
+ This function performs the second step of graph repair by traversing the
103
+ graph and adding split/asplit nodes where a stream is used as input to
104
+ multiple downstream nodes. In FFmpeg, each stream can only be used once
105
+ unless explicitly split.
106
+
107
+ The function detects cases where a stream has multiple outgoing connections
108
+ and inserts the appropriate split filter (split for video, asplit for audio),
109
+ connecting each output of the split to the corresponding downstream node.
80
110
 
81
111
  Args:
82
- current_stream: The stream to add split nodes to.
83
- down_node: The node use current_stream as input.
84
- down_index: The index of the input stream in down_node.
85
- context: The DAG context.
86
- mapping: The mapping to add split nodes to.
112
+ current_stream: The stream to process for potential splitting
113
+ down_node: The downstream node that uses current_stream as input (for recursion)
114
+ down_index: The input index in down_node where current_stream connects (for recursion)
115
+ context: The DAG context containing graph relationship information
116
+ mapping: Dictionary tracking the transformations (used for recursion,
117
+ pass None for initial call)
87
118
 
88
119
  Returns:
89
- A tuple of the new node and the new mapping.
120
+ Stream: The new stream (possibly from a split node output) for the specified downstream connection
121
+ dict: A mapping dictionary relating original stream/connections to their new versions
122
+
123
+ Raises:
124
+ FFMpegValueError: If an unsupported stream type is encountered
90
125
  """
91
126
 
92
127
  if not context:
@@ -149,17 +184,24 @@ def add_split(
149
184
 
150
185
  def fix_graph(stream: Stream) -> Stream:
151
186
  """
152
- Fix the graph by removing and adding split nodes.
187
+ Fix stream reuse issues in the filter graph by properly adding split nodes.
188
+
189
+ This function performs a complete graph repair operation by:
190
+ 1. First removing all existing split/asplit nodes from the graph
191
+ 2. Then adding new split/asplit nodes where needed to handle stream reuse
192
+
193
+ This ensures that the graph follows FFmpeg's requirement that each stream
194
+ output can only be used as input to one filter unless explicitly split.
153
195
 
154
196
  Args:
155
- stream: The stream to fix.
197
+ stream: The root stream of the graph to fix (typically an output stream)
156
198
 
157
199
  Returns:
158
- The fixed stream.
200
+ A new stream representing the fixed graph with proper splitting
159
201
 
160
202
  Note:
161
- Fix the graph by resetting split nodes.
162
- This function is for internal use only.
203
+ This function creates a new graph structure rather than modifying the
204
+ existing one, preserving the original graph.
163
205
  """
164
206
 
165
207
  stream, _ = remove_split(stream)
@@ -169,14 +211,35 @@ def fix_graph(stream: Stream) -> Stream:
169
211
 
170
212
  def validate(stream: Stream, auto_fix: bool = True) -> Stream:
171
213
  """
172
- Validate the given DAG. If auto_fix is True, the graph will be automatically fixed to follow ffmpeg's rule.
214
+ Validate a filter graph and optionally fix stream reuse issues.
215
+
216
+ This function validates that the filter graph follows FFmpeg's rules,
217
+ particularly regarding stream reuse. In FFmpeg, a stream cannot be used
218
+ as input to multiple filters without an explicit split/asplit filter.
219
+
220
+ When auto_fix is True (the default), this function automatically inserts
221
+ the necessary split filters where needed, ensuring the graph is valid for
222
+ FFmpeg processing.
173
223
 
174
224
  Args:
175
- stream: The DAG to validate.
176
- auto_fix: Whether to automatically fix the graph.
225
+ stream: The stream representing the filter graph to validate
226
+ auto_fix: Whether to automatically fix stream reuse issues by adding
227
+ appropriate split nodes
177
228
 
178
229
  Returns:
179
- The validated DAG context.
230
+ Either the original stream (if no fixing needed/requested) or a new
231
+ stream representing the fixed graph
232
+
233
+ Example:
234
+ ```python
235
+ # Create a graph where the same stream is used twice (reused)
236
+ input_stream = ffmpeg.input("input.mp4")
237
+ # Use the same stream for both scaling and blurring (invalid in FFmpeg)
238
+ scaled = input_stream.filter("scale", 1280, 720)
239
+ blurred = input_stream.filter("boxblur", 2)
240
+ # Validate will automatically insert a split filter
241
+ valid_stream = ffmpeg.dag.validate(scaled.output("output.mp4"))
242
+ ```
180
243
  """
181
244
  if auto_fix:
182
245
  stream = fix_graph(stream)
@@ -1,6 +1,18 @@
1
+ """
2
+ Exception classes for handling FFmpeg-related errors.
3
+
4
+ This module defines a hierarchy of exceptions that can be raised during
5
+ FFmpeg operations, providing detailed error reporting for type errors,
6
+ value errors, and execution failures.
7
+ """
8
+
9
+
1
10
  class FFMpegError(Exception):
2
11
  """
3
- Base exception for all ffmpeg errors.
12
+ Base exception for all FFmpeg errors.
13
+
14
+ This is the parent class for all exceptions that may be raised by the
15
+ typed-ffmpeg library. It inherits from the standard Python Exception class.
4
16
  """
5
17
 
6
18
  ...
@@ -8,30 +20,51 @@ class FFMpegError(Exception):
8
20
 
9
21
  class FFMpegTypeError(FFMpegError, TypeError):
10
22
  """
11
- Base exception for all ffmpeg type errors.
23
+ Exception raised for FFmpeg-related type errors.
24
+
25
+ This exception is raised when an operation receives an argument of incorrect
26
+ type, such as passing a string when a numeric value is expected, or vice versa.
27
+
28
+ Inherits from both FFMpegError and the standard Python TypeError.
12
29
  """
13
30
 
14
31
 
15
32
  class FFMpegValueError(FFMpegError, ValueError):
16
33
  """
17
- Base exception for all ffmpeg value errors.
34
+ Exception raised for FFmpeg-related value errors.
35
+
36
+ This exception is raised when an operation receives an argument with the correct
37
+ type but an inappropriate value, such as a negative duration or invalid codec name.
38
+
39
+ Inherits from both FFMpegError and the standard Python ValueError.
18
40
  """
19
41
 
20
42
 
21
43
  class FFMpegExecuteError(FFMpegError):
22
44
  """
23
- FFmpeg error
45
+ Exception raised when an FFmpeg command fails during execution.
46
+
47
+ This exception is raised when an FFmpeg process returns a non-zero exit code,
48
+ indicating that the command failed to execute properly. The exception captures
49
+ the return code, command string, and stdout/stderr output to help diagnose
50
+ the issue.
51
+
52
+ Attributes:
53
+ stdout: The standard output of the failed command
54
+ stderr: The standard error output of the failed command
55
+ cmd: The command string that was executed
56
+ retcode: The process return code
24
57
  """
25
58
 
26
59
  def __init__(self, retcode: int | None, cmd: str, stdout: bytes, stderr: bytes):
27
60
  """
28
- Initialize the exception.
61
+ Initialize the FFMpegExecuteError with execution details.
29
62
 
30
63
  Args:
31
- retcode: The return code of the command.
32
- cmd: The command that was run.
33
- stdout: The stdout of the command.
34
- stderr: The stderr of the command.
64
+ retcode: The return code of the FFmpeg command
65
+ cmd: The FFmpeg command string that was executed
66
+ stdout: The captured standard output from the process
67
+ stderr: The captured standard error from the process
35
68
  """
36
69
 
37
70
  self.stdout = stdout
typed_ffmpeg/info.py CHANGED
@@ -1,3 +1,12 @@
1
+ """
2
+ Module for querying FFmpeg codec and encoder information.
3
+
4
+ This module provides functionality to query the available FFmpeg codecs,
5
+ decoders, and encoders on the system. It parses the output of FFmpeg's
6
+ command-line tools to extract information about supported formats and
7
+ capabilities.
8
+ """
9
+
1
10
  import logging
2
11
  import subprocess
3
12
  from dataclasses import dataclass
@@ -10,24 +19,56 @@ logger = logging.getLogger(__name__)
10
19
 
11
20
 
12
21
  class CodecFlags(Flag):
13
- video = auto()
14
- audio = auto()
15
- subtitle = auto()
16
- frame_level_multithreading = auto()
17
- slice_level_multithreading = auto()
18
- experimental = auto()
19
- draw_horiz_band = auto()
20
- direct_rendering_method_1 = auto()
22
+ """
23
+ Flag enumeration representing the capabilities of a codec.
24
+
25
+ These flags correspond to the character flags in FFmpeg's codec information
26
+ output and indicate what features a codec supports.
27
+ """
28
+
29
+ video = auto() # Codec supports video encoding/decoding
30
+ audio = auto() # Codec supports audio encoding/decoding
31
+ subtitle = auto() # Codec supports subtitle processing
32
+ frame_level_multithreading = auto() # Codec supports frame-level multithreading
33
+ slice_level_multithreading = auto() # Codec supports slice-level multithreading
34
+ experimental = auto() # Codec is considered experimental
35
+ draw_horiz_band = auto() # Codec supports drawing horizontal bands
36
+ direct_rendering_method_1 = auto() # Codec supports direct rendering method 1
21
37
 
22
38
 
23
39
  @dataclass(frozen=True)
24
40
  class Codec:
41
+ """
42
+ Represents an FFmpeg codec with its capabilities and description.
43
+
44
+ This immutable dataclass stores information about a codec identified by the
45
+ `ffmpeg -codecs` command, including its name, supported features (as flags),
46
+ and its textual description.
47
+
48
+ Attributes:
49
+ name: The codec identifier used in FFmpeg commands
50
+ flags: Bitmap of capabilities supported by the codec
51
+ description: Human-readable description of the codec
52
+ """
53
+
25
54
  name: str
26
55
  flags: CodecFlags
27
56
  description: str
28
57
 
29
58
 
30
59
  def parse_codec_flags(flags: str) -> CodecFlags:
60
+ """
61
+ Parse the FFmpeg codec flags string into a CodecFlags enum.
62
+
63
+ This function interprets the character flags from FFmpeg's codec listing
64
+ and converts them to the corresponding CodecFlags enum values.
65
+
66
+ Args:
67
+ flags: A string of flag characters from FFmpeg codec information
68
+
69
+ Returns:
70
+ A CodecFlags bitmap with the appropriate flags set
71
+ """
31
72
  flags_enum = CodecFlags(0)
32
73
  if flags[0] == "V":
33
74
  flags_enum |= CodecFlags.video
@@ -49,6 +90,18 @@ def parse_codec_flags(flags: str) -> CodecFlags:
49
90
 
50
91
 
51
92
  def get_codecs() -> tuple[Codec, ...]:
93
+ """
94
+ Query the system for all available FFmpeg codecs.
95
+
96
+ This function calls `ffmpeg -codecs` and parses the output to create
97
+ a tuple of Codec objects representing all codecs available on the system.
98
+
99
+ Returns:
100
+ A tuple of Codec objects with information about each codec
101
+
102
+ Raises:
103
+ FFMpegExecuteError: If the ffmpeg command fails
104
+ """
52
105
  args = ["ffmpeg", "-hide_banner", "-codecs"]
53
106
  logger.info("Running ffmpeg command: %s", command_line(args))
54
107
  p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@@ -79,17 +132,36 @@ def get_codecs() -> tuple[Codec, ...]:
79
132
 
80
133
 
81
134
  class CoderFlags(Flag):
82
- video = auto()
83
- audio = auto()
84
- subtitle = auto()
85
- frame_level_multithreading = auto()
86
- slice_level_multithreading = auto()
87
- experimental = auto()
88
- draw_horiz_band = auto()
89
- direct_rendering_method_1 = auto()
135
+ """
136
+ Flag enumeration representing the capabilities of a coder (encoder/decoder).
137
+
138
+ These flags correspond to the character flags in FFmpeg's encoder/decoder
139
+ information output and indicate what features a coder supports.
140
+ """
141
+
142
+ video = auto() # Coder supports video processing
143
+ audio = auto() # Coder supports audio processing
144
+ subtitle = auto() # Coder supports subtitle processing
145
+ frame_level_multithreading = auto() # Coder supports frame-level multithreading
146
+ slice_level_multithreading = auto() # Coder supports slice-level multithreading
147
+ experimental = auto() # Coder is considered experimental
148
+ draw_horiz_band = auto() # Coder supports drawing horizontal bands
149
+ direct_rendering_method_1 = auto() # Coder supports direct rendering method 1
90
150
 
91
151
 
92
152
  def parse_coder_flags(flags: str) -> CoderFlags:
153
+ """
154
+ Parse the FFmpeg coder flags string into a CoderFlags enum.
155
+
156
+ This function interprets the character flags from FFmpeg's encoder/decoder
157
+ listing and converts them to the corresponding CoderFlags enum values.
158
+
159
+ Args:
160
+ flags: A string of flag characters from FFmpeg encoder/decoder information
161
+
162
+ Returns:
163
+ A CoderFlags bitmap with the appropriate flags set
164
+ """
93
165
  flags_enum = CoderFlags(0)
94
166
  if flags[0] == "V":
95
167
  flags_enum |= CoderFlags.video
@@ -112,12 +184,37 @@ def parse_coder_flags(flags: str) -> CoderFlags:
112
184
 
113
185
  @dataclass
114
186
  class Coder:
187
+ """
188
+ Represents an FFmpeg encoder or decoder with its capabilities and description.
189
+
190
+ This dataclass stores information about a coder identified by the
191
+ `ffmpeg -encoders` or `ffmpeg -decoders` command, including its name,
192
+ supported features (as flags), and its textual description.
193
+
194
+ Attributes:
195
+ name: The coder identifier used in FFmpeg commands
196
+ flags: Bitmap of capabilities supported by the coder
197
+ description: Human-readable description of the coder
198
+ """
199
+
115
200
  name: str
116
201
  flags: CoderFlags
117
202
  description: str
118
203
 
119
204
 
120
205
  def get_coders(codes: str) -> tuple[Coder, ...]:
206
+ """
207
+ Parse the output of an FFmpeg encoder/decoder listing into Coder objects.
208
+
209
+ This function parses the text output from `ffmpeg -encoders` or
210
+ `ffmpeg -decoders` commands and constructs Coder objects for each entry.
211
+
212
+ Args:
213
+ codes: String output from the FFmpeg encoders/decoders command
214
+
215
+ Returns:
216
+ A tuple of Coder objects representing the available encoders/decoders
217
+ """
121
218
  codecs_lines = codes.strip().split("\n")
122
219
  # Skip header lines until we find the separator
123
220
  for i, line in enumerate(codecs_lines):
@@ -136,6 +233,18 @@ def get_coders(codes: str) -> tuple[Coder, ...]:
136
233
 
137
234
 
138
235
  def get_decoders() -> tuple[Coder, ...]:
236
+ """
237
+ Query the system for all available FFmpeg decoders.
238
+
239
+ This function calls `ffmpeg -decoders` and parses the output to create
240
+ a tuple of Coder objects representing all decoders available on the system.
241
+
242
+ Returns:
243
+ A tuple of Coder objects with information about each decoder
244
+
245
+ Raises:
246
+ FFMpegExecuteError: If the ffmpeg command fails
247
+ """
139
248
  args = ["ffmpeg", "-hide_banner", "-decoders"]
140
249
  logger.info("Running ffmpeg command: %s", command_line(args))
141
250
  p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@@ -152,6 +261,18 @@ def get_decoders() -> tuple[Coder, ...]:
152
261
 
153
262
 
154
263
  def get_encoders() -> tuple[Coder, ...]:
264
+ """
265
+ Query the system for all available FFmpeg encoders.
266
+
267
+ This function calls `ffmpeg -encoders` and parses the output to create
268
+ a tuple of Coder objects representing all encoders available on the system.
269
+
270
+ Returns:
271
+ A tuple of Coder objects with information about each encoder
272
+
273
+ Raises:
274
+ FFMpegExecuteError: If the ffmpeg command fails
275
+ """
155
276
  args = ["ffmpeg", "-hide_banner", "-encoders"]
156
277
  logger.info("Running ffmpeg command: %s", command_line(args))
157
278
  p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
typed_ffmpeg/probe.py CHANGED
@@ -1,3 +1,11 @@
1
+ """
2
+ Module for analyzing media files with ffprobe.
3
+
4
+ This module provides functionality to extract detailed metadata from media files
5
+ using FFmpeg's ffprobe utility. It returns a structured representation of the
6
+ file's format, streams, and other relevant information.
7
+ """
8
+
1
9
  import json
2
10
  import logging
3
11
  import subprocess
@@ -18,16 +26,33 @@ def probe(
18
26
  **kwargs: Any,
19
27
  ) -> dict[str, Any]:
20
28
  """
21
- Run ffprobe on the given file and return a JSON representation of the output
29
+ Analyze a media file using ffprobe and return its metadata as a dictionary.
30
+
31
+ This function executes ffprobe to extract detailed information about a media file,
32
+ including format metadata (container format, duration, bitrate) and stream information
33
+ (codecs, resolution, sample rate, etc). The result is returned as a Python dictionary
34
+ parsed from ffprobe's JSON output.
22
35
 
23
36
  Args:
24
- filename: The path to the file to probe.
25
- cmd: The ffprobe command to run. Defaults to "ffprobe".
26
- timeout: The timeout for the command. Defaults to None.
27
- **kwargs: The arguments for the ffprobe command.
37
+ filename: Path to the media file to analyze
38
+ cmd: Path or name of the ffprobe executable (default: "ffprobe")
39
+ timeout: Maximum time in seconds to wait for ffprobe to complete (default: None, wait indefinitely)
40
+ **kwargs: Additional arguments to pass to ffprobe as command line parameters
41
+ (e.g., loglevel="quiet", skip_frame="nokey")
28
42
 
29
43
  Returns:
30
- The JSON representation of the ffprobe output.
44
+ A dictionary containing the parsed JSON output from ffprobe with format and stream information
45
+
46
+ Raises:
47
+ FFMpegExecuteError: If ffprobe returns a non-zero exit code
48
+ subprocess.TimeoutExpired: If the timeout is reached before ffprobe completes
49
+
50
+ Example:
51
+ ```python
52
+ info = probe("video.mp4")
53
+ print(f"Duration: {float(info['format']['duration']):.2f} seconds")
54
+ print(f"Streams: {len(info['streams'])}")
55
+ ```
31
56
  """
32
57
  args = [cmd, "-show_format", "-show_streams", "-of", "json"]
33
58
  args += convert_kwargs_to_cmd_line_args(kwargs)
typed_ffmpeg/schema.py CHANGED
@@ -1,5 +1,10 @@
1
1
  """
2
- Defines the basic schema for the ffmpeg command line options.
2
+ Defines the basic schema for the FFmpeg command line options.
3
+
4
+ This module contains base classes used throughout the typed-ffmpeg library to handle
5
+ default values, automatic parameter derivation, and stream type definitions.
6
+ These components form the foundation of the type annotation system that
7
+ enables static type checking in the FFmpeg filter graph.
3
8
  """
4
9
 
5
10
  from .common.schema import StreamType
@@ -7,8 +12,18 @@ from .common.schema import StreamType
7
12
 
8
13
  class Default(str):
9
14
  """
10
- This is the default value for an option. It is used for annotation purposes only
11
- and will not be passed to the ffmpeg command line.
15
+ Represents a default value for an FFmpeg option.
16
+
17
+ This class is used for annotation purposes only and indicates that a parameter
18
+ should use its default value. When a parameter is marked with Default, it
19
+ will not be explicitly passed to the FFmpeg command line, letting FFmpeg use
20
+ its built-in default value instead.
21
+
22
+ Example:
23
+ ```python
24
+ # This will use FFmpeg's default crf value
25
+ video.output("output.mp4", crf=Default("23"))
26
+ ```
12
27
  """
13
28
 
14
29
  ...
@@ -16,8 +31,20 @@ class Default(str):
16
31
 
17
32
  class Auto(Default):
18
33
  """
19
- This is the auto value for an option. It is used for annotation purposes only
20
- and will not be passed to the ffmpeg command line.
34
+ Represents an automatically derived value for an FFmpeg option.
35
+
36
+ This is a special case of Default that indicates the value should be
37
+ calculated automatically based on the context. For example, the number
38
+ of inputs to a filter might be derived from the number of streams passed
39
+ to that filter.
40
+
41
+ Auto contains an expression string that defines how the value should be computed.
42
+
43
+ Example:
44
+ ```python
45
+ # The number of inputs is automatically derived from the length of streams
46
+ hstack(*streams, inputs=Auto("len(streams)"))
47
+ ```
21
48
  """
22
49
 
23
50
 
@@ -1,3 +1,16 @@
1
+ """
2
+ Audio channel layout definitions for FFmpeg.
3
+
4
+ This module defines a mapping between FFmpeg's channel layout names and
5
+ the corresponding number of audio channels. This information is used for
6
+ audio stream processing, filtering, and conversion operations.
7
+
8
+ Channel layouts in FFmpeg represent different speaker configurations,
9
+ such as mono (1 channel), stereo (2 channels), 5.1 (6 channels), etc.
10
+ The names used in this dictionary match the standard names used by FFmpeg's
11
+ channel layout API.
12
+ """
13
+
1
14
  CHANNEL_LAYOUT = {
2
15
  "mono": 1,
3
16
  "stereo": 2,