CHRISTIAN OHLE
Zurück zu Bauen

Bauen

Faceless YouTube Channel automatisieren — KI-Video-Pipeline mit echtem Code (2026)

Wie ich eine vollautomatische YouTube-Pipeline für KI-Erklärvideos gebaut habe. Mit echtem Code, allen Crashes und der Lösung über Replicate API.

22 Min Lesezeit faceless youtube channel automatisieren · ki video pipeline · comfyui amd rocm · replicate api tutorial · manim erklärvideos · elevenlabs voice cloning deutsch · youtube automation pipeline 2026
Hero-Image: Faceless YouTube Channel automatisieren — KI-Video-Pipeline mit echtem Code (2026)

TL;DR — Was du nach diesem Artikel hast

  • Vollständiger Bauplan einer vollautomatischen YouTube-Pipeline für deutsche KI-Erklärvideos: News-Scraping → Script → Visuals → Voiceover → Final-MP4.
  • Realer Stack: Python 3.12, Claude API, Replicate (Flux Schnell), Manim, ElevenLabs, ffmpeg, Whisper — mit Code zum 1:1 Übernehmen.
  • Lokales GPU-Rendering auf AMD Radeon RX 7900 XTX hat viermal das System gecrasht — die ganze ROCm-Memory-Leak-Geschichte.
  • Lösung: Cloud-API (Replicate Flux Schnell) für T2I + ffmpeg Ken-Burns-Pan statt lokalem I2V — stabil, ~$0.91 pro Video.
  • Production-Run: ein 4:30-Minuten-Video in ~25 Minuten fertig, inklusive Untertitel und BGM.

Wenn du einen faceless YouTube Channel automatisieren willst, gibt es 2026 zwei Wege: All-in-One-SaaS-Tools mieten oder eine eigene Pipeline bauen. Ich habe mich für letzteres entschieden — und in zwei Tagen alles erlebt, was beim faceless YouTube Channel automatisieren schiefgehen kann. Vier System-Crashes, ein Pivot von lokalem auf Cloud-Rendering, und am Ende eine Pipeline, die in 25 Minuten ein fertiges Video produziert.

Dieser Artikel ist die ehrliche Build-Doku. Keine Marketing-Story über „wie einfach es ist”. Code 1:1 wie er produktiv läuft, alle Sackgassen mit Screenshots, und am Ende der Stack der wirklich funktioniert.

Warum überhaupt eine eigene Pipeline?

Marktlage 2026: Faceless YouTube-Channels mit KI sind etabliert. Tools wie FluxNote, Pictory, Clippie und AutoClips bieten All-in-One-Lösungen ab $19–39/Monat. Du gibst ein Topic ein, das Tool spuckt ein Video aus.

Warum trotzdem selber bauen?

Volle Kontrolle über Stil und Niche. Mein Channel christianohle macht deutsche KI-Erklärvideos im 3Blue1Brown-Stil mit Manim-Animationen. Kein All-in-One-Tool kann das.

Manim-Integration. Mathematische und konzeptionelle Animationen sind der Differenzierer im DACH-KI-Niche. Während alle anderen Stock-Footage und KI-Bilder reincutten, kannst du mit Manim Vektorräume, ELO-Kurven oder Reward-Funktionen sauber visualisieren.

Skalierbar gegen Null Grenzkosten. Eine eigene Pipeline kostet pro Video nur die API-Calls. Bei 100 Videos pro Monat sind das ~$150 statt $390+ bei SaaS.

Lerneffekt. Ich verstehe meinen Stack komplett. Wenn was kaputt geht, kann ich es fixen. Bei einem Black-Box-SaaS bist du auf den Vendor angewiesen.

Trade-off: Setup-Zeit. Was ich anfangs auf 90 Minuten geschätzt hatte, wurden zwei Tage. Davon will ich erzählen.

Der ursprüngliche Plan

Hardware:

  • AMD Radeon RX 7900 XTX (24 GB VRAM)
  • 32 GB RAM
  • Windows 11

Geplanter Stack:

  • Python 3.12 für Pipeline-Code
  • Claude Opus 4.7 für Script-Generation
  • Manim für mathematische Animationen
  • ComfyUI mit Wan 2.2 14B für lokales Image-to-Video Rendering
  • RealVisXL V5.0 für T2I Hero-Shots
  • ElevenLabs für deutsche TTS
  • ffmpeg + Whisper für Assembly

Die Idee war: alles lokal rendern, kein Cloud-Lock-In, volle Datenhoheit. Eine 7900 XTX mit 24 GB VRAM ist 2026 hardwareseitig mehr als genug für T2I- und I2V-Workflows.

Spoiler: Mit AMD ROCm auf Windows funktioniert das in 2026 immer noch nicht zuverlässig. Hier die Story.

Die Crash-Saga — warum lokales Video-Rendering auf AMD scheiterte

Tag 1, 14:00 Uhr — Setup läuft, ComfyUI erkennt die GPU

ComfyUI Desktop installiert, ROCm-PyTorch-Build ausgewählt. Auf den ersten Blick sieht alles gut aus:

Devices: cuda:0 AMD Radeon RX 7900 XTX : native
VRAM Total: 23.98 GB
PyTorch: 2.9.1+rocmsdk20260116

Native ROCm-Build, kein ZLUDA-Hack. PyTorch erkennt die GPU als cuda:0 weil ROCm sich als CUDA-kompatibel ausgibt.

ComfyUI Desktop System-Information mit AMD Radeon RX 7900 XTX als cuda:0 erkannt ComfyUI Desktop nach frischer Installation — die 7900 XTX wird korrekt erkannt, ROCm 7.1 läuft.

