视频生成
大约 7 分钟
视频生成
接入结论
- 创建任务:
POST https://www.yuzhixiaolongxia.com/v1/videos - 查询任务:
GET https://www.yuzhixiaolongxia.com/v1/videos/{id} - 下载内容:
GET https://www.yuzhixiaolongxia.com/v1/videos/{id}/content - 认证:
Authorization: Bearer <你的 API 令牌> - 令牌分组:
sora-veo-grok-video - 接口类型:异步任务
- 本页范围:Sora-2、Veo 3.1、Grok-Video 等通用视频模型
Seedance 看专文
Seedance 2.0 视频生成有完整专文,包含 6 个模型选择、metadata.reference_images/videos/audios、本地素材上传等:
- Seedance 2.0 视频生成教程(业务视角)
- Seedance 2.0 程序接入文档(工程师视角)
Seedance 的端点是 /v1/video/generations(带 s),本页讲的是通用 /v1/videos,两者不通用。
模型速查
| 系列 | 模型 ID | 当前售价 | 特点 |
|---|---|---|---|
| Sora-2 | sora-2 / sora-2-pro | 0.308 元/秒 | 写实短视频,Pro 版支持更高规格尺寸 |
| Veo 3.1 | veo3.1-fast / veo3.1-pro | 0.308 元/秒 / 1.538 元/秒 | Fast 适合试稿,Pro 适合质量优先 |
| Grok-Video | grok-video | 0.431 元/秒 | 社媒短视频、节奏自由的视频 |
实际可用 ID 和价格以 模型广场 为准。程序里可以保留白名单校验,但不要把旧价格表写死成唯一事实。
第一步:创建任务
请求体(JSON)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
model | string | 是 | 视频模型 ID |
prompt | string | 是 | 视频描述 |
seconds | string/int | 否 | 视频秒数,建议显式传,方便对账 |
duration | integer | 否 | 兼容字段;新接入优先用 seconds |
size | string | 否 | 尺寸/规格,不同模型支持范围不同 |
image | string | 否 | 单张参考图 URL/base64 |
images | string[] | 否 | 多张参考图 URL/base64 |
input_reference | file/string | 否 | multipart 参考图文件,或兼容参考图字段 |
TypeScript
const create = await fetch("https://www.yuzhixiaolongxia.com/v1/videos", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.YZX_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "sora-2",
prompt: "A cinematic shot of a cat walking through a sunlit garden, smooth motion",
seconds: "8",
size: "1280x720",
}),
});
if (!create.ok) {
throw new Error(`create video failed: ${create.status} ${await create.text()}`);
}
const task = await create.json();
const taskId = task.id ?? task.task_id;
if (!taskId) {
throw new Error(`missing task id: ${JSON.stringify(task)}`);
}
console.log("task id:", taskId);Python
import os
import requests
API_BASE = "https://www.yuzhixiaolongxia.com/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['YZX_API_KEY']}",
"Content-Type": "application/json",
}
resp = requests.post(
f"{API_BASE}/videos",
headers=HEADERS,
json={
"model": "sora-2",
"prompt": "A cinematic shot of a cat walking through a sunlit garden",
"seconds": "8",
"size": "1280x720",
},
timeout=60,
)
resp.raise_for_status()
task = resp.json()
task_id = task.get("id") or task.get("task_id")
if not task_id:
raise RuntimeError(f"missing task id: {task}")
print("task id:", task_id)提交响应
{
"id": "video-task-abc123",
"task_id": "video-task-abc123",
"object": "video",
"model": "sora-2",
"status": "queued",
"created_at": 1714000000,
"seconds": "8"
}提交响应里必须保存任务 ID。提交成功不代表视频已完成,必须进入下一步轮询。
第二步:轮询状态
GET /v1/videos/{id}
Authorization: Bearer <你的令牌>状态机
| status | 本地动作 |
|---|---|
queued / pending | 排队中,保持轮询 |
in_progress / processing | 生成中,保持轮询 |
completed / succeeded / success | 优先读取响应里的视频地址,再调用下载或入库 |
unknown | 有视频地址按完成处理;没有继续轮询 |
failed / cancelled | 停止轮询,保存错误信息,提示用户重试或换模型 |
轮询建议
- 创建后等 8-10 秒再首次查询,太频繁没有意义。
- 后续 8-15 秒查一次。
- 总等待 3-5 分钟;高规格任务可以放宽到 8-10 分钟。
- 不要因为一次
queued就重复创建新任务。 failed或task timeout按失败处理,等待预扣回退后再重试。
完成响应
{
"id": "video-task-abc123",
"status": "completed",
"metadata": {
"url": "https://www.yuzhixiaolongxia.com/v1/videos/video-task-abc123/content"
}
}视频地址可能出现在多个字段,建议按以下优先级取:
metadata.urldata[0].urldata.result_urlresult_urlurl/video_urloutput_url/download_url
如果都没有,再走 GET /v1/videos/{id}/content 兜底下载。
第三步:下载视频
curl -L "https://www.yuzhixiaolongxia.com/v1/videos/video-task-abc123/content" \
-H "Authorization: Bearer $YZX_API_KEY" \
--output result.mp4下载链路同样需要带 Authorization。直接把受保护链接贴到浏览器里,可能会因为缺 Header 而 401/403。
参数校验建议
生产接入不要把所有字段原样透传给用户输入,至少做下面这些校验:
const VIDEO_MODELS = new Set([
"sora-2",
"sora-2-pro",
"veo3.1-fast",
"veo3.1-pro",
"grok-video",
]);
function validateVideoRequest(input: {
model: string;
prompt: string;
seconds?: string | number;
size?: string;
images?: string[];
image?: string;
input_reference?: unknown;
}) {
if (!VIDEO_MODELS.has(input.model)) throw new Error("unsupported video model");
if (!input.prompt?.trim()) throw new Error("prompt is required");
const seconds = Number(input.seconds ?? 8);
if (!Number.isFinite(seconds) || seconds <= 0) throw new Error("seconds must be positive");
if (input.model.startsWith("sora-2") && ![4, 8, 12].includes(seconds)) {
throw new Error("sora-2 supports seconds 4, 8, or 12");
}
const highSpec = input.size === "1792x1024" || input.size === "1024x1792";
if (input.model === "sora-2" && highSpec) {
throw new Error("high spec size requires sora-2-pro");
}
return { ...input, seconds: String(seconds) };
}Python 完整闭环示例
import os
import time
import requests
API_BASE = "https://www.yuzhixiaolongxia.com/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['YZX_API_KEY']}",
"Content-Type": "application/json",
}
def create_task() -> str:
resp = requests.post(
f"{API_BASE}/videos",
headers=HEADERS,
json={
"model": "sora-2",
"prompt": "A beautiful sunset over the ocean, cinematic",
"seconds": "8",
"size": "1280x720",
},
timeout=60,
)
resp.raise_for_status()
body = resp.json()
task_id = body.get("id") or body.get("task_id")
if not task_id:
raise RuntimeError(f"missing task id: {body}")
return task_id
def wait_done(task_id: str, max_attempts: int = 30) -> dict:
for _ in range(max_attempts):
time.sleep(10)
r = requests.get(f"{API_BASE}/videos/{task_id}", headers=HEADERS, timeout=30)
r.raise_for_status()
body = r.json()
status = body.get("status")
if status in ("completed", "succeeded", "success"):
return body
if status in ("failed", "cancelled"):
raise RuntimeError(f"task failed: {body}")
raise TimeoutError(f"task {task_id} timeout")
def find_video_url(body: dict) -> str | None:
metadata = body.get("metadata") or {}
if metadata.get("url"):
return metadata["url"]
data = body.get("data") or []
if data and isinstance(data[0], dict) and data[0].get("url"):
return data[0]["url"]
for key in ("result_url", "url", "video_url", "output_url", "download_url"):
if body.get(key):
return body[key]
return None
def download(task_id: str, body: dict, out: str) -> None:
url = find_video_url(body) or f"{API_BASE}/videos/{task_id}/content"
r = requests.get(url, headers=HEADERS, stream=True, timeout=120)
r.raise_for_status()
with open(out, "wb") as f:
for chunk in r.iter_content(chunk_size=1 << 20):
f.write(chunk)
task_id = create_task()
result = wait_done(task_id)
download(task_id, result, "result.mp4")
print("done")给 Codex / Claude Code 的接入提示词
请接入预制小龙虾通用视频生成 API。
要求:
1. API Base URL 使用 https://www.yuzhixiaolongxia.com/v1,API Key 从服务端环境变量 YZX_API_KEY 读取。
2. 创建任务用 POST /videos,查询用 GET /videos/{task_id},下载用 GET /videos/{task_id}/content。
3. 支持模型白名单:sora-2、sora-2-pro、veo3.1-fast、veo3.1-pro、grok-video。
4. 请求体必须包含 model 和 prompt;生产接入必须显式传 seconds。
5. 图生视频必须把图片放在 image、images 或 input_reference 字段,不要只写在 prompt 里。
6. Sora 系列 seconds 只允许 4/8/12;sora-2 不允许 1792x1024 或 1024x1792,高规格尺寸必须用 sora-2-pro。
7. 创建任务成功后保存 id 或 task_id,不要把提交响应当最终视频。
8. 每 8-15 秒轮询状态;completed/succeeded/success 才下载;failed/cancelled 停止并记录错误。
9. 下载时继续带 Authorization;优先用响应里的 metadata.url/data[0].url/result_url/url/video_url,拿不到再用 /videos/{task_id}/content。
10. 增加测试:创建成功、缺 task_id、queued 继续轮询、completed 下载、failed 停止、轮询超时、401、403、model 拼错、Sora 非法 seconds、sora-2 高规格尺寸拒绝、图生视频缺图片字段提示。测试清单
| 测试项 | 验收标准 |
|---|---|
| 创建任务 | POST /v1/videos 返回 200,响应中有 id 或 task_id |
| 轮询状态 | queued / in_progress 不重复创建任务,只继续轮询 |
| 完成任务 | completed 后能拿到视频 URL,或 /content 可下载 mp4 |
| 失败任务 | failed / cancelled 停止轮询,完整记录错误响应 |
| 超时任务 | 本地超时后标记为待人工处理,不静默丢任务 |
| 图生视频 | 真实发送 image、images 或 input_reference 字段 |
| 参数校验 | 不支持的模型、空 prompt、Sora 非法秒数、高规格尺寸错误能在本地拦截 |
| 安全 | API Key 只从环境变量读取,不进入前端包、日志正文或公开仓库 |
常见问题
一直 pending
视频队列偶尔较长,等 2-3 分钟再看;不要重复提交。高规格任务可以把总等待放宽到 8-10 分钟。
failed
- 提示词触发内容审核(暴力 / 露骨 / 真人肖像滥用等) -> 改写提示词
- 令牌不在视频分组 -> 看 令牌分组介绍
- 模型 ID 拼写错 -> 用模型广场复制正确 ID
task timeout-> 等退款后降低时长 / 尺寸或换快速模型重试
n8n HTTP 节点超时
把节点 Timeout 调到 300000 ms(5 分钟)以上,用 Wait 节点拆分轮询,不要让一个节点阻塞等最终视频。
下载链接 403
链接必须带 Authorization。直接在浏览器粘贴是打不开的,需要走脚本带 Header 下载。
