No description
  • Python 61.1%
  • Nix 20.6%
  • JavaScript 12.8%
  • Dockerfile 5.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
marti 9a9728726b
All checks were successful
ci / check (push) Successful in 1m30s
chore: wire graft for agents
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 12:28:35 +02:00
.claude chore: wire graft for agents 2026-09-27 12:28:35 +02:00
.forgejo/workflows ci: build, test and check formatting on forgejo 2026-09-27 12:28:35 +02:00
docs docs: describe the api, pipeline, development and deployment 2026-09-27 12:28:35 +02:00
nix feat(nix): add a nixos module for the renderer 2026-09-27 12:28:35 +02:00
src/shorts_render feat: render a vertical short from a script and stills 2026-09-27 12:25:35 +02:00
tests feat: render a vertical short from a script and stills 2026-09-27 12:25:35 +02:00
.editorconfig build: scaffold the python project and its validated config 2026-09-27 12:25:35 +02:00
.env.example build(docker): add a container image and compose file 2026-09-27 12:28:35 +02:00
.envrc chore: wire graft for agents 2026-09-27 12:28:35 +02:00
.gitattributes build: scaffold the python project and its validated config 2026-09-27 12:25:35 +02:00
.gitignore chore: wire graft for agents 2026-09-27 12:28:35 +02:00
.ignore chore: wire graft for agents 2026-09-27 12:28:35 +02:00
.mcp.json chore: wire graft for agents 2026-09-27 12:28:35 +02:00
AGENTS.md docs: describe the api, pipeline, development and deployment 2026-09-27 12:28:35 +02:00
CLAUDE.md docs: describe the api, pipeline, development and deployment 2026-09-27 12:28:35 +02:00
compose.yaml build(docker): add a container image and compose file 2026-09-27 12:28:35 +02:00
Dockerfile build(docker): add a container image and compose file 2026-09-27 12:28:35 +02:00
flake.lock build(nix): package with pinned tools, voices and whisper model 2026-09-27 12:25:35 +02:00
flake.nix build(nix): package with pinned tools, voices and whisper model 2026-09-27 12:25:35 +02:00
opencode.json chore: wire graft for agents 2026-09-27 12:28:35 +02:00
pyproject.toml build: scaffold the python project and its validated config 2026-09-27 12:25:35 +02:00
README.md docs: describe the api, pipeline, development and deployment 2026-09-27 12:28:35 +02:00

shorts-render

Turn a voiceover script and a handful of stills into a vertical short, with spoken audio and burned-in word-timed captions.

curl -s localhost:8310/render -H 'content-type: application/json' \
  -d '{"script": "Octopuses have three hearts.", "images": ["<base64 png>", "<base64 png>"]}' \
  -o short.mp4

It is the render step of a MoneyPrinter-style pipeline: an n8n workflow writes the script and generates the images, this service turns them into a 1080x1920 mp4, and n8n takes it from there. Speech is Piper, caption timing is whisper.cpp, the video is ffmpeg. Everything runs locally on the CPU.

Run it

Three ways, all serving on port 8310:

Path Command Needs
Nix nix run . Nix with flakes
Container docker compose up --build Docker
Local uv run shorts-render Python 3.11+, uv, ffmpeg, whisper-cli, piper, a voice and a whisper model; see docs/development.md

API

Route What
POST /render JSON body, returns video/mp4; see below
GET /voices Installed Piper voices and the default
GET /healthz Liveness
GET /readyz 503 until every binary, voice and model is present

POST /render body:

Field Type Default Notes
script string required Spoken verbatim
images string[] required Base64 PNG, JPEG or WebP, or data URLs, in display order; 1 to 20
voice string DEFAULT_VOICE A name from /voices
language string auto ISO code for caption timing
speed number 1.0 0.5 to 2.0
subtitles bool true Burn in captions

The response header X-Video-Duration carries the length in seconds. There is no authentication: bind it to loopback or a trusted LAN only.

Docs

  • docs/architecture.md - the pipeline and why it is shaped this way
  • docs/development.md - local setup and tests
  • docs/deployment.md - NixOS module and container

Configuration is environment variables, all listed in .env.example.