Modelle gedownloaded (~60 GB):

  • RealVisXL V5.0 für T2I
  • LTX-Video 2B für I2V
  • Wan 2.2 14B I2V (High + Low Noise Expert) für hochwertiges I2V

Tag 1, 16:30 Uhr — Wan 2.2 läuft sich tot

Erster Test mit Wan 2.2 14B I2V Template. Drachenkrieger-Demo-Bild reingeladen, Run.

Nach 16 Minuten: Total bei 0%, Render-Stage bei 30%.

ComfyUI Job-Queue zeigt Wan 2.2 14B Render hängt bei 30 Prozent nach 16 Minuten Wan 2.2 nach 16 Minuten immer noch bei 30% einer einzigen Stage. Hochgerechnet 50+ Minuten pro 5-Sekunden-Clip.

Bei der Geschwindigkeit hochgerechnet wären das 50+ Minuten pro 5-Sekunden-Clip. Bei einer Pipeline mit 2 Comfy-Szenen pro Video und 3 Videos pro Woche wäre das fast 5 Stunden pure Render-Zeit. Praxistauglich = Nein.

Bekannte Probleme bei AMD ROCm + Wan 2.2:

  • ROCm Kernel-Recompilation zwischen den zwei Diffusion-Modellen kostet jedes Mal Sekunden
  • fp8-Quantisierung auf AMD ist nicht so optimiert wie auf NVIDIA
  • Lightning LoRA-Loading zwischen Stages dauert auf AMD länger

Job gestoppt. Switch auf LTX-Video.

Tag 1, 18:45 Uhr — Erster System-Crash

LTX-Video lief beim ersten Test in 76 Sekunden durch. Erfolg, dachte ich.

Aber: Output zeigt nicht den Drachenkrieger, sondern eine Winterszene. Grund: strength=0.15 im LTX-Workflow ist sehr niedrig. LTX nimmt das Input-Bild nur als lose Inspiration und halluziniert frei.

Fix: strength=0.75 für Image-treues Animieren.

Zweiter Test-Run. Mitten im Render: kompletter System-Hänger. Maus reagiert nicht, Bildschirm eingefroren.

Windows Task Manager mit kritischer RAM-Auslastung 99 Prozent während ComfyUI-Render Vom Handy fotografiert: Task Manager zeigt 30,8 von 31,2 GB RAM belegt, Datenträger 99%. Das System hat keine Atemluft mehr.

Diagnose: Wan-Modelle waren noch im VRAM, plus shared GPU memory hatte 3,6 GB ins System-RAM gespillt. ROCm gibt RAM zwischen Runs nicht sauber frei. Hard-Reboot.

Tag 2, 10:00 Uhr — Refiner-Bug bei T2I

Nach dem Reboot wollte ich erst mal isoliert T2I testen. RealVisXL allein, kein I2V. Output:

RealVisXL T2I-Output mit Noise-Artefakten durch return_with_leftover_noise enable Bug T2I-Output mit Noise-Mantle. Das Bild sieht aus wie eine Aquarell-Skizze, nicht wie ein scharfes Foto. Ursache: KSampler-Default für 2-Stage-Workflows mit Refiner.

Diagnose: das „SDXL Simple”-Template ist auf 2-Stage-Inference ausgelegt (Base + Refiner). Ich hatte den Refiner gelöscht, aber im KSampler stand:

"return_with_leftover_noise": "enable",
"end_at_step": 20

Das heißt: Sampler stoppt bei Step 20 von 25 und gibt Latent mit Restnoise raus, weil er denkt der Refiner kommt noch. Ohne Refiner = noisy Output.

Lesson: Wenn ein Template aus mehreren Stages besteht und du eine Stage löschst, sind die KSampler-Defaults nicht für Single-Stage gedacht. Lieber von Grund auf bauen als Refiner-Templates „vereinfachen”.

Fix: Sauberen 7-Node Minimal-Workflow von Hand gebaut — Load Checkpoint, 2× CLIP Text Encode, Empty Latent Image, KSampler (kein Advanced!), VAE Decode, Save Image. KSampler statt KSamplerAdvanced, dpmpp_2m + karras, cfg=5.

Resultat:

RealVisXL V5.0 generiertes Bild eines London Office Buildings bei Blue Hour Knackscharfes London-Office bei Blue Hour mit RealVisXL V5.0 und sauberem Workflow. Endlich.

T2I funktioniert jetzt. Aber I2V steht noch aus.

Tag 2, 14:00 Uhr — Crash 2, 3, 4

Mit dem sauberen Workflow nochmal Versuch: T2I → I2V Pipeline-Lauf.

RealVisXL T2I-Generation eines futuristischen AI Server Rooms mit blauem Glow Der T2I-Output für die Server-Raum-Szene — atemberaubend scharf, exakt der Sci-Fi-Look den ich wollte.

LTX produziert qualitativ sehr gute Outputs. ABER: nach jedem Render-Zyklus crasht das System.

Versuche zur Rettung:

  1. Auto-Restart-Workaround: ComfyUI-Prozess nach jedem Render killen und neustarten. Crash beim zweiten Render.
  2. Niedrigere Auflösung: 512×288 statt 768×432. Crash früher als vorher.
  3. Nur T2I, kein I2V: Sogar das crasht jetzt, akkumulierter Memory-Druck.

Vier System-Crashes in zwei Tagen. Jedes Mal Hard-Reboot.

Windows Task Manager dritter System-Crash durch ROCm Memory Leak ComfyUI Crash Nr. 3. Speicherauslastung 65% — aber „Zugesichert” 56,8 von 60,3 GB. Klassischer ROCm-Memory-Leak.

Recherche bestätigt: bekanntes Problem. Zitat aus dem ComfyUI GitHub:

