A web application for rendering ranked/countdown-style videos from uploaded clips. Built with Python (Flask), FFmpeg, Pillow, Stripe, and ElevenLabs.
Developed by Knight Logics
Recommended distribution model: downloadable Windows desktop app so each user renders on their own machine.
- Drag-and-drop clip upload — Upload 3–10 video clips per render job
- Animated overlays — Ranked number reveals + animated title cards generated via Pillow
- ElevenLabs narration — Optional AI-generated voiceover with synced word captions
- Intermissions — Configurable transition segments between clips
- Background music — Optional mix-in with adjustable volume
- Output presets — 1440×2560 (portrait), 1920×1080 (landscape), and custom dimensions
- Credit billing system — Stripe-powered credit purchases with per-render deduction
- Email recovery — Link your access token to an email for recovery via Gmail SMTP
- Job queue — Threaded background render jobs with real-time SSE progress updates
- Audit log — Append-only JSONL billing audit trail
If you want each user to use their own computer resources (instead of your machine), distribute this app as a local package.
Quick path:
- User downloads the Windows exe release zip
- User extracts it and runs
VideoForge-Studio.exe - App opens in a standalone desktop window using the same VideoForge UI
- Rendering still runs locally on that user's hardware
See full details in DOWNLOAD_AND_RUN.md and USER_LOCAL_MODE.md.
RawGitHack is for serving static files (HTML/CSS/JS) directly from a Git repository.
VideoForge Studio is not a static app. It requires:
- Python server execution (Flask + Waitress)
- FFmpeg processes at runtime
- Writable server storage for uploads/jobs/outputs
- Server-side billing/token handling
Because of this, use a backend host (for example Render, Railway, Fly.io, or a VPS with Docker), not RawGitHack.
If you need zero monthly hosting cost, run VideoForge on your own machine and expose it using a Cloudflare Tunnel.
This keeps infrastructure cost at $0, with these tradeoffs:
- Your computer must stay on while the app is public
- Upload/render performance depends on your machine and internet
- Availability is not guaranteed like managed cloud hosting
- Start the app locally:
./run_prod.ps1
- In another terminal, run:
cloudflared tunnel --url http://127.0.0.1:5050
- Cloudflare prints a temporary
trycloudflare.comURL you can share.
Use this when you want videoforge.knightlogics.com without a paid host:
- Move DNS for your domain to Cloudflare (free plan)
- Install
cloudflaredon the machine running VideoForge - Authenticate once:
cloudflared tunnel login
- Create a named tunnel:
cloudflared tunnel create videoforge
- Route DNS:
cloudflared tunnel route dns videoforge videoforge.knightlogics.com
- Run the tunnel mapped to local app:
cloudflared tunnel --url http://127.0.0.1:5050 --hostname videoforge.knightlogics.com
When this tunnel is running, your app is publicly reachable at https://videoforge.knightlogics.com with no monthly hosting bill.
| Layer | Technology |
|---|---|
| Desktop shell | PySide6 |
| Web server | Python 3.11+ · Flask · Waitress (WSGI) |
| Video processing | FFmpeg |
| Image / overlay generation | Pillow |
| Narration (optional) | ElevenLabs REST API |
| Payments | Stripe Checkout |
| SMTP · Gmail App Password | |
| Config | python-dotenv |
- Python 3.11+
- FFmpeg — must be on system
PATH - A Stripe account (optional — credit purchasing disabled without it)
- A Gmail App Password (optional — email recovery disabled without it)
# From the project root
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -r requirements.txtCopy-Item .env.example .env
# Edit .env with your keys (see Configuration section below)# Production (Waitress WSGI)
.\run_prod.ps1
# Development (Flask dev server, auto-reload)
.\run_dev.ps1The app will be available at http://localhost:5050
For public hosting, deploy this repository as a standalone service instead of trying to fold it into the static Knight Logics website repo. This app needs Python execution, FFmpeg, writable storage, and long-running render jobs, so it belongs in its own backend-capable deployment target.
This repository is already container-ready via Dockerfile and can be deployed to any provider that supports Docker containers.
Recommended public URL pattern:
https://videoforge.knightlogics.com
- Docker runtime with FFmpeg available inside the container (handled by included
Dockerfile) - Writable runtime storage for:
workspace/uploadsworkspace/jobsworkspace/outputsworkspace/billing_tokens.jsonworkspace/billing_audit.jsonl
- Environment variables set from
.env.example
- Create a new GitHub repository for this app (recommended org: Knight-Logics)
- Push this project and connect it to your container host
- Set environment variables from
.env.example - Set
APP_BASE_URLto your live domain (for examplehttps://videoforge.knightlogics.com) - Ensure your domain DNS points to the deployment
- Validate health endpoint:
GET /health
This repository now includes render.yaml for one-click Render Blueprint deployment.
- Push this project to its own GitHub repository
- In Render, choose New + > Blueprint
- Select your repository
- Confirm
render.yamlvalues, then deploy - Add required secrets in Render dashboard:
- Stripe keys (if billing enabled)
- SMTP values (if email recovery enabled)
- ElevenLabs key (if shared hosted narration enabled)
- Point DNS for
videoforge.knightlogics.comto the Render service - Update
APP_BASE_URLto your final domain and redeploy if needed
- Open
/healthand confirm status OK - Open
/and confirm UI loads - Run one render job without narration
- Run one render job with narration (if enabled)
- Verify output download works
| Variable | Required | Description |
|---|---|---|
SECRET_KEY |
Yes | Random secret for session signing — generate one: python -c "import secrets; print(secrets.token_hex(32))" |
APP_LOG_LEVEL |
No | Server logging level (default: INFO) |
MAX_UPLOAD_MB |
No | Max upload size in MB (default: 4096) |
MAX_TITLE_LENGTH |
No | Max title length in characters (default: 120) |
MAX_CAPTION_LENGTH |
No | Max caption length per clip in characters (default: 240) |
FREE_TRIAL_CREDITS |
No | Free credits per new IP (default: 3) |
STRIPE_SECRET_KEY |
For billing | Stripe secret key |
STRIPE_PUBLISHABLE_KEY |
For billing | Stripe publishable key |
STRIPE_WEBHOOK_SECRET |
For billing | Stripe webhook signing secret (required for webhook verification) |
MAX_CHECKOUT_CREDITS |
No | Upper bound for one checkout purchase (default: 250) |
EXPOSE_INTERNAL_ERRORS |
No | Include tracebacks in API/SSE error payloads (default: false) |
STRIPE_SUCCESS_URL |
No | Redirect after successful payment |
STRIPE_PRICE_1_CREDIT_CENTS |
No | Price per credit in cents (default: 100 = $1.00) |
SMTP_HOST |
For email | smtp.gmail.com |
SMTP_PORT |
For email | 587 |
SMTP_USER |
For email | Your Gmail address |
SMTP_PASS |
For email | 16-character Gmail App Password |
SMTP_FROM |
For email | Display name + address, e.g. VideoForge Studio <you@gmail.com> |
APP_BASE_URL |
For email | Full URL shown in recovery emails (default: http://127.0.0.1:5050) |
ALLOW_SHARED_ELEVENLABS_KEY |
No | Allow users to use your hosted ElevenLabs key (default: false) |
ELEVENLABS_API_KEY |
If shared key | Your ElevenLabs API key |
ADMIN_BILLING_KEY |
No | Admin key for /api/admin/* endpoints |
VideoForge-Studio/
app.py # Flask application + all route handlers
wsgi.py # WSGI entry point (for Waitress / Gunicorn)
run_prod.ps1 # Production launcher (Waitress)
run_dev.ps1 # Development launcher (Flask dev server)
requirements.txt # Python dependencies
.env.example # Environment variable template
src/
billing.py # Credit store — thread-safe JSON billing ledger
captions.py # Word-timed caption frame generation
ffmpeg_tools.py # FFmpeg subprocess wrappers
job_manager.py # Threaded job queue + SSE event streaming
models.py # RenderSettings / ClipInput dataclasses
narration.py # ElevenLabs TTS + timestamp sync
overlays.py # Pillow-based number reveal + title animation frames
pipeline_engine.py # Full render pipeline orchestrator
token_service.py # Access token creation / validation
utils.py # Shared helpers
static/
app.js # Frontend JS (fetch API, SSE progress, UI state)
styles.css # UI stylesheet
knightlogics-logo.png # Brand asset
templates/
index.html # Single-page app shell
workspace/ # Runtime data (gitignored)
uploads/
jobs/
outputs/
| Method | Path | Description |
|---|---|---|
GET |
/ |
App UI |
GET |
/health |
Health check |
GET |
/api/app-config |
App feature flags |
GET |
/api/app-update |
Update check metadata |
GET |
/api/voice-preview/<voice_id> |
Voice preview audio |
GET |
/api/billing/config |
Billing + SMTP status |
GET |
/api/billing/status |
Check account credit status |
POST |
/api/billing/create-token |
Create token and apply free-trial policy |
POST |
/api/billing/token/balance |
Check token balance via POST form |
POST |
/api/billing/link-email |
Link recovery email to token |
POST |
/api/billing/recover |
Recover token by email |
POST |
/api/render |
Submit a final render job |
POST |
/api/preview |
Submit preview render job |
GET |
/api/jobs/<job_id> |
Job status snapshot |
GET |
/api/jobs/<job_id>/events |
SSE progress stream |
GET |
/outputs/<filename> |
Download rendered video |
POST |
/api/renders/save |
Save output metadata to account history |
GET |
/api/renders/list |
List account render history |
POST |
/api/payments/create-checkout-session |
Start Stripe checkout |
POST |
/api/payments/confirm-session |
Confirm paid checkout session |
POST |
/api/payments/stripe-webhook |
Stripe webhook handler |
MIT — see LICENSE