تولید ویدئو — Video generation

آشا تولید ویدئو از متن (و تصویر مرجع) را از طریق POST /v1/videos پشتیبانی می‌کند. تولید ویدئو در سرور ناهمگام انجام می‌شود؛ آشا کار را ثبت و تا پایان پیگیری می‌کند و نتیجهٔ نهایی را در همان پاسخ برمی‌گرداند. فهرست مدل‌ها و تعرفه‌ها در صفحهٔ مدل‌ها در دسترس است.

کشف مدل‌ها

مدل‌های تولید ویدئو از طریق endpoint سازگار با OpenAI یعنی GET /v1/models در دسترس‌اند. هر مدل فیلد architecture.output_modalities دارد که حالت‌های خروجیِ پشتیبانی‌شده (مثل video) را فهرست می‌کند:

cURL GET /v1/models
curl "https://app.asha-ai.ir/v1/models"

مدل‌هایی که "video" را در architecture.output_modalities دارند، مدل‌های تولید ویدئو هستند. فهرست کامل و قیمت به تومان در صفحهٔ مدل‌ها هم نمایش داده می‌شود.

روش کار

تولید ویدئو ناهمگام است، چون ساختن ویدئو به‌طور قابل‌توجهی طول می‌کشد. آشا در POST /v1/videos این کار را برای شما ساده کرده است:

  1. ارسال درخواست تولید به POST /v1/videos
  2. انتظار — آشا کار را نزد ارائه‌دهنده ثبت می‌کند و تا رسیدن به وضعیت نهایی پیگیری (نظرسنجی) می‌کند
  3. دریافت کارِ تکمیل‌شده در همان پاسخ، همراه آدرس‌های دانلود (unsigned_urls) و هزینهٔ نهایی (usage.cost)
  4. دانلود ویدئو از آدرس‌های ارائه‌شده

استفاده از API

ارسال درخواست تولید ویدئو

import os

import requests

url = "https://app.asha-ai.ir/v1/videos"
headers = {
    "Authorization": f"Bearer {os.environ['ASHA_API_KEY']}",
    "Content-Type": "application/json",
}

payload = {
    "model": "~google/veo-3.1",
    "prompt": "A golden retriever playing fetch on a sunny beach with waves crashing in the background",
}

# Asha submits the job and polls until completion, then returns the final job.
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
result = response.json()

if result["status"] != "completed":
    raise RuntimeError(f"Generation failed: {result.get('error', 'Unknown error')}")

# Download the video from the returned content URL
content_url = result["unsigned_urls"][0]
video_response = requests.get(content_url)
with open("output.mp4", "wb") as f:
    f.write(video_response.content)
print("Video saved to output.mp4")
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	payload := map[string]any{
		"model":  "~google/veo-3.1",
		"prompt": "A golden retriever playing fetch on a sunny beach with waves crashing in the background",
	}
	body, _ := json.Marshal(payload)

	req, _ := http.NewRequest("POST", "https://app.asha-ai.ir/v1/videos", bytes.NewReader(body))
	req.Header.Set("Authorization", "Bearer "+os.Getenv("ASHA_API_KEY"))
	req.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	var result struct {
		Status       string   `json:"status"`
		UnsignedURLs []string `json:"unsigned_urls"`
		Error        string   `json:"error"`
	}
	_ = json.NewDecoder(resp.Body).Decode(&result)

	if result.Status != "completed" {
		fmt.Printf("Generation failed: %sn", result.Error)
		return
	}

	videoResp, err := http.Get(result.UnsignedURLs[0])
	if err != nil {
		panic(err)
	}
	defer videoResp.Body.Close()

	out, _ := os.Create("output.mp4")
	defer out.Close()
	_, _ = io.Copy(out, videoResp.Body)
	fmt.Println("Video saved to output.mp4")
}
const headers = {
  Authorization: `Bearer ${process.env.ASHA_API_KEY}`,
  "Content-Type": "application/json",
};

// Asha submits the job and polls until completion, then returns the final job.
const response = await fetch("https://app.asha-ai.ir/v1/videos", {
  method: "POST",
  headers,
  body: JSON.stringify({
    model: "~google/veo-3.1",
    prompt: "A golden retriever playing fetch on a sunny beach with waves crashing in the background",
  }),
});

const result = await response.json();

if (result.status !== "completed") {
  throw new Error(`Generation failed: ${result.error ?? "Unknown error"}`);
}

// Download the video from the returned content URL
const videoResponse = await fetch(result.unsigned_urls[0]);
const videoBuffer = await videoResponse.arrayBuffer();
// Save or process the video buffer
console.log(`Video ready: ${result.unsigned_urls[0]}`);
const headers = {
  Authorization: `Bearer ${process.env.ASHA_API_KEY}`,
  "Content-Type": "application/json",
};

// Asha submits the job and polls until completion, then returns the final job.
const response = await fetch("https://app.asha-ai.ir/v1/videos", {
  method: "POST",
  headers,
  body: JSON.stringify({
    model: "~google/veo-3.1",
    prompt: "A golden retriever playing fetch on a sunny beach with waves crashing in the background",
  }),
});

