Skip to content
Conduit

mcp server

media-forge

alitrack/media-forgecommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

Before you start

  • uv (`curl -LsSf https://astral.sh/uv/install.sh | sh`)
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "media-forge": {
      "command": "uvx",
      "args": [
        "mediaforge"
      ]
    }
  }
}

same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12

Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs

README

synced Oct 25, 2026

MediaForge

Content → Multi-Format Media Pipeline

Turn any URL, PDF, or text into a professional podcast or video — fully offline, no NotebookLM required.

┌────────┐    ┌──────────┐    ┌─────────────┐    ┌──────────┐    ┌──────────┐
│ Ingest │ →  │ Compose  │ →  │ Synthesize  │ →  │ Render   │ →  │ Publish  │
│ URL/PDF│    │ LLM      │    │ edge-tts    │    │Playwright│    │cloudflared│
│ /text  │    │ script   │    │ MP3 concat  │    │+ ffmpeg  │    │tunnel    │
└────────┘    └──────────┘    └─────────────┘    └──────────┘    └──────────┘
   plugins         4 styles        3 backends        HTML frames       public URL

5-stage pipeline, each layer independently swappable.

Why MediaForge?

NotebookLMRemotionMediaForge
Offline❌ Cloud-only✅✅
LicenseProprietaryCompany license requiredMIT
InputsURL/PDF/textN/A (video SDK)URL/PDF/text/image/Office
OutputsAudio + VideoVideo (React)Audio + Video
TTSGoogle voicesN/Aedge-tts / Azure / CosyVoice
Video renderAI-generated overviewsReact componentPlaywright + ffmpeg
Programmable❌ Frontend only❌ Own SDK✅ MCP + CLI + Python API

MediaForge gives you the podcast-generation power of NotebookLM, the video rendering of Remotion, and the extensibility of a modular pipeline — all as open-source MIT software.

Quick Start

Zero-install (uv)

# Generate a podcast — no pip install needed
uv run --with mediaforge mediaforge podcast --url https://example.com/article

# Or from GitHub directly
uv run --with git+https://github.com/alitrack/media-forge mediaforge podcast --url https://example.com

pip install

pip install mediaforge

15-Second Demo

from mediaforge import Ingester, Composer, Synthesizer
from mediaforge.render import get_render_engine
from mediaforge.types import Source, SourceType, ContentStyle

# 1. Ingest — URL, PDF, or raw text
ingester = Ingester()
result = ingester.ingest([Source(type=SourceType.URL, content="https://example.com/article")])

# 2. Compose — LLM generates dialogue script
composer = Composer(model="deepseek-chat", api_key="...", base_url="...")
script = composer.compose(result.content, style=ContentStyle.INTERVIEW)

# 3. Synthesize — TTS generates MP3
synth = Synthesizer(backend="edge")
audio = synth.synthesize(script, "/tmp/podcast.mp3")

# 4. Render — choose your engine
# Static frames (default):
engine = get_render_engine("default")
video = engine.render(script, audio, "/tmp/video.mp4", frame_count=6)

# Animated 30fps (hyperframes):
engine = get_render_engine("hyperframes", fps=30)
video = engine.render(script, audio, "/tmp/video.mp4")

CLI

# Podcast from URL
mediaforge podcast --url https://example.com/article

# Video — static frames (default engine)
mediaforge video --text "$(cat notes.md)" --frames 8

# Video — animated hyperframes with hooks
mediaforge video --text "$(cat notes.md)" \
    --render hyperframes --fps 30 \
    --frame-style gradient \
    --watermark "MediaForge" \
    --progress-bar

# Video — vertical shorts (9:16)
mediaforge video --text "$(cat notes.md)" \
    --render hyperframes \
    --output-preset 9:16 \
    --frame-style clean

# Serve generated media
mediaforge serve /tmp/output/

Architecture

Stage 1: Ingest

Plugin-based content extraction. Add backends by implementing IngesterBackend.

Source TypeDefault BackendFallback Chain
URLtrafilatura→ readability → browser
PDFpdfplumber→ MinerU OCR
Textpassthrough—
Office(planned)MarkItDown → python-docx
Image(planned)MinerU OCR → Tesseract
# Register a custom backend
from mediaforge.ingest import IngesterBackend, BACKEND_REGISTRY, SourceType

class MyBackend(IngesterBackend):
    name = "my-backend"
    def ingest(self, source): ...

BACKEND_REGISTRY[SourceType.PDF].insert(0, MyBackend())

Stage 2: Compose

LLM-driven script generation. 4 built-in styles, configurable prompt templates.

StyleSpeakersFormatBest For
INTERVIEWHost + ExpertQ&A dialogueBlog posts, papers
TUTORIALSingle narratorStep-by-stepHow-to guides
EXPLAINERSingle narratorConcept deep-diveTechnical docs
DEBATEPro + ConPoint-counterpointOpinion pieces
script = composer.compose(
    text,
    style=ContentStyle.DEBATE,
    voice_map={"pro": "xiaoxiao", "con": "yunyang"},
)

