Aller au contenu

Sonder une vidéo

Objectif : extraire les métadonnées d'une vidéo uploadée avec ffprobe.

Ce que vous allez apprendre : probe_video. La fonction lance ffprobe
(lecture seule) sur le fichier d'origine d'une vidéo et en extrait durée,
dimensions, codecs et conteneur. C'est l'étape qui précède le transcodage :
on inspecte la source avant de la convertir.

Premier palier du niveau avancé de la progression vidéo, la bascule vers le
transcodage réel. Après le niveau intermédiaire.

ffprobe requis

Ce palier exécute ffprobe. Installez ffmpeg/ffprobe et renseignez au
besoin FORGE_VIDEO_FFPROBE_BIN. ffprobe lit la source ; il ne
transcode pas.

Ce que ce starter montre

  • la sonde d'un fichier via probe_video (ffprobe, lecture seule) ;
  • l'extraction de duration_seconds, width, height, video_codec,
    audio_codec, container ;
  • des réponses pédagogiques : 404 (UUID inconnu), 502 (sonde échouée).

La table videos est garantie par la migration fournie plus bas.

Classes Forge utilisées

Classe / fonction Rôle dans ce starter Référence
VideoRepository.get_by_uuid Retrouver le chemin du fichier d'origine. Parcours vidéo
forge_mvc_video.probe.probe_video Lancer ffprobe et extraire les métadonnées. Parcours vidéo
VideoProbeError Sonde échouée (source illisible / non vidéo). Parcours vidéo

Tester

forge db:init
forge run

Avec une vidéo uploadée (niveau intermédiaire) et ffprobe installé, ouvrez
https://localhost:8000/video-probe/<uuid> : la réponse JSON donne les
métadonnées extraites du fichier.

Le contrôleur

# mvc/controllers/video_probe_controller.py
from pathlib import Path

from core.http.request import Request
from core.http.response import Response
from core.mvc.controller.base_controller import BaseController

from forge_mvc_video.config import load_video_config
from forge_mvc_video.probe import VideoProbeError, probe_video
from forge_mvc_video.storage.repository import VideoRepository


_STORAGE_NOT_READY = {
    "error": "video_storage_not_ready",
    "message": (
        "La table videos n'est pas encore disponible. "
        "Applique la migration Forge Vidéo (forge video:init)."
    ),
}


class VideoProbeController(BaseController):

    @staticmethod
    def index(request: Request) -> Response:
        uuid = request.route("uuid")
        try:
            video = VideoRepository().get_by_uuid(uuid)
        except Exception:
            return Response.json(_STORAGE_NOT_READY, status=503)
        if video is None:
            return Response.json(
                {"error": "video_not_found", "uuid": uuid}, status=404
            )
        cfg = load_video_config()
        path = str(Path(cfg.storage_root) / video["original_path"])
        try:
            meta = probe_video(path, config=cfg)
        except VideoProbeError as exc:
            return Response.json(
                {"error": "probe_failed", "message": str(exc)}, status=502
            )
        return Response.json({
            "uuid": uuid,
            "metadata": {
                "duration_seconds": meta.duration_seconds,
                "width": meta.width,
                "height": meta.height,
                "video_codec": meta.video_codec,
                "audio_codec": meta.audio_codec,
                "container": meta.container,
            },
        })

Comprendre ce code

  • On retrouve le chemin du fichier via la ligne videos (original_path,
    relatif à storage_root), jamais un chemin fourni par le client.
  • probe_video(path, config=cfg) exécute ffprobe en lecture seule : il
    n'altère pas le fichier. Une VideoProbeError (source illisible, pas une vidéo)
    devient un 502 clair.
  • C'est l'inspection préalable : le transcodage proprement dit vient au palier
    suivant.

La migration

La sonde lit le chemin du fichier d'origine depuis la table videos. Créez-la
si elle n'existe pas déjà. CREATE TABLE IF NOT EXISTS reste idempotent.

-- mvc/migrations/20260601230000_create_videos.sql
CREATE TABLE IF NOT EXISTS videos (
    id               BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    uuid             CHAR(36)        NOT NULL,
    title            VARCHAR(255)    NULL,
    original_path    VARCHAR(500)    NOT NULL,
    mp4_path         VARCHAR(500)    NULL,
    poster_path      VARCHAR(500)    NULL,
    mime_type        VARCHAR(120)    NULL,
    size_bytes       BIGINT UNSIGNED NOT NULL,
    duration_seconds INT UNSIGNED    NULL,
    width            INT UNSIGNED    NULL,
    height           INT UNSIGNED    NULL,
    status           VARCHAR(30)     NOT NULL,
    error_message    TEXT            NULL,
    created_at       DATETIME(6)     NOT NULL,
    updated_at       DATETIME(6)     NOT NULL,
    PRIMARY KEY (id),
    UNIQUE KEY uq_videos_uuid (uuid),
    INDEX idx_videos_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

La route

Ajoutez la route paramétrée dans le groupe public de mvc/routes.py.

# mvc/routes.py
from mvc.controllers.video_probe_controller import VideoProbeController

with router.group("", public=True) as public:
    public.add("GET", "/video-probe/{uuid}", VideoProbeController.index, name="video_probe_index")

À retenir

  • probe_video lance ffprobe (lecture seule) et extrait les métadonnées.
  • On sonde la source à partir de son chemin stocké, pas d'une entrée client.
  • La sonde précède le transcodage : on inspecte avant de convertir.

Après ce starter

Vous savez inspecter une source. La suite : la transcoder en MP4.

Transcoder une vidéo