OpenAI Videos API 视频生成入门:创建任务、轮询与结果保存
调用视频生成接口时,创建请求只是第一步。把任务 ID 保存下来,正确处理 queued、in_progress、completed 与 failed 状态,并在完成后下载二进制结果,才能形成可上线的工作流。
开始前准备
调用视频生成接口却不知道任务是否已经完成,常见后果是刚创建任务就尝试下载、页面一直等待、失败任务被误当成成片,或者文件下载后无法追溯对应的提示词和参数。OpenAI Python SDK 中的 Videos 资源把流程拆成创建、查询、轮询和下载四步。先把这四个环节的责任分开,接入内容工作流时才不会把一次生成当成一次同步接口调用。
- 在服务端保存 API 密钥,不要把密钥放进浏览器端代码或公开配置。
- 准备一个可持久化的任务记录,至少保存内部任务号、返回的
video_id、提交时间、当前状态、提示词摘要、模型、时长、尺寸和输出文件位置。 - 创建输出目录,并约定不会互相覆盖的命名方式,例如按
video_id保存视频、缩略图和精灵图。 - 明确成片用途和画幅方向。横向内容与竖向内容应在请求前选择对应尺寸,而不是下载后才靠裁切补救。
- 将生成、轮询和下载拆成独立动作。网页请求可以只负责创建并返回内部任务号,后台工作进程负责后续状态查询和文件保存。
资料包确认,创建视频任务时 prompt 是必填文本描述;可选参数包括 input_reference、model、seconds 与 size。其中 input_reference 可传入参考素材上传结果或参考对象,用于引导生成。可选模型为 sora-2 与 sora-2-pro,未指定时默认使用 sora-2。时长可用值是 4、8、12 秒,默认 4 秒;尺寸可用值是 720x1280、1280x720、1024x1792、1792x1024,默认 720x1280。
| 参数 | 作用 | 接入时的处理方式 |
|---|---|---|
prompt | 描述要生成的视频画面 | 作为必填字段保存原文或可追溯版本。 |
model | 选择视频生成模型 | 固定为当前业务允许的值,避免由前端任意传入。 |
seconds | 设置片段时长 | 仅使用 4、8、12 之一,并把值写入任务记录。 |
size | 设置输出画幅 | 按横向或竖向发布位选择已支持的尺寸。 |
input_reference | 提供可选参考素材 | 没有参考输入时不要传空占位值,有输入时保留其来源和用途记录。 |
分步操作
创建调用提交到 /videos 后,返回的是一个视频任务对象,不等于最终文件已经可下载。工作流应立即取出任务 ID,写入自己的任务表,再由后台按状态推进。SDK 支持直接查询指定任务,也提供 poll 与 create_and_poll 辅助方法;面向网站或批量队列时,保留自己的任务状态记录会更便于重试、展示进度和排查。
- 创建任务:调用
client.videos.create,提交提示词与经过校验的模型、时长、尺寸参数。创建请求使用 multipart/form-data,并采用 Bearer 鉴权。 - 立即记录 ID:从返回对象取出
id与初始status。内部记录的主键不要只依赖文件名,因为此时还没有成片文件。 - 按终态判断:
queued和in_progress表示任务仍在等待或处理中;completed与failed是当前 SDK 轮询逻辑识别的终态。只有completed可以进入下载步骤。 - 执行轮询:每次用
retrieve(video_id)获取最新元数据。仍在排队或处理中就等待后继续查询;状态为失败时停止下载,并保留任务 ID、请求参数和返回信息供排查。 - 下载内容:完成后调用
download_content(video_id)。默认返回视频内容,也可以指定thumbnail或spritesheet下载派生预览素材。 - 落盘并更新记录:视频二进制文件写入目标目录后,再将内部任务状态标为已保存,同时记录文件路径和保存时间。不要在文件写入前把任务标记为成功。
from openai import OpenAI
import time
from pathlib import Path
client = OpenAI()
created = client.videos.create(
prompt="雨后城市街道的横向镜头,路面倒映霓虹灯,镜头缓慢向前移动",
model="sora-2",
seconds=4,
size="1280x720",
)
video_id = created.id
print(video_id, created.status)
while True:
video = client.videos.retrieve(video_id)
if video.status in ("queued", "in_progress"):
time.sleep(1)
continue
if video.status == "completed":
break
if video.status == "failed":
raise RuntimeError(f"video job failed: {video_id}")
raise RuntimeError(f"unexpected status: {video.status}")
output_dir = Path("outputs")
output_dir.mkdir(exist_ok=True)
content = client.videos.download_content(video_id, variant="video")
content.write_to_file(output_dir / f"{video_id}.mp4")示例里的 1 秒是便于看清逻辑的保守间隔。SDK 内置 poll 在没有指定自定义间隔时,会优先读取响应中的 openai-poll-after-ms,没有该响应头时才回退到 1000 毫秒。因此,生产环境若直接使用 client.videos.poll 或 client.videos.create_and_poll,可以利用 SDK 的轮询提示;若自己实现轮询,也应避免高频无间隔查询。
可复制模板
下面的模板把画面描述、接口参数和内部任务记录分开。不要把业务字段直接原样透传到接口,也不要把接口返回对象当成唯一记录。方括号替换为真实内容,未使用参考素材时删除对应项。
生成目标:[用于社交平台竖版短片/横向内容配图/内部素材测试]
画面主体:[人物、产品、场景或物体]
主体动作:[一个清楚、可观察的动作]
场景与光线:[地点、时间、光线关系]
镜头:[景别和一种镜头运动,避免同时堆叠多个运动]
画面限制:[不需要出现的对象或画面元素]
接口参数:
model:[sora-2 或 sora-2-pro]
seconds:[4/8/12]
size:[720x1280/1280x720/1024x1792/1792x1024]
input_reference:[没有则省略;有则传入实际参考素材]
内部任务记录:
internal_job_id:[系统任务号]
video_id:[创建后写入]
status:[创建后写入,轮询时更新]
prompt_version:[提示词版本]
output_video:[完成下载后写入文件路径]
output_thumbnail:[需要时写入文件路径]
output_spritesheet:[需要时写入文件路径]下面是一份更贴近内容团队协作的请求配置。它不替代接口调用,而是用于先校验前端表单、队列消息或后台任务入参。将参数限制集中在这一层,能避免不支持的时长或尺寸进入队列后才失败。
{
"prompt": "清晨的咖啡店窗边,一杯热咖啡放在木桌中央,窗外有轻微雨滴,横向中景,镜头缓慢推近,画面干净,不出现文字、字母、数字、标识或水印",
"model": "sora-2",
"seconds": 4,
"size": "1280x720",
"input_reference": null
}要使用参考素材时,保留同一份提示词与参考输入的关联关系。后续若需要生成新版本、排查结果差异或清理资源,才能知道一个 video_id 对应的是哪一轮素材,而不是只剩一个无法解释的 MP4 文件。
翻车修正
创建成功后立刻下载失败:创建接口返回的是任务对象,先检查其状态。只有状态变为 completed 后才调用内容下载;queued 与 in_progress 都应继续轮询,不应被视为异常成片。
轮询一次就结束:不要把一次 retrieve 当成最终结论。轮询循环必须把等待状态继续送回查询,把 completed 和 failed 分开处理,并为未知状态保留显式报错,避免静默写入错误结果。
轮询过密导致流程不稳:不要使用无等待的死循环。SDK 的轮询辅助逻辑会读取服务端返回的建议等待时间,未提供时使用 1000 毫秒;自定义轮询时也应设置合理间隔,并把轮询工作放到后台,不占用用户的网页请求。
参数在提交阶段报错:逐项核对 model、seconds 和 size 是否属于当前资料确认的可用集合。创建视频时的时长不是任意整数,尺寸也不是只在提示词中写“横版”就能替代的画幅参数。
完成任务被覆盖:不要用固定的 output.mp4 保存所有结果。至少使用 video_id 或内部任务号组成文件名,并将提示词版本、模型、时长和尺寸写入任务记录。下载中断时,应保留临时文件与失败状态,确认写入完整后再发布为最终路径。
把失败任务当成已完成:failed 是终态,不是可下载成功态。遇到失败时,记录任务 ID、提交参数、最后状态和应用侧错误信息;需要重试时创建新任务并建立新的关联,不要伪造旧任务已经成功。
只保存视频没有预览素材:需要后台审核、素材库浏览或发布前确认时,可在视频完成后分别下载 thumbnail 和 spritesheet。它们属于同一任务的派生下载内容,应与原视频保存在同一条任务记录下。
完成前检查
prompt是否非空,model、seconds和size是否属于当前接口确认的可用值。- 使用参考素材时,
input_reference是否是实际可用的参考输入;未使用时是否已省略,而不是传入无效占位值。 - 创建任务后是否已经保存
video_id、初始状态、提交时间与完整参数记录。 - 轮询是否持续处理
queued与in_progress,并设置了等待间隔。 - 是否仅在
completed后下载内容,并在failed时停止下载流程。 - 下载时是否按用途选择
video、thumbnail或spritesheet,没有把预览素材误当作成片。 - 视频文件是否写入完成后再更新内部状态,文件路径是否包含可追溯的任务标识。
- 原视频、缩略图和精灵图是否关联到同一内部任务,便于审核、重试和清理。
- 页面请求是否只负责提交和查询,耗时轮询与文件保存是否移交给后台任务。
- 失败记录是否保留了任务 ID、状态、参数和应用侧日志,能够区分接口失败、保存失败与业务校验失败。
- 用于发布的视频是否经过人工检查,确认主体、画面连续性、尺寸和内容用途符合实际发布要求。
把视频生成接入工作流的关键,不是让一次调用返回文件,而是将任务 ID 当作贯穿全程的凭据:创建时记录它,处理中用它查询,完成后用它下载视频和预览素材,失败时也用它追溯问题。这样才能把 OpenAI Videos API 从一次性试验,变成可监控、可保存、可复查的内容生产环节。
下一步
把这篇的方法练一遍
提示词和步骤可以带到创作里直接试做一版。