# Meta Short Drama

> Create a complete AI short-drama package from a topic: shot script, review/approval pause, consistent images and clips, titles, subtitles, and final delivery. Do not use for isolated script or image requests, slides, document analysis, or historical examples.

- Skill: `gabrielmoreira/meta-short-drama` (Agent Skill)
- Install (CLI): `npx skillmds@latest add gabrielmoreira/meta-short-drama`
- Raw SKILL.md: https://api.skillmd.com/api/skills/gabrielmoreira/meta-short-drama/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: gabrielmoreira (https://skillmd.com/u/gabrielmoreira)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/gabrielmoreira/meta-short-drama

---


# 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

1. **`intake_extract`** scans the user message for RENDER_STYLE,
   IDENTITY_ANCHOR, and N_SHOTS (1-10). Fills in defaults when missing.
2. **`script_draft`** calls `ai-video-script` with the inferred values
   pasted verbatim into every shot prompt.
3. **`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.
4. **`review_intent`** is local and deterministic. Explicit approval may
   proceed, while a recognizable adjustment emits `DECISION: revise`; the
   adjustment never authorizes an external call.
5. **`script_revised`** (conditional) applies requested overrides, then
   **`revision_confirm_gate`** shows the revised preview and requires a new
   explicit approval. **`review_normalize`** is the final paid-media consent
   authority; cancel, missing, ambiguous, off-topic, and further-edit replies
   fail closed without provider calls.
6. **`final_script`** freezes the canonical scheduler snapshot in memory; it
   never re-reads the user-editable artifact.
7. **`script_save`** writes that same canonical content to `script.txt` in the run folder
   (always — even on cancel, so the user keeps the draft).
8. **`title_extract` / `subtitle_extract` / `ending_text_extract`**
   pull cover/ending text in the script's language.
9. **`cover_image` + `cover_video`** — Pillow title card + 2s Ken-Burns
   clip (`0_cover.mp4` — sorts first in merge).
10. **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.
11. **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.
12. **`reference_prompt_extract` + `reference_image`** — one extra
    `nano-banana-pro` call produces `reference.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).
13. **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).
13. **`ending_image` + `ending_video`** — Pillow "完" / "THE END" card
    + 2s Ken-Burns clip (`99_ending.mp4` — sorts last).
14. **`merge`** — `video-merger` stitches `0_cover` + active shots
    + `99_ending` via numeric-prefix sort. ffmpeg cross-fade transitions.
15. **`subtitles_srt`** — SRT cues from VOICEOVER per shot, shifted by
    the 2-second cover duration so cue timing matches the merged
    timeline.
16. **`subtitled_final`** — `subtitle-burner` burns the SRT into
    `final_subtitled.mp4`.
17. **`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.
18. **`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`, or
    `receipt`; only a conclusive receipt becomes `confirmed`). 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.
19. **`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_KEY` must be set.
- `ffmpeg` and `ffprobe` on 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_id` subdirectory, so concurrent
  runs and post-review `additional_notes` cannot redirect or collide outputs.

## When NOT to use

- Single image / single clip / script-only / stitch-only — use the
  underlying skills directly.

