> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apiyi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Навык Video Agent Seedance 2.0

> Объедините все три модели Seedance 2.0 (mini / fast / standard) в один готовый к использованию Agent-навык — просто добавьте его в Codex, OpenClaw, Claude Code или любой coding agent и одним предложением генерируйте видео по тексту, по изображению и по опорному изображению; скрипт опрашивает асинхронную задачу и автоматически скачивает готовый клип.

<Note>
  Эта страница поставляется с **готовым к использованию навыком агента**: один скрипт без зависимостей охватывает все три модели Seedance 2.0 (`mini` / `fast` / стандартную), переключаемые с помощью `--model`. Поместите его в любой используемый вами агент для кодинга, и одна фраза создаст видео — всё решение состоит всего из двух файлов. Это также **первый навык для video-моделей** на этом сайте: в отличие от изображений, генерация видео — асинхронная задача, и скрипт уже автоматизирует полный процесс submit → poll → download.
</Note>

## Что делает навык

Один объединённый навык; скрипт выбирает режим генерации по **тому, какие изображения вы передаёте и какие у них роли**:

<CardGroup cols={3}>
  <Card title="Текст в видео" icon="clapperboard">
    Только prompt → совершенно новое видео, со звуком по умолчанию (диалог, звуковые эффекты, фоновые шумы).
  </Card>

  <Card title="Изображение в видео" icon="image-play">
    Передайте изображение первого кадра, чтобы анимировать статичное изображение; добавьте изображение последнего кадра для плавного перехода от начала к концу.
  </Card>

  <Card title="Референс в видео" icon="layers">
    Передайте до 9 референсных изображений → новый видеоряд, который сохраняет их персонажей, объекты или стиль.
  </Card>
</CardGroup>

## Какую модель выбрать

Эти три модели **вызываются совершенно одинаково**; они различаются по максимальному разрешению, скорости и цене. Скрипт обрабатывает различия в `--model`:

| Модель (`--model`)    | Model ID                          | Макс. разрешение | Скорость (720p/5s, измерено) | Прайсовая цена 720p/5s (0.18x) | Группа скидки (до 2026-09-07)  | Подходит для                                                                        |
| --------------------- | --------------------------------- | ---------------- | ---------------------------- | ------------------------------ | ------------------------------ | ----------------------------------------------------------------------------------- |
| `mini` (по умолчанию) | `doubao-seedance-2-0-mini-260615` | 720p             | **самая быстрая, \~87–170s** | \$0.4508                       | `SD2Mini` 0.10x → **\$0.2504** | частого использования в агентных сценариях, больших объёмов, быстрых предпросмотров |
| `fast`                | `doubao-seedance-2-0-fast-260128` | 720p             | \~100–290s                   | \$0.7253                       | `SD2Fast` 0.15x → **\$0.6044** | золотая середина между качеством и стоимостью                                       |
| `std`                 | `doubao-seedance-2-0-260128`      | **1080p**        | \~100–290s                   | \$0.9074                       | нет (`SeeDance2` 0.18x)        | 1080p, наивысшее качество                                                           |

<Tip>
  Практическое правило: **для повседневных и агентных сценариев оставайтесь на модели `mini` по умолчанию**; **если нужен 1080p или высочайшее качество** → `--model std`. Все соотношения сторон стоят одинаково в пределах одного уровня разрешения, тарификация по длительности идёт линейно по секундам, а частота кадров фиксирована на 24fps. Группы скидки `SD2Mini` / `SD2Fast` действуют до **September 7, 2026 23:59 (UTC+8)**; после этого группы останутся доступными, а коэффициент вернётся к 0.18x — см. [примечания по группе на странице обзора](/ru/api-capabilities/seedance2/overview).
</Tip>

## Какие Agent могут использовать его

<Info>
  Skill по сути — это **папка**: заметка, которую агент должен прочитать (`SKILL.md`), плюс скрипт, который выполняет работу. Поэтому **любой coding agent, который умеет читать локальные файлы и выполнять shell-команды, может использовать его** — **Codex, OpenClaw, hermes-agent, Claude Code** и тому подобные.

  Единственное требование: на машине, где запущен агент (ваш ноутбук или сервер), должны быть **Python 3** и **доступ к интернету** (скрипт напрямую вызывает `api.apiyi.com`). Скрипт использует только стандартную библиотеку Python — **ничего не нужно `pip install`**.
</Info>

## Настройка в 3 шага

### ① Создайте папку и вставьте файлы

Создайте папку с навыком и поместите в неё два файла ниже (полное содержимое — в следующих двух разделах):

```
seedance2/
├── SKILL.md
├── scripts/
│   └── seedance_video.py
└── .env          # created in step ②, holds your key
```

### ② Поместите рядом ваш ключ

Запишите свой **APIYI API key** в `seedance2/.env` (создайте его в консоли `api.apiyi.com`; **у token должен быть включён group `SeeDance2`** — либо льготные `SD2Mini` / `SD2Fast` — с тарификацией pay-as-you-go):

```bash theme={null}
APIYI_API_KEY=sk-your-api-key
```

Скрипт автоматически читает ключ из этого `.env` — **дополнительная конфигурация или переменные окружения не нужны**.

<Warning>
  `.env` хранит ваш secret key. Если skill используется совместно внутри репозитория проекта, **добавьте `.env` в `.gitignore` и никогда не коммитьте его**.
</Warning>

### ③ Передайте его своему Agent

* **Агенты с автообнаружением навыков** (например, Claude Code): поместите всю папку `seedance2/` в каталог навыков — личный `~/.claude/skills/` или уровень проекта `.claude/skills/` (общий для репозитория).
* **Другие агенты**: следуйте их собственным соглашениям для навыков/плагинов; или, что проще всего, — **попросите агента «прочитайте SKILL.md в этой папке и следуйте ему»**.

Вот и всё — перейдите к [Как использовать это](#how-to-use-it) за примерами.

## SKILL.md

Создайте `seedance2/SKILL.md` с полным содержимым ниже (раздел `description` содержит «что он делает + когда его использовать», что и используется агентом для автозапуска):

````markdown theme={null}
---
name: seedance2
description: Generate videos via APIYI's Seedance 2.0 (doubao-seedance-2-0 series) models — text-to-video, image-to-video (first/last frame), and reference-image-to-video, with synced audio by default. Use this when the user asks to create, generate, or animate a video clip.
allowed-tools: Bash(python3 *)
---

# Seedance 2.0 Video Skill

Generate videos through the APIYI platform using Seedance 2.0 (`doubao-seedance-2-0` series). Defaults to the fastest and cheapest mini model, with synced audio built in.

## Key configuration

The script auto-reads `APIYI_API_KEY` from a `.env` file in the skill folder (an environment variable of the same name also works).
If it reports "API key not found", ask the user to add a line `APIYI_API_KEY=sk-xxx` to `.env`;
the token must have the `SeeDance2` (or discounted `SD2Mini` / `SD2Fast`) group enabled with pay-as-you-go billing.

## Usage (important: generation takes 2-5 minutes)

Video generation is an **async task**: the script submits the task and polls until done, so one call usually takes 2-5 minutes in total.
**Run it with a long command timeout (600+ seconds) or in the background** — do not use a default 2-minute timeout, or the script gets killed before the video is ready.

```bash
# Текст в видео (по умолчанию mini / 720p / 5 s / со звуком)
python3 ${CLAUDE_SKILL_DIR}/scripts/seedance_video.py "Оранжевый кот гонится за бабочками на лугу, медленный проезд камеры, естественный свет" -o cat.mp4

# Изображение в видео (первый кадр — анимируйте статичное изображение)
python3 ${CLAUDE_SKILL_DIR}/scripts/seedance_video.py "Камера медленно наезжает, свет струится" -i photo.jpg -o animated.mp4

# Переход от первого к последнему кадру
python3 ${CLAUDE_SKILL_DIR}/scripts/seedance_video.py "Плавный переход от первого кадра к последнему" -i first.png --last-frame last.png -o morph.mp4

# Из референсных изображений в видео (сохраняйте указанного персонажа/стиль, до 9 изображений)
python3 ${CLAUDE_SKILL_DIR}/scripts/seedance_video.py "Персонаж из референсных изображений бежит по снегу" --ref-image role.png -o run.mp4

# Высокое качество: стандартная модель + 1080p + 10 s
python3 ${CLAUDE_SKILL_DIR}/scripts/seedance_video.py "Кадр с дрона над осенней долиной, кинематографично" --model std --resolution 1080p --duration 10 -o valley.mp4
```

Parameters:

- 1st positional argument: the prompt (required). Describing scene + camera motion + mood works best.
- `--model`: `mini` (default, fastest and cheapest) / `fast` / `std` (the only one supporting 1080p).
- `--resolution`: `480p` / `720p` (default) / `1080p` (std only).
- `--ratio`: `adaptive` (default) / `16:9` / `4:3` / `1:1` / `3:4` / `9:16` / `21:9`; all ratios cost the same within a tier.
- `--duration`: integer seconds 4-15, default 5; `-1` lets the model decide. Longer costs more.
- `--no-audio`: disable synced audio (on by default).
- `-i / --image`: first-frame image (local path / URL / `asset://` asset ID); passing it switches to image-to-video. Combine with `--last-frame` for first/last-frame mode.
- `--ref-image`: reference image, repeatable up to 9, mutually exclusive with `-i`.
- `-o / --out`: output file name, default `output.mp4`.

## Clip count and cost (important)

- **One call produces exactly 1 video** — there is no batch flag. If the user wants several, run sequential calls and warn about cost first.
- Video bills by tokens and is far pricier than images (about \$0.45-0.91 list per 720p/5s clip; longer or sharper costs more).
  Unless the user explicitly asks, **keep the defaults mini / 720p / 5s** — never bump duration, resolution, or switch to std on your own.

## Output location (important)

- With a **bare file name** for `-o` (like `cat.mp4`), the video goes to the **`seedance-output/` folder in the project root**.
- With a **path containing directories**, it saves to that path (relative paths resolve against the current working directory).
- Never write videos to `/tmp`, scratchpads, or other temp folders — the user won't find them.
- The returned video URL expires in 24 hours; the script already downloads the file locally — the local file is the deliverable.

## When done

The script prints the saved path, file size, elapsed time, and billed tokens — report the path back to the user verbatim.
If it reports a failure (including content-moderation rejections), relay the error as-is and do not retry the same prompt.
On "no available channel for this model", ask the user to check the token's group includes `SeeDance2` / `SD2Mini` / `SD2Fast`.
````

<Tip>
  `name` должны состоять из строчных букв и дефисов. В агентах со slash-командами имя папки — это команда: `seedance2` даёт вам `/seedance2`. `${CLAUDE_SKILL_DIR}` — это переменная каталога навыков Claude Code; в других агентах просто используйте фактический путь к скрипту.
</Tip>

## scripts/seedance\_video.py

Создайте `seedance2/scripts/seedance_video.py` — только на стандартной библиотеке Python, с тем же кодом запроса, что и в [справочнике API генерации видео](/ru/api-capabilities/seedance2/video-generation) этого сайта, проверено, работает:

```python theme={null}
#!/usr/bin/env python3
"""Generate videos via APIYI's Seedance 2.0 (text / image / reference-image to video). Pure stdlib, zero dependencies."""
import argparse
import base64
import json
import os
import shutil
import sys
import time
import urllib.error
import urllib.request

# Line-buffer stdout even when redirected, so agents can tail progress from the background
sys.stdout.reconfigure(line_buffering=True)

TASKS_URL = "https://api.apiyi.com/seedance/api/v3/contents/generations/tasks"

# Short name -> full model ID
MODELS = {
    "mini": "doubao-seedance-2-0-mini-260615",
    "fast": "doubao-seedance-2-0-fast-260128",
    "std": "doubao-seedance-2-0-260128",
}
# Resolution caps per model (mini/fast reject 1080p upstream with 400; fail fast client-side)
MODEL_CAPS = {
    "mini": ("480p", "720p"),
    "fast": ("480p", "720p"),
    "std": ("480p", "720p", "1080p"),
}
RATIOS = ("adaptive", "16:9", "4:3", "1:1", "3:4", "9:16", "21:9")
MAX_REF_IMAGES = 9

# Generation is async: wait a bit before the first poll; 720p/5s takes ~90-170s in practice
POLL_FIRST_DELAY = 25
POLL_INTERVAL = 15
POLL_TIMEOUT = 15 * 60


def load_api_key():
    """Prefer the environment variable; otherwise look for .env in the script dir and its parent."""
    key = os.environ.get("APIYI_API_KEY")
    if key:
        return key
    here = os.path.dirname(os.path.abspath(__file__))
    for d in (here, os.path.dirname(here)):
        env_path = os.path.join(d, ".env")
        if os.path.exists(env_path):
            with open(env_path, encoding="utf-8") as f:
                for line in f:
                    line = line.strip()
                    if line.startswith("APIYI_API_KEY") and "=" in line:
                        return line.split("=", 1)[1].strip().strip('"').strip("'")
    return None


def project_root():
    """Walk up from the script location to the first dir containing .git or .claude; else use cwd."""
    d = os.path.dirname(os.path.abspath(__file__))
    while True:
        if os.path.isdir(os.path.join(d, ".git")) or os.path.isdir(os.path.join(d, ".claude")):
            return d
        parent = os.path.dirname(d)
        if parent == d:
            return os.getcwd()
        d = parent


def resolve_path(out):
    """Bare file name -> save under <project root>/seedance-output/ so it's easy to find; else use the given path."""
    if os.path.dirname(out):
        return os.path.abspath(out)
    out_dir = os.path.join(project_root(), "seedance-output")
    os.makedirs(out_dir, exist_ok=True)
    return os.path.join(out_dir, out)


def image_source(src):
    """Image inputs: pass URLs / asset:// / data: through as-is; encode local files as base64 data URIs."""
    if src.startswith(("http://", "https://", "asset://", "data:")):
        return src
    if not os.path.exists(src):
        sys.exit(f"Image file not found: {src}")
    ext = src.lower().rsplit(".", 1)[-1]
    mime = {"png": "image/png", "webp": "image/webp"}.get(ext, "image/jpeg")
    with open(src, "rb") as f:
        return f"data:{mime};base64,{base64.b64encode(f.read()).decode()}"


def api_request(url, api_key, body=None):
    """The gateway labels responses content-encoding: gzip without compressing them — Accept-Encoding: identity is required."""
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
        "Accept-Encoding": "identity",
    }
    data = json.dumps(body).encode() if body is not None else None
    req = urllib.request.Request(url, data=data, headers=headers,
                                 method="POST" if body is not None else "GET")
    try:
        with urllib.request.urlopen(req, timeout=60) as r:
            return json.loads(r.read())
    except urllib.error.HTTPError as e:
        raise RuntimeError(f"Request failed HTTP {e.code}: {e.read().decode(errors='replace')[:800]}")


def download(url, path):
    """Download the result video: it's a signed URL — do NOT send the Authorization header."""
    req = urllib.request.Request(url)
    with urllib.request.urlopen(req, timeout=300) as r, open(path, "wb") as f:
        shutil.copyfileobj(r, f)
    return os.path.getsize(path)


def build_content(args):
    content = [{"type": "text", "text": args.prompt}]
    if args.image:
        content.append({"type": "image_url",
                        "image_url": {"url": image_source(args.image)},
                        "role": "first_frame"})
        if args.last_frame:
            content.append({"type": "image_url",
                            "image_url": {"url": image_source(args.last_frame)},
                            "role": "last_frame"})
    for src in args.ref_image:
        content.append({"type": "image_url",
                        "image_url": {"url": image_source(src)},
                        "role": "reference_image"})
    return content


def main():
    api_key = load_api_key()
    if not api_key:
        sys.exit("API key not found: add a line APIYI_API_KEY=sk-xxx to .env in the skill folder"
                 " (the token must have the SeeDance2 / SD2Mini / SD2Fast group enabled)")

    parser = argparse.ArgumentParser(description="Seedance 2.0 video generation")
    parser.add_argument("prompt", help="prompt (scene + camera motion + mood)")
    parser.add_argument("--model", default="mini", choices=sorted(MODELS),
                        help="mini=fastest & cheapest (default) / fast / std (only one with 1080p)")
    parser.add_argument("--resolution", default="720p", choices=("480p", "720p", "1080p"),
                        help="resolution, default 720p")
    parser.add_argument("--ratio", default="adaptive", choices=RATIOS,
                        help="aspect ratio, default adaptive (same price across ratios per tier)")
    parser.add_argument("--duration", type=int, default=5,
                        help="duration 4-15 integer seconds, or -1 to let the model decide; default 5")
    parser.add_argument("--no-audio", action="store_true",
                        help="disable synced audio (on by default)")
    parser.add_argument("--seed", type=int, default=None, help="random seed, for reproducibility")
    parser.add_argument("-i", "--image", help="first-frame image (local path / URL / asset://); enables image-to-video")
    parser.add_argument("--last-frame", help="last-frame image, used together with -i")
    parser.add_argument("--ref-image", action="append", default=[],
                        help=f"reference image (repeatable, up to {MAX_REF_IMAGES}); mutually exclusive with -i")
    parser.add_argument("-o", "--out", default="output.mp4", help="output file name")
    args = parser.parse_args()

    if args.image and args.ref_image:
        sys.exit("First-frame mode (-i) and reference mode (--ref-image) are mutually exclusive.")
    if args.last_frame and not args.image:
        sys.exit("--last-frame must be used together with -i (first-frame image).")
    if len(args.ref_image) > MAX_REF_IMAGES:
        sys.exit(f"At most {MAX_REF_IMAGES} reference images.")
    if args.duration != -1 and not 4 <= args.duration <= 15:
        sys.exit("Duration must be an integer of 4-15 seconds, or -1 for smart duration.")
    if args.resolution not in MODEL_CAPS[args.model]:
        sys.exit(f"{args.model} supports up to {MODEL_CAPS[args.model][-1]}; "
                 f"use --model std for 1080p.")

    body = {
        "model": MODELS[args.model],
        "content": build_content(args),
        "resolution": args.resolution,
        "ratio": args.ratio,
        "duration": args.duration,
    }
    if args.no_audio:
        body["generate_audio"] = False
    if args.seed is not None:
        body["seed"] = args.seed

    try:
        task = api_request(TASKS_URL, api_key, body)
    except (RuntimeError, OSError) as e:
        sys.exit(f"Submission failed: {e}")
    task_id = task.get("id")
    if not task_id:
        sys.exit(f"Submission failed, response: {json.dumps(task, ensure_ascii=False)[:500]}")
    print(f"Task submitted task_id={task_id}; generation usually takes 2-5 minutes, polling...")

    t0 = time.time()
    time.sleep(POLL_FIRST_DELAY)
    while True:
        try:
            task = api_request(f"{TASKS_URL}/{task_id}", api_key)
        except (RuntimeError, OSError) as e:  # network hiccups don't stop the poll loop
            print(f"  poll error (continuing): {e}")
            time.sleep(POLL_INTERVAL)
            continue
        status = task.get("status", "unknown")
        elapsed = round(time.time() - t0)
        print(f"  [{elapsed:>4}s] status={status}")
        if status in ("succeeded", "failed", "expired"):
            break
        if time.time() - t0 > POLL_TIMEOUT:
            sys.exit(f"Polling timed out ({POLL_TIMEOUT}s). The task is still server-side; query it later:\n"
                     f"  GET {TASKS_URL}/{task_id}")
        time.sleep(POLL_INTERVAL)

    if status != "succeeded":
        err = task.get("error") or task
        sys.exit(f"Generation failed (status={status}): {json.dumps(err, ensure_ascii=False)[:500]}")

    video_url = (task.get("content") or {}).get("video_url")
    if not video_url:
        sys.exit(f"Task succeeded but no video URL returned: {json.dumps(task, ensure_ascii=False)[:500]}")

    path = resolve_path(args.out)
    size = download(video_url, path)
    tokens = (task.get("usage") or {}).get("completion_tokens", "?")
    print(f"Video saved to {path} ({size / 1e6:.1f} MB, {round(time.time() - t0)}s elapsed, "
          f"billed {tokens} tokens)")


if __name__ == "__main__":
    main()
```

## Как переключать модели

Переключение моделей — это **всего лишь `--model`**, три значения:

```text theme={null}
... seedance_video.py "prompt"                                    # default mini (fastest & cheapest)
... seedance_video.py "prompt" --model fast                       # fast edition
... seedance_video.py "prompt" --model std --resolution 1080p     # standard - the only 1080p model
```

<Info>
  **Группа token определяет, какие модели он может вызывать**: обычная `SeeDance2` группа (0.18x) запускает все три; скидочные группы — это **одномодельные каналы** — `SD2Mini` (0.10x) пропускает только `mini`, `SD2Fast` (0.15x) пропускает только `fast`, а при вызове любой другой модели со скидочным token возвращается «для этой модели нет доступного канала». Для всех трёх моделей на одном ключе `SeeDance2` token — самый простой вариант; откройте отдельные скидочные token, когда ваш объём оправдает это — см. [примечания к группам на странице обзора](/ru/api-capabilities/seedance2/overview).
</Info>

## Ожидайте 2–5 минут ожидания (важно)

Генерация видео — это **асинхронная задача**, и это главное отличие от возможностей работы с изображениями:

* Скрипт оборачивает весь процесс: отправка → опрос каждые 15 секунд → автоматическое скачивание mp4 при успехе. **Клип 720p/5s занимает примерно 2–3 минуты от начала до конца**; 1080p или более длительные ролики требуют больше времени.
* **Задайте для команды большой timeout (600+ секунд) или запустите её в фоновом режиме** — многие агенты завершают команды по умолчанию через 2 минуты, ещё до того, как видео будет готово. `SKILL.md` это, а агенты с фоновым выполнением (например, Claude Code) обрабатывают это автоматически.
* Если опрос когда-либо истечёт по timeout (15 минут), задача всё равно останется в очереди на стороне сервера; скрипт выведет `task_id` и команду запроса, чтобы вы могли получить её позже. **Деньги не расходуются** — Seedance 2.0 предварительно списывает средства при отправке и возвращает разницу после завершения, а отклонённые отправки (HTTP 400) никогда не тарифицируются.

## Почему одно предложение генерирует видео

Часто задаваемый вопрос: я никогда не вводил команду — как фраза «сделай видео с котом» создала клип?

Вот как это работает: при запуске агент **считывает `description` каждого навыка в его `SKILL.md`** (небольшой фрагмент метаданных, в котором указано «что делает этот навык и когда его использовать»). Когда ваш запрос **соответствует** этому описанию (например, «generate/make a video», «animate this image»), агент **сам решает вызвать навык**, читает полный `SKILL.md` и запускает скрипт — вам не нужно запоминать команду.

Если вы предпочитаете не полагаться на догадки агента, используйте **явный вызов** ниже для **полного контроля**.

## Как использовать

### Естественный язык (неявный триггер)

После установки просто общайтесь с вашим агентом:

| Вы говорите                                                  | Поведение навыка                                           |
| ------------------------------------------------------------ | ---------------------------------------------------------- |
| "Создай видео с котом, бегущим по траве"                     | по умолчанию `mini` / 720p / 5 s, один mp4 со звуком       |
| "Преврати этот постер в motion video"                        | добавляет `-i poster.png`, image-to-video по первому кадру |
| "10-секундный drone shot в 1080p"                            | добавляет `--model std --resolution 1080p --duration 10`   |
| "Используй эти изображения персонажей для ролика с паркуром" | добавляет `--ref-image` с референсами                      |
| "Без фонового звука"                                         | добавляет `--no-audio`                                     |

### Явный вызов (больше контроля)

* **Агенты с slash-командами** (например, Claude Code):

  ```text theme={null}
  /seedance2 Drone shot over an autumn valley, golden forest, cinematic --duration 8 --ratio 16:9
  ```

* **Любой агент / скажите ему запустить скрипт напрямую** (наиболее универсально):

  ```text theme={null}
  Run python3 seedance2/scripts/seedance_video.py "Drone shot over an autumn valley, cinematic" --duration 8
  ```

## Куда попадает сгенерированное видео

* При использовании **имени файла без пути** для `-o` (например, `-o cat.mp4`) видео попадает в **папку `seedance-output/` в корне проекта** (создаётся автоматически) — прямо в вашем проекте.
* «Корень проекта» = первый каталог, содержащий `.git` или `.claude`, найденный при движении вверх от скрипта — **где бы ни запускался агент, видео остаётся в вашем проекте**, никогда не теряясь во временной папке.
* По завершении скрипт **выводит одну строку с полным абсолютным путём**, а также размером файла, прошедшим временем и списанными token, например `Video saved to /Users/you/project/seedance-output/cat.mp4 (3.8 MB, 132s elapsed, billed 108900 tokens)`.
* Возвращаемый URL видео **истекает через 24 часа**, поэтому скрипт всегда сначала скачивает его — **локальный mp4 является итоговым результатом**; никогда не сохраняйте URL как результат.
* При использовании **пути с каталогами** (например, `-o videos/cat.mp4` или абсолютного пути) он сохраняет файл именно туда, пропуская `seedance-output/`.

## Связанные документы

* [Обзор Seedance 2.0](/ru/api-capabilities/seedance2/overview) (модели, тарификация, группы)
* [Справочник API для генерации видео](/ru/api-capabilities/seedance2/video-generation) (полные параметры и эндпоинты)
* [Генерация видео с привязкой к ассетам](/ru/api-capabilities/seedance2/asset-reference) (согласованность персонажей, `asset://` ассеты)
* [Навык агента серии GPT-Image-2](/ru/api-capabilities/gpt-image-2/skills) (image-ориентированный аналог)
* [Навык агента Nano Banana Pro](/ru/api-capabilities/nano-banana-image/skills)
