Skip to content

Repository files navigation

Darc Studio / DarcAds

Enterprise Open-Source AI Video, Movie & Direct-Response Ad Engine. Turn any story concept, viral premise, or product URL into finished multi-scene video productions.

Darc Studio is a local-first alternative to Runway, Sora, Arcads, Creatify, and Higgsfield. It generates cinematic quick movies, viral social scenes & micro-dramas, POV suspense shorts, and high-converting direct-response video ads powered by the full roster of state-of-the-art video models including ByteDance Seedance 2.5/2.0, Kling 2.0 Pro, Wan 2.2 Turbo, Minimax Hailuo-02, Luma Ray 2, and Runway Gen-3 Alpha.

Zero monthly subscriptions. Zero per-video credits. 100% local-first and privacy-preserving.

# 1. Generate a cinematic micro-drama with ByteDance Seedance 2.5
python3 main.py --mode movie --premise "A lone astronaut discovers an ancient glowing doorway on Europa" --video-model seedance-2.5 --style cyberpunk

# 2. Or generate high-converting e-commerce video ads from any product URL
python3 main.py --url https://your-store.com/products/your-product --variants 3

# 3. Launch the visual Web Studio
python3 app.py

Table of contents


Why this exists

Commercial AI video & ad platforms charge $40–500/month and cap resolution, generation time, camera control, and script lengths. Darc Studio gives you direct access to the world's best models (Seedance 2.5, Kling 2.0, Wan 2.2, ElevenLabs, Whisper, FFmpeg) with unified prompt engineering, professional camera rig motion, and word-level karaoke subtitles.

Everything runs on your machine. Your data and renders stay 100% private.

Creation Modes & Capabilities

Creation Mode Description
🎬 Quick Movies & Cinema Multi-scene cinematic storytelling with plot escalation, character dialogue, Hollywood camera physics (Dolly, Drone Orbit, FPV Flythrough, Whip Pan), and film-grade visual grading.
📱 Viral POV & Social Shorts High-retention social scenes, Reddit confessions, suspense thrillers, and comedy skits with word-level glowing karaoke subtitles.
🛍️ Direct-Response Video Ads Automated storefront scraping, 7 conversion frameworks (PAS, UGC Testimonial, 3 Reasons, Unboxing), UGC avatar presenters, and branded end cards.
🎨 Multi-Model Canvas Side-by-side model comparison across ByteDance Seedance 2.5, Kling 2.0, Wan 2.2, Minimax Hailuo-02, Luma Ray 2, and Runway Gen-3.
Real product photography Scrapes and normalizes the actual images from your store, so the product on screen is the product you sell
Direct-response scripts Seven proven hook frameworks (PAS, UGC testimonial, listicle, before/after, us-vs-them, myth-bust, unboxing)
A/B variants in one run --variants 3 writes three genuinely different angles, not three rewordings
Word-level captions TikTok-style karaoke captions with the spoken word highlighted, aligned to the real audio
Ken Burns motion Every still gets a camera move, varied per scene, so nothing sits static
Crossfade transitions Frame-accurate: the finished runtime always equals the narration length
Music that behaves Loops to length, side-chain ducks under the voiceover, normalized to −14 LUFS
UGC presenters Lip-synced talking-head actors delivering your script
Brand kit Logo watermark, accent colour, caption styling, CTA end card
Multi-platform 9:16 vertical, 1:1 square, 16:9 landscape
Script auditing Warns when a hook runs long, a CTA is missing, or copy drifts into ad-speak

Install

DarcAds needs Python 3.9+ and ffmpeg.

1. ffmpeg

# macOS
brew install ffmpeg

# Debian / Ubuntu
sudo apt update && sudo apt install ffmpeg

# Fedora
sudo dnf install ffmpeg

# Windows
winget install Gyan.FFmpeg

DarcAds does not require ffmpeg to be built with libass or libfreetype — all text is rendered with Pillow, so a stock build is fine.

2. DarcAds

git clone https://github.com/<your-username>/DarcAds.git
cd DarcAds

python3 -m venv venv
source venv/bin/activate          # Windows: venv\Scripts\activate

pip install -r requirements.txt

3. Verify

python3 main.py --doctor
DarcAds environment check

  ok   ffmpeg + ffprobe

  -    Anthropic (script engine)
  -    OpenAI (script engine + TTS)
  ...

  DarcAds runs fully offline with no keys - quality simply improves as you add them.

A row of - is fine. Only the ffmpeg line has to say ok.


Quick start