„I was happy to saw this, but after clean install on AMD rocm drivers, ComfyUI Desktop and Portable, every Workflow crashs.”

Andere Reports: HIP errors, illegal memory access, instant crashes after Generate-click. Alles dasselbe Pattern.

Stand 2026: AMD ROCm auf Windows ist für T2I in einfachen Workflows brauchbar. Für I2V mit Memory-Heavy-Models wie Wan 2.2 oder LTX in einer automatisierten Pipeline ist es nicht produktionsreif. Wer eine KI Video Pipeline bauen will, die 24/7 zuverlässig durchläuft, kann sich auf ComfyUI AMD ROCm unter Windows derzeit nicht verlassen.

Zeit für einen Pivot.

Der Pivot — Cloud-API statt lokales Rendering

Die Entscheidung: lokales GPU-Rendering aufgeben, Cloud-API nutzen.

Optionen:

  • Replicate (Flux Schnell, Flux Dev, SDXL): einfache API, $0.003–$0.025 pro Bild
  • Fal.ai (Flux): schnellste Inference, queue-basiert
  • Together AI (Flux): günstigste Pricing für Flux

Bei meinem Volumen (24 Bilder/Monat) sind die Kosten vernachlässigbar — unter $1/Monat selbst bei Flux Dev. Wahl nach Stabilität, nicht Preis.

Entscheidung: Replicate Flux Schnell.

  • $0.003 pro 1024×1024 Bild
  • ~3 Sekunden pro Render
  • Stabile API mit Python-SDK
  • Keine Cold-Start-Probleme bei populären Modellen

Für die Bewegung im Video: kein I2V mehr. Stattdessen ffmpeg Ken-Burns-Pan über das generierte Standbild. Das ist wie wenn du in iMovie ein Foto reinziehst und einen subtilen Zoom oder Pan drauf legst. Sieht professionell aus, kostet keine GPU-Zyklen, crasht nichts.

Inspiration: 3Blue1Brown nutzt fast nur Standbilder mit subtilen Bewegungen. Für KI-Erklärvideos ist das absolut tragfähig.

Die finale Architektur

Pipeline-Übersicht

Eine Pipeline-Stage pro Modul. Jede Stage hat einen klaren Input und Output:

News-Scraper          → JSONL mit Headline-Kandidaten
Topic-Selector        → 1 Topic-JSON pro Video
Script-Generator      → Scene-JSON mit 12-15 Szenen, Visual-Types
Visual-Renderer       → Pro Szene ein MP4
Voice-Synthesizer     → Pro Szene ein MP3
Assembly              → Final-MP4 mit BGM und SRT-Captions

Visual-Types:

  • manim: mathematische/strukturelle Animationen via Manim
  • slide: HTML-Templates mit Bullets, gerendert via Playwright
  • comfy: Hero-Shots via Replicate Flux Schnell + Ken-Burns
  • stock: Pexels-Footage als Fallback
  • title_card: große Titel-Slide

Parallelisierung: CPU/API-Visuals parallel (Manim, Slide, Replicate API), Voice seriell (rate-limit-bedingt).

Module-Übersicht

Die Pipeline ist in fokussierte Module aufgeteilt — jedes mit klar definierter Aufgabe und einem eigenen Verantwortungsbereich:

  • Scraper — News aus RSS, Hacker News, arXiv, Reddit einsammeln
  • Script-Module — Claude Haiku rankt Topics, Claude Opus generiert das Szenen-JSON, Pydantic validiert das Schema
  • Visual-Module — Manim-Animationen via Claude-generated Code, HTML-Slides via Playwright, Stock-Clips via Pexels-API, atmosphärische Hero-Bilder via Replicate Flux Schnell mit Ken-Burns-Pan
  • Voice-Modul — ElevenLabs Turbo v2.5 für deutsche Synthese
  • Assembly-Modul — ffmpeg-Merge, Whisper-Subtitles, BGM mit Sidechain-Ducking
  • Pipeline-Orchestrator — koordiniert alle Module, parallel/seriell, mit Cache- und Fallback-Logik

Der Replicate-Renderer (Cloud-T2I + Ken-Burns)

Der Comfy-Renderer (Name historisch — „comfy” für ComfyUI) ruft inhaltlich die Replicate API auf. Hier der Code, der produktiv läuft:

"""ComfyUI-Adapter ersetzt durch Replicate API.

PRODUCTION VERSION: T2I via Replicate Flux Schnell + Ken-Burns via ffmpeg.

Hintergrund: ComfyUI auf AMD ROCm Windows verursachte wiederholt System-Crashes
(VRAM-/RAM-Memory-Leaks, sowohl bei T2I als auch bei I2V). Stattdessen jetzt:
- T2I via Replicate Cloud API (Flux Schnell, ~3s/Bild, ~$0.003/Bild)
- Ken-Burns Pan via ffmpeg lokal (kein GPU-Stress)
"""
from __future__ import annotations
import os
import random
import subprocess
from pathlib import Path

import requests
from rich.console import Console

from config.settings import ASSETS_DIR

console = Console()

REPLICATE_API_TOKEN = os.getenv("REPLICATE_API_TOKEN")
REPLICATE_MODEL = "black-forest-labs/flux-schnell"


# ============================================================
# T2I via Replicate Flux Schnell
# ============================================================

