typed-ffmpeg-compatible 3.0.2a0__py3-none-any.whl → 3.2.1__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 (60) hide show
  1. typed_ffmpeg/__init__.py +2 -1
  2. typed_ffmpeg/_version.py +21 -0
  3. typed_ffmpeg/compile/compile_cli.py +28 -4
  4. typed_ffmpeg/compile/compile_python.py +3 -4
  5. typed_ffmpeg/ffprobe/__init__.py +0 -0
  6. typed_ffmpeg/ffprobe/parse.py +133 -0
  7. typed_ffmpeg/ffprobe/probe.py +272 -0
  8. typed_ffmpeg/ffprobe/schema.py +455 -0
  9. typed_ffmpeg/ffprobe/xml2json.py +70 -0
  10. {typed_ffmpeg_compatible-3.0.2a0.dist-info → typed_ffmpeg_compatible-3.2.1.dist-info}/METADATA +36 -17
  11. typed_ffmpeg_compatible-3.2.1.dist-info/RECORD +57 -0
  12. {typed_ffmpeg_compatible-3.0.2a0.dist-info → typed_ffmpeg_compatible-3.2.1.dist-info}/WHEEL +2 -1
  13. typed_ffmpeg_compatible-3.2.1.dist-info/top_level.txt +1 -0
  14. typed_ffmpeg/common/cache/.gitignore +0 -3
  15. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/acrossover.json +0 -6
  16. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/afir.json +0 -9
  17. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/aiir.json +0 -6
  18. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/ainterleave.json +0 -9
  19. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/amerge.json +0 -9
  20. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/amix.json +0 -9
  21. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/amovie.json +0 -6
  22. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/anequalizer.json +0 -6
  23. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/aphasemeter.json +0 -6
  24. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/asegment.json +0 -6
  25. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/aselect.json +0 -6
  26. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/asplit.json +0 -6
  27. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/astreamselect.json +0 -9
  28. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/bm3d.json +0 -6
  29. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/channelsplit.json +0 -6
  30. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/concat.json +0 -9
  31. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/decimate.json +0 -6
  32. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/ebur128.json +0 -6
  33. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/extractplanes.json +0 -6
  34. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/fieldmatch.json +0 -6
  35. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/guided.json +0 -6
  36. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/headphone.json +0 -6
  37. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/hstack.json +0 -9
  38. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/interleave.json +0 -9
  39. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/join.json +0 -9
  40. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/libplacebo.json +0 -9
  41. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/limitdiff.json +0 -6
  42. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/mergeplanes.json +0 -6
  43. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/mix.json +0 -9
  44. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/movie.json +0 -6
  45. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/premultiply.json +0 -6
  46. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/segment.json +0 -6
  47. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/select.json +0 -6
  48. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/signature.json +0 -9
  49. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/split.json +0 -6
  50. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/streamselect.json +0 -9
  51. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/unpremultiply.json +0 -6
  52. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/vstack.json +0 -9
  53. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/xmedian.json +0 -9
  54. typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/xstack.json +0 -9
  55. typed_ffmpeg/common/cache/list/filters.json +0 -90747
  56. typed_ffmpeg/common/cache/list/options.json +0 -1694
  57. typed_ffmpeg/probe.py +0 -75
  58. typed_ffmpeg_compatible-3.0.2a0.dist-info/RECORD +0 -95
  59. typed_ffmpeg_compatible-3.0.2a0.dist-info/entry_points.txt +0 -3
  60. {typed_ffmpeg_compatible-3.0.2a0.dist-info → typed_ffmpeg_compatible-3.2.1.dist-info/licenses}/LICENSE +0 -0
typed_ffmpeg/__init__.py CHANGED
@@ -26,8 +26,8 @@ from . import compile, dag, filters, sources
26
26
  from .base import afilter, filter_multi_output, input, merge_outputs, output, vfilter
27
27
  from .dag import Stream
28
28
  from .exceptions import FFMpegExecuteError, FFMpegTypeError, FFMpegValueError
29
+ from .ffprobe.probe import probe, probe_obj
29
30
  from .info import get_codecs, get_decoders, get_encoders
