Video Studio
Bundled module for HTML product videos and AI storyboard videos — characters, templates, Bausteine, Feinschliff, and agent-driven rendering.
What it does
Video Studio is a bundled module for creating and watching Clapilot-branded demo videos. Videos are
authored as HyperFrames HTML compositions (real clapilot-*
UI rendered as live DOM — camera zoom, typing, animated logo intro/outro) and rendered server-side to MP4.
You describe the video in an embedded chat; the agent composes it from reusable scene blocks (Bausteine),
renders it in the background, and the finished MP4 appears in the module gallery.
How to open / enable it
- Open Video Studio from the module navigation (
/modules/video-studio). - Video Studio is generally available to signed-in users when the bundled module is installed. The
ADMIN_ONLY_MODULE_SLUGSmechanism remains in place for other modules, butvideo-studiois not in that set. - Module manifest:
bundled-modules/video-studio/module.json— slugvideo-studio, entryindex.html, rendererreact(rendered bysrc/components/modules/video-studio-module.tsx; the legacyindex.htmliframe view is kept as a fallback), iconvideo(app-tilepublic/assets/module-app-icons/video-studio.png, navpublic/icons/navigation/videostudio.png). - On iOS/macOS the native section (
clients/apple/ClapilotApple/Sources/Clapilot/Views/VideoStudioView.swiftplusVideoStudioComposerToolbar.swift,MainAppSection.videoStudio) appears in the side menu only when a probe of the module API succeeds (AppModel.refreshVideoStudioAvailabilityIfNeeded); an unavailable or uninstalled module stays hidden.
Key workflows
Create a video (Neues Video)
Open the single "+ Neues Video" menu and choose one of two creation paths:
- KI-Video opens the full-screen storyboard creation form over the gallery.
- HTML-Video opens the chat-driven HTML/HyperFrames flow. Optionally drag a few Bausteine into the composer, toggle Voiceover/Musik, and describe the video (e.g. "Erstelle ein Video über den E-Mail-Flow."). The agent composes, renders (and muxes audio if requested), and the finished MP4 appears in the gallery.
The HTML-Video entry opens the full Clapilot chat in-module — it embeds the real
ChatFloatingWidget (the same component as the floating dock: pet animation, tool-call log, response timing,
timestamps, markdown), not a separate chat implementation. The widget is embeddable via an additive embedded
config ({ sessionId, composerAccessory, onDidSend }): with no config it is the unchanged floating dock, and
with the config it drops the floating shell and renders just the thread + composer pinned to a forced session.
The composer accessory provides:
- Dedicated session — the module gets-or-creates a stable, non-main "Video Studio" chat session
(id
chat-video-studio-<user>) viaPOST /api/chat/sessions { ensureScope: "video-studio" }(getOrCreateNamedChatSession), so video chat history stays separate from the user's Hauptchat. - Drag-and-drop Bausteine — the library blocks render as chips above the composer that can be dragged (or
clicked) to pin them. Pinned slugs are published in the chat
pageContextasselectedBlocks; on send the module prompt (src/app/api/chat/route.ts) instructs the agent to runcompose-video.mjswith exactly those blocks, then the pins clear (onDidSend). - Voiceover & Musik controls — app-styled toggles with provider/voice and provider/model selectors,
published in
pageContext(voiceover=provider:voice,music=provider_slug:model):- Voiceover →
media_tts_speak(provideropenai|gemini|openai_compatible; voices openaialloy/echo/fable/nova/shimmer/onyx, geminiKore/Puck/Charon/Fenrir/Leda, or the configured Spark/self-hosted TTS runtime). - Musik →
livestream_generate_music; the web and Apple composers load the enabled provider/model choices from the curated AI Media music catalog when it is non-empty, otherwise they use the enabled provider's configured models and default.
- Voiceover →
- AI Video provider controls use the same AI media video defaults as chat-generated videos (
videos_generate/videos_status) and can selectxai-grok-video(grok-imagine-video-1.5/grok-imagine-video) alongside Gemini/Kie video rows configured underSettings -> ClapilotAICore -> AI Media. Live Stream Studio-specific jobs still uselivestream_generate_video. - Format controls — aspect ratio (
16:9/9:16), a portrait fill mode (shown only for 9:16), and output resolution (1080p/4K), published inpageContextasaspectRatio,portraitFill,resolution:compose-video.mjstakes--aspect 16:9|9:16and--portrait-fill frame|bleed. frame scales the whole 16:9 stage into the portrait width and centers it with the brand gradient above/below (works with every existing Baustein); bleed emits a bare 1080×1920 composition that the agent then re-lays-out per scene.- The render uses HyperFrames'
--resolutionpreset matching the aspect:landscape/landscape-4kfor 16:9,portrait/portrait-4kfor 9:16 (the composition is unchanged; Chrome renders at a higher device-pixel-ratio, and the preset aspect must match the composition).
KI-Storyboard-Videos
AI studio data is workspace-global: every authenticated user sees and can edit or delete all KI storyboard
projects, reusable characters, and character voices. The owner_user_id columns on these rows are kept purely as
creator attribution (who created the row) and are never used as a read or write filter — the same model as the Wiki
and Kunden modules. Generated scene clips and start frames are likewise readable by any authenticated user so a
shared storyboard renders for non-creators.
The Galerie and Charaktere tabs expose the KI storyboard path beside HTML/HyperFrames product videos. There is no separate KI-Videos tab: the gallery combines rendered videos and unfinished or failed KI projects, with a compact Alle / HTML / KI filter. Ready KI results use the normal video card with their generated thumbnail, a KI badge, and a secondary action that reopens the storyboard. Project cards show status, scene count, last update, and the error hint when generation failed. Unfinished and failed project cards can be deleted with an inline confirmation; active generation warns that pending results will be discarded. The storyboard header exposes the same project deletion for every status. Deleting a ready project removes its storyboard rows but keeps the final MP4 in the gallery.
Reusable characters hold a stable appearance prompt, canonical portrait, an optional uploaded voice sample, and an
optional provider preset voiceId.
The voice sample can be uploaded or replaced in the character editor, accepts MP3, WAV, M4A, OGG, or FLAC up to
25 MB, and is stored under the character creator's directory in .clapilot/video-studio-voices/ on the shared
workspace volume (the directory name is attribution only; any user can manage the sample). During AI
scene generation, Clapilot matches the scene's explicit characters (or its script speakers). Provider-native voice
conditioning is enabled by default per project in metadata.voiceConditioning: direct Spark/OpenAI-compatible video
receives up to three workspace reference_audio_paths plus a speaker-reference prompt preamble; Kie Seedance receives
up to three ordered, tokenized public input.reference_audio_urls when a public base URL is available; and xAI receives
one top-level voice_id only when exactly one scene character has a preset. Unsupported or ambiguous provider paths
omit voice conditioning. Creating or regenerating a character
persists the character immediately and builds its portrait in the background, so the editor resets for the next
entry instead of waiting on image generation. Character cards show generating, ready, or failed, refresh every
six seconds only while work is active, and offer a retry after failure. A generation left active for more than ten
minutes (for example after a server restart) is reconciled to failed. Characters whose portraits are still being
created remain selectable: frame generation continues without the optional portrait reference and the finished
portrait appears in the catalog when ready. A project then turns a creative brief
into a storyboard whose scenes contain a complete video prompt, a dedicated start-frame prompt, explicit dialogue
lines with speaker assignments, duration, and character references. After review, Clapilot creates missing start
frames, starts deterministic per-scene provider jobs, polls their real status, and normalizes and concatenates the
finished clips with ffmpeg. Each completed concat creates a new numbered MP4/thumbnail pair; the latest version lands
in the same Video Studio gallery as HTML videos while the storyboard retains playable version history.
Project progress advances server-side. An in-process reconciler started with the web server checks active projects
every 60 seconds by default, updates scene state from persisted provider jobs, applies stale/stall failure rules, and
claims final concatenation through the same single-winner transition used by the status API. Browser and native
polling only observe that durable progress; closing a client or restarting it is not required to finish a project.
Set CLAPILOT_VIDEO_STUDIO_RECONCILE_SECONDS=0 to disable the worker.
Voice consistency
The storyboard voice menu controls two independent metadata-only project settings. Provider voice references
(metadata.voiceConditioning, default true) applies the native provider contracts described above. Replace voices
(dubbing) (metadata.dubbing, default false) is provider-independent post-production: after a scene reaches
clip_ready, each script line is synthesized through Clapilot's configured TTS runtime using the speaking character's
voice sample as a clone reference, or the default TTS voice when no sample exists. Lines start at even intervals across
the probed clip duration. ffmpeg delays and mixes the line audio, retains the original soundtrack at -30 dB as ambience,
and atomically writes <clip>-dub.mp4.
Dubbing does not change the scene's main status. metadata.dubStatus advances
pending → dubbing → done|failed, and stores dubPath only after the remux is complete. Final concatenation waits for
done on every scene that has dialogue and uses dubPath instead of the provider clip. The server reconciler claims
pending work after restarts; a dubbing claim older than 15 minutes becomes failed. Resubmitting or regenerating a
scene video clears all prior dub metadata so the new clip must be dubbed again. Native iOS/macOS storyboard menus show
both project settings read-only; the web storyboard can change them while the project is editable.
Each clip_ready scene with dialogue also has a user-driven Voiceover action on the web. Generate preview
synthesizes the assigned character voices and places the lines on the scene timeline, but writes only a standalone
M4A under the project's ai-build/dubs workspace directory. The asynchronous state is stored in
scene.metadata.dubPreview as generating → ready|failed; a generating preview older than ten minutes fails during
normal project polling. A ready preview can be heard alone or started approximately in sync with the muted scene
video. Apply to scene remuxes that exact preview over the current clip with the same -30 dB ambience mix used by
automatic dubbing, records metadata.dubApplied, and makes the dubbed MP4 the scene's active clip and concat source.
Applying to an already-ready project reopens it as storyboard_ready so the next generation creates a new final
version. Discard removes only the preview file and dubPreview metadata; regenerating the scene video clears both
preview and applied dub state. Native iOS/macOS continues to display the resulting active clip read-only.
Stimme ändern (ElevenLabs Voice Changer)
Every clip_ready scene additionally offers a Stimme ändern action (waveform icon) once an ElevenLabs provider is
configured (Einstellungen → ClapilotAICore → Provider → ElevenLabs, API key; the legacy global ElevenLabs key keeps
working as a fallback). The popover lists the account's own/custom voices (cloned, generated, professional) first and
the provider default voices below. Applying a voice extracts the scene's current audio (the applied dub clip when a
dub exists, otherwise the generated clip), converts it with the ElevenLabs speech-to-speech voice changer while
preserving content and timing, remuxes the converted track over the clip, and stores the replacement under the
project's ai-build/voice directory. The asynchronous state lives in scene.metadata.voiceChange
(generating → ready|failed, stale after ten minutes); final concatenation prefers a ready voice-change clip over the
dub/raw clip, and applying to a ready project reopens it as storyboard_ready. Original wiederherstellen deletes
the converted clip and restores the previous audio. Characters can carry an assigned ElevenLabs voice
(dropdown in the character dialog, visible once ElevenLabs is configured; stored as
metadata.elevenLabsVoiceId/elevenLabsVoiceName): the change-voice popover preselects the scene speaker's
assigned voice, video_studio_change_scene_voice resolves it automatically when voice_id is omitted, and
dubbing uses it as the per-line TTS voice whenever the workspace TTS runtime is ElevenLabs. Regenerating the scene video or applying a new dub clears the
voice change. The same flow is available to agents via video_studio_list_voices and
video_studio_change_scene_voice. ElevenLabs can also be selected as the workspace TTS and STT runtime
(Einstellungen → ClapilotAICore → Audio) once the provider is configured.
Start- und Endbilder
Projects can opt into Start- + Endbilder (toggle in the creation form and in the storyboard audio/continuity
settings, use_end_frames). When enabled, every AI scene additionally generates an end frame from its start frame —
same location, lighting, and wardrobe, but the scene's final instant — stored in end_frame_image_id and shown as a
small overlay on the scene card. Supported providers (Kie Kling 3.0 image_urls[1], Kie Seedance last_frame_url,
Kie Veo FIRST_AND_LAST_FRAMES_2_VIDEO, and OpenAI-compatible last_frame) then receive first and last frames for
each clip; other providers fall back to first-frame-only generation. Scene continuity also improves: a scene marked
An vorherige Szene anknüpfen uses the previous scene's end frame (when present) as its visual reference instead of
the previous start frame. End-frame generation is best-effort — a failed end frame degrades that scene to
first-frame-only generation instead of failing it.
Storyboards additionally follow a one-speaker-per-scene rule: multiple characters may be visible in a scene, but all script lines of one scene come from a single speaker; conversations are split across consecutive scenes. This keeps per-scene dubbing and voice changes unambiguous.
- Characters first — create recurring people in Charaktere, then attach their ids to the project and scenes. Assigning a character directly to a scene automatically links that character to the project as well, including assignments made through the Video Studio agent tools. Frame generation receives their stored visual identity and portrait references so a series does not rely on names alone.
- Storyboard review — selecting a KI project in the gallery opens the scene board as a full-screen gallery
takeover. Prompts and dialogue stay editable per scene, and a start frame can be generated again or edited while
it remains linked to the scene. Editable storyboards can append, delete, and drag-reorder scenes on the web; the
native list provides add, swipe-delete, and move actions. Structural changes are blocked during storyboard or
clip generation/concatenation. Changing a ready storyboard returns it to
storyboard_ready, after which the existing new-version generation flow creates the next output. - Project aspect ratio — the storyboard header always shows
16:9or9:16. Web users can change it while the project isdraft,storyboard_ready, orfailed; the native storyboard header displays the stored value read-only. Existing frames remain reviewable and should be regenerated for the best composition after a change. - Per-scene clip preview — as soon as an AI scene reaches
clip_ready, its card and native detail view can play the authenticated generated clip over the start-frame poster. This makes each moving scene reviewable before the final concatenated gallery video is ready. - Scene continuity — each AI scene after scene 1 can be marked as continuing the immediately previous setting.
Its start-frame generation then uses the previous AI scene's ready start frame as the first environment reference
for location, lighting, props, and wardrobe, followed by portraits of characters in the current scene. Image edits
keep the current frame first and add the previous frame when the four-reference limit allows. HTML scenes and
missing or failed previous frames break the chain without blocking generation.
Reordering does not rewrite this flag:
continues_previousstays attached to the moved scene, so “previous” means whichever scene precedes it in the new order. - HTML scene mixing — a scene can reference an existing gallery slug or select ordered Bausteine directly from
the library. The picker shows looping previews, metadata duration defaults, curated per-block text fields, and an
AI content prompt for every selection. The prompt is disabled with an explanation only for an explicitly slotless
block. Rendering
uses the project aspect through the existing module manifest/HyperFrames handler, probes a deterministic per-scene
MP4, and marks it
clip_ready; both source forms then normalize and concatenate with AI clips. Re-rendering asks for confirmation. Native clients identify both kinds and play ready clips, while creation/editing is web+agent only. - Provider and model — a project stores its exact video provider and model. If neither is selected, creation uses the enabled video provider carrying the configured default model (or the first enabled video provider). Explicit choices are preserved; generation does not silently replace them with another default.
- Start-frame image model — creation stores the selected image provider/model in
metadata.imageModel. The project header can change that selection, and frame regeneration can override it for one call. Generation and edit requests pass the pair through the normal image resolver; a selected model that cannot edit falls back to an edit-capable configured provider without changing the stored project choice. Every generation/edit prompt also states the project orientation explicitly. Clapilot probes the returned image with ffprobe; a frame outside the five-percent canonical-ratio tolerance is scaled to cover and center-cropped with ffmpeg (never padded) to1536x1024for16:9projects or1024x1536for9:16projects. The corrected PNG is stored as a newgenerated_imagesasset whose metadata recordsaspectNormalizedFromwith the original provider asset id, and only that corrected asset is linked to the scene. - Final-video versions — every concat writes
<output>-v<N>.mp4and<output>-v<N>.jpg. Ordered entries inmetadata.versionsrecord the duration and exact video provider/model, while the legacy final-path columns keep pointing to the latest file. The web storyboard can play and delete non-latest versions; native clients list and play every version but intentionally leave deletion to the web app. An existing unversioned final is recorded as v1 when the next concat writes v2. - Storyboard model —
app_settings.video_storyboard_modeloptionally selects the text model that produces the strict storyboard JSON. An empty value falls back toagent:main; the model actually used is stored on the project.
Project state machine:
draft → storyboard_generating → storyboard_ready → generating → concatenating → ready
Adding, deleting, or reordering a scene on a ready project transitions ready → storyboard_ready; the user then
reviews the changed structure and uses the normal new-version generation flow.
failed is a terminal/error branch from storyboard generation, scene generation, provider-unreachable stall
detection, or concatenation. A reviewed, failed, actively generating, or ready project can be restarted. Restart
preserves scene start frames, detaches every prior AI clip, and submits every AI scene as a fresh provider request;
for a ready project this is the Generate new version flow and earlier final files remain untouched. Cancelling active clip generation returns the project to storyboard_ready and resets
in-flight scenes.
Scene state machine:
pending → frame_ready → clip_generating → clip_ready
failed records a missing frame, provider failure, or block-render failure. Linked HTML scenes become clip_ready
after validation; block scenes use clip_generating → clip_ready|failed. A persisted block render older than 15
minutes without a result reconciles to a localized failed state.
Troubleshooting stalled generation
The project status endpoint polls provider jobs and then reconciles the persisted asset state even when one provider
status request throws. Network errors are stored per generated-video asset with an error count, first-error timestamp,
last check, and bounded retry time. The normal configured poll failure window still moves a persistently unreachable
asset to failed. Independently, a clip_generating scene is marked failed with a localized provider-unreachable
message once its asset has at least three poll errors spanning five minutes, so the UI never remains silently stuck.
Use Neu starten / Restart (or video_studio_start_generation { restart: true }) after the provider recovers; this
creates fresh provider jobs and may spend provider budget again.
The storyboard header model menu can override the stored video provider/model at generation start or restart. The
selection is persisted before scene requests are submitted, and scene durations are normalized again for the selected
model. A ready or failed scene can also be regenerated individually after budget confirmation; the project returns to
generating, then the normal single-winner reconciler appends a new numbered final and updates the gallery pointer.
Install content from the bundle store
The studio ships general: only the generic skill engine (scripts/ + a generic SKILL.md) is seeded into
new instances. Branded scenes, templates, base shells and design styleguides live in a bundle catalog
that ships with the module but is not auto-seeded:
- catalog:
bundled-modules/video-studio/bundles/<id>/— each withbundle.json(id,name,description,version,includesBase,blocks[],templates[],styles[],extras[],legacyPaths[]) +blocks/(incl._base) +templates/+ theextras(styles/,agents/). - shipped bundles:
clapilot-video-bausteine— the 6 Clapilot Bausteine (brand-intro,brand-outro,email-workspace,ai-draft,decision,chat-scene), 2 templates, the shared base shell, and theclapilotdesign style.nordlicht-dark— a dark studio set (aurora-intro,aurora-metrics,aurora-outro) with its own base shell and thenordlichtdesign style.
A Store button (bottom-right on the Bibliothek/Stile tabs) opens a popup listing bundles with
install/uninstall (each row shows the shipped Bausteine/Vorlagen/Stile counts). Install copies the bundle's
blocks/_base/templates and each extras item into the seeded skill dir; uninstall removes them plus
any legacyPaths left by older bundle layouts. A bundle reads as installed when its blocks are present in the
workspace, so existing live instances (already seeded before the store existed) show it installed
automatically — nothing is removed from their volumes. New instances start empty and install on demand.
Extensible for more bundles and, later, user uploads.
Migration note: the entrypoint seed (
seed_workspace_tree_if_missing) syncs files present inworkspace-seedbut never deletes volume-only files. Moving content out ofworkspace-seedleaves existing volumes' content intact; the seededSKILL.mdis auto-synced to the generic version, and the styles layout lands on an existing instance when the bundle is (re)installed from the store (uninstall also cleans the legacy top-levelSTYLEGUIDE.md/references//assets/).
Bibliothek (templates, Bausteine, assets)
The Bibliothek tab surfaces the building-blocks library the agent composes from:
- Vorlagen — full templates in
skills/clapilot-video-styleguide/templates/(render-as-is or customize). - Bausteine — reusable scene blocks in
skills/clapilot-video-styleguide/blocks/<slug>/(block.json+block.html+block.css+block.js, scoped under.b-<slug>). Each card shows an inline looping preview (a small mutedpreview.mp4rendered per block). The agent compounds a video from them withscripts/compose-video.mjs --blocks a,b,c --name <slug>, and can create new blocks (which then appear here). Template blocks declare editable copy inblock.json.textSlots[]as{ key, label, find, defaultValue, scope, required?, multiline? }, wherescopeishtml,js, orboth. The block picker shows the label anddefaultValue, and persists edits through the per-scene{ find, replace, scope? }compose contract. The composer replaces the first literal occurrence in the selected scope; omitted override scopes still target both sources for backward compatibility. Intentionally slotless blocks declare an explicit empty array. Third-party blocks with notextSlotskey derive up to twelve deduplicated slots from visible HTML text nodes. See the skill'sSKILL.md→ Bausteine. Regenerate previews after adding/changing a block withscripts/render-block-previews.mjs(runs in the runtime container — composes each block solo, renders, then downscales to a 480p/15fps muted clip intoblocks/<slug>/preview.mp4). - Assets — shared images (the Clapilot logo) from
blocks/_base/assets/plus custom uploads: drag images (or use the upload tile) into the assets grid to add your own logos/graphics. Custom assets live inskills/clapilot-video-styleguide/custom-assets/(outside the bundle dirs, so store install/uninstall never touches them), are badged Eigene with a delete action, andcompose-video.mjscopies them into every composed project'sassets/— scenes reference them asassets/<file>(the agent is told about them in the module prompt).
Design styles (Stile tab)
Design styles are first-class entities at skills/clapilot-video-styleguide/styles/<slug>/:
style.json—{ slug, title, description, source, version }(source= shipping bundle id orcustom).STYLEGUIDE.md— the style's design rules; the agent reads and follows it when the style is selected.- optional
tokens.css(inlined deterministically viacompose-video.mjs --style <slug>),references/,assets/.
A style may also ship a human-facing HTML reference page under references/*.html (the Clapilot style ships
clapilot-video-styleguide.html); the Stile card then shows an HTML-Referenz button that opens it in a
sandboxed iframe popup, streamed via style-reference.
Surfaced in the UI as the Stile tab (cards with source badge and a rendered styleguide popup). The
"Neues Video" composer has a Stil selector (defaults to the first installed style) published as
pageContext.style; the chat route instructs the agent to read + follow that style's STYLEGUIDE.md. Bundles
ship styles under their styles/ dir; new styles are created by the agent in chat (writing
styles/<slug>/style.json + STYLEGUIDE.md with source: "custom"), and new Bausteine are tagged with their
style via block.json.style (shown on the Bibliothek cards).
Fine-tune a video (Feinschliff editor)
Gallery videos with a matching project show a pencil action that opens the Feinschliff editor — a native, in-module rebuild of the essential HyperFrames-Studio workflow (no preview server, no proxy, docker-friendly):
- Preview + transport — the project's
index.htmlloads in a same-origin iframe (served viaproject/…); because HyperFrames compositions expose a paused, seek-safe GSAP timeline atwindow.__timelines, the editor drivesplay/pause/seekdirectly for frame-accurate scrubbing, with a time ruler and clickable scene lanes. - Scene edits — per-scene duration inputs and reorder controls, plus per-scene text fields (leaf text nodes).
- Post-production voiceover — finished block-based projects expose a scene-level voiceover editor in
Feinschliff. The initial action synthesizes one TTS file per scene through the configured Clapilot TTS runtime
(Gemini/Kore by default). Text and voice remain editable; regenerating one scene only calls TTS for that segment
and remuxes audio onto the existing MP4 without rendering the visual composition again. Audio is aligned to the
scene start, padded when short, and moderately accelerated (up to 1.35x) then clipped when it exceeds the scene.
Voiceover metadata and relative segment paths are stored under
voiceoverincompose.json. Choosing an uploaded character voice sends that character asvoice_character_id, locks the provider to the OpenAI-compatible Spark TTS path, and uses the same sample as theref_audiovoice-clone reference for every synthesized scene segment. - Persistence model —
compose.json(emitted bycompose-video.mjsfor every composed project) is the single editable source:{ blocks, durations, aspect, portraitFill, style, textOverrides }. Saving writes the manifest and recomposesindex.htmlfrom it (--manifest); text overrides are first-occurrence string replacements per scene, re-applied on every recompose. Duration semantics: shorter scenes clip at their end, longer ones hold the final state (tween offsets stay local to the scene). - Re-render —
render { fromProject: true }renders the recomposed project over the existing MP4. - Legacy migration — composed projects from before the manifest existed are auto-migrated on first open:
project-manifestparses blocks/durations/aspect/style back out ofindex.htmland diffs each scene against its pristine block to preserve agent-made text edits astextOverrides, then persists thecompose.json. - Clip emulation — raw compositions stack all scenes absolutely (the HyperFrames runner normally toggles them per frame), so the editor applies clip windows itself on load/seek/playback — exactly one scene visible.
- Hand-authored projects (no
compose.json, not composer-shaped) open in preview-only mode (scrub + re-render, no scene edits).
Manage the gallery
The Alle / HTML / KI filter separates non-KI rendered videos from matched KI outputs and KI project cards.
Rendered outputs match projects by the gallery slug and the project's outputSlug/finalVideoPath. Each video
card exposes a trash action with in-place confirmation. video-delete removes only the rendered MP4 — the
composition project stays on disk, so the video can be recomposed and re-rendered later.
KI project deletion is separate: DELETE /api/video-studio/ai/projects/:id removes the project (any authenticated
user may delete any project) and cascading storyboard rows. It never removes the finished gallery MP4. The native iOS/macOS gallery and
storyboard use the same endpoint through destructive confirmation dialogs.
How the agent can drive it
When chatContext.moduleSlug === "video-studio", src/app/api/chat/route.ts injects a module system
prompt instructing the agent to either compound a video from Bausteine (scripts/compose-video.mjs) or
scaffold a template (scripts/new-clapilot-video.mjs), edit the German scene copy, lint, and render in the
background with npx hyperframes render --output /app/workspace/video-studio/videos/<slug>.mp4 via
exec_command (detached so it beats the exec time limit). The MP4 surfaces in the gallery on the next poll.
UI selections (pinned Bausteine, voiceover/music, style, aspect/fill/resolution) reach the agent through the
chat pageContext keys described above.
Vertonung pipeline — when the user requests a voiceover and/or music, the agent generates the audio with
the first-party media tools (media_tts_speak → absolutePath; livestream_generate_music → audioPath,
polling livestream_get_asset for the async kie-ai-music provider), then renders silently to
projects/<slug>/silent.mp4 and muxes the tracks into the final videos/<slug>.mp4 with
scripts/add-audio.mjs (voiceover at full volume, music looped + ducked, output trimmed to the video length).
See the skill SKILL.md → Audio — Voiceover & Musik.
Configuration & limits
Runtime data and storage
All compositions and rendered videos live on the shared workspace volume so both the web container
(module API) and the agent container (exec_command renders) see them:
- rendered videos:
/app/workspace/video-studio/videos/<slug>.mp4 - composition projects:
/app/workspace/video-studio/projects/<slug>/ - character voice samples:
/app/workspace/.clapilot/video-studio-voices/<owner-user-id>/<character-id>.<ext> - skill engine (always seeded):
/app/workspace/skills/clapilot-video-styleguide/— a genericSKILL.md(compose/render/audio mechanics, points to the installed bundle'sSTYLEGUIDE.mdfor the house style) +scripts/. No house style of its own. The content AND the design styleguide (base shell + Bausteine + templates +STYLEGUIDE.md+references/+ brandassets/+agents/) are NOT seeded; they install from a bundle (see the bundle store above). - installed-bundle marker:
/app/workspace/video-studio/installed-bundles.json.
Implementation: bundled-modules/video-studio/api/handler.mjs.
Runtime requirements
- HyperFrames CLI is installed globally in the runtime image (
Dockerfileglobalnpm install -g). - The image already ships Node 22, ffmpeg, and Chromium; HyperFrames renders with system Chromium
(
--disable-dev-shm-usage, so the default 64 MB/dev/shmis sufficient).
Module API endpoints
Base: /api/modules/video-studio/api
| Method | Endpoint | Purpose |
|---|---|---|
| GET | list | List rendered MP4s in videos/ with title, size, mtime, url. |
| GET | templates | List full video templates from the seeded skill (clapilot-feature-flow, clapilot-chat-interface). |
| GET | blocks | List composable scene blocks (Bausteine) from the skill blocks/; each block includes previewUrl when a preview.mp4 exists and normalized textSlots[] (key, label, exact compose find, displayed defaultValue, scope, required, multiline). First-party slots come from the bundled catalog so existing installs receive curation updates; a third-party manifest with no slots key falls back to visible HTML text extraction, while an explicit empty array stays slotless. |
| GET | assets | List video assets: bundle assets (blocks/_base/assets/) plus user-uploaded custom assets, each with a source flag. |
| GET | asset?path=<name> | Stream an asset image (custom assets shadow bundle names). |
| POST | asset-upload | Multipart upload (files) of custom images (PNG/JPG/WEBP/SVG, ≤20 MB each) into custom-assets/ — sanitized + deduped names; the Bibliothek exposes it via drag-and-drop + a file picker. |
| POST | asset-delete | Body { name } — delete a custom asset (bundle assets are managed by the store). |
| GET | block-preview?slug=<slug> | Stream a block's looping inline preview MP4 (blocks/<slug>/preview.mp4, HTTP Range, short-cached). |
| GET | file?path=videos/<name> | Stream an MP4 (supports HTTP Range) with the session cookie. |
| GET | styles | List installed design styles ({ slug, title, description, source, version, hasGuide }; legacy top-level STYLEGUIDE.md is synthesized as a legacy style). |
| GET | style?slug=<slug> | Style detail incl. the full STYLEGUIDE.md text (guide). |
| GET | style-reference?slug=&file= | Stream a style's bundled HTML reference page (e.g. the Clapilot video styleguide HTML) for the UI iframe popup. |
| GET | bundles | List the bundle catalog with install status ({ id, name, description, version, blockCount, templateCount, styleCount, installed }). |
| POST | bundles/install | Body { id } — copy a bundle's blocks/_base/templates into the skill dir. |
| POST | bundles/uninstall | Body { id } — remove a bundle's blocks/_base/templates from the skill dir. |
| GET | project/<slug>/<...file> | Serve a project file (index.html + relative assets) for the Feinschliff editor's same-origin iframe preview. |
| GET | project-manifest?slug= | The project's editable compose.json ({ hasManifest, manifest }; hand-authored projects report hasManifest: false). |
| POST | project-edit | Body { slug, blocks?, durations?, textOverrides?, title? } — merge into compose.json and recompose index.html via compose-video.mjs --manifest. |
| POST | render | Render { name, html }, { name, template }, { name, blocks, durations?, textOverrides?, aspect?, style? }, or { name, fromProject: true }. Block input writes the normal manifest and invokes compose-video.mjs --manifest; per-block picker/agent overrides are flattened to the manifest's scene-indexed { find, replace } entries before this call. Every form then runs HyperFrames into videos/. |
| POST | video-delete | Body { name } — delete a rendered MP4 from the gallery (the project stays for re-rendering). |
Apple client (iOS/macOS)
- Galerie / Vorlagen / Bibliothek — native grids/lists over the module API. MP4s and the looping Baustein
previews are fetched authenticated through
ClapilotAPI.fetchVideoStudioMedia(session cookie), cached as temp files (VideoStudioMediaCache), and played withAVPlayer/AVPlayerLooper. - AI project status — the Galerie shows a compact native strip for active projects and recently failed projects. It polls the workspace-global project list only while work is in progress. Native v1 deliberately leaves character and storyboard editing to the conversational agent flow; it does not duplicate the web editor.
- Mixed-scene parity — native storyboard rows show linked-gallery versus block-rendered kind badges, continue polling in-flight block renders, and play authenticated ready clips. Authoring remains web-and-agent only.
- Neues Video — a fourth segmented tab that embeds the real chat:
ChatView(presentation: .embedded)— a chrome-free presentation of the standard chat (full markdown/canvas rendering, tool-call log, attachments, the real composer) — pinned to the dedicated session (AppModel.activateVideoStudioChatSession,POST /api/chat/sessions { ensureScope: "video-studio" }; the previously active session is restored when leaving the tab). The video controls (tap-to-pin Baustein chips, voiceover/music menus, 16:9/9:16 + Füllung + 1080p/4K) render as acomposerAccessory(VideoStudioComposerToolbar.swift) above the composer and publish via the live context. Every Apple text-chat send attaches the client context (AppModel.currentClientContextPayload, snapshotted onto the queued message at submit time to avoid racing UI state) — web parity for module-aware prompts.
Troubleshooting
compose-video.mjs --listshows no Bausteine / the Bibliothek is empty — new instances start without content; install a bundle from the Store popup first.- Portrait (9:16) output does not match the selected fill — aspect ratio and resolution are deterministic,
but the fill style is agent-guided: in practice the agent often prefers to adapt the scene layout to fill
the portrait frame (stacking the 16:9 columns) even when
frameis selected — this usually looks more native, though quality can vary. The strict brandedframewrapper is always available viacompose-video.mjs --portrait-fill frame; nudge the agent with a follow-up if you want it. - The video does not appear immediately — renders run detached in the agent container and typically take ~30–60 s; the gallery picks the MP4 up on its next poll.
- The module is missing on iOS/macOS — the native section is hidden when the module API probe returns 403 (non-admin users).