def render_t2i(prompt: str, output_dir: Path, scene_id: int,
               negative: str = "", width: int = 1280, height: int = 720) -> Path:
    """Text-zu-Bild via Replicate Flux Schnell."""
    if not REPLICATE_API_TOKEN:
        raise RuntimeError(
            "REPLICATE_API_TOKEN nicht gesetzt. In .env eintragen."
        )

    output_dir.mkdir(parents=True, exist_ok=True)
    aspect = _aspect_ratio(width, height)

    console.print(f"[cyan]→ Replicate Flux Schnell: {prompt[:80]}…[/cyan]")

    import replicate
    client = replicate.Client(api_token=REPLICATE_API_TOKEN)

    output = client.run(
        REPLICATE_MODEL,
        input={
            "prompt": prompt,
            "aspect_ratio": aspect,
            "num_outputs": 1,
            "num_inference_steps": 4,
            "output_format": "png",
            "output_quality": 95,
        },
    )

    image_url = output[0] if isinstance(output, list) else output
    if hasattr(image_url, "url"):
        url = image_url.url() if callable(image_url.url) else image_url.url
    else:
        url = str(image_url)

    target = output_dir / f"scene_{scene_id:03d}_keyframe.png"
    r = requests.get(url, timeout=60)
    r.raise_for_status()
    target.write_bytes(r.content)

    console.print(f"[green]✓ T2I keyframe scene_{scene_id:03d}[/green]")
    return target


def _aspect_ratio(width: int, height: int) -> str:
    """Mappe Pixel-Auflösung auf Flux Schnell aspect_ratio strings."""
    ratio = width / height
    candidates = {
        "1:1": 1.0, "16:9": 16/9, "9:16": 9/16,
        "4:3": 4/3, "3:4": 3/4, "21:9": 21/9, "3:2": 3/2, "2:3": 2/3,
    }
    best = min(candidates.items(), key=lambda kv: abs(kv[1] - ratio))
    return best[0]


# ============================================================
# Ken-Burns-Pan via ffmpeg
# ============================================================

KEN_BURNS_MODES = ["zoom_in", "zoom_out", "pan_left", "pan_right", "pan_up", "drift_diag"]


def _build_kenburns_filter(mode: str, duration_sec: float, fps: int = 30,
                           src_w: int = 1280, src_h: int = 720,
                           dst_w: int = 1920, dst_h: int = 1080) -> str:
    total_frames = max(2, int(round(duration_sec * fps)))
    upscale = src_w * 4
    upscale_h = src_h * 4

    if mode == "zoom_in":
        zoom = "zoom='min(zoom+0.0015,1.20)'"
        x = "x='iw/2-(iw/zoom/2)'"
        y = "y='ih/2-(ih/zoom/2)'"
    elif mode == "zoom_out":
        zoom = "zoom='if(lte(zoom,1.0),1.20,max(1.001,zoom-0.0015))'"
        x = "x='iw/2-(iw/zoom/2)'"
        y = "y='ih/2-(ih/zoom/2)'"
    elif mode == "pan_left":
        zoom = "zoom='1.15'"
        x = f"x='iw-iw/zoom-(on/{total_frames})*(iw-iw/zoom)'"
        y = "y='ih/2-(ih/zoom/2)'"
    elif mode == "pan_right":
        zoom = "zoom='1.15'"
        x = f"x='(on/{total_frames})*(iw-iw/zoom)'"
        y = "y='ih/2-(ih/zoom/2)'"
    elif mode == "pan_up":
        zoom = "zoom='1.15'"
        x = "x='iw/2-(iw/zoom/2)'"
        y = f"y='ih-ih/zoom-(on/{total_frames})*(ih-ih/zoom)'"
    elif mode == "drift_diag":
        zoom = "zoom='min(zoom+0.0008,1.12)'"
        x = f"x='(on/{total_frames})*(iw-iw/zoom)'"
        y = f"y='(on/{total_frames})*(ih-ih/zoom)'"
    else:
        zoom = "zoom='min(zoom+0.0012,1.15)'"
        x = "x='iw/2-(iw/zoom/2)'"
        y = "y='ih/2-(ih/zoom/2)'"

    return (
        f"scale={upscale}:{upscale_h}:flags=lanczos,"
        f"zoompan={zoom}:{x}:{y}:d={total_frames}:s={dst_w}x{dst_h}:fps={fps},"
        f"format=yuv420p"
    )


def render_kenburns(keyframe: Path, duration_sec: float,
                    output_dir: Path, scene_id: int,
                    mode: str | None = None) -> Path:
    """Ken-Burns-Pan über Standbild via ffmpeg."""
    output_dir.mkdir(parents=True, exist_ok=True)
    if mode is None:
        rng = random.Random(scene_id * 7919 + 11)
        mode = rng.choice(KEN_BURNS_MODES)

    from PIL import Image
    with Image.open(keyframe) as img:
        src_w, src_h = img.size

    filter_str = _build_kenburns_filter(
        mode=mode, duration_sec=duration_sec, fps=30,
        src_w=src_w, src_h=src_h, dst_w=1920, dst_h=1080,
    )

    target = output_dir / f"scene_{scene_id:03d}.mp4"
    cmd = [
        "ffmpeg", "-y", "-loop", "1", "-i", str(keyframe),
        "-vf", filter_str,
        "-t", f"{duration_sec:.2f}",
        "-r", "30", "-an",
        "-c:v", "libx264", "-preset", "veryfast", "-crf", "20",
        str(target),
    ]
    proc = subprocess.run(cmd, capture_output=True, text=True)
    if proc.returncode != 0:
        raise RuntimeError(f"ffmpeg ken-burns failed: {proc.stderr[-1500:]}")

    console.print(f"[green]✓ Ken-Burns ({mode}) scene_{scene_id:03d}.mp4[/green]")
    return target


def render(visual_prompt: str, motion_hint: str | None, duration_sec: float,
           output_dir: Path, scene_id: int) -> Path:
    """End-to-end: T2I (Replicate Flux Schnell) → Ken-Burns (ffmpeg)."""
    keyframe = render_t2i(visual_prompt, output_dir, scene_id,
                          width=1280, height=720)
    return render_kenburns(keyframe, duration_sec, output_dir, scene_id)

