Content Pipeline: From Script to Video
Every content creator faces the same bottleneck: production speed. You can ideate ten videos before lunch, but turning those ideas into publishable content — script, voiceover, visuals, subtitles, thumbnails, branding — eats your entire afternoon. Multiply that across a weekly publishing schedule and you're spending more time rendering than thinking. We built an automated content pipeline that takes a topic and produces a finished video with subtitles, custom thumbnails, and branded intro/outro cards — all on a single ARM64 machine, all using open-source models, and all without touching a cloud API. This is how it works.
The Problem: Production Overhead
A typical YouTube video for a technical channel involves:
- Research and scripting — 1-2 hours
- Voiceover recording — 30 minutes (plus retakes)
- Visual asset creation — 1-3 hours (screenshots, diagrams, B-roll)
- Video editing and assembly — 2-4 hours
- Subtitle generation and syncing — 30 minutes
- Thumbnail design — 30 minutes
- Upload and metadata — 15 minutes
That's 6-11 hours per video. For a solo creator running a company, that's unsustainable. The question wasn't whether to automate — it was how much could be automated without sacrificing quality.
The Stack: Everything Local
Our pipeline runs entirely on a DGX Spark (ARM64, NVIDIA GPU). No cloud API calls, no SaaS dependencies, no per-render costs. The components:
| Stage | Tool | Purpose | |-------|------|---------| | Script generation | LiteLLM + Qwen 3 | Topic → structured script with scenes | | Voiceover | Kokoro TTS | Text → natural speech (GPU-accelerated) | | Scene illustration | ComfyUI + PIL fallback | Background images per scene | | Video assembly | FFmpeg | Concatenate scenes, add transitions, burn subtitles | | Subtitles | Speaches STT (Whisper) | Audio → SRT, then burn into video | | Thumbnails | ComfyUI | 1280×720 custom thumbnail per video | | Branding | FFmpeg overlays | Intro card, outro card, watermark | | Orchestration | n8n | Trigger pipeline every 3 days, notify on completion |
All of these are open-source, self-hosted, and run on the same machine. The pipeline is orchestrated by a Python script (pipeline.py) that chains these stages together, with an n8n workflow triggering it on a recurring schedule.
Stage 1: Script Generation
The pipeline starts with a topic. The script generator sends a structured prompt to Qwen 3 (our primary reasoning model, routed through LiteLLM for tracing and fallback management) and receives a JSON response containing:
- Video title and description
- Scenes — each with a title, narration text, and visual description
- Tags for YouTube metadata
The prompt engineering is critical here. We constrain the model to produce scenes of 15-45 seconds each (short enough for engagement, long enough for substance), with narration that sounds natural when spoken aloud — not written. This means:
- No parenthetical asides
- No long subordinated clauses
- No semicolons (TTS handles them awkwardly)
- Active voice, second person where possible
- Technical terms spelled out on first use
The script generator also produces a visual_description for each scene, which the illustrator uses in the next stage.
LiteLLM Routing
All LLM calls go through LiteLLM (http://localhost:4000/v1), which gives us:
- Automatic fallback — if Qwen 3 is unavailable, traffic routes to Qwen 2.5 7B
- Token tracking — every call is logged with input/output token counts
- Langfuse tracing — full prompt/response inspection for debugging
- Rate limiting — prevents any single pipeline run from saturating the GPU
This abstraction layer means we can swap models without touching the pipeline code. When a new model drops (Llama 4, Qwen 4, whatever's next), we update the LiteLLM config and the pipeline picks it up automatically.
Stage 2: Voiceover with Kokoro TTS
Kokoro is our primary TTS engine — a GPU-accelerated text-to-speech system that produces remarkably natural speech. We use the af_heart voice (warm, conversational) as our default.
The TTS stage takes each scene's narration text and generates a WAV audio file. The pipeline handles:
- Text preprocessing — strips markdown, expands abbreviations, normalizes numbers
- Chunking — long narrations are split at sentence boundaries and stitched together
- Speed adjustment — slight tempo increase (1.05x) for a more energetic delivery
- Audio normalization — LUFS targeting for consistent loudness across scenes
Kokoro runs on port 8880 and exposes an OpenAI-compatible /v1/audio/speech endpoint, so integration is straightforward. A fallback to Piper TTS (port 8002) is configured for when Kokoro is unavailable — lighter quality, but the pipeline never stops.
Stage 3: Scene Illustration
Each scene gets a background image. The pipeline tries ComfyUI first (port 8188) — our local Stable Diffusion instance — using the scene's visual_description as the prompt. If ComfyUI is down or the GPU is busy with other workloads, it falls back to PIL-generated gradient backgrounds with the scene title overlaid.
The ComfyUI workflow is a standard txt2img pipeline optimized for 1920×1080 output with a cinematic aesthetic. We use a fixed seed per scene (derived from the scene index) so re-runs produce consistent visuals — important when you're iterating on script changes and don't want the thumbnails to shift.
Stage 4: Video Assembly with FFmpeg
This is where everything comes together. The video assembler (video_assembler.py) takes the audio files, scene images, and branding assets and produces a final MP4.
Transitions
We support three transition types between scenes:
- Crossfade (default) — 0.5s dissolve
- Slide — 0.3s left-to-right wipe
- Cut — no transition (for fast-paced segments)
The transition type is specified in the script JSON, allowing the LLM to control pacing. A tutorial video might use cuts between code demonstration scenes and crossfades between explanatory segments.
Text Overlays
Each scene displays its title as a lower-third overlay — configurable font, size, position, and opacity. For tutorial videos, we also support code snippet overlays that appear at specific timestamps within a scene.
Branding
Every video gets:
- Intro card — J4SGON logo + tagline, 3 seconds
- Outro card — Subscribe prompt + social links, 5 seconds
- Watermark — Logo in bottom-right corner, 20% opacity, persistent throughout
These are rendered as FFmpeg overlay filters, not burned into the source footage, so they can be updated without re-rendering the entire video.
Stage 5: Subtitles
After the video is assembled, the audio track is sent to Speaches STT (port 8001), which runs Whisper for speech-to-text. The result is an SRT file with timestamps. The pipeline then:
- Cleans up the SRT — removes filler words ("um", "uh"), fixes capitalization
- Burns subtitles into the video — optional, controlled by a flag
- Saves the SRT alongside the video — for upload to YouTube as a separate caption track
Burning subtitles adds about 30% to the rendering time, so it's optional. For YouTube uploads, we upload the SRT separately so viewers can toggle captions.
Stage 6: Thumbnail Generation
ComfyUI generates a 1280×720 thumbnail for the video using a prompt derived from the video title and first scene's visual description. The thumbnail includes:
- Background image (AI-generated)
- Video title text overlay (high contrast, readable at small sizes)
- J4SGON branding element
The thumbnail is the single most important factor for click-through rate, so we generate two variants and let the human pick one (or request a re-roll). This is the one stage that still benefits from human curation.
Orchestration with n8n
The entire pipeline is triggered by an n8n workflow that runs every 3 days. The workflow:
- Picks the next topic from the content calendar
- Calls the pipeline API (
http://localhost:8095/api/pipeline/run) - Polls for completion
- Sends a Discord notification with the summary (duration, file size, scene count)
The n8n workflow handles error cases — if the pipeline API returns an error, it retries once after 5 minutes, then sends a failure notification.
What's Next
The pipeline is functional but not perfect. Current limitations and next steps:
- YouTube upload automation — requires YouTube Data API credentials; currently a manual step
- Social cross-posting — auto-generate LinkedIn/Twitter posts from the video description
- A/B thumbnail testing — upload two thumbnails and switch based on CTR after 24h
- Multi-language voiceover — generate EN and ES audio tracks from the same script
- Human-in-the-loop review — n8n approval step before publishing
The goal isn't to remove humans from the loop entirely — it's to compress 8 hours of production work into 30 minutes of review. The pipeline handles the mechanical work; the human handles the creative judgment. That's the division of labor that makes a solo creator sustainable.
Try It Yourself
All the code for this pipeline is open and runs on commodity ARM64 hardware with an NVIDIA GPU. If you're running a similar stack (Ollama, LiteLLM, ComfyUI, Kokoro TTS), you can adapt this pipeline for your own channel. The key insight is that local AI models are now good enough for production content — you don't need GPT-4 or ElevenLabs to make a decent video. You just need a well-orchestrated pipeline and the patience to tune your prompts.
Start with the script generator. Get that producing output you're happy with. Then add TTS. Then add visuals. Build the pipeline one stage at a time, and by the end you'll have something that turns ideas into videos while you sleep.