🔥 MetaForge — your video's preflight, before you hit upload

One upload → every publish asset. And the gate that stops the bad ones.

MetaForge takes a video (or a URL) and returns everything you need to publish it: checked captions, a moderation verdict you can trust, SEO title, description and hashtags, platform-native social posts, translations, and voiceovers — including multi-speaker voiceovers that match each person in your video, fitted to the exact length of your video.

Then it does the thing almost no creator tool does: it can tell you "no."

Like a pilot's preflight checklist — but for hitting "upload."


Why MetaForge exists

Publishing a video today means juggling 5–6 disconnected tools: a transcriber, a moderation checker, an SEO generator, a hashtag tool, a caption translator, a TTS service. Each one generates. None of them stops you. A flagged word in your transcript flows silently into SEO copy, translations, and voiceovers — and you find out from a demonetization notice after the fact.

MetaForge collapses the whole toolchain into one run, with a safety gate at its heart:

video.mp4 (or a URL)
  ├─ 1. 🎧 Speech-to-Text   → timed captions (.srt)
  ├─ 2. ✋ You               → verify/edit captions (human checkpoint)
  ├─ 3. 🛡️ THE GATE          → profanity / unsafe-content check
  │                            ⛔ pipeline PAUSES here if flagged
  ├─ 4. 📝 LLM               → SEO title + description + hashtags
  ├─ 5. 📣 LLM               → platform posts (TikTok / Instagram / X / LinkedIn)
  ├─ 6. 🌐 LLM               → translations (54 languages)
  └─ 7. 🎙️ Cloud TTS        → voiceovers, duration-fitted to your video

The gate is the product. Every stage downstream of the transcript is only as safe as the transcript itself — so MetaForge checks it before spending a single generation token. Flagged content pauses the pipeline with a redacted preview and a "fix → re-check → continue" editor, instead of quietly generating metadata for content that gets demonetized.

And the gate can't be outlived: verdicts are bound to the sha256 of the text they checked, so the moment you edit a caption, the old "passed" verdict is invalid and the gate re-checks automatically. Every consumer of transcript text goes through the same gate module — CI fails if a new consumer tries to skip it.


What you get, feature by feature

Feature What it does for you
URL ingest Paste a YouTube/Atom-feed/direct-MP4 link instead of downloading and re-uploading. Allowlist-enforced, no private-network access.
Captions (STT) Accurate timed .srt from audio — in the language your video is spoken in (55+ languages: pick Hindi, Japanese, Arabic, … and captions come out in that language). Long videos (>55 s sync-API limit, measured) are auto-chunked on silence and stitched back with corrected timestamps. No audio? Paste a script and the pipeline continues.
Moderation gate One inline check stands between your transcript and every generated asset. Offline dictionary layer (free, no key) + optional fail-closed AI layer. Flagged → redacted preview, fix-and-resume editor — never silent generation.
SEO package Title, description, hashtags in one shot, plus platform-native post variants with per-platform character counts. Every output derivative-moderated before it reaches you.
54-language translations Title/description and captions in every language Google Cloud TTS can voice. Each translation re-checked by the gate before it's written.
Voiceovers Same captions, spoken, returned inline as playable audio. Pick the persona: male or female voice, an emotional tone (cheerful, excited, sad, calm), and a budget tier with visible price labels ($4 / $16 / $30 per 1M chars).
Multi-speaker voiceovers Speechmatics diarization detects who speaks when; each speaker gets a distinct, consistent voice. The "Duck and Cover" clip comes out as two distinct voices talking.
Duration-fitted audio Voiceovers are calibrated to your video's length (rate-clamped 0.7×–1.6×, never chipmunk) then padded/trimmed to match it to the frame — the track drops onto your video at full length, ready to mux.
Dubbed video Pick a language, get back an .mp4 with that language's narration muxed in — audio stream replaced, video stream untouched.
Preflight report The deliverable: gate verdict, publish-readiness checklist, SEO package, char counts, stage timings, and a measured cost estimate of the run (not a guess).
Live pipeline UI Drop a video, watch every stage light up over server-sent events; pick your voiceover languages from toggling chips.

The two API keys (and the optional third)

Key Required What it powers Where to get it
GOOGLE_CLOUD_API_KEY ✅ Speech-to-Text + Cloud TTS (restrict it to those two APIs) Cloud Console → Credentials
GEMINI_API_KEY or FEATHERLESS_API_KEY ✅ (one) SEO, hashtags, platform posts, translations AI Studio / featherless.ai
SPEECHMATICS_API_KEY optional Alternative STT: no 55 s limit + speaker diarization (multi-person voiceovers) portal.speechmatics.com