Erklärung der Hauptfunktionen:

render_t2i() ruft Replicate Flux Schnell mit dem Prompt auf, lädt das resultierende Bild herunter und speichert es als scene_NNN_keyframe.png. Das ist das Replicate API Tutorial in seiner kompakten Form: Token aus .env, client.run() mit Modell und Input, Bild von der returnten URL ziehen.

render_kenburns() baut einen ffmpeg zoompan-Filter mit einem von 6 Modi (zoom_in, pan_left, drift_diag, etc.). Pro Scene-ID wird deterministisch ein Modus gewählt, damit aufeinanderfolgende Hero-Shots unterschiedlich wirken. Der scale=…:lanczos-Vorpass hochskaliert das Bild um Faktor 4 — sonst rendert ffmpegs zoompan matschig, weil es intern auf der Originalauflösung samplet.

render() ist die Public-API: T2I + Ken-Burns als End-to-End. Render-Zeit pro Comfy-Szene: ~30 Sekunden (3s Replicate + 2s Download + 10s ffmpeg + Overhead).

Der Manim-Renderer (Claude generiert Code)

Statt Manim-Code per Hand zu schreiben, lasse ich Claude den Code für jede Szene generieren — basierend auf dem visual_prompt aus dem Script-JSON. Das ist der Schritt, der Manim Erklärvideos wirklich skalierbar macht: man muss nicht für jede Animation Python-Code schreiben, sondern beschreibt nur, was visualisiert werden soll.

"""Manim-Renderer: Claude generiert Manim-Code, dann lokales Render via subprocess.

WICHTIG: max_tokens=8000 (nicht 3000!) damit komplexe Manim-Szenen vollständig
generiert werden. Mit 3000 Tokens kommen Syntax-Fehler durch abgeschnittenen Code.
"""
from pathlib import Path
import subprocess
from anthropic import Anthropic
from rich.console import Console
from config.settings import ANTHROPIC_API_KEY, MANIM_MODEL

console = Console()
client = Anthropic(api_key=ANTHROPIC_API_KEY)

MANIM_SYSTEM_PROMPT = """Du bist ein Manim-Experte. Generiere für den gegebenen
Visual-Prompt eine vollständige Manim-Scene-Klasse die ChristianOhleScene heißt.

Constraints:
- from manim import *
- class ChristianOhleScene(Scene): mit construct(self) Methode
- Dauer in Sekunden ist gegeben - nutze entsprechende Wait()s und Animation-Längen
- Dunkler Hintergrund (BLACK), helle Objekte
- Keep it visually clean, kein Clutter
- KEINE imports außer manim, KEIN if __name__ block
"""


def generate_manim_code(visual_prompt: str, duration_sec: float) -> str:
    """Claude generiert Manim-Code für die Szene."""
    user_msg = (
        f"Visual-Prompt: {visual_prompt}\n\n"
        f"Dauer: {duration_sec:.1f} Sekunden\n\n"
        "Antworte AUSSCHLIESSLICH mit Python-Code, kein Markdown, keine Erklärungen."
    )

    resp = client.messages.create(
        model=MANIM_MODEL,
        max_tokens=8000,  # KRITISCH: 3000 reicht nicht für komplexe Szenen!
        system=MANIM_SYSTEM_PROMPT,
        messages=[{"role": "user", "content": user_msg}],
    )

    code = resp.content[0].text.strip()
    if code.startswith("```"):
        code = code.split("```")[1]
        if code.startswith("python"):
            code = code[6:].strip()
        code = code.strip("`").strip()

    return code


def render(visual_prompt: str, duration_sec: float,
           output_dir: Path, scene_id: int) -> Path:
    """Generiere Manim-Code, render via subprocess, return final mp4."""
    output_dir.mkdir(parents=True, exist_ok=True)

    # 1. Code generieren
    code = generate_manim_code(visual_prompt, duration_sec)

    # 2. Code in Datei schreiben
    code_file = output_dir / f"scene_{scene_id:03d}.py"
    code_file.write_text(code, encoding="utf-8")
    console.print(f"[dim]Manim-Code geschrieben: {code_file.name}[/dim]")

    # 3. Manim-CLI aufrufen
    console.print(f"[cyan]→ Manim render scene {scene_id}[/cyan]")
    proc = subprocess.run(
        ["manim", "render", "-ql", str(code_file), "ChristianOhleScene"],
        cwd=output_dir,
        capture_output=True, text=True,
    )

    if proc.returncode != 0:
        console.print("[red]Manim-Render fehlgeschlagen:[/red]")
        console.print(proc.stderr[-2000:])
        raise RuntimeError(f"Manim returncode {proc.returncode}")

    # 4. Output-MP4 finden und in Pipeline-Format umbenennen
    media_dir = output_dir / "media" / "videos" / f"scene_{scene_id:03d}" / "480p15"
    rendered = next(media_dir.glob("*.mp4"))
    target = output_dir / f"scene_{scene_id:03d}.mp4"
    rendered.replace(target)

    size_kb = target.stat().st_size // 1024
    console.print(f"[green]✓ scene_{scene_id:03d}.mp4 ({size_kb} KB)[/green]")
    return target

Wie es funktioniert:

  1. Claude bekommt den visual_prompt aus dem Script-JSON („Schachbrett-Animation mit ELO-Kurve”).
  2. Generiert Python-Code, der Manim-Scenes definiert.
  3. Code wird in scene_NNN.py geschrieben.
  4. manim render -ql scene_NNN.py rendert das MP4.

