LogoXiaomi MiMo API Provider
  • 功能
  • 價格
  • 模型
  • 部落格
  • 文件
Generate Video Scripts and Shot Lists With an LLM API (Python Tutorial)
2026/10/08

Generate Video Scripts and Shot Lists With an LLM API (Python Tutorial)

Build a small Python pipeline on the MiMo API: turn a topic into a timed script and a JSON shot list, check it against your video model's clip lengths, and render the narration with MiMo-V2.5-TTS.

If you publish short videos regularly, the slowest step is often not the video generation. It is the planning: writing a script that fits 30 seconds, splitting it into shots, and writing a prompt for each shot that a video model can actually render.

That planning is a good job for a language model, as long as you ask for structured output and check it before using it. In this tutorial you will build a small Python script that:

  1. Turns a topic into a narrated script and a shot list in JSON.
  2. Validates the timing against the clip lengths your video model supports.
  3. Writes the narration to audio with a text-to-speech model.
  4. Saves one ready-to-paste prompt per shot.

Everything runs through the MiMo API's OpenAI-compatible endpoint, so you can use the official openai Python SDK.

What you need

  • Python 3.9 or newer and pip install openai.
  • A MiMo API key in an environment variable called MIMO_API_KEY. The first API call guide shows where to create one.
  • A text-to-video tool to render the shots. Any model that takes a text prompt works. For example, Dolai's Seedance 2.5 video generator turns a prompt or a photo into a 5 to 30-second clip with its own sound in the browser, so you can paste the prompts this script produces straight in.

The base URL for the OpenAI-compatible endpoint is https://api.mimo-v2.com/v1.

Step 1: Pick a model

For planning, use a text model with strong reasoning. On the models page, MiMo-V2.5-Pro (mimo-v2.5-pro) is listed as the flagship for agent and tool-heavy work, with a 1M-token context window and a 128K output window. MiMo-V2-Pro (mimo-v2-pro) is also available if you already use it.

For the voiceover, use mimo-v2.5-tts, which takes the text to speak as an assistant message and returns audio.

Step 2: Ask for a script and shot list in JSON

The trick is to describe the exact JSON shape you want, give hard limits, and tell the model what each field is for. Shot prompts need to describe what is visible, not what the narrator says.

import json
import os

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["MIMO_API_KEY"],
    base_url="https://api.mimo-v2.com/v1",
)

SYSTEM_PROMPT = """You plan short vertical videos.
Return only valid JSON, no markdown, in this shape:
{
  "title": string,
  "hook": string,
  "shots": [
    {
      "id": integer,
      "seconds": integer,
      "narration": string,
      "visual_prompt": string
    }
  ]
}
Rules:
- Total seconds must equal the target length.
- Each shot's seconds must be one of the allowed clip lengths.
- Narration: at most 2.5 words per second of the shot.
- visual_prompt: one shot only. Describe subject, action, camera
  movement, lighting and style. No on-screen text. No narration."""


def plan_video(topic, target_seconds=30, clip_lengths=(5, 10)):
    user_prompt = (
        f"Topic: {topic}\n"
        f"Target length: {target_seconds} seconds\n"
        f"Allowed clip lengths: {list(clip_lengths)}\n"
        "Style: 3D animated, bright, family friendly."
    )
    response = client.chat.completions.create(
        model="mimo-v2.5-pro",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_prompt},
        ],
        temperature=0.8,
        max_completion_tokens=8000,
    )
    text = response.choices[0].message.content.strip()
    # Tolerate a stray code fence around the JSON.
    text = text.removeprefix("```json").removesuffix("```").strip()
    return json.loads(text)

Two details matter here. First, max_completion_tokens is set well above the length of the JSON you expect. According to the model parameters guide, this limit includes the model's internal reasoning tokens when thinking mode is on, so a tight limit can cut the answer off. Second, the temperature is slightly below the recommended default of 1.0. You still want creative scripts, but you also want the model to follow the format.

Step 3: Validate before you spend credits on video

Never send model output straight to a paid video render. Check it in code first:

def check_plan(plan, target_seconds, clip_lengths, words_per_second=2.5):
    problems = []
    total = sum(shot["seconds"] for shot in plan["shots"])
    if total != target_seconds:
        problems.append(f"Total is {total}s, expected {target_seconds}s")
    for shot in plan["shots"]:
        if shot["seconds"] not in clip_lengths:
            problems.append(f"Shot {shot['id']}: {shot['seconds']}s is not allowed")
        words = len(shot["narration"].split())
        if words > shot["seconds"] * words_per_second:
            problems.append(f"Shot {shot['id']}: {words} words is too long")
    return problems

If check_plan returns problems, send them back to the model in a follow-up user message ("Fix these problems and return the full JSON again") and validate once more. In practice one retry fixes most issues. If the second attempt still fails, stop and look at the prompt instead of looping.

