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.
- typed_ffmpeg/__init__.py +2 -1
- typed_ffmpeg/_version.py +21 -0
- typed_ffmpeg/compile/compile_cli.py +28 -4
- typed_ffmpeg/compile/compile_python.py +3 -4
- typed_ffmpeg/ffprobe/__init__.py +0 -0
- typed_ffmpeg/ffprobe/parse.py +133 -0
- typed_ffmpeg/ffprobe/probe.py +272 -0
- typed_ffmpeg/ffprobe/schema.py +455 -0
- typed_ffmpeg/ffprobe/xml2json.py +70 -0
- {typed_ffmpeg_compatible-3.0.2a0.dist-info → typed_ffmpeg_compatible-3.2.1.dist-info}/METADATA +36 -17
- typed_ffmpeg_compatible-3.2.1.dist-info/RECORD +57 -0
- {typed_ffmpeg_compatible-3.0.2a0.dist-info → typed_ffmpeg_compatible-3.2.1.dist-info}/WHEEL +2 -1
- typed_ffmpeg_compatible-3.2.1.dist-info/top_level.txt +1 -0
- typed_ffmpeg/common/cache/.gitignore +0 -3
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/acrossover.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/afir.json +0 -9
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/aiir.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/ainterleave.json +0 -9
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/amerge.json +0 -9
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/amix.json +0 -9
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/amovie.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/anequalizer.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/aphasemeter.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/asegment.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/aselect.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/asplit.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/astreamselect.json +0 -9
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/bm3d.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/channelsplit.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/concat.json +0 -9
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/decimate.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/ebur128.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/extractplanes.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/fieldmatch.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/guided.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/headphone.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/hstack.json +0 -9
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/interleave.json +0 -9
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/join.json +0 -9
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/libplacebo.json +0 -9
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/limitdiff.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/mergeplanes.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/mix.json +0 -9
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/movie.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/premultiply.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/segment.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/select.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/signature.json +0 -9
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/split.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/streamselect.json +0 -9
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/unpremultiply.json +0 -6
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/vstack.json +0 -9
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/xmedian.json +0 -9
- typed_ffmpeg/common/cache/FFMpegFilterManuallyDefined/xstack.json +0 -9
- typed_ffmpeg/common/cache/list/filters.json +0 -90747
- typed_ffmpeg/common/cache/list/options.json +0 -1694
- typed_ffmpeg/probe.py +0 -75
- typed_ffmpeg_compatible-3.0.2a0.dist-info/RECORD +0 -95
- typed_ffmpeg_compatible-3.0.2a0.dist-info/entry_points.txt +0 -3
- {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",
|
typed_ffmpeg/_version.py
ADDED
@@ -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)
|