Wichtige Lesson aus dem Build: max_tokens=3000 reicht nicht für komplexe Manim-Szenen. Mit 3000 Tokens hatte ich Syntax-Fehler, weil Claude mitten im Code abgeschnitten wurde. Mit 8000 Tokens läuft’s stabil. Das ist ein Detail, das in keinem Tutorial steht — kostete mich aber zwei kaputte Pipeline-Runs, bis ich es bemerkt hatte.

Der Slide-Renderer (Playwright)

Strategie: Claude generiert HTML mit Tailwind-Style aus dem visual_prompt. Playwright rendert das HTML in einem headless Chromium und macht einen Screenshot pro Frame, ffmpeg setzt die Frames zu einem MP4 zusammen.

Vorteile gegenüber direktem Bildgenerieren:

  • Saubere Typografie
  • Konsistentes Layout/Branding
  • Schnell zu rendern
  • Editierbar als HTML

Der ElevenLabs-Voice-Renderer

Standard-Setup mit eleven_multilingual_v2 für Deutsch. Voice-ID via .env konfigurierbar. Pro Szene wird der Voiceover-Text aus dem Scene-JSON in MP3 synthetisiert.

Aktuell läuft die Pipeline mit Standard-Voice. Plan ist Wechsel zu ElevenLabs Voice Cloning Deutsch mit eigener Stimme — braucht 30–60 Min Trainings-Audio, Setup dauert 1–2 Wochen serverseitig, bis ElevenLabs das Voice-Modell freischaltet.

Assembly mit ffmpeg + Whisper

Im Assembly-Code laufen folgende Schritte:

  1. Pro Szene: Visual-MP4 + Voiceover-MP3 zusammenmergen, Visual auf Audio-Länge stretchen/loopen.
  2. Alle Szenen-Clips concatten zu master.mp4.
  3. BGM mixen (optional, Pfad in data/assets/bgm.mp3).
  4. Whisper transkribiert den Audio-Track, schreibt SRT.
  5. Final-MP4 in data/output/<topic_id>.mp4.

Wichtiger Windows-Fix: ffmpegs subtitles=-Filter hasst Windows-Pfade mit : und \. Workaround: ins SRT-Verzeichnis cd-en und nur den Filename übergeben.

import os
cwd = os.getcwd()
try:
    os.chdir(srt_file.parent)
    cmd = ["ffmpeg", "-y", "-i", str(video),
           "-vf", f"subtitles={srt_file.name}:force_style='{style}'",
           ...]
    _run(cmd)
finally:
    os.chdir(cwd)

Pipeline-Orchestrierung mit Caching und Error-Handling

Im Pipeline-Orchestrator laufen zwei kritische Patches, die im Production-Run unverzichtbar waren:

"""Pipeline-Orchestrator: Topic → fertiges MP4.

Patches die im Production-Run unverzichtbar waren:
1. Cache-Logik: existierende Renders werden übersprungen
2. Fallback statt Crash: kaputte Manim-Szenen → Slide-Renderer
"""
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
from rich.console import Console

from src.script.schema import VideoScript, Scene
from src.visual.manim import renderer as manim_r
from src.visual.slide import renderer as slide_r
from src.visual.stock import renderer as stock_r
from src.visual.comfy import renderer as comfy_r
from src.voice import tts
from src.assembly import build as asm
from config.settings import RAW_DIR, ensure_dirs

console = Console()


def _render_scene(scene: Scene, raw_dir: Path) -> Path:
    """Dispatch auf den richtigen Renderer mit Cache-Check."""
    sub = raw_dir / scene.visual_type

    # PATCH 1: Cache-Hit für bereits gerenderte Szenen
    cached = sub / f"scene_{scene.scene_id:03d}.mp4"
    if cached.exists() and cached.stat().st_size > 1024:
        console.print(f"[dim]Cache hit: scene_{scene.scene_id:03d} ({scene.visual_type})[/]")
        return cached

    if scene.visual_type == "manim":
        return manim_r.render(scene.visual_prompt, scene.duration_sec, sub, scene.scene_id)
    if scene.visual_type in ("slide", "title_card"):
        prompt = scene.visual_prompt
        if scene.visual_type == "title_card":
            prompt = f"BULLETS-Layout mit nur dem Titel als h1: {prompt}"
        return slide_r.render(prompt, scene.duration_sec, sub, scene.scene_id)
    if scene.visual_type == "stock":
        return stock_r.fetch_clip(scene.visual_prompt, sub, scene.scene_id, scene.duration_sec)
    if scene.visual_type == "comfy":
        return comfy_r.render(scene.visual_prompt, scene.motion_hint, scene.duration_sec, sub, scene.scene_id)
    raise ValueError(f"Unbekannter visual_type: {scene.visual_type}")


def run(script_path: Path, parallel_visuals: int = 2) -> Path:
    ensure_dirs()
    script = VideoScript.model_validate_json(script_path.read_text(encoding="utf-8"))
    topic_id = script.topic_id
    raw = RAW_DIR / topic_id
    raw.mkdir(parents=True, exist_ok=True)

    visual_paths: dict[int, Path] = {}
    gpu_scenes = [s for s in script.scenes if s.visual_type == "comfy"]
    other_scenes = [s for s in script.scenes if s.visual_type != "comfy"]

    # Parallel rendern (außer Comfy/Replicate)
    with ThreadPoolExecutor(max_workers=parallel_visuals) as ex:
        futures = {ex.submit(_render_scene, s, raw): s for s in other_scenes}
        for fut in as_completed(futures):
            s = futures[fut]
            try:
                visual_paths[s.scene_id] = fut.result()
            except Exception as e:
                # PATCH 2: Fallback auf Slide-Renderer statt Pipeline-Abbruch
                console.print(f"[red]Scene {s.scene_id} ({s.visual_type}) failed: {e}[/]")
                console.print(f"[yellow]  Fallback auf Slide-Renderer für Scene {s.scene_id}[/]")
                fallback_sub = raw / "slide"
                visual_paths[s.scene_id] = slide_r.render(
                    s.visual_prompt, s.duration_sec, fallback_sub, s.scene_id
                )

    # Comfy/Replicate seriell (rate-limit-bewusst)
    if gpu_scenes:
        for s in gpu_scenes:
            visual_paths[s.scene_id] = _render_scene(s, raw)

    # Voiceover seriell
    audio_dir = raw / "voiceover"
    audio_paths = tts.synthesize_scenes(script.scenes, audio_dir)
    audio_map = {s.scene_id: p for s, p in zip(script.scenes, audio_paths)}

    # Assembly: visual + audio in Render-Reihenfolge
    pairs = [(visual_paths[s.scene_id], audio_map[s.scene_id]) for s in script.scenes]
    final = asm.build(topic_id, pairs)
    return final