The 2.5 words per second limit is a rough speaking pace for narration. Slow it to about 2 for children's content or calm explainers.

Step 4: Match the clip lengths to your video model

The clip_lengths argument is where most generated shot lists go wrong. Video models only accept certain durations, and these differ a lot between models: some offer 4, 6 or 8 seconds, others anything from 5 to 30. Before you run the planner, look up the exact options for the model you will use. A side-by-side table of video models with their clip lengths, resolutions and credit cost per second makes this quick, and you can copy the allowed durations straight into clip_lengths.

Choosing the target length also sets your video budget. Many video models charge per second, so the planner's total length is effectively the cost of the render. At per-second pricing the split does not change the total, so fewer, longer shots give each one more time to settle without costing more.

Step 5: Render the narration

With a valid plan, turn the narration into audio. Following the MiMo text-to-speech guide, the text goes in an assistant message and the audio comes back base64-encoded:

import base64


def render_narration(plan, out_dir="out"):
    os.makedirs(out_dir, exist_ok=True)
    for shot in plan["shots"]:
        completion = client.chat.completions.create(
            model="mimo-v2.5-tts",
            messages=[{"role": "assistant", "content": shot["narration"]}],
            audio={"format": "wav", "voice": "mimo_default"},
        )
        audio = base64.b64decode(completion.choices[0].message.audio.data)
        path = os.path.join(out_dir, f"shot_{shot['id']:02d}.wav")
        with open(path, "wb") as f:
            f.write(audio)

One file per shot keeps editing simple: each clip and its narration line up one to one on the timeline. If a line runs longer than its clip, you will see it immediately.

Step 6: Put it together

if __name__ == "__main__":
    TARGET, CLIPS = 30, (5, 10)
    plan = plan_video("Why do cats knock things off tables?", TARGET, CLIPS)
    problems = check_plan(plan, TARGET, CLIPS)
    if problems:
        raise SystemExit("\n".join(problems))
    render_narration(plan)
    with open("out/shots.json", "w", encoding="utf-8") as f:
        json.dump(plan, f, indent=2, ensure_ascii=False)
    for shot in plan["shots"]:
        print(f"[{shot['seconds']}s] {shot['visual_prompt']}\n")

Run it, paste each printed prompt into your video tool with the matching duration, and drop the clips and WAV files into your editor.

Ideas for extending it

  • Keep characters consistent. Add a characters array to the JSON with one fixed description each, and tell the model to copy that description word for word into every visual_prompt that includes the character.
  • Batch topics. Loop over a list of topics overnight and review the shot lists in the morning.
  • Use tools for stricter output. The chat completions endpoint accepts tools and tool_choice. Defining a save_plan function with a JSON schema for its parameters is another way to get structured arguments back.

When you do not need a planner

Not every video needs a generated script. Trend formats with a fixed story beat are usually better served by single-purpose tools that already encode the structure. Zombie AI, for instance, makes the viral zombie love story from two uploaded photos without any prompt: one person has turned, the other cannot shoot, then a happy memory, with music included. A planner like the one above pays off when every video is different and the structure has to be invented each time.

Cost and next steps

A shot list is a short text answer, so planning calls are small next to the cost of rendering every second of video. API usage is paid with credit packages rather than a subscription; see the pricing page. Start with one topic and refine the system prompt until the shot lists need little editing, then scale up.

所有文章

作者

avatar for MiMo 团队
MiMo 团队

分類

  • 产品
What you needStep 1: Pick a modelStep 2: Ask for a script and shot list in JSONStep 3: Validate before you spend credits on videoStep 4: Match the clip lengths to your video modelStep 5: Render the narrationStep 6: Put it togetherIdeas for extending itWhen you do not need a plannerCost and next steps

更多文章

小米 MiMo API 接入服务正式上线
产品

小米 MiMo API 接入服务正式上线

我们很高兴地宣布小米 MiMo API 接入服务正式上线。通过我们简单可靠的 API,即可轻松使用强大的 MiMo 能力。

avatar for MiMo 团队
MiMo 团队
2025/03/19

電子報

加入我們的社群

訂閱電子報,掌握最新消息與更新

LogoXiaomi MiMo API Provider

統一接入 Xiaomi MiMo 模型,服務於 agent、多模態與語音工作負載。

Email
產品
  • 功能
  • 價格
  • 模型
  • 常見問題
資源
  • 部落格
  • 文件
  • 更新日誌
  • 路線圖
  • Happy Horse
  • Faceless Reels Video
  • Faceless Reels Studio
  • Autober
公司
  • 關於我們
  • 聯絡我們
  • 電子報
法律
  • Cookie政策
  • 隱私權政策
  • 服務條款
© 2026 Xiaomi MiMo API Provider All Rights Reserved.