跳至內容
English
影片生成

影片生成概覽 ​

提交影片任務、查詢進度並下載成品。

本頁使用已實作的 OpenAI 相容影片路由,示範 Sora 類型通道的提交、輪詢與下載。先在帳戶中選擇支援此路由的影片模型;其他通道的參數與回包可能不同。

MethodPathPurpose
POST/v1/videosSubmit
GET/v1/videos/{id}Poll status
GET/v1/videos/{id}/contentDownload

多語言 API 呼叫 ​

執行方式見多語言範例說明。先設定 API_KEY,並替換模型、檔案網址及 ID 占位值;四種方式會顯示相同請求的原始回應。

這組範例只提交任務,成功回應中的 id 需保留供後續查詢。

bash
curl --fail-with-body --silent --show-error --max-time 180 \
  --request POST \
  --url "https://api.tokatlas.ai/v1/videos" \
  --header "Authorization: Bearer $API_KEY" \
  --form model=YOUR_ENABLED_SORA_MODEL_ID \
  --form 'prompt=A ceramic cup on a table, steam rising, slow camera movement' \
  --form seconds=4 \
  --form size=720x1280
python
import os
import requests

headers = {
    'Authorization': 'Bearer ' + os.environ["API_KEY"],
}
files = {'model': (None, 'YOUR_ENABLED_SORA_MODEL_ID'),
 'prompt': (None, 'A ceramic cup on a table, steam rising, slow camera movement'),
 'seconds': (None, '4'),
 'size': (None, '720x1280')}
response = requests.request(
    'POST', 'https://api.tokatlas.ai/v1/videos', headers=headers,
    files=files,
    timeout=180,
)
response.raise_for_status()
print(response.text)
js
if (!process.env.API_KEY) throw new Error("Set API_KEY first.");
const form = new FormData();
form.append("model", "YOUR_ENABLED_SORA_MODEL_ID");
form.append("prompt", "A ceramic cup on a table, steam rising, slow camera movement");
form.append("seconds", "4");
form.append("size", "720x1280");
const response = await fetch("https://api.tokatlas.ai/v1/videos", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.API_KEY,
  },
  body: form,
  signal: AbortSignal.timeout(180_000),
});
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
console.log(await response.text());
java
import java.net.URI;
import java.net.http.*;
import java.time.Duration;

public class Example {
    public static void main(String[] args) throws Exception {
        String apiKey = System.getenv("API_KEY");
        if (apiKey == null || apiKey.isBlank()) {
            throw new IllegalArgumentException("Set API_KEY first.");
        }
        String boundary = "TokatlasExampleBoundary";
        String payload =
            "--TokatlasExampleBoundary\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\nYOUR_ENABLED_SORA_MODEL_ID\r\n" +
            "--TokatlasExampleBoundary\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nA ceramic cup on a table, steam rising, slow camera movement\r\n" +
            "--TokatlasExampleBoundary\r\nContent-Disposition: form-data; name=\"seconds\"\r\n\r\n4\r\n" +
            "--TokatlasExampleBoundary\r\nContent-Disposition: form-data; name=\"size\"\r\n\r\n720x1280\r\n" +
            "--TokatlasExampleBoundary--\r\n";
        HttpClient client = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(30)).build();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.tokatlas.ai/v1/videos"))
            .timeout(Duration.ofSeconds(180))
            .header("Authorization", "Bearer " + apiKey)
            .header("Content-Type", "multipart/form-data; boundary=" + boundary)
            .method("POST", HttpRequest.BodyPublishers.ofString(payload))
            .build();
        HttpResponse<String> response = client.send(
            request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() < 200 || response.statusCode() >= 300) {
            throw new IllegalStateException("HTTP " + response.statusCode() + ": "
                + response.body());
        }
        System.out.println(response.body());
    }
}

查詢任務 ​

將 YOUR_TASK_ID 替換為提交回應的 id。請間隔查詢,直到 status 為 completed 或 failed;不要重複提交新任務。

bash
curl --fail-with-body --silent --show-error --max-time 180 \
  --request GET \
  --url "https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID" \
  --header "Authorization: Bearer $API_KEY"
python
import os
import requests

headers = {
    'Authorization': 'Bearer ' + os.environ["API_KEY"],
}
response = requests.request(
    'GET', 'https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID', headers=headers,
    timeout=180,
)
response.raise_for_status()
print(response.text)
js
if (!process.env.API_KEY) throw new Error("Set API_KEY first.");
const response = await fetch("https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID", {
  method: "GET",
  headers: {
    "Authorization": "Bearer " + process.env.API_KEY,
  },
  signal: AbortSignal.timeout(180_000),
});
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
console.log(await response.text());
java
import java.net.URI;
import java.net.http.*;
import java.time.Duration;

public class Example {
    public static void main(String[] args) throws Exception {
        String apiKey = System.getenv("API_KEY");
        if (apiKey == null || apiKey.isBlank()) {
            throw new IllegalArgumentException("Set API_KEY first.");
        }
        HttpClient client = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(30)).build();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID"))
            .timeout(Duration.ofSeconds(180))
            .header("Authorization", "Bearer " + apiKey)
            .method("GET", HttpRequest.BodyPublishers.noBody())
            .build();
        HttpResponse<String> response = client.send(
            request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() < 200 || response.statusCode() >= 300) {
            throw new IllegalStateException("HTTP " + response.statusCode() + ": "
                + response.body());
        }
        System.out.println(response.body());
    }
}