30
- from .probe import probe
31
31
  from .streams import AudioStream, AVStream, VideoStream
32
32
 
33
33
  __all__ = [
@@ -41,6 +41,7 @@ __all__ = [
41
41
  "FFMpegValueError",
42
42
  "Stream",
43
43
  "probe",
44
+ "probe_obj",
44
45
  "compile",
45
46
  "AudioStream",
46
47
  "VideoStream",
@@ -0,0 +1,21 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
6
+ TYPE_CHECKING = False
7
+ if TYPE_CHECKING:
8
+ from typing import Tuple
9
+ from typing import Union
10
+
11
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
12
+ else:
13
+ VERSION_TUPLE = object
14
+
15
+ version: str
16
+ __version__: str
17
+ __version_tuple__: VERSION_TUPLE
18
+ version_tuple: VERSION_TUPLE
19
+
20
+ __version__ = version = '3.2.1'
21
+ __version_tuple__ = version_tuple = (3, 2, 1)
@@ -21,13 +21,10 @@ import shlex
21
21
  from collections import defaultdict
22
22
  from collections.abc import Mapping
23
23
 
24
- from ffmpeg.dag.factory import filter_node_factory
25
- from ffmpeg.streams.audio import AudioStream
26
- from ffmpeg.streams.video import VideoStream
27
-
28
24
  from ..base import input, merge_outputs, output
29
25
  from ..common.cache import load
30
26
  from ..common.schema import FFMpegFilter, FFMpegFilterDef, FFMpegOption, StreamType
27
+ from ..dag.factory import filter_node_factory
31
28
  from ..dag.nodes import (
32
29
  FilterableStream,
33
30
  FilterNode,
@@ -39,7 +36,9 @@ from ..dag.nodes import (
39
36
  from ..dag.schema import Node, Stream
40
37
  from ..exceptions import FFMpegValueError
41
38
  from ..schema import Default
39
+ from ..streams.audio import AudioStream
42
40
  from ..streams.av import AVStream
41
+ from ..streams.video import VideoStream
43
42
  from ..utils.escaping import escape
44
43
  from ..utils.lazy_eval.schema import LazyValue
45
44
  from ..utils.run import command_line
@@ -153,6 +152,15 @@ def parse_output(
153
152
  stream = parse_stream_selector(map_option, in_streams)
154
153
  inputs.append(stream)
155
154
 
155
+ if not inputs:
156
+ # NOTE: if there is no inputs, and there is only one input node
157
+ if len([k for k in in_streams if isinstance(in_streams[k], AVStream)]) == 1:
158
+ inputs = [
159
+ in_streams[k]
160
+ for k in in_streams
161
+ if isinstance(in_streams[k], AVStream)
162
+ ]
163
+
156
164
  assert inputs, f"No inputs found for output {filename}"
157
165
  export.append(output(*inputs, filename=filename, extra_options=options))
158
166
  buffer = []
@@ -677,6 +685,22 @@ def get_args_output_node(node: OutputNode, context: DAGContext) -> list[str]:
677
685
  if context:
678
686
  for input in node.inputs:
679
687
  if isinstance(input.node, InputNode):
688
+ # NOTE: specially rules,
689
+ # if there is only one input node,
690
+ # only one output node,
691
+ # the output node has only one input,
692
+ # and the stream selector is not specified,
693
+ # then the map can be ignore.
694
+ if (
695
+ input.index is None
696
+ and isinstance(input, AVStream)
697
+ and len([k for k in context.all_nodes if isinstance(k, InputNode)])
698
+ == 1
699
+ and len([k for k in context.all_nodes if isinstance(k, OutputNode)])
700
+ == 1
701
+ and len(node.inputs) == 1
702
+ ):
703
+ continue
680
704
  commands += ["-map", get_stream_label(input, context)]
681
705
  else:
682
706
  commands += ["-map", f"[{get_stream_label(input, context)}]"]
@@ -3,10 +3,6 @@ from __future__ import annotations
3
3
  from collections.abc import Mapping
4
4
  from typing import Any
5
5
 
6
- from ffmpeg.streams.audio import AudioStream
7
- from ffmpeg.streams.av import AVStream
8
- from ffmpeg.streams.video import VideoStream
9
-
10
6
  from ..common.cache import load
11
7
  from ..common.schema import FFMpegFilter
12
8
  from ..dag.nodes import (
@@ -19,6 +15,9 @@ from ..dag.nodes import (
19
15
  OutputStream,
20
16
  )
21
17
  from ..dag.schema import Node, Stream
18
+ from ..streams.audio import AudioStream
19
+ from ..streams.av import AVStream
20
+ from ..streams.video import VideoStream
22
21
  from .context import DAGContext
23
22
  from .validate import validate
24
23
 
File without changes
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import json
4
+ import types
5
+ from dataclasses import is_dataclass
6
+ from typing import (
7
+ Any,
8
+ TypeGuard,
9
+ TypeVar,
10
+ Union,
11
+ cast,
12
+ get_args,
13
+ get_origin,
14
+ get_type_hints,
15
+ )
16
+
17
+ from .schema import ffprobeType, registered_types
18
+ from .xml2json import xml_string_to_json
19
+
20
+ T = TypeVar("T")
21
+
22
+
23
+ def _get_actual_type(type_hint: Any) -> type[Any]:
24
+ """
25
+ Get the actual type from a type hint, handling Optional and Union (including | syntax).
26
+
27
+ Args:
28
+ type_hint: The type hint to get the actual type from
29
+
30
+ Returns:
31
+ The actual type
32
+ """
33
+ # If type_hint is a string, evaluate it in the schema's module context
34
+ if isinstance(type_hint, str):
35
+ type_hint = registered_types[type_hint]
36
+
37
+ origin = get_origin(type_hint)
38
+ # Handle typing.Union and types.UnionType (for int | None in Python 3.10+)
39
+ if origin is Union or (hasattr(types, "UnionType") and origin is types.UnionType):
40
+ non_none_types = [t for t in get_args(type_hint) if t is not type(None)]
41
+ if non_none_types:
42
+ return _get_actual_type(non_none_types[0])
43
+ # Handle direct types.UnionType (for int | None in Python 3.10+)
44
+ if hasattr(types, "UnionType") and isinstance(type_hint, types.UnionType):
45
+ non_none_types = [t for t in type_hint.__args__ if t is not type(None)]
46
+ if non_none_types:
47
+ return _get_actual_type(non_none_types[0])
48
+ return type_hint
49
+
50
+
51
+ def is_dataclass_type(obj: type[Any]) -> TypeGuard[type[T]]:
52
+ """
53
+ Check if an object is a dataclass type.
54
+
55
+ Args:
56
+ obj: The object to check
57
+
58
+ Returns:
59
+ True if the object is a dataclass type, False otherwise
60
+ """
61
+ return is_dataclass(obj)
62
+
63
+
64
+ def _parse_obj_from_dict(data: Any, cls: type[T]) -> T | None:
65
+ """
66
+ Parse a dictionary into a dataclass instance.
67
+
68
+ Args:
69
+ data: The dictionary to parse
70
+ cls: The dataclass to parse into
71
+
72
+ Returns:
73
+ The parsed dataclass instance
74
+ """
75
+
76
+ if data is None:
77
+ return None
78
+
79
+ if isinstance(cls, type):
80
+ if cls is str:
81
+ return cast(T, str(data))
82
+ elif cls is int:
83
+ return cast(T, int(data))
84
+ elif cls is float:
85
+ return cast(T, float(data))
86
+ elif cls is bool:
87
+ return cast(T, bool(data))
88
+
89
+ if not isinstance(data, dict):
90
+ return cls()
91
+
92
+ if isinstance(cls, str): # NOTE: python 3.10
93
+ cls = registered_types[cls]
94
+
95
+ type_hints = get_type_hints(cls)
96
+ kwargs: dict[str, Any] = {}
97
+
98
+ for field_name, field_type in type_hints.items():
99
+ actual_type = _get_actual_type(field_type)
100
+
101
+ if get_origin(actual_type) is tuple:
102
+ tuple_args = get_args(actual_type)
103
+ if not tuple_args:
104
+ continue
105
+ item_type = tuple_args[0]
106
+ value = data.get(field_name, [])
107
+ if not isinstance(value, list):
108
+ value = [value]
109
+ kwargs[field_name] = tuple(
110
+ _parse_obj_from_dict(item, item_type) for item in value
111
+ )
112
+ continue
113
+
114
+ kwargs[field_name] = _parse_obj_from_dict(data.get(field_name), actual_type)
115
+
116
+ return cls(**kwargs)
117
+
118
+
119
+ def parse_ffprobe(xml_string: str) -> ffprobeType:
120
+ """
121
+ Parse ffprobe XML output into ffprobeType dataclass using JSON dict.
122
+
123
+ Args:
124
+ xml_string: The XML string to parse
125
+
126
+ Returns:
127
+ The parsed ffprobeType instance
128
+ """
129
+ json_str = xml_string_to_json(xml_string)
130
+ json_dict = json.loads(json_str)
131
+ # The root key is 'ffprobe'
132
+ root_data = json_dict.get("ffprobe", json_dict)
133
+ return _parse_obj_from_dict(root_data, ffprobeType) or ffprobeType()
@@ -0,0 +1,272 @@
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
+
9
+ import json
10
+ import logging
11
+ import subprocess
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+ from ..exceptions import FFMpegExecuteError
16
+ from ..utils.escaping import convert_kwargs_to_cmd_line_args
17
+ from ..utils.run import command_line
18
+ from .parse import parse_ffprobe
19
+ from .schema import ffprobeType
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ def _probe(
25
+ filename: str | Path,
26
+ *,
27
+ show_program_version: bool = False,
28
+ show_library_versions: bool = False,
29
+ show_pixel_formats: bool = False,
30
+ show_packets: bool = False,
31
+ show_frames: bool = False,
32
+ show_programs: bool = False,
33
+ show_streams: bool = True,
34
+ show_chapters: bool = False,
35
+ show_format: bool = True,
36
+ show_error: bool = False,
37
+ cmd: str = "ffprobe",
38
+ timeout: int | None = None,
39
+ format: str = "json",
40
+ **kwargs: Any,
41
+ ) -> str:
42
+ """
43
+ Analyze a media file using ffprobe and return its metadata as a dictionary.
44
+
45
+ This function executes ffprobe to extract detailed information about a media file,
46
+ including format metadata (container format, duration, bitrate) and stream information
47
+ (codecs, resolution, sample rate, etc). The result is returned as a Python dictionary
48
+ parsed from ffprobe's JSON output.
49
+
50
+ Args:
51
+ filename: Path to the media file to analyze
52
+ cmd: Path or name of the ffprobe executable
53
+ timeout: Maximum time in seconds to wait for ffprobe to complete (default: None, wait indefinitely)
54
+ show_program_version: Show the program version
55
+ show_library_versions: Show the library versions
56
+ show_pixel_formats: Show the pixel formats
57
+ show_packets: Show the packets. Note: When both show_packets and show_frames are True,
58
+ ffprobe will output a combined "packets_and_frames" section instead of separate sections.
59
+ show_frames: Show the frames. Note: When both show_packets and show_frames are True,
60
+ ffprobe will output a combined "packets_and_frames" section instead of separate sections.
61
+ show_programs: Show the programs
62
+ show_streams: Show the streams
63
+ show_chapters: Show the chapters
64
+ show_format: Show the format
65
+ show_error: Show the error
66
+ **kwargs: Additional arguments to pass to ffprobe as command line parameters
67
+ (e.g., loglevel="quiet", skip_frame="nokey")
68
+
69
+ Returns:
70
+ A dictionary containing the parsed JSON output from ffprobe with format and stream information
71
+
72
+ Raises:
73
+ FFMpegExecuteError: If ffprobe returns a non-zero exit code
74
+ subprocess.TimeoutExpired: If the timeout is reached before ffprobe completes
75
+
76
+ Example:
77
+ ```python
78
+ info = probe("video.mp4")
79
+ print(f"Duration: {float(info['format']['duration']):.2f} seconds")
80
+ print(f"Streams: {len(info['streams'])}")
81
+ ```
82
+ """
83
+ args = [
84
+ cmd,
85
+ *(["-show_program_version"] if show_program_version else []),
86
+ *(["-show_library_versions"] if show_library_versions else []),
87
+ *(["-show_pixel_formats"] if show_pixel_formats else []),
88
+ *(["-show_packets"] if show_packets else []),
89
+ *(["-show_frames"] if show_frames else []),
90
+ *(["-show_programs"] if show_programs else []),
91
+ *(["-show_streams"] if show_streams else []),
92
+ *(["-show_chapters"] if show_chapters else []),
93
+ *(["-show_format"] if show_format else []),
94
+ *(["-show_error"] if show_error else []),
95
+ "-of",
96
+ format,
97
+ ]
98
+ args += convert_kwargs_to_cmd_line_args(kwargs)
99
+ args += [str(filename)]
100
+
101
+ logger.info("Running ffprobe command: %s", command_line(args))
102
+ p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
103
+
104
+ if timeout is not None:
105
+ out, err = p.communicate(timeout=timeout)
106
+ else:
107
+ out, err = p.communicate()
108
+
109
+ retcode = p.poll()
110
+ if p.returncode != 0:
111
+ raise FFMpegExecuteError(
112
+ retcode=retcode, cmd=command_line(args), stdout=out, stderr=err
113
+ )
114
+
115
+ return out.decode("utf-8")
116
+
117
+
118
+ def probe(
119
+ filename: str | Path,
120
+ *,
121
+ show_program_version: bool = False,
122
+ show_library_versions: bool = False,
123
+ show_pixel_formats: bool = False,
124
+ show_packets: bool = False,
125
+ show_frames: bool = False,
126
+ show_programs: bool = False,
127
+ show_streams: bool = True,
128
+ show_chapters: bool = False,
129
+ show_format: bool = True,
130
+ show_error: bool = False,
131
+ cmd: str = "ffprobe",
132
+ timeout: int | None = None,
133
+ **kwargs: Any,
134
+ ) -> dict[str, Any]:
135
+ """
136
+ Analyze a media file using ffprobe and return its metadata as a dictionary.
137
+
138
+ This function executes ffprobe to extract detailed information about a media file,
139
+ including format metadata (container format, duration, bitrate) and stream information
140
+ (codecs, resolution, sample rate, etc). The result is returned as a Python dictionary
141
+ parsed from ffprobe's JSON output.
142
+
143
+ Args:
144
+ filename: Path to the media file to analyze
145
+ cmd: Path or name of the ffprobe executable
146
+ timeout: Maximum time in seconds to wait for ffprobe to complete (default: None, wait indefinitely)
147
+ show_program_version: Show the program version
148
+ show_library_versions: Show the library versions
149
+ show_pixel_formats: Show the pixel formats
150
+ show_packets: Show the packets. Note: When both show_packets and show_frames are True,
151
+ ffprobe will output a combined "packets_and_frames" section instead of separate sections.
152
+ show_frames: Show the frames. Note: When both show_packets and show_frames are True,
153
+ ffprobe will output a combined "packets_and_frames" section instead of separate sections.
154
+ show_programs: Show the programs
155
+ show_streams: Show the streams
156
+ show_chapters: Show the chapters
157
+ show_format: Show the format
158
+ show_error: Show the error
159
+ **kwargs: Additional arguments to pass to ffprobe as command line parameters
160
+ (e.g., loglevel="quiet", skip_frame="nokey")
161
+
162
+ Returns:
163
+ A dictionary containing the parsed JSON output from ffprobe with format and stream information
164
+
165
+ Raises:
166
+ FFMpegExecuteError: If ffprobe returns a non-zero exit code
167
+ subprocess.TimeoutExpired: If the timeout is reached before ffprobe completes
168
+
169
+ Example:
170
+ ```python
171
+ info = probe("video.mp4")
172
+ print(f"Duration: {float(info['format']['duration']):.2f} seconds")
173
+ print(f"Streams: {len(info['streams'])}")
174
+ ```
175
+ """
176
+ return json.loads(
177
+ _probe(
178
+ filename,
179
+ show_program_version=show_program_version,
180
+ show_library_versions=show_library_versions,
181
+ show_pixel_formats=show_pixel_formats,
182
+ show_packets=show_packets,
183
+ show_frames=show_frames,
184
+ show_programs=show_programs,
185
+ show_streams=show_streams,
186
+ show_chapters=show_chapters,
187
+ show_format=show_format,
188
+ show_error=show_error,
189
+ cmd=cmd,
190
+ timeout=timeout,
191
+ **kwargs,
192
+ )
193
+ )
194
+
195
+
196
+ def probe_obj(
197
+ filename: str | Path,
198
+ *,
199
+ show_program_version: bool = False,
200
+ show_library_versions: bool = False,
201
+ show_pixel_formats: bool = False,
202
+ show_packets: bool = False,
203
+ show_frames: bool = False,
204
+ show_programs: bool = False,
205
+ show_streams: bool = True,
206
+ show_chapters: bool = False,
207
+ show_format: bool = True,
208
+ show_error: bool = False,
209
+ cmd: str = "ffprobe",
210
+ timeout: int | None = None,
211
+ **kwargs: Any,
212
+ ) -> ffprobeType | None:
213
+ """
214
+ Analyze a media file using ffprobe and return its metadata as a dataclass.
215
+
216
+ This function executes ffprobe to extract detailed information about a media file,
217
+ including format metadata (container format, duration, bitrate) and stream information
218
+ (codecs, resolution, sample rate, etc). The result is returned as a Python dataclass
219
+ parsed from ffprobe's output.
220
+
221
+ Args:
222
+ filename: Path to the media file to analyze
223
+ cmd: Path or name of the ffprobe executable
224
+ timeout: Maximum time in seconds to wait for ffprobe to complete (default: None, wait indefinitely)
225
+ show_program_version: Show the program version
226
+ show_library_versions: Show the library versions
227
+ show_pixel_formats: Show the pixel formats
228
+ show_packets: Show the packets. Note: When both show_packets and show_frames are True,
229
+ ffprobe will output a combined "packets_and_frames" section instead of separate sections.
230
+ show_frames: Show the frames. Note: When both show_packets and show_frames are True,
231
+ ffprobe will output a combined "packets_and_frames" section instead of separate sections.
232
+ show_programs: Show the programs
233
+ show_streams: Show the streams
234
+ show_chapters: Show the chapters
235
+ show_format: Show the format
236
+ show_error: Show the error
237
+ **kwargs: Additional arguments to pass to ffprobe as command line parameters
238
+ (e.g., loglevel="quiet", skip_frame="nokey")
239
+
240
+ Returns:
241
+ A dataclass containing the parsed ffprobe output
242
+
243
+ Raises:
244
+ FFMpegExecuteError: If ffprobe returns a non-zero exit code
245
+ subprocess.TimeoutExpired: If the timeout is reached before ffprobe completes
246
+
247
+ Example:
248
+ ```python
249
+ info = probe_obj("video.mp4")
250
+ print(f"Duration: {float(info.format.duration):.2f} seconds")
251
+ print(f"Streams: {len(info.streams)}")
252
+ ```
253
+ """
254
+
255
+ xml = _probe(
256
+ filename,
257
+ show_program_version=show_program_version,
258
+ show_library_versions=show_library_versions,
259
+ show_pixel_formats=show_pixel_formats,
260
+ show_packets=show_packets,
261
+ show_frames=show_frames,
262
+ show_programs=show_programs,
263
+ show_streams=show_streams,
264
+ show_chapters=show_chapters,
265
+ show_format=show_format,
266
+ show_error=show_error,
267
+ cmd=cmd,
268
+ timeout=timeout,
269
+ format="xml",
270
+ **kwargs,
271
+ )
272
+ return parse_ffprobe(xml)