const result = await response.json();

if (result.status !== "completed") {
  throw new Error(`Generation failed: ${result.error ?? "Unknown error"}`);
}

// Download the video from the returned content URL
const videoResponse = await fetch(result.unsigned_urls[0]);
const videoBuffer = await videoResponse.arrayBuffer();
console.log(`Video ready: ${result.unsigned_urls[0]}`);
# Asha submits the job and polls until completion, then returns the final job.
curl -X POST "https://app.asha-ai.ir/v1/videos" 
  -H "Authorization: Bearer $ASHA_API_KEY" 
  -H "Content-Type: application/json" 
  -d '{
    "model": "~google/veo-3.1",
    "prompt": "A golden retriever playing fetch on a sunny beach with waves crashing in the background"
  }'

# Response:
# {
#   "id": "<job_id>",
#   "status": "completed",
#   "unsigned_urls": ["https://.../output.mp4"],
#   "usage": { "cost": 3750, "is_byok": false }
# }

# Once completed, download from unsigned_urls[0]

پارامترهای درخواست

پارامترنوعالزامیشرح
modelstringبلهمدل تولید ویدئو (مثل ~google/veo-3.1)
promptstringبلهتوصیف متنی ویدئوی موردنظر
durationintegerخیرمدت ویدئوی تولیدی بر حسب ثانیه
resolutionstringخیررزولوشن خروجی (مثل 720p، 1080p)
aspect_ratiostringخیرنسبت تصویر خروجی (مثل 16:9، 9:16، 3:2)
sizestringخیرابعاد پیکسل دقیق با فرمت WIDTHxHEIGHT (مثل 1280x720). به‌جای resolution + aspect_ratio هم کار می‌کند
frame_imagesarrayخیرتصاویر قاب اول/آخر (تبدیل تصویر به ویدئو)
input_referencesarrayخیرتصاویر مرجع برای هدایت سبک (مرجع به ویدئو)
generate_audiobooleanخیرآیا صدا هم کنار ویدئو تولید شود. برای مدل‌های دارای خروجی صوتی به‌طور پیش‌فرض true است
seedintegerخیرSeed برای تولید قطعی (توسط همهٔ ارائه‌دهنده‌ها تضمین نمی‌شود)
providerobjectخیرپیکربندی عبور پارامترهای مختص ارائه‌دهنده

رزولوشن‌های پشتیبانی‌شده

  • 480p
  • 720p
  • 768p
  • 1080p
  • 1K
  • 2K
  • 4K

نسبت‌های تصویر پشتیبانی‌شده

  • 16:9 — گسترده (سینمایی)
  • 9:16 — عمودی (پرتره)
  • 1:1 — مربع
  • 4:3 — استاندارد
  • 3:4 — پرترهٔ استاندارد
  • 3:2 — چشم‌انداز عکاسی
  • 2:3 — پرترهٔ عکاسی
  • 21:9 — فوق‌عریض
  • 9:21 — فوق‌بلند

استفاده از تصاویر

دو راه برای دادن تصویر وجود دارد که هر کدام حالت متفاوتی را فعال می‌کند:

  • frame_images — تصاویر قاب اول یا آخر برای حالتِ تصویر به ویدئو. هر ورودی باید frame_type از نوع first_frame یا last_frame داشته باشد.
  • input_references — تصاویر مرجعِ سبک یا محتوا برای حالتِ مرجع به ویدئو. مدل از آن‌ها به‌عنوان راهنمای بصری استفاده می‌کند، نه قاب دقیق.

اگر هر دو فیلد پر شوند، frame_images اولویت دارد و درخواست «تصویر به ویدئو» تلقی می‌شود.

تصویر به ویدئو (frame_images)

JSON request
{
  "model": "~alibaba/wan-2.7",
  "prompt": "A character walking through a forest",
  "frame_images": [
    {
      "type": "image_url",
      "image_url": {
        "url": "https://example.com/first-frame.png"
      },
      "frame_type": "first_frame"
    }
  ],
  "resolution": "1080p"
}

مرجع به ویدئو (input_references)

JSON request
{
  "model": "~alibaba/wan-2.7",
  "prompt": "A colossal solar flare beside a planet",
  "input_references": [
    {
      "type": "image_url",
      "image_url": {
        "url": "https://example.com/style-ref.png"
      }
    }
  ],
  "resolution": "1080p"
}

گزینه‌های مختص ارائه‌دهنده

با پارامتر provider گزینه‌های مختص ارائه‌دهنده را بفرستید. گزینه‌ها با نام ارائه‌دهنده کلید می‌خورند و فقط گزینه‌های ارائه‌دهندهٔ جورشده منتقل می‌شوند:

JSON request
{
  "model": "~google/veo-3.1",
  "prompt": "A time-lapse of a flower blooming",
  "provider": {
    "options": {
      "google-vertex": {
        "parameters": {
          "personGeneration": "allow",
          "negativePrompt": "blurry, low quality"
        }
      }
    }
  }
}

گزینه‌های عبوری که هر مدل می‌پذیرد، در دادهٔ مدل در GET /v1/models قابل مشاهده است.