Patch 1 — Cache-Logik in _render_scene: Wenn eine Szene schon gerendert wurde, skip. Sonst rendert die Pipeline bei jedem Re-Run alle 14 Szenen neu, was bei einem Crash in Szene 12 sehr teuer ist. Der Check ist simpel: existiert die Ziel-Datei, ist sie größer als 1 KB? Cache-Hit. Das spart bei jedem Re-Run minutenlang Rechenzeit.

Patch 2 — Fallback statt Crash: Wenn ein Manim-Renderer fehlschlägt (Claude generiert mal kaputten Code), nicht die ganze Pipeline killen. Stattdessen: auf Slide-Renderer fallen und weiter. Die Pipeline ist damit robust gegen einzelne Render-Fehler — und weil Slide auch via Claude-Generation läuft, ist die Qualität meist akzeptabel.

Production-Run — die David-Silver-Story

Erster vollständiger Pipeline-Run zum Test.

Topic (von News-Scraper geliefert):

„DeepMind’s David Silver raised $1.1B to build AI that learns without human data”

Generiertes Script (Claude Opus 4.7):

  • Titel: „1,1 Mrd. Dollar für KI ohne Menschen-Daten — Silvers Wette”
  • 14 Szenen, ~256 Sekunden Sprechdauer
  • Visual-Mix: 8× Manim, 5× Slide, 2× Comfy/Replicate, 1× Title-Card

Manim-Animation in faceless YouTube Video zu Self-Play AI Training mit ELO-Kurve Eine der finalen Manim-Szenen aus dem Test-Video: Self-Play-Training mit ELO-Kurve, automatisch generiert aus dem Visual-Prompt im Script-JSON.

Render-Zeiten:

  • Manim parallel: ~12 Min für 8 Szenen (2 Worker)
  • Slide parallel: ~5 Min für 5 Szenen
  • Replicate Comfy seriell: ~1 Min für 2 Szenen
  • ElevenLabs TTS seriell: ~3 Min für 14 Szenen
  • Assembly + Whisper: ~5 Min

Total: ~25 Minuten für ein 4:30-Minuten-Video.

Kosten:

  • Claude API (Script + Manim-Code + Slide-HTML): ~$0.40
  • Replicate (2 Bilder Flux Schnell): ~$0.01
  • ElevenLabs (14 Szenen DE): ~$0.50
  • Total: ~$0.91 pro Video

Bei 12 Videos pro Monat: ~$11/Monat operative Kosten. Plus Anthropic-Plan und ElevenLabs-Sub. Eine YouTube Automation Pipeline 2026 auf diesem Stand ist betriebswirtschaftlich kein Thema mehr — der größte Kostenblock ist mein Stundensatz für Topic-Auswahl und Review, nicht die API-Calls.

Was ich gelernt habe

1. AMD ROCm Windows ist 2026 noch nicht produktionsreif für I2V. Für T2I in einfachen Workflows läuft es. Für Memory-Heavy I2V crasht es. Wenn du eine Pipeline brauchst, die zuverlässig durchläuft, ist Cloud-API der pragmatische Weg — auch wenn das ideologisch unbefriedigend ist.

2. Pivots sind Erfolg, kein Scheitern. Mein ursprünglicher Plan (alles lokal) ist nicht aufgegangen. Der finale Stack (Hybrid lokal+Cloud) ist besser als der Originalplan — stabiler, schneller, fast genauso günstig. Wer einen faceless YouTube Channel automatisieren will, sollte nicht stur am ersten Architekturentwurf festhalten.

3. Templates sind Tradeoffs. „SDXL Simple” mit Base+Refiner sieht harmlos aus, aber halbe Modifikationen führen zu still failing Output. Lieber Minimal-Workflow von Hand bauen als Refiner-Templates „vereinfachen”.

4. Caching und Error-Handling sind nicht optional. Eine Pipeline, die bei einem einzigen Fehler abbricht, ist im Production-Use unbrauchbar. Cache + Fallback retten Stunden, jedes Mal wenn ein einzelner Render-Step zickt.

5. Manim ist der Differenzierer im DACH-KI-Niche. Während alle anderen Stock-Footage und KI-Bilder reincutten, kannst du mit Manim mathematische Konzepte sauber visualisieren. Das ist 3Blue1Brown-Territorium — und in deutscher Sprache mit KI-Fokus völlig unbesetzt.

Das fertige Video

Das Resultat dieser Pipeline: ein 4:30-Minuten-Video über David Silvers $1.1B Funding für menschen-data-freie KI — komplett automatisch gerendert.

<!-- YouTube-Embed wird hier eingebunden, sobald veröffentlicht -->
<div class="video-container">
  <iframe src="https://www.youtube.com/embed/VIDEO_ID"
          allowfullscreen></iframe>
