Skip to main content

media-gate

Fail-closed media QC for content pipelines: catches black video, silent audio, and duplicate/near-duplicate images before they go out the door.

$ media-gate check output/final_render.mp4
FAIL: final_render.mp4
  FAIL: SILENT_AUDIO — mean_volume -61.2 dB (at/below -50.0 dB threshold)
  PASS: no black intervals detected

Why this exists

If you run any kind of automated content pipeline — video generation, faceless-channel automation, bulk image export, TTS narration — you will, eventually, publish something broken. Not because the pipeline is bad, but because silent failures are the default failure mode of media processing:

  • A TTS/rendering step silently drops the audio track and the video looks fine in a file browser (correct duration, correct thumbnail) right up until someone presses play.
  • A render step crashes partway through and produces a video that is technically valid (openable, correct container) but is just black frames.
  • A "new" asset is actually the same image re-exported, resized, or re-encoded from something already published — byte-different, but visually identical. A checksum won't catch it; a platform's spam/dedup detection will.

Each of these is invisible to a naive if file exists and file size > 0 check, and each one is exactly the kind of thing that erodes trust with an audience or gets an account flagged. media-gate gives you a small, dependency-light, scriptable tool to run as a pre-publish gate so these never make it out silently.

Fail-closed by design: if a check can't produce a confident answer (e.g. ffmpeg is missing, or a video's duration can't be read), the result leans toward failure/warning rather than a silent pass. The entire point of a QC gate is defeated if "I couldn't check" is treated the same as "it's fine."

Install

pip install media-gate

This installs the Python dependencies (Pillow, imagehash). The video checks additionally require ffmpeg and ffprobe on your PATH:

CLI usage

# Video: checks for black frames + silent/missing audio
media-gate check my_video.mp4

# Image: computes a perceptual hash, and (optionally) compares it against
# already-published images to catch near-duplicates
media-gate check new_thumbnail.png --compare-against published/*.png

# Machine-readable output for use in scripts/CI
media-gate check my_video.mp4 --json
echo $?   # 0 = PASS, 1 = FAIL, 2 = error (file not found, bad args, ...)

Useful flags (see media-gate check --help for the full list):

Flag Default Meaning
--silence-threshold-db -50.0 mean_volume at/below this dB counts as silent
--max-black-fraction 0.20 fail if more than this fraction of the video is black
--hash-algorithm phash perceptual hash algorithm (phash, dhash, ahash, whash)
--max-hamming-distance 6 bit distance at/below which two images count as near-duplicates

Exit codes are designed for shell/CI gating: 0 = PASS, non-zero = do not publish.

Library usage

from media_gate import dedup, video_qc

# --- video ---
audio = video_qc.check_silent_audio("clip.mp4")
black = video_qc.check_black_frames("clip.mp4")
if not audio.passed or not black.passed:
    raise RuntimeError(f"QC failed: {audio.reason} / {black.reason}")

# --- images ---
known = dedup.build_registry(["published/a.png", "published/b.png"])
matches = dedup.find_duplicates("new/candidate.png", known)
if matches:
    fp, distance = matches[0]
    print(f"near-duplicate of {fp.path} (hamming distance {distance})")

How the checks work

Black video — runs ffmpeg's blackdetect video filter (-vf blackdetect=d=...:pic_th=...:pix_th=...) and parses the black_start/black_end/black_duration lines it writes to stderr. Fails if the total black duration exceeds --max-black-fraction of the video's total length.

Silent audio — first checks (via ffprobe) that the file has an audio stream at all (a missing stream is NO_AUDIO_TRACK, a distinct and common failure mode from a present-but-silent track). Then runs ffmpeg's volumedetect audio filter and parses mean_volume/max_volume from stderr; a mean volume at or below the threshold is SILENT_AUDIO.

Duplicate images — computes a perceptual hash (default: phash, via the imagehash library) for each image and compares hashes by Hamming distance. Unlike a checksum, this catches the same image re-encoded, resized, or lightly edited — the actual failure mode automated pipelines produce, since two independent generations are almost never byte-identical but are often visually identical.

Development

git clone https://github.com/luandv92/media-gate
cd media-gate
pip install -e ".[dev]"
pytest

License

MIT — see LICENSE.

Metadata

Release files for media-gate 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for media-gate 0.1.0
File Size Uploaded
media_gate-0.1.0.tar.gz 18.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for media-gate 0.1.0
File Interpreter ABI Platform
media_gate-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 31.9 kB

Release files / media_gate-0.1.0.tar.gz

Download URL media_gate-0.1.0.tar.gz
Size 18.3 kB
Tags Source
SHA-256 checksum
How to use checksums
707cf1a65cd06d8e1703e7a8012f9e28d5397252b1d481d4992cab5fdae5dd92
BLAKE2b-256 checksum
How to use checksums
5585716ab99ef2fd89a6a88a537ecd91506cdbc8358a0c164917bcb29e2a953c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 19, 2026.

Transparency log

Release files / media_gate-0.1.0-py3-none-any.whl

Download URL media_gate-0.1.0-py3-none-any.whl
Size 13.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fc76f861f939f57d8a195ce0f162cb227c7c7b3ec5e0bf79235cbcb3b6f668c7
BLAKE2b-256 checksum
How to use checksums
3f654abd0a6328194618f0c6187ce2c7e14b95f5f1391bba3f0d10217e510eb9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 19, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page