# The simplest possible run
python3 main.py --url https://your-store.com/products/your-product

# A 30-second ad with three A/B variants, music and a brand kit
python3 main.py \
  --url https://your-store.com/products/your-product \
  --variants 3 \
  --seconds 30 \
  --music assets/music/upbeat.mp3 \
  --brand-name "Acme" \
  --accent "#FF4D6D" \
  --cta "Get 20% off"

# Browser UI instead
python3 app.py          # opens http://localhost:7860

Output lands in output/, alongside a *_run.json manifest recording exactly what was scraped, written, generated and rendered.


It works with zero API keys

This is the important part. With nothing configured, DarcAds still:

  • scrapes your real product images, price, features and reviews
  • writes a complete 5-scene script using a built-in framework director
  • generates a voiceover with free TTS
  • composes each product photo into the frame without cropping the product away
  • animates every scene with a Ken Burns move
  • burns in word-level captions, hook text and a progress bar
  • crossfades, ducks music, normalizes loudness and renders a branded end card

Here is a real frame from a keyless run — no LLM, no video model, no paid voice:

 ┌────────────────────┐
 │     3 REASONS      │  ← hook text, brand accent colour
 │                    │
 │      [product]     │  ← your real photo, whole product visible
 │                    │
 │  replaced          │  ← karaoke captions, spoken word highlighted
 │  everything else.  │
 │ ▰▰▰▰▰▱▱▱▱▱▱▱▱▱▱▱▱  │  ← progress bar
 └────────────────────┘

Adding keys improves individual stages. It is never required to get a video.


Adding engines

DarcAds detects what is installed and configured, and upgrades that stage automatically. Copy .env.example to .env and fill in only what you want.

cp .env.example .env
pip install -r requirements-optional.txt   # or install individual packages
Stage Free default Upgrade How
Script Built-in framework director Claude / GPT / local Llama ANTHROPIC_API_KEY, OPENAI_API_KEY, or run Ollama
Voice gTTS edge-tts (free, much better) pip install edge-tts
Kokoro-82M (free, local) pip install kokoro
ElevenLabs (best) ELEVENLABS_API_KEY
Video Product photos + Ken Burns Wan 2.1 / Kling / Hunyuan / Luma / MiniMax FAL_KEY
Your own GPU run ComfyUI, set COMFYUI_WORKFLOW
Captions Estimated timings True word alignment pip install faster-whisper
Avatars Lip-synced UGC presenters FAL_KEY + an actor asset

Best value for one command: pip install edge-tts. It replaces the robotic default voice with Microsoft neural voices, costs nothing, and needs no API key.

Local LLMs are auto-detected — if Ollama is running on its default port, DarcAds will use it with no configuration:

ollama serve
ollama pull llama3.1
python3 main.py --url <product-url>       # picks up Ollama automatically

The web studio

python3 app.py

Opens a studio at http://localhost:7860 with everything the CLI exposes: variants, hook framework, platform, quality, voice, caption style, transitions, UGC presenter, brand kit, music and logo upload, plus optional session-only API key fields.

Keys typed into the UI apply to that process only and are never written to disk.


CLI reference

python3 main.py --url <product-url> [options]

Core

Flag Default Description
--url required Product page URL
--output output/final_ad.mp4 Output path
--variants 1 Number of creative variants (each uses a different framework)
--seconds 20 Target runtime
--framework pas pas, testimonial, three_reasons, before_after, us_vs_them, myth_bust, unboxing
--doctor Report configured engines and exit

Script engine

Flag Default Description
--provider auto auto, anthropic, openai, openai_compatible, offline
--model provider default Model name override
--base-url Custom LLM endpoint (Ollama, LM Studio, vLLM)

Audio

Flag Default Description
--voice female_energetic female_energetic, female_calm, male_confident, male_casual, neutral
--tts auto Force an engine: elevenlabs, openai, kokoro, edge, gtts
--music Background track
--music-volume 0.18 Music level, 0–1
--no-duck Disable side-chain ducking

Visuals

Flag Default Description
--platform tiktok tiktok, reels, shorts, vertical, square, landscape
--quality standard draft (fast), standard, high
--captions karaoke karaoke, word_pop, block, none
--transition fade none, fade, dissolve, slideleft, slideright, wipeleft, circleopen, smoothleft
--video-model wan-2.1 wan-2.1, wan-2.2, kling, kling-standard, hunyuan, luma, minimax, ltx
--no-generation Never call a paid video model — stay free
--no-whisper Skip Whisper alignment, estimate caption timings

UGC avatar