قالب پاسخ

درخواست POST /v1/videos پس از رسیدن کار به وضعیت نهایی، خودِ کارِ تکمیل‌شده را برمی‌گرداند. فیلد status برابر completed است، آرایهٔ unsigned_urls آدرس‌های دانلود محتوا را دارد و usage.cost هزینهٔ نهایی را به تومان گزارش می‌کند:

JSON response
{
  "id": "abc123",
  "generation_id": "gen-1234567890-abcdef",
  "status": "completed",
  "unsigned_urls": [
    "https://storage.openrouter.ai/files/abc123.mp4"
  ],
  "usage": {
    "cost": 3750,
    "is_byok": false
  }
}

وضعیت‌های کار

چون آشا تا رسیدن به وضعیت نهایی نظرسنجی می‌کند، کلاینت معمولاً فقط وضعیت completed را می‌بیند. وضعیت‌های شکست با خطای 502 Bad Gateway و پیام حاوی خطای ارائه‌دهنده برمی‌گردند و هزینه‌ای کسر نمی‌شود:

وضعیتشرح
completedویدئو آمادهٔ دانلود است (پاسخ موفق)
failedتولید شکست خورده است (فیلد error را ببینید)
cancelledکار لغو شده است
expiredکار از محدودهٔ زمانی مجاز گذشته است

دانلود ویدئو

پس از تکمیل، فایل را مستقیم از آدرس‌های داخل unsigned_urls دانلود کنید (بدون نیاز به هدر احراز هویت):

cURL download
curl "https://storage.openrouter.ai/files/abc123.mp4" 
  --output video.mp4

وقتی مدل چند خروجی تولید کند، هر خروجی یک آدرس جداگانه در همین آرایه دارد.

بهترین روش‌ها

  • Prompt دقیق — برای کیفیت بهتر ویدئو، promptهای مشخص و توصیفی بدهید؛ جزئیات حرکت، زاویهٔ دوربین، نور و ترکیب‌بندی صحنه را بنویسید.
  • رزولوشن مناسب — رزولوشن بالاتر، کندتر و گران‌تر است؛ بر اساس کاربردتان انتخاب کنید.
  • مدیریت انتظار — درخواست تا رسیدن به وضعیت نهایی باز می‌ماند؛ تایم‌اوت سمت کلاینت را به‌اندازهٔ کافی بلند بگیرید (تولید ویدئو معمولاً بین ۳۰ ثانیه تا چند دقیقه طول می‌کشد).
  • مدیریت خطا — همیشه وضعیت failed را چک کنید و فیلد error را مناسب مدیریت کنید.
  • تصاویر مرجع — هنگام استفاده از تصویر مرجع، مطمئن شوید باکیفیت و مرتبط با خروجی مطلوب است.

عیب‌یابی

درخواست زمان زیادی طول می‌کشد؟

  • تولید ویدئو بسته به مدل، رزولوشن و بار سرور می‌تواند چند دقیقه طول بکشد
  • تایم‌اوت سمت کلاینت را به‌اندازهٔ کافی افزایش دهید

تولید شکست خورد؟

  • پیام خطا و فیلد error را در پاسخ چک کنید
  • مطمئن شوید مدل تولید ویدئو را پشتیبانی می‌کند — در GET /v1/models، architecture.output_modalities شامل video باشد
  • از مناسب‌بودن prompt و رعایت راهنمای مدل مطمئن شوید
  • چک کنید تصاویر مرجع در دسترس و با فرمت پشتیبانی‌شده باشند

مدل پیدا نمی‌شود؟

  • با GET /v1/models یا صفحهٔ مدل‌ها مدل‌های موجود را پیدا کنید
  • درستی نام مدل را چک کنید (مثل ~google/veo-3.1)

سؤالات متداول

تولید ویدئو چقدر طول می‌کشد؟

بسته به مدل و رزولوشن معمولاً بین ۳۰ ثانیه تا چند دقیقه. آشا نتیجهٔ نهایی را به‌صورت هم‌زمان (server-side) پس از تکمیل برمی‌گرداند.

آیا تولید ویدئو ناهمگام است؟

بله؛ آشا درخواست را به‌صورت ناهمگام نزد ارائه‌دهنده ثبت می‌کند، تا رسیدن به وضعیت نهایی پیگیری می‌کند و سپس همان پاسخ، کارِ تکمیل‌شده را با آدرس‌های دانلود (unsigned_urls) برمی‌گرداند.

می‌توانم با تصویر شروع کنم؟

بله؛ با frame_images برای قاب اول/آخر (تصویر به ویدئو) و با input_references برای هدایت سبک (مرجع به ویدئو).

هزینهٔ ویدئو چگونه حساب می‌شود؟

بر اساس مدت (ثانیه) و رزولوشن؛ فیلد pricing_skus هر مدل و هزینهٔ نهایی در usage.cost به تومان گزارش می‌شود.

آیا صدا هم تولید می‌شود؟

برای مدل‌های پشتیبانی‌کننده بله، به‌صورت پیش‌فرض با generate_audio: true؛ می‌توانید آن را خاموش کنید.