meta-short-drama
End-to-end short-drama generator with an explicit-consent review flow before any paid external-media step. 1-10 shots (default 5), title card + ending card, in-language burned subtitles, and the generated script is saved to disk regardless of outcome. A direct approval continues immediately; an edit only produces a revised preview and requires a second explicit approval.
What it does
intake_extractscans the user message for RENDER_STYLE, IDENTITY_ANCHOR, and N_SHOTS (1-10). Fills in defaults when missing.script_draftcallsai-video-scriptwith the inferred values pasted verbatim into every shot prompt.review_gate— free-form draft review. The user can approve, request changes to render style / character / shot count / shot details, or cancel in plain language.review_intentis local and deterministic. Explicit approval may proceed, while a recognizable adjustment emitsDECISION: revise; the adjustment never authorizes an external call.script_revised(conditional) applies requested overrides, thenrevision_confirm_gateshows the revised preview and requires a new explicit approval.review_normalizeis the final paid-media consent authority; cancel, missing, ambiguous, off-topic, and further-edit replies fail closed without provider calls.final_scriptfreezes the canonical scheduler snapshot in memory; it never re-reads the user-editable artifact.script_savewrites that same canonical content toscript.txtin the run folder (always — even on cancel, so the user keeps the draft).title_extract/subtitle_extract/ending_text_extractpull cover/ending text in the script's language.cover_image+cover_video— Pillow title card + 2s Ken-Burns clip (0_cover.mp4— sorts first in merge).- Per-shot extracts × 10 — all slots are declared, but an exact
=== SHOT_N ===header check deterministically skips absent script blocks before calling the LLM. Active extracts still use__SHOT_ABSENT__as a second fail-closed guard, and paid image/video steps repeat the exact-header check so an LLM cannot activate an unused slot. - Image generation per active shot —
nano-banana-pro, at most one paid submit, followed by a local placeholder PNG on a verified policy refusal (the image step never aborts the DAG). Provider responses and ambiguous transport outcomes never trigger an automatic second paid request. reference_prompt_extract+reference_image— one extranano-banana-procall producesreference.png, a full-cast neutral lineup of every named character on a neutral backdrop. Used as the universal IDENTITY anchor for every shot's seedance call so the character does not drift across cuts (nano-banana would otherwise re-roll subtly different character designs per shot).- Video generation per active shot —
seedance-2.0; paid submit failures are never retried automatically because an ambiguous response may already represent a billed job. After a job id is issued, transient polling failures may retry that same job up to the configured limit. Any provider-policy refusals stop immediately without another paid submission. The Ken-Burns substitute then fires using the shot's PNG. Each shot passes TWO reference images to seedance, AND the per-shot prompt is wrapped with an explicit "Assets Mapping" preamble in the upstream JiMeng convention so seedance knows the role of each reference: reference[1] =reference.png(full-cast fictional design anchor — preserves silhouette / hairstyle / costumes / accessories without reproducing real-person likeness) reference[2] =N_shot.png(this shot's scene composition reference — used for camera angle, framing, blocking, prop placement, background layout) The Assets Mapping preamble is in English even when the per-shot directive is Chinese — seedance parses English instruction prefixes reliably regardless of the user-content language. Empty / missing references are still filtered before the API call (so direct CLI callers using a single anchor remain backwards-compatible). ending_image+ending_video— Pillow "完" / "THE END" card- 2s Ken-Burns clip (
99_ending.mp4— sorts last).
- 2s Ken-Burns clip (
merge—video-mergerstitches0_cover+ active shots99_endingvia numeric-prefix sort. ffmpeg cross-fade transitions.
subtitles_srt— SRT cues from VOICEOVER per shot, shifted by the 2-second cover duration so cue timing matches the merged timeline.subtitled_final—subtitle-burnerburns the SRT intofinal_subtitled.mp4.publish_final_video+publish_script— register the final MP4 (video/mp4) and script (text/plain) with the active surface so browser users receive artifact controls instead of only a local path. The script is published even when the user cancels; the video is published only after a successful proceed path.delivery_audit— deterministic receipt/fallback/ffprobe gate. It combines validated receipts with the scheduler's bounded, parent-owned paid-submission dispositions (safe_no_submit,maybe_accepted, orreceipt; only a conclusive receipt becomesconfirmed). It is the sole authority for API provenance and reports both story content duration and the probed final duration (content + 4s bookends). A fallback after a proven pre-submit failure does not trigger a billing warning; ambiguous submission outcomes still emit only a sanitized asset list and check-history warning.deliver— always runs, branches on DECISION, and waits for the deterministic audit before composing delivery. It only restates the machine-owned verdict and cannot promote fallback media to a verified real-API result.
Outputs
<workspace>/meta_short_drama/<meta_run_id>/ script.txt # full final script (always; published artifact) reference.png # full-cast identity reference (used by every shot_video) 0_cover.png 0_cover.mp4 1_shot.png 1_shot.mp4 ┐ 2_shot.png 2_shot.mp4 ├ only for active shots (1..N_SHOTS) ... ┘ *.png.receipt.json # image provider/request or placeholder status *.mp4.receipt.json # video provider/model/job status 99_ending.png 99_ending.mp4 subs.srt final.mp4 # merged, no subtitles final_subtitled.mp4 # subtitled — published video deliverable
Dependencies
| Skill | Purpose | Models / Tools |
|---|---|---|
ai-video-script | Structured shot list (1-10 shots) | LLM |
short-drama-review-normalizer | Local fail-closed review/consent decision | Python stdlib |
nano-banana-pro | Per-shot first-frame PNG | OpenRouter Gemini 3.1 / 3 pro |
seedance-2-prompt | Per-shot MP4 | OpenRouter Seedance 2.0 (or Volcengine ARK) |
video-still-animator | Ken-Burns fallback / cover & ending clips | ffmpeg ≥ 5.0 |
video-merger | Stitch cover + shots + ending | ffmpeg ≥ 5.0 |
srt-from-script | VOICEOVER → SRT with cover offset | Python stdlib |
subtitle-burner | Burn SRT into MP4 | ffmpeg + libass |
title-card-image | Pillow cover + ending PNG cards | Pillow |
(builtin) write_file | Save script.txt (no skill needed) | OpenSquilla builtin |
text-file-read | Re-read script.txt after review pause | Python stdlib |
Environment:
OPENROUTER_API_KEYmust be set.ffmpegandffprobeon PATH.- Pillow installed (already in opensquilla deps).
Risk
high — writes files, spends real OpenRouter credits, runs ffmpeg
subprocesses. The review gate plus deterministic normalizer ensures explicit
approval or a meaningful requested adjustment before any external media step.
Limits (v2)
- 1-10 shots; default 5. The DAG always declares 10 slots but
__SHOT_ABSENT__gating keeps unused slots dormant. - Per-shot duration follows the script's DURATION_S (clamped 3-15s by seedance API). OVERVIEW.DURATION_S means story-shot content duration; the final MP4 adds a fixed 2s title + 2s ending (content + 4s).
- 9:16 portrait.
- Per-shot seedance failures fall back to Ken-Burns. Image step has its own placeholder fallback. Both are explicitly reported as degraded and cannot satisfy the verified-real-API E2E status. Prompt-extract llm_chats still abort the run if they return malformed output.
- Every run uses its runtime-owned
meta_run_idsubdirectory, so concurrent runs and post-reviewadditional_notescannot redirect or collide outputs.
When NOT to use
- Single image / single clip / script-only / stitch-only — use the underlying skills directly.