Flag Description
--avatar Use a lip-synced presenter for every scene
--actor Actor name, file path or URL
--avatar-model auto, hedra, sync-lipsync, latentsync, veed
--list-actors List available actors and exit

Brand kit

Flag Default Description
--brand-name scraped vendor Name on the end card
--logo Transparent PNG watermark
--accent #00E5A0 Highlight colour for captions and CTA
--text-color #FFFFFF Base caption colour
--cta Shop now End card call to action
--cta-seconds 1.6 End card length (0 disables)
--font auto Font family name or .ttf path

UGC avatars

The Arcads-style feature: a person on camera delivering your script.

  1. Put a portrait photo or a short clip of a presenter in assets/actors/
  2. Add a FAL_KEY to .env
  3. Run with --avatar
python3 main.py --url <product-url> --avatar --actor maya

A still portrait is animated to speak the narration (Hedra Character-2). A video clip is re-synced to it (sync-lipsync). DarcAds picks the right mode from the file type.

Only use likenesses you have the rights to, and disclose synthetic media where your market requires it. See assets/actors/README.md.


Brand kit

python3 main.py --url <product-url> \
  --brand-name "Acme" \
  --logo assets/brand/logo.png \
  --accent "#FF4D6D" \
  --text-color "#FFFFFF" \
  --cta "Get 20% off" \
  --cta-seconds 2.0 \
  --font assets/fonts/Montserrat-ExtraBold.ttf

The accent colour drives the highlighted caption word, the hook text, the progress bar and the CTA button, so one value re-skins the whole ad.


How it works

Product URL
    │
    ▼
1. INGEST          src/ingest.py
   Shopify JSON → JSON-LD → OpenGraph → <img> sweep
   Extracts title, price, features, reviews, ratings, high-res images
    │
    ▼
2. DIRECT          src/llm_director.py
   Framework + product data → LLM → validated scene timeline
   Hallucinated image references are snapped back to real URLs
    │
    ▼
3. GENERATE        src/voice.py · src/visuals.py · src/avatars.py
   Narration first (its duration times everything downstream)
   Then visuals: product photo → animated photo → generated clip → designed frame
    │
    ▼
4. ALIGN           src/captions.py
   Word timings (Whisper or estimated) → one transparent overlay layer
   containing captions, hook text, logo and progress bar
    │
    ▼
5. COMPILE         src/compiler.py
   Scene clips (Ken Burns) → crossfade spine → audio bed (duck + loudnorm)
   → composite overlay → final MP4
    │
    ▼
output/final_ad.mp4 + run.json

Three design decisions worth knowing:

Narration drives all timing. Each scene clip is cut to the exact length of its voiceover. Clips are rendered transition seconds longer than their narration (all but the last), so each crossfade consumes exactly that overlap and the finished video lands on the exact sum of the narration durations. Captions therefore stay frame-accurate against the audio.

Text is drawn with Pillow, not ffmpeg. Many ffmpeg builds ship without libass or libfreetype. Rendering captions and overlays with Pillow means DarcAds behaves identically on every machine, and gives pixel-level control over the caption pop. The whole burned-in layer is emitted as a PNG sequence plus a concat playlist, so it costs exactly one extra ffmpeg input.

Every stage degrades instead of failing. No API key, a dead image URL, a provider outage or a missing optional package all fall back to the next-best option and log what happened. A run that cannot reach a single external service still produces a finished video.


Ad-craft rules DarcAds follows

These are encoded in the director prompt and enforced by audit_timeline():

  • The hook is under 9 words and never opens with the brand name, "Introducing", or a question the viewer would answer "no" to
  • One idea per scene, roughly one spoken breath
  • Second person, contractions, short sentences — written like a person, not a brand
  • On-screen text never duplicates the narration — captions already carry the words, so the burned-in headline reinforces instead of repeating
  • No invented claims — specs, prices, guarantees and reviews come from the scraped data or are omitted
  • Banned ad-speak: "elevate", "game-changer", "revolutionary", "seamless", "unlock the", "look no further"
  • The last scene always has a call to action with a reason to act now

Warnings are printed after every run and stored in the run manifest:

Script notes for 'testimonial':
  - Hook is 13 words; under 9 keeps the scroll-stop.

Configuration

Beyond the CLI, src/config.py exposes platform and quality presets:

from src.config import build_render_config
from src.models import BrandKit

config = build_render_config(
    platform="tiktok",          # frame size and fps
    quality="high",             # x264 preset, CRF, bitrate, motion intensity
    brand=BrandKit(name="Acme", accent_color="#FF4D6D"),
    caption_position=0.72,      # any RenderConfig field can be overridden
    motion_intensity=0.22,
)