Stage 3: Synthesize

Multi-backend TTS with per-segment voice assignment.

BackendQualityCostLicense
edge-ttsGoodFreeMicrosoft (gratis)
Azure SpeechExcellentPaidCommercial
CosyVoice 3ExcellentFree (local GPU)Apache 2.0
Kokoro-82M ⓘGood (EN)Free (CPU/GPU)Apache 2.0
synth = Synthesizer(backend="azure", azure_key="...", azure_region="eastus")

ⓘ Kokoro-82M excels at English content (200× real-time on GPU, 54 voices). Not recommended for mixed Chinese-English text. Integration guide: docs/kokoro-tts-backend.md

Stage 4: Render

Pluggable render engines. Choose static or animated output.

Default Engine (static frames)

HTML frames → Playwright screenshots → ffmpeg video compositing.

from mediaforge.render import get_render_engine
engine = get_render_engine("default")
video = engine.render(script, audio, "output.mp4", frame_count=8)

Hyperframes Engine (CSS-animated, 30fps)

LLM-generated animated HTML → Playwright per-frame capture → ffmpeg compositing. Smooth transitions between dialogue segments with fade-in, slide, and highlight effects.

New in v2: Render hooks, frame styling, export engine selection.

from mediaforge.render import get_render_engine
from mediaforge.render.hooks import HookRegistry
from mediaforge.render.builtin_hooks import watermark_hook, progress_bar_hook

# Build hooks
hooks = HookRegistry()
hooks.register("post-frame", watermark_hook("MediaForge"))
hooks.register("post-frame", progress_bar_hook())

# Animated 30fps with watermark + progress bar + gradient style
engine = get_render_engine(
    "hyperframes",
    fps=30,
    hooks=hooks,
    style="gradient",        # clean | dark | gradient
    export_engine="ffmpeg",  # ffmpeg | webcodecs
)
video = engine.render(script, audio, "output.mp4")

Render hooks inject effects at 4 pipeline stages: pre-frame → post-frame (modify HTML) → pre-ffmpeg (overlay images) → post-output

Built-in hooks: watermark_hook(), progress_bar_hook(), qrcode_hook().

Frame styles (inspired by Recordly) via style= parameter: clean, dark, gradient — or pass a FrameStyle object with custom gradients, shadows, rounded corners, and aspect ratios (16:9/1:1/9:16/4:3).

Export engines: ffmpeg (stable, default) or webcodecs (fast preview via Chromium WebCodecs).

Session files: Save pipeline state as .mediaforge YAML — resume from any stage.

from mediaforge.session import save_session, load_session

state = PipelineState(stage="synthesized", script={...})
save_session(state, "demo.mediaforge")

Engine Registry

EnginenameOutputBest For
DefaultEngine"default"Static frames (30s)Quick drafts, low CPU
Hyperframes"hyperframes"30fps animation (90s)Final publish, demos
HtmlVideoTemplateEngine"html-video-templates"GSAP template animationProfessional visuals, 23 templates

Html-Video Templates Engine (GSAP, 3rd-party templates)

Render scripts using professional HTML/CSS/GSAP templates from the open-source html-video project (Apache-2.0, 3.4k⭐).

from mediaforge.render import get_render_engine

# Swiss grid layout — clean typography
engine = get_render_engine("html-video-templates", template="swiss-grid")
video = engine.render(script, audio, "output.mp4")

# Or kinetic-type for punchy text animations
engine = get_render_engine("html-video-templates", template="kinetic-type")
video = engine.render(script, audio, "output.mp4")
# CLI
mediaforge video --text "$(cat notes.md)" \
    --render html-video-templates \
    --template warm-grain

# List available templates
mediaforge list-templates

First run downloads GSAP and fonts offline (no CDN dependency):

Rendering Quality

For crisp, ghost-free text in hyperframes output, the engine applies these optimizations by default:

FixDetail
Font size48px body text on 1920×1080 canvas
Font smoothing-webkit-font-smoothing: antialiased + text-rendering: optimizeLegibility
Text contrastPure white (#ffffff) on dark gradient background
Segment transitionsopacity + transform only (no all), past segments fully hidden (opacity 0)
EncodingFFmpeg -preset medium -crf 18 preserves text detail

If you encounter blurry text or ghosting, check: nested <style> tags, font size below 48px, missing font smoothing, low contrast text colors, or aggressive encoding presets (-preset fast -crf 23).

Add custom engines by implementing the RenderEngine protocol:

from mediaforge.render import RenderEngine, register_engine

class MyEngine:
    name = "my-engine"
    def render(self, script, audio_path, output_path, **kwargs): ...
    @classmethod
    def available(cls): return True

register_engine(MyEngine)

Stage 5: Publish

Expose generated media via cloudflared tunnel — get a public URL instantly.

from mediaforge import Publisher
pub = Publisher()
url = pub.publish("/tmp/podcast.mp3")
# → https://xxx.trycloudflare.com/podcast.mp3

Hermes Agent Integration

MediaForge ships as both a standalone tool and a Hermes MCP server:

MCP Server (primary integration)

5 tools registered, callable from any Hermes session.

Zero-install via uv (recommended):

# ~/.hermes/config.yaml
mcp_servers:
  mediaforge:
    command: uvx
    args: ["mediaforge-mcp"]

Or pip install:

pip install mediaforge
mcp_servers:
  mediaforge:
    command: mediaforge-mcp
ToolDescription
mediaforge__generate_podcastURL/text → interview MP3
mediaforge__generate_videoURL/text → MP4 with frames
mediaforge__list_voicesAvailable TTS voices
mediaforge__publishExpose file via cloudflared
mediaforge__serve_dirServe entire directory

Skill (UX layer)

The media-forge Hermes skill provides trigger words and style guidance:

  • Trigger: "生成播客" / "做一期播客" / "转成访谈" / "MediaForge"
  • Maps 4 content styles to Chinese descriptions
  • Orchestrates the full pipeline via MCP tools

MediaForge as Standalone Agent

The MCP server is self-contained — any MCP-compatible client (Claude Desktop, Cursor, etc.) can use MediaForge tools. No Hermes dependency, no pip install:

// claude_desktop_config.json
{
  "mcpServers": {
    "mediaforge": {
      "command": "uvx",
      "args": ["mediaforge-mcp"]
    }
  }
}

Development

git clone https://github.com/your-org/media-forge.git
cd media-forge
pip install -e ".[dev]"
pytest

Project Structure

media-forge/
├── mediaforge/          # Core pipeline package
│   ├── __init__.py      # Public API surface
│   ├── types.py         # Source, Script, FrameStyle, ContentStyle
│   ├── ingest.py        # Plugin-based content extraction
│   ├── compose.py       # LLM script generation (4 styles)
│   ├── synthesize.py    # TTS backends (edge/Azure/CosyVoice)
│   ├── session.py       # .mediaforge session files (save/resume)
│   ├── publish.py       # cloudflared tunnel serving
│   └── render/          # Pluggable render engines
│       ├── base.py          # RenderEngine Protocol + registry
│       ├── hyperframes.py   # Hyperframes engine (CSS animation)
│       ├── html_video_templates.py  # html-video GSAP template engine (NEW)
│       ├── hooks.py         # 4-stage render hook pipeline
│       ├── builtin_hooks.py # Watermark, progress bar, QR code hooks
│       ├── export.py        # ExportEngine (FFmpeg + WebCodecs)
│       ├── styling.py       # FrameStyle CSS generator + squircle
│       └── _default_impl.py # Default static-frame engine
├── mcp/
│   └── server.py        # Hermes MCP server (5 tools)
├── demo/
│   └── index.html       # Demo page template
├── openspec/            # Design docs & specs
├── pyproject.toml
└── README.md

License

MIT — use it for anything. Commercial, personal, open-source — no restrictions.

FAQ

Q: Does this require a NotebookLM subscription? No. Completely offline — use your own LLM API key for script generation, edge-tts for voice synthesis. NotebookLM's Video Overviews are AI-generated and can't be customized or exported; MediaForge gives you full control over every frame.

Q: Can I use my own voices? Yes. The Synthesizer supports edge-tts (free), Azure Speech (paid), and CosyVoice 3 (local GPU, open-source).

Q: How is this different from Remotion? Remotion renders React components to video and requires a company license for commercial use. MediaForge uses Playwright + ffmpeg (both MIT/LGPL) with no license fees. MediaForge also handles content ingestion and TTS, which Remotion doesn't.

Q: Can I customize the video frames? Edit FRAME_TEMPLATE in mediaforge/render.py — it's just HTML/CSS. Change colors, layout, fonts, add logos.

Q: What LLM providers work? Any OpenAI-compatible API. Tested with DeepSeek, OpenAI, and local Ollama models.

Cost Note

The MediaForge pipeline itself is MIT-licensed and free. However, the Compose stage requires an LLM to generate the dialogue script — that's your cost:

StageCost
Ingest (document parsing)Free
Compose (LLM script generation)Your LLM (API or local GPU)
Synthesize (edge-tts)Free
Render (Playwright + ffmpeg)Free
PublishFree

Use a local model (Ollama / LlamaCpp) for zero-cost operation, or any OpenAI-compatible API.

Author

Created by alitrack. Follow on WeChat: alitrack(微信搜一搜)

Demo videos →

↑↓ navigate openesc close