Skip to content

ffmpeg_media_type

FFProbeInfo dataclass

FFProbeInfo(
    *,
    format: FFProbeFormat,
    streams: tuple[FFProbeStream, ...]
)

The media information return by ffprobe.

format instance-attribute

format: FFProbeFormat

The media format info.

streams instance-attribute

streams: tuple[FFProbeStream, ...]

The media streams info.

MediaInfo dataclass

MediaInfo(
    *,
    type: Literal["image", "video", "audio"],
    width: int = 0,
    height: int = 0,
    duration: float = 0,
    format: str | None = None,
    size: int = 0,
    suggest_ext: str | None = None
)

The Basic Media info.

duration class-attribute instance-attribute

duration: float = 0

The media duration.

format class-attribute instance-attribute

format: str | None = None

The media format.

height class-attribute instance-attribute

height: int = 0

The media height.

size class-attribute instance-attribute

size: int = 0

The media size.

suggest_ext class-attribute instance-attribute

suggest_ext: str | None = None

The suggested file extension.

type instance-attribute

type: Literal['image', 'video', 'audio']

The media type.

width class-attribute instance-attribute

width: int = 0

The media width.

detect

detect(uri: str | Path) -> MediaInfo

Detect the media type of a file.

Parameters:

Name Type Description Default
uri str | Path

the URI of the file

required

Returns:

Type Description
MediaInfo

the media type information

Raises:

Type Description
FFmpegMediaTypeError

If the ffmpeg command fails.

FFMpegMediaCorruptedError

If the media file is corrupted.

Source code in src/ffmpeg_media_type/info.py
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
def detect(uri: str | Path) -> MediaInfo:
    """
    Detect the media type of a file.

    Args:
        uri: the URI of the file

    Returns:
        the media type information

    Raises:
        FFmpegMediaTypeError: If the ffmpeg command fails.
        FFMpegMediaCorruptedError: If the media file is corrupted.
    """
    uri = str(uri)
    info = ffprobe(uri)
    current_ext = extract_file_extension_from_uri(uri)

    format_name = info.format.format_name
    duration = info.format.duration

    # NOTE: handle ffmpeg's image compatibility
    if format_name == "image2":
        if not info.streams[0].codec_name:
            raise FFMpegMediaCorruptedError(f"Corrupted image file {uri}")
        format_name = info.streams[0].codec_name

    # NOTE: detect file extension
    if format_name in KNOWN_CODEC_EXTS:
        common_exts = KNOWN_CODEC_EXTS[format_name]
    elif support_info := load(FFMpegSupport, format_name):
        common_exts = support_info.common_exts
    else:
        common_exts = ()

    if current_ext in common_exts:
        suggest_ext = current_ext
    elif common_exts:
        suggest_ext = common_exts[0]
    else:
        suggest_ext = None

    # NOTE: we classify gif and mjpeg as imageg
    if not duration or format_name in ("gif", "mjpeg"):
        if not (info.streams[0].width and info.streams[0].height):
            raise FFMpegMediaCorruptedError(f"Corrupted image file {uri}")

        return MediaInfo(
            type="image",
            width=info.streams[0].width or 0,
            height=info.streams[0].height or 0,
            duration=float(duration) if duration is not None else 0,
            format=format_name,
            size=int(info.format.size) if info.format.size is not None else 0,
            suggest_ext=suggest_ext,
        )

    for stream in info.streams:
        # NOTE: if there is at least one video stream, the media is video
        if stream.codec_type == "video" and format_name not in ("mp3",):
            width = stream.width
            height = stream.height

            if not (width and height):
                raise FFMpegMediaCorruptedError(f"Corrupted image file {uri}")

            return MediaInfo(
                type="video",
                width=width or 0,
                height=height or 0,
                duration=float(duration) if duration is not None else 0,
                format=format_name,
                size=int(info.format.size) if info.format.size is not None else 0,
                suggest_ext=suggest_ext,
            )

    # NOTE: if there is no video stream, the media is audio
    return MediaInfo(
        type="audio",
        width=0,
        height=0,
        duration=float(duration) if duration is not None else 0,
        format=format_name,
        size=int(info.format.size) if info.format.size is not None else 0,
        suggest_ext=suggest_ext,
    )

ffprobe

ffprobe(input_url: str | Path) -> FFProbeInfo

Get media information using FFprobe.

Parameters:

Name Type Description Default
input_url str | Path

the URI of the media file

required

Returns:

Type Description
FFProbeInfo

the media information

Raises:

Type Description
FFmpegMediaTypeError

If the FFprobe command fails.

Source code in src/ffmpeg_media_type/utils/ffprobe.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
def ffprobe(input_url: str | Path) -> FFProbeInfo:
    """
    Get media information using FFprobe.

    Args:
        input_url: the URI of the media file

    Returns:
        the media information

    Raises:
        FFmpegMediaTypeError: If the FFprobe command fails.
    """

    input_url = hotfix_animate_webp(input_url)

    # Construct the FFprobe command with JSON output format
    ffprobe_cmd = ["ffprobe"] + [
        "-v",
        "error",
        "-show_format",
        "-show_streams",
        "-of",
        "json",
        str(input_url),
    ]

    # Execute the FFprobe command and capture the output
    output = call(ffprobe_cmd)
    probe_info = from_dict(FFProbeInfo, json.loads(output))

    return probe_info

generate_thumbnail

generate_thumbnail(
    video_path: str | Path,
    suffix: str = ".png",
    *,
    width: int = 320,
    height: int = -1,
    time_offset: float = 0
) -> str

Generate a thumbnail from a video file at a specified time offset.

Parameters:

Name Type Description Default
video_path str | Path

the path to the video file

required
suffix str

the suffix of the generated thumbnail

'.png'
width int

the width of the generated thumbnail

320
height int

the height of the generated thumbnail

-1
time_offset float

the time offset in seconds to generate the thumbnail

0

Raises:

Type Description
FFmpegMediaTypeError

If the ffmpeg command fails.

Returns:

Type Description
str

the path to the generated thumbnail

Source code in src/ffmpeg_media_type/utils/thumbnail.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
def generate_thumbnail(
    video_path: str | Path,
    suffix: str = ".png",
    *,
    width: int = 320,
    height: int = -1,
    time_offset: float = 0,
) -> str:
    """
    Generate a thumbnail from a video file at a specified time offset.

    Args:
        video_path: the path to the video file
        suffix: the suffix of the generated thumbnail
        width: the width of the generated thumbnail
        height: the height of the generated thumbnail
        time_offset: the time offset in seconds to generate the thumbnail

    Raises:
        FFmpegMediaTypeError: If the ffmpeg command fails.

    Returns:
        the path to the generated thumbnail
    """

    video_path = hotfix_animate_webp(video_path)

    thumbnail_path = create_temp_filename(suffix)
    ffmpeg_cmd = ["ffmpeg"] + [
        "-y",  # Overwrite output file if it exists
        "-i",
        str(video_path),  # Input video path
        "-ss",
        str(time_offset),  # Time offset (seek to the specified position)
        "-vframes",
        "1",  # Number of frames to output
        "-vf",
        f"scale={width}:{height}",  # Thumbnail size (width: 320, height: proportional)
        "-q:v",
        "2",  # Quality (2 - high, 5 - low)
        thumbnail_path,  # Output thumbnail path
    ]

    call(ffmpeg_cmd)

    return thumbnail_path