视频生成与任务产物
GlobalStore 视频接口是异步任务 API。客户只调用 YUNiversity API 的公开地址, 不接触 GlobalStore 的上游地址或凭证。
提交任务
接口:POST /v1/tasks/globalstore
POST https://api.yuniversity.cc/v1/tasks/globalstore
请求必须包含 model 和非空 prompt。下面是一个最小请求:
curl https://api.yuniversity.cc/v1/tasks/globalstore \
-H "Authorization: Bearer <YOUR_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"model": "sd_2.5_discount",
"prompt": "A cinematic product shot with natural lighting",
"duration": 5,
"aspect_ratio": "16:9",
"resolution": "720p",
"generate_audio": true
}'
成功响应会给出 New API 的公开 task_id。保存这个 ID;上游任务 ID 和
GlobalStore API Key 不会暴露给客户。
查询状态和产物
公开接口:
GET /v1/tasks/{task_id}GET /v1/tasks/{task_id}/artifactsGET /v1/tasks/{task_id}/artifacts/{artifact_key}/content
使用公开 task_id 查询任务:
GET https://api.yuniversity.cc/v1/tasks/{task_id}
GET https://api.yuniversity.cc/v1/tasks/{task_id}/artifacts
GET https://api.yuniversity.cc/v1/tasks/{task_id}/artifacts/{artifact_key}/content
任务状态包括:
| 状态 | 含义 | 客户端动作 |
|---|---|---|
QUEUED |
已提交,等待处理。 | 继续轮询。 |
IN_PROGRESS |
正在生成;可能带 progress,例如 42%。 |
继续轮询。 |
SUCCESS |
已完成。 | 获取 artifacts,找到 key 为 video 的产物并下载。 |
FAILURE |
任务失败;响应可能包含 fail_reason。 |
立即停止轮询,记录原因;修正请求后再提交。 |
建议每 5 秒查询一次,单个任务最多等待 30 分钟。不要高频轮询,也不要在
提交响应未知时盲目重复提交:服务端可能已经接受任务。成功后先调用
GET /v1/tasks/{task_id}/artifacts,响应中的视频产物通常具有:
key: "video"、type: "video"、mime_type: "video/mp4"。再请求
GET /v1/tasks/{task_id}/artifacts/{artifact_key}/content;其中
artifact_key 使用 video。该内容请求会代理到临时视频 URL,通常不需要
GlobalStore 凭证。结果 URL 可能过期,下载应尽快完成。
参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
model |
string | 是 | 下方能力矩阵中的模型 ID。 |
prompt |
string | 是 | 非空的视频描述。 |
duration |
integer | 视模型而定 | 秒数;除 Grok 外当前模型都要求提供,范围见矩阵。 |
aspect_ratio |
string | 否 | 必须是所选模型允许的比例。 |
resolution |
string | 否 | 必须是所选模型允许的分辨率。 |
seed |
integer 或整数字符串 | 否 | 仅支持 seed 的模型接受整数。 |
reference_images |
URL 数组 | 否 | 每项必须是 HTTP(S) URL,数量见矩阵。 |
reference_videos |
URL 数组 | 否 | 仅支持视频参考的模型接受,数量见矩阵。 |
reference_audios |
URL 数组 | 否 | 仅支持音频参考的模型接受,数量见矩阵。 |
first_image |
URL 或单元素数组 | 否 | 仅支持首帧的模型接受。 |
last_image |
URL 或单元素数组 | 否 | 仅支持尾帧的模型接受。 |
generate_audio |
boolean | 否 | 可写布尔值;支持的模型才可使用。 |
未知字段会被拒绝。引用资源必须是可访问的 HTTP(S) URL;file://、本地路径、
多元素的首/尾帧数组和超出上限的引用都会返回错误。generate_audio 也接受
字符串 "true" 或 "false",但建议始终发送 JSON 布尔值。
11 个模型能力矩阵
下表来自 globalstore 插件的模型配置;不要根据模型名称推断未列出的能力。
“参考上限”依次为图片 / 视频 / 音频;“—”表示不支持。
| 模型 | 时长(秒) | 比例 | 分辨率(默认) | 参考上限 | 首帧 / 尾帧 | 音频(默认) | seed |
|---|---|---|---|---|---|---|---|
sd_2.5_discount |
5–30(必填,默认 5) | 16:9, 9:16, 1:1, 4:3, 3:4, 21:9, adaptive |
480p, 720p, 1080p(720p) |
30 / 10 / 10 | — / — | 支持(开) | 支持 |
sd_2.5_special |
5–30(必填,默认 5) | 同上 | 720p, 1080p, 2k, 4k(720p) |
30 / 10 / 10 | 支持 / 支持 | 支持(开) | 支持 |
seedance_2.0_nsfw_c1 |
5–15(必填,默认 5) | 16:9, 9:16, 4:3, 3:4, 1:1 |
480p, 720p, 1080p, 4k(720p) |
9 / 3 / 3 | 支持 / 支持 | 支持(关) | 支持 |
sd_2.0_discount |
5–15(必填,默认 5) | SD 比例 | 480p, 720p, 1080p, 4k(720p) |
9 / 3 / 3 | — / — | 支持(开) | 支持 |
sd_2.0_fast_discount |
5–15(必填,默认 5) | SD 比例 | 480p, 720p(720p) |
9 / 3 / 3 | — / — | 支持(开) | 支持 |
sd_2.0_mini_discount |
5–15(必填,默认 5) | SD 比例 | 480p, 720p(720p) |
9 / 3 / 3 | — / — | 支持(开) | 支持 |
sd_2.0_fast_special |
4–15(必填,默认 5) | SD 比例 | 720p(720p) |
9 / 3 / 3 | 支持 / 支持 | 支持(开) | 支持 |
sd_2.0_mini_special |
4–15(必填,默认 5) | SD 比例 | 720p(720p) |
9 / 3 / 3 | 支持 / 支持 | 支持(开) | 支持 |
sd_2.0_special |
5–15(必填,默认 5) | SD 比例 | 720p, 1080p, 2k, 4k(720p) |
9 / 3 / 3 | 支持 / 支持 | 支持(开) | 支持 |
grok_video |
1–15(可省略,默认 8) | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 |
480p, 720p(720p) |
7 / — / — | — / — | 不支持 | 不支持 |
grok_1.5 |
1–15(可省略,默认 8) | Grok 比例 | 480p, 720p(720p) |
7 / — / — | 支持 / — | 不支持 | 不支持 |
“SD 比例”表示 16:9、9:16、1:1、4:3、3:4、21:9、
adaptive。grok_1.5 的首帧使用 first_image;其他首尾帧能力必须
严格按表选择。未列出的组合会在提交前被拒绝。
Python 示例
示例使用 Python 标准库,每 5 秒轮询,30 分钟截止;运行前请设置
YUNIVERSITY_API_KEY。
import json
import os
import time
from urllib.request import Request, urlopen
BASE_URL = "https://api.yuniversity.cc"
API_KEY = os.environ["YUNIVERSITY_API_KEY"]
def request_json(method, path, body=None):
data = None if body is None else json.dumps(body).encode("utf-8")
headers = {"Authorization": f"Bearer {API_KEY}"}
if body is not None:
headers["Content-Type"] = "application/json"
request = Request(BASE_URL + path, data=data, headers=headers, method=method)
with urlopen(request, timeout=30) as response:
return json.load(response)
created = request_json(
"POST",
"/v1/tasks/globalstore",
{
"model": "sd_2.5_discount",
"prompt": "A cinematic product shot with natural lighting",
"duration": 5,
"resolution": "720p",
},
)
task_id = created["task_id"]
deadline = time.monotonic() + 30 * 60
while time.monotonic() < deadline:
task = request_json("GET", f"/v1/tasks/{task_id}")
status = task.get("status")
if status == "SUCCESS":
artifacts = request_json("GET", f"/v1/tasks/{task_id}/artifacts")
print(json.dumps(artifacts, ensure_ascii=False, indent=2))
break
if status == "FAILURE":
raise RuntimeError(task.get("fail_reason") or "video task failed")
time.sleep(5)
else:
raise TimeoutError("video task did not finish within 30 minutes")
Node.js 示例
Node.js 18+ 自带 fetch,示例同样每 5 秒轮询,并在 FAILURE 或 30 分钟
截止时停止。成功时打印最终产物 JSON。
const baseUrl = "https://api.yuniversity.cc";
const apiKey = process.env.YUNIVERSITY_API_KEY;
if (!apiKey) throw new Error("YUNIVERSITY_API_KEY is required");
async function requestJSON(method, path, body) {
const response = await fetch(baseUrl + path, {
method,
signal: AbortSignal.timeout(30000),
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: body === undefined ? undefined : JSON.stringify(body),
});
if (!response.ok) {
throw new Error(`${method} ${path} failed: ${response.status} ${await response.text()}`);
}
return response.json();
}
const created = await requestJSON("POST", "/v1/tasks/globalstore", {
model: "sd_2.5_discount",
prompt: "A cinematic product shot with natural lighting",
duration: 5,
resolution: "720p",
});
const taskId = created.task_id;
const deadline = Date.now() + 30 * 60 * 1000;
let completed = false;
while (Date.now() < deadline) {
const task = await requestJSON("GET", `/v1/tasks/${taskId}`);
if (task.status === "SUCCESS") {
const artifacts = await requestJSON("GET", `/v1/tasks/${taskId}/artifacts`);
console.log(JSON.stringify(artifacts, null, 2));
completed = true;
break;
}
if (task.status === "FAILURE") {
throw new Error(task.fail_reason || "video task failed");
}
await new Promise((resolve) => setTimeout(resolve, 5000));
}
if (!completed) throw new Error("video task did not finish within 30 minutes");