Setup: cp .env.example .env, fill the keys, done. Keys travel in x-goog-api-key / Authorization headers — never in URLs — and every error boundary redacts them (a leak caught in self-audit: a failed STT call once echoed the key back in an error body; that whole leak class is closed and tested).

Auth alternatives for production: Workload Identity on Cloud Run (no string secret at all) or GOOGLE_APPLICATION_CREDENTIALS_JSON. The local profanity matcher needs no key and no network.


💰 How the cost optimization works

Every stage spends paid-API tokens, so MetaForge treats cost as a first-class output — measured, optimized, and cached. A 55-second video costs ~$0.03 cold and $0.00 on re-runs (live-measured; older 10-minute estimate was $0.70, before tier-aware voices and caching).

1. Gate-first ordering (don't pay for doomed work)

The moderation gate runs before any generation. Flagged content costs $0.00 in LLM/TTS tokens — the pipeline stops at the free dictionary check.

2. Tier-aware voice selection (METAFORGE_TTS_TIER, default standard)

Google TTS prices by voice tier: Standard $4 / 1M chars, Wavenet/Neural2 $16, Chirp3-HD $30 — a 7.5× swing for the same words. MetaForge generates its voice catalog live from Google's /v1/voices endpoint (55 languages, tier-classified, unusable voices filtered) and resolves each language to the cheapest tier that has a voice, falling up the ladder when it doesn't (52 of 55 languages have Standard voices). One env var buys the quality back for a demo: METAFORGE_TTS_TIER=neural2.

3. Truncation-safe LLM calls

A response that hits max_tokens is wasted money — broken mid-sentence translations force retries that spend again. The LLM client detects truncation and retries once internally at 2× the token cap before surfacing an error, so a spend either produces usable output or a clear failure — never a silent half-answer that gets paid for twice.

4. Content-addressed response cache (response_cache.py)

Every paid call is keyed by the sha256 of exactly what would have been sent:

Cache Key Effect
STT audio bytes + language + provider re-uploading the same video skips transcription
TTS text + voice + rate + pitch re-rendering the same captions is free
LLM model + both prompts re-running SEO/translations is free

Live-measured: the same video re-run goes from 81 s → 10 s wall time and $0.03 → $0.00 in API spend, with identical outputs. TTL (72 h) and size budget (500 MB, throttled sweep) are env-tunable; METAFORGE_RESPONSE_CACHE=0 disables it. The cache sits behind the moderation gate — it can serve nothing the gate wouldn't have allowed fresh.

5. Measured, not estimated

The preflight report computes the run's real cost from actual inputs — video minutes × STT rate + TTS characters × tier price — and shows the breakdown, so cost stays visible as you scale to more languages.

Cost model at a glance

55 s video, Standard tier:
  STT   0.92 min × $0.024/min          ≈ $0.022
  TTS   ~1,400 chars × $4/1M           ≈ $0.006
  LLM   on your Featherless/Gemini key (metered there)
  ─────────────────────────────────────────────
  cold run                              ≈ $0.028
  re-run (cache)                        ≈ $0.000

Quick Start (local)

git clone <repo>
cd MetaForge
pip install -e .

cp .env.example .env
# Fill in GOOGLE_CLOUD_API_KEY and GEMINI_API_KEY (or FEATHERLESS_API_KEY)

metaforge check                # environment diagnostics
metaforge full-auto video.mp4  # whole pipeline

Outputs land next to your video:

video.mp4
captions/video.en.srt          ← Speech-to-Text captions
description/td.en.txt          ← SEO title + description + hashtags
description/td.fr.txt ...      ← translated TD (54 languages)
captions/video.fr.srt ...      ← translated captions
voiceovers/video.fr.mp3 ...    ← Cloud TTS voiceovers

Deploy to Cloud Run

./deploy.sh <your-project-id> us-central1

One script, keys in Secret Manager, --max-instances=1. See DEPLOY.md for key creation, API restrictions, cost sketch, and rollback.


CLI Reference

Command What it does
metaforge full-auto <video> Whole pipeline, resumable at every stage
metaforge audio <video> Extract audio + generate captions
metaforge moderate <video> Run the moderation gate on captions
metaforge desc <video> SEO title + description + hashtags
metaforge translate <video> Translate title/description to all target languages
metaforge captions <video> Translate captions to all target languages
metaforge tts <video> Multilingual voiceovers from captions
metaforge continue <video> Resume from the last completed stage
metaforge check Environment diagnostics
metaforge kb --help Brand knowledge base (optional)

Stage state lives in a .metaforge.json sidecar next to each video, so full-auto and continue always resume exactly where you left off.


Web Demo

uvicorn server:app --reload
# → http://localhost:8000

Drop a video (or paste a link), pick your voiceover languages from the chip picker, and watch the pipeline run live: transcription progress, the moderation verdict (a visible ⛔ block when content is flagged), the SEO package, inline <audio> voiceovers, and the dubbed video — no downloads. Voiceover language chips + dub language flow into the API's languages / dub_lang parameters.

On Cloud Run the same UI is served at your service URL — there's no separate frontend.

REST API

Endpoint Method Purpose
/api/health GET Config diagnostics (+ the target-language map the UI picker uses)
/api/jobs POST Upload a video → job_id
/api/jobs/url POST Ingest from a platform URL instead of an upload
/api/jobs/{id}/transcribe POST Captions → SRT inline (srt_b64); diarized speaker_turns when the provider supports it
/api/jobs/{id}/moderate POST Moderation verdict
/api/jobs/{id}/seo POST Title/description/hashtags/posts (409 if flagged)
/api/jobs/{id}/tts POST Voiceovers (languages, dub_lang, sync) → MP3s + dubbed MP4 inline
/api/jobs/{id} GET Full job state
/api/jobs/{id}/report GET The preflight report (incl. measured cost)

Every artifact is returned inline (base64 in JSON) rather than as a download — Cloud Run discards /tmp between requests, so files don't survive; the design ships every byte back to the browser.

Abuse protection: METAFORGE_DEMO_TOKEN (Bearer auth), per-IP rate limit (6/h), and a daily spend cap ($5 default) are on by default.


Configuration

All configuration lives in .env (see .env.example):

Variable Purpose
GOOGLE_CLOUD_API_KEY STT + TTS (restrict to those APIs)
GEMINI_API_KEY / FEATHERLESS_API_KEY LLM generation (provider via METAFORGE_LLM_PROVIDER)
SPEECHMATICS_API_KEY Optional STT provider (diarization → multi-speaker voiceovers)
METAFORGE_SPEECHMATICS_BASE_URL Speechmatics region endpoint (default eu1)
METAFORGE_TTS_TIER Voice price tier: standard (default) / wavenet / neural2 / chirp3-hd
— (per-request) voice_gender / emotion / budget Voice persona on POST /tts: `"male"
— (per-request) language Caption language on POST /transcribe (BCP-47 or 2-letter)
METAFORGE_TTS_VOICE_<CODE> Pin a specific voice for one language
METAFORGE_RESPONSE_CACHE / _TTL_H / _MAX_MB Response cache on/off (default on), TTL, size budget
METAFORGE_SYNC_RATE_FLOOR / _CEIL Narration rate clamp (0.7×–1.6×)
METAFORGE_MODERATION_API_KEY Optional AI moderation layer (fail-closed)
METAFORGE_DEMO_TOKEN / _RATE_LIMIT_PER_HOUR / _DAILY_USD_CAP Demo auth + abuse limits
METAFORGE_MAX_UPLOAD_MB / _MAX_DURATION_SEC Upload + duration limits (200 MB / 600 s)

Architecture

See ARCHITECTURE.md for the module map, data flow, the moderation gate, and the unified LLM/STT/TTS client pattern.

metaforge/
├── cli.py              # Click CLI — every command
├── core/
│   ├── audio.py        # FFmpeg extraction + STT (chunking + response cache)
│   ├── speechmatics.py # Speechmatics batch v2 (diarization, no 55s limit)
│   ├── sync.py         # rate calibration + exact-duration fit + mux
│   ├── ingest.py       # platform URL ingestion (allowlist + SSRF guards)
│   ├── response_cache.py  # content-addressed paid-call cache
│   ├── description.py  # SEO title/description/hashtags
│   ├── translate.py / captions.py / caption_translate.py
│   ├── tts.py          # Cloud TTS (tier-aware voices, multi-speaker)
│   ├── llm.py          # unified LLM client (cache + truncation retry)
│   ├── constants.py    # env-driven config
│   └── utils.py        # metadata sidecar, retry helpers
├── moderation/         # two-layer moderation (dictionary + optional AI)
└── kb/                 # ChromaDB + LlamaIndex brand context (optional)
server.py               # FastAPI orchestrator + demo UI
static/index.html       # demo UI

Engineering guarantees

  • 138 offline tests — no keys, no network, hermetic; the gate invariant (flagged → nothing generated; un-moderated → 409; edited SRT → re-checked) is tested end-to-end in both CLI and API paths.
  • 0 known secret-leak paths — header auth everywhere, one redaction function at every error boundary, including the 500 handler.
  • scripts/check_gate_coverage.py fails CI if a new consumer of transcript text skips the gate.
  • Deterministic demo — committed clean + flagged clips with a labeled offline cache mode; the cached flagged sample still 409s, because the cache sits behind the same gate.

Contributing

See CONTRIBUTING.md.

License

MIT

Acknowledgements

Built With

Share this project:

Updates

Submission history