Source code for annie.media.probe

"""Cheap media probing via ffprobe (service).

Browse shows ``video`` / ``audio`` tags per sample. Whether a file *has frames* is
known from the scan (a video file is present); whether it *has an audio stream* is
not, so it is probed on demand with ``ffprobe`` — much cheaper than decoding. The
command builder is pure and unit-tested; the thin runner spawns it.
"""

from __future__ import annotations

import subprocess
from typing import TYPE_CHECKING

from annie.core.runtime import hidden_console

if TYPE_CHECKING:
    from pathlib import Path


def _stream_probe_command(path: str | Path, kind: str) -> list[str]:
    """Build the ffprobe argv that lists a file's streams of ``kind`` (``a``/``v``)."""
    return [
        "ffprobe", "-v", "error",
        "-select_streams", kind,
        "-show_entries", "stream=index",
        "-of", "csv=p=0",
        str(path),
    ]  # fmt: skip


[docs] def audio_probe_command(path: str | Path) -> list[str]: """Build the ffprobe argv that lists a file's audio streams. Args: path: The media file to inspect. Returns: The ffprobe argv (no shell). Non-empty stdout means an audio stream exists. """ return _stream_probe_command(path, "a")
[docs] def video_probe_command(path: str | Path) -> list[str]: """Build the ffprobe argv that lists a file's video streams. Args: path: The media file to inspect. Returns: The ffprobe argv (no shell). Empty stdout means the file has no video stream (e.g. an ``.mp4`` that is really audio-only and yields no frames). """ return _stream_probe_command(path, "v")
def _has_stream(cmd: list[str]) -> bool: # pragma: no cover - needs system ffprobe """Run an ffprobe stream-listing command; ``True`` if it reports any stream.""" try: proc = subprocess.run( # noqa: S603 - argv list, no shell cmd, capture_output=True, text=True, check=False, creationflags=hidden_console() ) except OSError: return False return proc.returncode == 0 and bool(proc.stdout.strip())
[docs] def has_audio(path: str | Path) -> bool: # pragma: no cover - needs system ffprobe """Return whether ``path`` has at least one audio stream. Args: path: The media file to inspect. Returns: ``True`` if ffprobe reports an audio stream, ``False`` otherwise (including on any ffprobe error, so a missing ffprobe degrades to "no audio"). """ return _has_stream(audio_probe_command(path))
[docs] def has_video(path: str | Path) -> bool: # pragma: no cover - needs system ffprobe """Return whether ``path`` has at least one video stream. Args: path: The media file to inspect. Returns: ``True`` if ffprobe reports a video stream, ``False`` otherwise. """ return _has_stream(video_probe_command(path))