Check a Format run's video with ffprobe: duration and audio stream

Before you publish a Format run's clip, run ffprobe on primary_output_url, confirm an audio stream exists and compare its length with duration_ms.

4 min readSume
All posts

Before you publish a Format run's video, run ffprobe on primary_output_url and check two things: that the file has an audio stream, and that its duration matches the duration_ms on the matching artifact. A completed receipt proves the run finished. It does not prove the file is the one you meant to ship.

Why probe a finished run

The Sume docs say it plainly for video: read the file, not the number next to it. ffprobe takes about two seconds and is the only method that shows the difference between an assembled cut and a clip that merely has the shape of one. This matters most for voiceover work, where a silent export looks fine in a thumbnail.

What Sume checks and what it does not

Sume already guards some of this. A duration_ms inside a SumeMediaFile must agree with the artifact ledger within 10%, and a receipt that fills an assembled-video field with one of its own scene clips is rejected when two or more scenes succeeded. Those checks cover the number you are told. They do not decode the file, so the probe is your own last gate.

Checks before publishing (Sume docs, read 2026-10-05)
CheckSource of truthFails when
Run finishedstatus completedAny other terminal status
Deliverable existsprimary_output_url not nullNull on every non-completed run
Schema satisfiedoutput_error is nulloutput_error set, even on completed
Audio presentffprobe stream listNo audio codec_type in the file
Length agreesffprobe duration vs artifact duration_msMore than a tenth apart

A publish gate

The script reads the receipt, finds the artifact whose URL equals primary_output_url, probes the file and prints one verdict. It exits non-zero on a problem so a CI step or a publish job can stop. ffprobe can open an HTTPS URL directly, so there is no download step.

import json, os, subprocess, sys, urllib.request

def receipt(run_id):
    req = urllib.request.Request(
      "https://api.sume.com/v1/format-runs/" + run_id,
      headers={"Authorization": "Bearer " + os.environ["SUME_API_KEY"]})
    with urllib.request.urlopen(req) as r:
        return json.load(r)["data"]

def probe(url):
    out = subprocess.run(["ffprobe", "-v", "error", "-show_entries",
      "format=duration:stream=codec_type", "-of", "json", url],
      capture_output=True, text=True, check=True).stdout
    info = json.loads(out)
    return float(info["format"]["duration"]), [s["codec_type"] for s in info["streams"]]

def main(run_id):
    run = receipt(run_id)
    url = run["primary_output_url"]
    if run["status"] != "completed" or run["output_error"] or not url:
        sys.exit("not a deliverable: " + run["status"])
    seconds, kinds = probe(url)
    art = next((a for a in run["artifacts"] if a["url"] == url), {})
    ms = art.get("duration_ms")
    ok = "audio" in kinds and (ms is None or abs(seconds * 1000 - ms) <= 0.1 * ms)
    print("OK" if ok else "CHECK", round(seconds, 1), kinds)
    sys.exit(0 if ok else 1)

main(sys.argv[1])

Null means not measured

Treat a null duration_ms as not measured, not as zero. The script skips the length comparison in that case and still enforces the audio check. If you need to compare a standalone voiceover against the video, probe both files and require the audio to be no longer than the video.

Every clip, not only the first

Run the gate on every variant clip, not only the primary. A loop over artifacts[] with type equal to video costs seconds, and a silent or truncated variant is much cheaper to catch before it is queued for an ad account.

Sources

Related posts

More in Formats

All Formats posts

Written by Sume