Video
The one Video capability, merged from the legacy leumas-clips (long video → transcribe → scored 9:16 clips) and leumas-clips-shorts (text → narrated vertical short) tools. Stateless primitives live...
Video (the merged shorts + clips engine)
The one Video capability, merged from the legacy leumas-clips (long video → transcribe → scored 9:16 clips) and leumas-clips-shorts (text → narrated vertical short) tools. Stateless primitives live in the video adapter (shared/engines/adapters/media/video); the stateful pipeline — clip jobs, uploads, the library, the shorts gallery — is the /api/video router. Both share one ffmpeg render layer and an offline-first say TTS default.
How agents call it
| Surface | How |
|---|---|
| CLI (headless, no server/auth) | node ops/tools/run-tool/run-tool.mjs video <op> '<jsonArgs>' |
| MCP client / chatbot / Leviathan | adapter video → tool/functioncall (generateShort, scoreTranscript, capabilities) |
| MCP (read the stateful backend) | leumas-clips-server server — inspects videos/clips/health of a running /api/video |
| Automation rule / grid cell | the three adapter ops as chainable cells |
| HTTP | /api/video/* (JSON + multipart, requireAuth) |
| Studio | Video domain (/admin/video) — Shorts + Clips tabs |
| Code | generateShort, scoreTranscript via the video adapter; router = createVideoRouter |
The two headline HTTP calls
Text → narrated vertical short. POST /api/video/shorts/generate:
{ "text": "Three facts about octopuses.", "mode": "full-gameplay",
"voice": { "provider": "say" }, "title": "Octopus facts" }
mode ∈ full-gameplay (default) | split-layout | full-media | media-gameplay-inset | auto; voice.provider ∈ say (default, offline OS voice) | mock | openai | elevenlabs. Gameplay/media backgrounds are optional — omit them and the engine synthesizes demo placeholders so a bare text generates. Enums are validated at the router (clean 400) before the SDK runs. Returns 201 { short, manifest }; the playable file is at short.video_url (/api/video/assets/shorts/...). Then GET /api/video/shorts (owner-scoped gallery), GET|DELETE /api/video/shorts/:id.
Long video → scored clips. POST /api/video/videos/import-url { url, title?, description?, autoProcess?, captureDurationSeconds? } ingests a remote/YouTube source and (unless autoProcess:false) enqueues a job that transcribes → scores highlights → renders 9:16 clips with burned captions + thumbnails. Or POST /api/video/videos/upload (multipart file). Track it with GET /api/video/videos, GET /api/video/videos/:id (video + clips + latest job), GET /api/video/videos/:id/status, GET /api/video/videos/:id/transcript, POST /api/video/videos/:id/process; fetch results via GET /api/video/clips/:id and GET /api/video/clips/:id/download, GET /api/video/jobs/:id. Public pre-gate probes: GET /api/video/health, GET /api/video/capabilities.
Run it from the CLI
node ops/tools/run-tool/run-tool.mjs video generateShort '{"text":"...","mode":"full-gameplay","provider":"say"}'
node ops/tools/run-tool/run-tool.mjs video scoreTranscript '{"segments":[{"start":0,"end":6,"text":"..."}],"maxClips":4}'
generateShort runs a real ffmpeg render; scoreTranscript (rank clip windows) and capabilities are pure. No API server or auth — the same registry the API loads runs headless.
The repointed leumas-clips-server MCP
shared/engines/mcp/mcp-servers/leumas-clips-server is the legacy YouTube-to-shorts MCP, repointed at the merged backend: getCapabilities, health, listVideos, getVideo {videoId}, listClips {videoId} — each takes an optional baseUrl (default LEUMAS_CLIPS_BASE_URL → http://127.0.0.1:3022). It is a read/inspect client over a running /api/video; generation goes through the video adapter's generateShort (auto-exposed as its own MCP tool) or the HTTP route.
Gating
Login-only and free for every member out of the box. The video membership seam is present in createVideoRouter (swap the no-op auth for requireMembership('video') to gate the whole engine behind a plan) but unapplied. PassNode meters the heavy render endpoints via the feature:video.render guard — a no-op until a passnode rule doc exists, so tenant metering switches on with no code change. Rendered assets are served auth-gated and allowlisted to clips|shorts|thumbnails (the data root — db index, raw uploads, transcripts — is never served).
Studio Video domain
/admin/video (nav id video, icon [video], "Free for every logged-in member") wraps four tabs in VideoShell: HTML → MP4 + Renders (HyperFrames), Shorts (calls /api/video/shorts/generate + gallery), Clips (calls /api/video/videos/import-url, upload, and the library). Surfaces: products/leumas-studio/src/admin/video/surfaces.jsx.