Use the pipeline directly for custom workflows:

from src.pipeline import run_darcads_pipeline

results = run_darcads_pipeline(
    product_url="https://store.com/products/thing",
    variants=3,
    target_seconds=25,
)
for result in results:
    print(result.variant_name, result.final_video_path, result.duration)
    print(result.timeline.scenes[0].narration_text)

Project layout

DarcAds/
├── main.py                 CLI
├── app.py                  Gradio studio
├── src/
│   ├── models.py           Pydantic schemas shared across every stage
│   ├── config.py           Settings, .env loading, presets, provider registry
│   ├── utils.py            ffmpeg wrappers, caching, downloads, font discovery
│   ├── ingest.py           Product page scraping
│   ├── llm_director.py     Hook frameworks, prompting, output repair, audit
│   ├── voice.py            Multi-provider TTS
│   ├── visuals.py          Image framing, generation, designed frames
│   ├── avatars.py          UGC lip-sync
│   ├── captions.py         Word timing and the burned-in overlay layer
│   ├── assets.py           Per-scene asset orchestration
│   ├── compiler.py         The ffmpeg render pipeline
│   └── pipeline.py         End-to-end orchestration and run manifests
├── assets/
│   ├── actors/             Your UGC presenters
│   ├── music/              Your background tracks
│   └── fonts/              Your fonts
├── tests/
└── output/                 Generated videos and manifests

Individual modules are runnable for debugging:

python3 src/ingest.py --url <product-url>              # inspect scraped data
python3 src/llm_director.py --url <product-url>        # print the script only
python3 -m src.voice --text "Test line" --engine edge  # audition a voice
python3 -m src.captions --text "Test caption"          # sample a caption frame
python3 -m src.visuals --text "Test frame"             # sample a designed frame
python3 -m src.avatars --list                          # list actors

Testing

python3 -m unittest discover -s tests           # full suite
python3 -m unittest tests.test_llm_director     # one module

The suite covers scraping and its fallbacks, script generation and repair, caption timing, image framing, the ffmpeg filter graphs and a full end-to-end render. Network access is mocked, so it runs offline. The compiler and pipeline suites perform real renders and take a couple of minutes; everything else is near-instant.


Troubleshooting

ffmpeg or ffprobe was not found on PATH Install ffmpeg (see Install) and reopen your terminal.

The scraper returns a page title instead of a product The URL redirected, usually because the product was discontinued. Check that the URL opens a real product page in a browser. Some storefronts (Cloudflare bot protection) return 403 to any script — those cannot be scraped.

No images found DarcAds falls back to designed frames automatically. To supply your own, generate the script and edit the run manifest, or pass images through the Python API.

The voice sounds robotic That is gTTS, the keyless default. Run pip install edge-tts — free, no key, dramatically better.

Captions drift out of sync on long scenes Install faster-whisper for true word alignment. Without it, timings are estimated from character counts, which is accurate over 2-4 second lines and loosens over longer ones.

Scenes are too short for crossfades; using hard cuts instead Expected. A crossfade needs headroom inside each clip, so DarcAds cuts instead of producing a broken filter graph. Lower --transition-seconds or write longer scenes.

Renders are slow Use --quality draft while iterating on copy. Ken Burns supersamples each still to 2× frame size, which is the main cost; --transition none and --captions none also speed things up.

Currency shows as $ on a non-USD store Shopify's product JSON endpoint does not report a currency code, so DarcAds defaults to USD. Pass the price through the Python API if you need another symbol.


Responsible use

DarcAds generates advertising. That carries obligations.

  • Do not fabricate claims. The director is instructed never to invent specs, prices, guarantees, medical claims or reviews, and it works from scraped data — but you are responsible for what ships.
  • Only use likenesses you have the rights to. See assets/actors/README.md.
  • Disclose synthetic media where your market requires it (EU AI Act, several US states, and the ad policies of TikTok, Meta and YouTube).
  • License your music. Platform Content ID will find it.
  • Scrape responsibly. Respect robots.txt and terms of service. DarcAds is built to scrape your own product pages.

Contributing

Contributions are welcome — see CONTRIBUTING.md. Useful places to start: additional storefront parsers, more hook frameworks, extra video providers, and caption styles.


Licence

MIT — see LICENSE. Do what you like with it, including commercially.

Third-party models and APIs you connect carry their own licences and terms.

About

No description, website, or topics provided.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages