Skip to content

Генерация видео

Ниже приведён перевод и адаптация официального руководства OpenAI Sora с сохранением структуры и примеров, адаптированных под AITUNNEL.

Обзор

Sora — новейшая видеомодель OpenAI, создающая детальные динамичные клипы с аудио по тексту или изображениям. Видео‑API (preview) открывает эти возможности: позволяет программно создавать, расширять и ремиксировать видео. Доступно три ключевых действия:

  • Создать видео (POST /v1/videos)
  • Получить статус (GET /v1/videos/{video_id})
  • Скачать видео (GET /v1/videos/{video_id}/content)

Модели

Sora 2

sora-2 — для скорости и гибкости. Подходит для быстрых итераций, концептов и соцсетей.

Sora 2 Pro

sora-2-pro — более стабильное и качественное выводимое видео. Подходит для продакшн‑задач и маркетинговых материалов.

Wan 2.6

wan2.6 — модель Qwen для текста и изображения в видео. Длительности 5, 10 или 15 секунд.

Wan 2.5

wan2.5 — модель Qwen для текста и изображения в видео. Длительности 5 или 10 секунд.

Генерация видео

Генерация — асинхронный процесс:

  1. POST /v1/videos возвращает объект задания с id и начальным status
  2. Либо опрашивайте GET /v1/videos/{video_id}, либо используйте вебхуки
  3. После completed скачайте MP4 GET /v1/videos/{video_id}/content

Начать рендер (curl)

bash
curl -X POST "https://api.aitunnel.ru/v1/videos" \
  -H "Authorization: Bearer $AITUNNEL_API_KEY" \
  -H "Content-Type: multipart/form-data" \
  -F prompt="Wide tracking shot of a teal coupe driving through a desert highway, heat ripples visible, hard sun overhead." \
  -F model="sora-2-pro" \
  -F size="1280x720" \
  -F seconds="8"

Ответ (пример):

json
{
  "id": "video_68d7512d07848190b3e45da0ecbebcde004da08e1e0678d5",
  "object": "video",
  "created_at": 1758941485,
  "status": "queued",
  "model": "sora-2-pro",
  "progress": 0,
  "seconds": "8",
  "size": "1280x720"
}

Мониторинг прогресса

javascript
import OpenAI from 'openai';

const openai = new OpenAI({
  baseURL: 'https://api.aitunnel.ru/v1',
  apiKey: process.env.AITUNNEL_API_KEY
});

async function main() {
  const video = await openai.videos.createAndPoll({
    model: 'sora-2',
    prompt: "A video of the words 'Thank you' in sparkling letters",
  });

  if (video.status === 'completed') {
    console.log('Video successfully completed: ', video);
  } else {
    console.log('Video creation failed. Status: ', video.status);
  }
}

main();
python
import asyncio
import os
from openai import AsyncOpenAI

client = AsyncOpenAI(
    base_url="https://api.aitunnel.ru/v1",
    api_key=os.getenv("AITUNNEL_API_KEY")
)

async def main() -> None:
    video = await client.videos.create_and_poll(
        model="sora-2",
        prompt="A video of a cat on a motorcycle",
    )

    if video.status == "completed":
        print("Video successfully completed:", video)
    else:
        print("Video creation failed. Status:", video.status)

asyncio.run(main())

Примечание о вебхуках: на проде используйте вебхуки (video.completed, video.failed) вместо частого опроса.

Получение результатов

Скачать MP4 (curl)

bash
curl -L "https://api.aitunnel.ru/v1/videos/video_abc123/content" \
  -H "Authorization: Bearer $AITUNNEL_API_KEY" \
  --output video.mp4

Дополнительные ассеты (thumbnail, spritesheet)

bash
# thumbnail
curl -L "https://api.aitunnel.ru/v1/videos/video_abc123/content?variant=thumbnail" \
  -H "Authorization: Bearer $AITUNNEL_API_KEY" \
  --output thumbnail.webp

# spritesheet
curl -L "https://api.aitunnel.ru/v1/videos/video_abc123/content?variant=spritesheet" \
  -H "Authorization: Bearer $AITUNNEL_API_KEY" \
  --output spritesheet.jpg

Использование изображений как референсов

Прикрепите изображение через input_reference, чтобы оно стало первым кадром видео. Разрешение изображения должно совпадать с параметром size. Поддерживаемые форматы: image/jpeg, image/png, image/webp.

Существует два способа передачи input_reference:

СпособContent-TypeОписание
Файл (multipart)multipart/form-dataИзображение загружается напрямую как файл
JSON (image_url)application/jsonПередаётся URL или base64 data URL внутри объекта {"image_url": "..."}

Распространённая ошибка

При использовании JSON поле называется image_url, а не file. Запрос с input_reference: {"file": "..."} вернёт ошибку Unknown parameter: 'input_reference.file'.

javascript
import fs from 'node:fs';
import OpenAI from 'openai';

const openai = new OpenAI({
  baseURL: 'https://api.aitunnel.ru/v1',
  apiKey: process.env.AITUNNEL_API_KEY
});

const fileStream = fs.createReadStream('image.png');

const video = await openai.videos.create({
  model: 'sora-2-pro',
  prompt: 'She turns around and smiles, then slowly walks out of the frame.',
  size: '1280x720',
  seconds: '8',
  input_reference: await OpenAI.toFile(fileStream, 'image.png', { contentType: 'image/png' })
});

console.log(video.id, video.status);
python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.aitunnel.ru/v1",
    api_key=os.getenv("AITUNNEL_API_KEY")
)

with open("image.png", "rb") as img:
    video = client.videos.create(
        model="sora-2-pro",
        prompt="She turns around and smiles, then slowly walks out of the frame.",
        size="1280x720",
        seconds="8",
        input_reference=img,
    )

print(video.id, video.status)
python
import asyncio
import aiohttp
import aiofiles
import os

AITUNNEL_API_KEY = os.getenv("AITUNNEL_API_KEY")
BASE_URL = "https://api.aitunnel.ru/v1"

async def create_video_with_image(image_path: str, prompt: str) -> dict:
    """Создаёт задачу генерации видео с референсным изображением (multipart)."""
    async with aiohttp.ClientSession() as session:
        async with aiofiles.open(image_path, "rb") as f:
            image_data = await f.read()

        form = aiohttp.FormData()
        form.add_field("model", "sora-2-pro")
        form.add_field("prompt", prompt)
        form.add_field("size", "1280x720")
        form.add_field("seconds", "8")
        form.add_field(
            "input_reference",
            image_data,
            filename="image.jpeg",
            content_type="image/jpeg",
        )

        async with session.post(
            f"{BASE_URL}/videos",
            headers={"Authorization": f"Bearer {AITUNNEL_API_KEY}"},
            data=form,
        ) as resp:
            resp.raise_for_status()
            return await resp.json()


async def poll_video(video_id: str) -> dict:
    """Опрашивает статус задачи до завершения."""
    async with aiohttp.ClientSession() as session:
        while True:
            async with session.get(
                f"{BASE_URL}/videos/{video_id}",
                headers={"Authorization": f"Bearer {AITUNNEL_API_KEY}"},
            ) as resp:
                resp.raise_for_status()
                data = await resp.json()

            status = data.get("status")
            progress = data.get("progress", 0)
            print(f"Status: {status}, progress: {progress}%")

            if status == "completed":
                return data
            if status == "failed":
                raise RuntimeError(f"Video generation failed: {data}")

            await asyncio.sleep(15)


async def download_video(video_id: str, output_path: str) -> None:
    """Скачивает готовое MP4."""
    async with aiohttp.ClientSession() as session:
        async with session.get(
            f"{BASE_URL}/videos/{video_id}/content",
            headers={"Authorization": f"Bearer {AITUNNEL_API_KEY}"},
        ) as resp:
            resp.raise_for_status()
            async with aiofiles.open(output_path, "wb") as f:
                async for chunk in resp.content.iter_chunked(1024 * 64):
                    await f.write(chunk)
    print(f"Saved to {output_path}")


async def main():
    job = await create_video_with_image(
        image_path="image.jpeg",
        prompt="She turns around and smiles, then slowly walks out of the frame.",
    )
    video_id = job["id"]
    print(f"Job created: {video_id}")

    await poll_video(video_id)
    await download_video(video_id, "output.mp4")


asyncio.run(main())
python
import os
import base64
import asyncio
import aiohttp
import aiofiles

AITUNNEL_API_KEY = os.getenv("AITUNNEL_API_KEY")
BASE_URL = "https://api.aitunnel.ru/v1"

async def create_video_json(image_path: str, prompt: str) -> dict:
    """Создаёт задачу через JSON с base64 data URL в поле image_url."""
    async with aiofiles.open(image_path, "rb") as f:
        raw = await f.read()

    b64 = base64.b64encode(raw).decode()
    data_url = f"data:image/jpeg;base64,{b64}"

    payload = {
        "model": "sora-2-pro",
        "prompt": prompt,
        "size": "1280x720",
        "seconds": "8",
        "input_reference": {
            "image_url": data_url   # ключ именно "image_url", не "file"
        },
    }

    async with aiohttp.ClientSession() as session:
        async with session.post(
            f"{BASE_URL}/videos",
            headers={
                "Authorization": f"Bearer {AITUNNEL_API_KEY}",
                "Content-Type": "application/json",
            },
            json=payload,
        ) as resp:
            resp.raise_for_status()
            return await resp.json()
bash
curl -X POST "https://api.aitunnel.ru/v1/videos" \
  -H "Authorization: Bearer $AITUNNEL_API_KEY" \
  -H "Content-Type: multipart/form-data" \
  -F prompt="She turns around and smiles, then slowly walks out of the frame." \
  -F model="sora-2-pro" \
  -F size="1280x720" \
  -F seconds="8" \
  -F input_reference="@sample_720p.jpeg;type=image/jpeg"

Ремикс готовых видео

Сделайте точечные правки без полного пересоздания. Передайте prompt c описанием изменения.

bash
curl -X POST "https://api.aitunnel.ru/v1/videos/video_abc123/remix" \
  -H "Authorization: Bearer $AITUNNEL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Shift the color palette to teal, sand, and rust, with a warm backlight."
  }'

Формат ответа (сводка)

json
{
  "id": "video_abc123",
  "object": "video",
  "status": "in_progress",
  "model": "sora-2",
  "progress": 33,
  "seconds": "8",
  "size": "1280x720"
}

Лучшие практики

  • Описывайте тип кадра, сюжет, действие, окружение и освещение
  • Используйте вебхуки вместо частого опроса
  • Соблюдайте guardrails: 18-, нет защищённых персонажей/музыки, без реальных лиц в input изображениях

API Reference (кратко)

POST /v1/videos — создать видео

Обязательные поля:

  • prompt (string) — описание видео
  • model (string) — модель (sora-2, sora-2-pro, wan2.6, wan2.5)

Опциональные поля:

  • seconds (string) — длительность, по умолчанию "4" (допустимые значения зависят от модели)
  • size (string) — разрешение, по умолчанию "720x1280"

Параметр input_reference (Image-to-Video):

При multipart/form-data — файл загружается напрямую:

-F input_reference="@image.jpeg;type=image/jpeg"

При application/json — объект с одним из ключей:

json
{ "input_reference": { "image_url": "https://..." } }
{ "input_reference": { "image_url": "data:image/jpeg;base64,..." } }
{ "input_reference": { "file_id": "file-abc123" } }

Формат ответа:

json
{
  "id": "video_abc123",
  "object": "video",
  "created_at": 1758941485,
  "status": "queued",
  "model": "sora-2-pro",
  "progress": 0,
  "seconds": "8",
  "size": "1280x720"
}

Поле id — идентификатор задачи, используется в последующих запросах.

Возможные значения status: queuedin_progresscompleted / failed.

GET /v1/videos/{video_id} — статус и метаданные

GET /v1/videos/{video_id}/content — скачать контент

Параметр variant: video (по умолчанию), thumbnail, spritesheet.

POST /v1/videos/{video_id}/remix — ремикс готового видео

Тело: prompt (string, required).

AITUNNEL