</div>

FAQ — Häufige Fragen zur Pipeline

Was ist ein faceless YouTube Channel?

Ein faceless YouTube Channel produziert Videos, ohne dass der Creator vor der Kamera steht. Stattdessen werden Voiceover, Stock-Footage, Animationen und KI-generierte Visuals genutzt. Der Vorteil: höhere Skalierbarkeit, weniger Production-Aufwand, Anonymität. Faceless-Channels in den Niches Finance, Tech und Productivity gehören zu den umsatzstärksten Formaten 2026.

Was kostet eine faceless YouTube Pipeline?

Eine self-hosted Pipeline kostet ~$1 pro Video an API-Calls (Claude, Replicate, ElevenLabs). Plus Anthropic-Plan ($20/Monat) und ElevenLabs-Subscription ($5–22/Monat). All-in-One-SaaS-Tools wie FluxNote oder Clippie liegen bei $19–39/Monat. Bei mehr als 20 Videos/Monat ist Self-Hosting deutlich günstiger.

Welche AI-Tools brauche ich für einen automatisierten YouTube Channel?

Minimum-Stack: ein LLM für Scripts (Claude oder GPT), ein TTS-Tool (ElevenLabs für höchste Qualität), eine Visual-Source (Replicate Flux für Bilder, Manim für Animationen, Pexels für Stock) und ffmpeg für Assembly. Optional: Whisper für automatische Untertitel, Playwright für HTML-basierte Slides.

Funktioniert ComfyUI auf AMD GPUs?

ComfyUI funktioniert grundsätzlich auf AMD GPUs mit ROCm. Stand 2026 ist die Stabilität auf Windows aber problematisch, besonders bei Memory-Heavy I2V-Modellen wie Wan 2.2 oder LTX. Bekannte Probleme: HIP errors, illegal memory access, System-Crashes. Für T2I-Workflows in einfacher Form ist es brauchbar. Für Production-Pipelines aktuell nicht zuverlässig.

Was ist Manim und warum ist es relevant für KI-Videos?

Manim ist eine Python-Library für mathematische Animationen, ursprünglich von 3Blue1Brown entwickelt. Für KI-Erklärvideos ist Manim relevant, weil es konzeptionelle Animationen (Vektoren, Graphen, Transformationen) sauber visualisiert. Im Gegensatz zu generierten KI-Videos sind Manim-Animationen reproduzierbar, präzise und perfekt für komplexe technische Themen.

Wie lange dauert die Produktion eines KI-generierten YouTube-Videos?

Mit einer vollautomatischen Pipeline: 25–30 Minuten für ein 4–7 Minuten Video. Ohne Automatisierung 6–8 Stunden manuell. Die Pipeline läuft headless — während sie rendert, kann der Creator anderen Aufgaben nachgehen. Tatsächlicher menschlicher Input: Topic-Auswahl, finale Review, Upload-Strategy.

Wie monetarisiert man einen faceless YouTube Channel?

Hauptkanäle: AdSense (1.000 Subscriber + 4.000 Watch-Hours notwendig), Affiliate-Marketing in Video-Beschreibungen, Sponsoring nach Channel-Wachstum. RPM (Revenue per Mille) variiert stark nach Niche: Finance/Tech bis $20 pro 1.000 Views, generische Themen unter $3. Faceless-Channels mit 100k Monthly Views erreichen typischerweise $200–2.000 monatlichen Umsatz.

Fazit

Eine vollautomatische YouTube-Pipeline zu bauen ist 2026 keine Nische-Engineering-Disziplin mehr. Die Bausteine — Claude für Scripts und Code-Gen, Replicate für Bilder, Manim für Animationen, ElevenLabs für Voiceover, ffmpeg + Whisper für Assembly — sind reif, dokumentiert und bezahlbar. Was den Unterschied macht, ist die Orchestrierung: Cache, Fallbacks, sinnvolle Parallelisierung, klares Schema zwischen den Stages.

Was nicht funktioniert hat — lokales I2V auf AMD ROCm — habe ich nicht versteckt. Das ist Stand 2026 die Realität, und wer es trotzdem versuchen will, soll es wenigstens mit offenen Augen tun. Der Pivot auf Replicate Flux Schnell + Ken-Burns hat die Pipeline rettungslos rettungsreich gemacht: stabil, schnell, ~$0.91 pro Video.

Wenn du den Stack nachbauen willst: das komplette Repo gibt’s auf github.com/christianohle (in Arbeit). Den Channel selbst findest du auf YouTube — christian-ohle, sobald die ersten Videos live sind. Wer wissen will, wie ich den Stack weiterentwickle — Voice-Cloning-Setup, A/B-Tests bei Topic-Selection, Brevo-Integration für Newsletter — der Newsletter hat alle 1–2 Wochen ein Update.

Vertiefend: MCP Server bauen zeigt, wie du eigene Tool-Integrationen für Claude baust, die du auch aus so einer Pipeline aufrufen kannst. KI-Automatisierung mit eigenen Pipelines gibt den allgemeineren Architektur-Überblick — wann lohnt No-Code, wann Hybrid, wann Full-Code. Im Tools-Verzeichnis findest du Replicate, Claude, ElevenLabs und ffmpeg mit aktueller DSGVO-Bewertung.

Quellen

Porträt von Christian Ohle

Geschrieben von

Christian Ohle

Builder · Schmied der christianohle

Seit 2005 mit dem Web. Online-Marketing, Coding, lokale KI. Schreibt auf christianohle über Agents, MCP, lokale LLMs und Workflow-Automation — alles selbst getestet. Wöchentlicher Newsletter mit aktuellen News & Tutorials.