跳转至

视频生成与任务产物

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}/artifacts
  • GET /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,找到 keyvideo 的产物并下载。
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, 1080p720p 30 / 10 / 10 — / — 支持(开) 支持
sd_2.5_special 5–30(必填,默认 5) 同上 720p, 1080p, 2k, 4k720p 30 / 10 / 10 支持 / 支持 支持(开) 支持
seedance_2.0_nsfw_c1 5–15(必填,默认 5) 16:9, 9:16, 4:3, 3:4, 1:1 480p, 720p, 1080p, 4k720p 9 / 3 / 3 支持 / 支持 支持(关) 支持
sd_2.0_discount 5–15(必填,默认 5) SD 比例 480p, 720p, 1080p, 4k720p 9 / 3 / 3 — / — 支持(开) 支持
sd_2.0_fast_discount 5–15(必填,默认 5) SD 比例 480p, 720p720p 9 / 3 / 3 — / — 支持(开) 支持
sd_2.0_mini_discount 5–15(必填,默认 5) SD 比例 480p, 720p720p 9 / 3 / 3 — / — 支持(开) 支持
sd_2.0_fast_special 4–15(必填,默认 5) SD 比例 720p720p 9 / 3 / 3 支持 / 支持 支持(开) 支持
sd_2.0_mini_special 4–15(必填,默认 5) SD 比例 720p720p 9 / 3 / 3 支持 / 支持 支持(开) 支持
sd_2.0_special 5–15(必填,默认 5) SD 比例 720p, 1080p, 2k, 4k720p 9 / 3 / 3 支持 / 支持 支持(开) 支持
grok_video 1–15(可省略,默认 8) 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 480p, 720p720p 7 / — / — — / — 不支持 不支持
grok_1.5 1–15(可省略,默认 8) Grok 比例 480p, 720p720p 7 / — / — 支持 / — 不支持 不支持

“SD 比例”表示 16:99:161:14:33:421:9adaptivegrok_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");