下載成品 ​

僅在狀態為 completed 時執行。使用同一任務 ID,成功後會保存 video.mp4。

bash
curl --fail-with-body --silent --show-error --max-time 180 \
  --request GET \
  --url "https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID/content" \
  --header "Authorization: Bearer $API_KEY" \
  --output video.mp4
python
import os
from pathlib import Path
import requests

headers = {
    'Authorization': 'Bearer ' + os.environ["API_KEY"],
}
response = requests.request(
    'GET', 'https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID/content', headers=headers,
    timeout=180,
)
response.raise_for_status()
Path('video.mp4').write_bytes(response.content)
print("Saved video.mp4")
js
import { writeFile } from "node:fs/promises";

if (!process.env.API_KEY) throw new Error("Set API_KEY first.");
const response = await fetch("https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID/content", {
  method: "GET",
  headers: {
    "Authorization": "Bearer " + process.env.API_KEY,
  },
  signal: AbortSignal.timeout(180_000),
});
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
await writeFile("video.mp4", Buffer.from(await response.arrayBuffer()));
console.log("Saved video.mp4");
java
import java.net.URI;
import java.net.http.*;
import java.time.Duration;
import java.nio.file.*;

public class Example {
    public static void main(String[] args) throws Exception {
        String apiKey = System.getenv("API_KEY");
        if (apiKey == null || apiKey.isBlank()) {
            throw new IllegalArgumentException("Set API_KEY first.");
        }
        HttpClient client = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(30)).build();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID/content"))
            .timeout(Duration.ofSeconds(180))
            .header("Authorization", "Bearer " + apiKey)
            .method("GET", HttpRequest.BodyPublishers.noBody())
            .build();
        HttpResponse<byte[]> response = client.send(
            request, HttpResponse.BodyHandlers.ofByteArray());
        if (response.statusCode() < 200 || response.statusCode() >= 300) {
            throw new IllegalStateException("HTTP " + response.statusCode() + ": "
                + new String(response.body(), java.nio.charset.StandardCharsets.UTF_8));
        }
        Files.write(Path.of("video.mp4"), response.body());
        System.out.println("Saved video.mp4");
    }
}

1. 準備 ​

安裝 Python 3,設定金鑰與已開通的 Sora 相容模型 ID。範例使用 4 秒、720×1280;模型必須支援這組設定。

bash
python3 -m pip install requests
export API_KEY='YOUR_TOKATLAS_API_KEY'
export VIDEO_MODEL_ID='YOUR_ENABLED_SORA_MODEL_ID'

2. 提交、等待與下載 ​

儲存為 generate_video.py,執行 python3 generate_video.py。腳本立即保存 video-task.txt;再次執行會接續查詢相同任務。要建立新任務,先將舊 ID 檔改名備份。請勿在尚未確認前重複提交。

python
import os
import time
from pathlib import Path
import requests

base = os.environ.get("API_BASE_URL", "https://api.tokatlas.ai").rstrip("/")
headers = {"Authorization": f"Bearer {os.environ['API_KEY']}"}
task_file = Path("video-task.txt")
if task_file.exists():
    task_id = task_file.read_text().strip()
else:
    response = requests.post(
        base + "/v1/videos", headers=headers,
        files={"model": (None, os.environ["VIDEO_MODEL_ID"]),
               "prompt": (None, "A ceramic cup on a table, steam rising, slow camera movement"),
               "seconds": (None, "4"), "size": (None, "720x1280")},
        timeout=120,
    )
    response.raise_for_status()
    body = response.json()
    task_id = body["id"]
    task_file.write_text(task_id)
print("Task:", task_id)
for attempt in range(120):
    response = requests.get(base + f"/v1/videos/{task_id}", headers=headers, timeout=60)
    response.raise_for_status()
    task = response.json()
    status = task.get("status")
    print(status, task.get("progress", ""))
    if status == "completed":
        with requests.get(base + f"/v1/videos/{task_id}/content",
                          headers=headers, stream=True, timeout=120) as download:
            download.raise_for_status()
            with Path("video.mp4").open("wb") as output:
                for chunk in download.iter_content(1024 * 1024):
                    output.write(chunk)
        print("Saved video.mp4")
        break
    if status == "failed":
        raise RuntimeError(task.get("error") or task)
    if status not in {"queued", "in_progress"}:
        raise RuntimeError(f"Unexpected task response: {task}")
    time.sleep(5)
else:
    raise TimeoutError("Task still pending; run again to resume polling the saved ID")

3. 判斷結果 ​

queued 與 in_progress 表示尚未完成;completed 後下載為 video.mp4,failed 時查看 error。達到輪詢次數上限只代表本機停止等待,不表示任務失敗;稍後重新執行可繼續查詢。

排錯 ​

  • 400:核對模型、秒數及尺寸;不要把圖像的 1:1 直接填入影片 size。
  • 401/403:確認金鑰及影片模型權限。
  • 查詢不到任務:使用提交時傳回的公開 id,並使用同一帳戶的金鑰。
  • 下載失敗:先確認狀態為 completed;保留任務 ID 與錯誤本文以便排查。
  • 網路超時或 5xx:若 ID 已保存,重新查詢即可;提交階段沒有收到 ID 時,先查看請求記錄再決定是否重送。

圖像輸入見檔案分析;聊天整合見工具呼叫。