GoodPut AI

视频生成

OpenAI 兼容 Videos API,异步任务式:提交任务 → 轮询状态 → 下载成片。覆盖文生视频、图生视频、参考生视频、首尾帧生视频四种模式。

视频生成是异步任务式的:提交后立即拿到任务 id,成片需要轮询到完成再下载。三个端点语义与 OpenAI 官方一致,模型库里所有视频模型共用同一套调用方式。

用途端点
提交生成任务POST /v1/videos
查询任务状态GET /v1/videos/{video_id}
下载生成结果GET /v1/videos/{video_id}/content

查询与下载不需要再传模型名:任务 id 里已经带了该任务归属的模型信息,一套代码可以对接所有视频模型。

完整流程

下面这段是所有模式共用的骨架 —— 换模式只需要改 create() 的入参,轮询与下载完全不变。

import time

from openai import OpenAI

client = OpenAI(
    base_url="https://api.goodputai.cn/v1",
    api_key="你的 GoodPut API Key",
)


def generate(**kwargs) -> str:
    """提交 → 轮询 → 下载,返回落盘路径。所有模式共用。"""
    video = client.videos.create(**kwargs)
    print(video.id, video.status)          # video_xxx queued

    deadline = time.time() + 15 * 60       # 整体超时,别无限轮询
    while video.status in ("queued", "in_progress"):
        if time.time() > deadline:
            raise TimeoutError(f"超时未完成:{video.id}")
        time.sleep(5)
        video = client.videos.retrieve(video.id)

    if video.status != "completed":
        raise RuntimeError(f"生成失败:{video.error}")

    path = f"{video.id[:16]}.mp4"
    client.videos.download_content(video.id).write_to_file(path)
    print("usage:", video.usage)           # 计费依据
    return path


# 最简:文生视频
generate(
    model="wan/wan3.0-video",
    prompt="海边日出,海浪拍打礁石,电影感运镜",
    size="1280x720",
    seconds="5",
)
# 1) 提交任务 —— 返回体里的 id 就是后续查询用的任务 id
curl https://api.goodputai.cn/v1/videos \
  -H "Authorization: Bearer $GOODPUT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "wan/wan3.0-video",
    "prompt": "海边日出,海浪拍打礁石",
    "size": "1280x720",
    "seconds": "5"
  }'

# 2) 轮询状态,直到 status 变成 completed 或 failed
curl https://api.goodputai.cn/v1/videos/{video_id} \
  -H "Authorization: Bearer $GOODPUT_API_KEY"

# 3) 下载成片(二进制流,直接落盘)
curl https://api.goodputai.cn/v1/videos/{video_id}/content \
  -H "Authorization: Bearer $GOODPUT_API_KEY" \
  -o output.mp4

四种生成模式

模式由「你给了什么输入」决定,不需要额外的模式参数。

模式怎么触发典型用途
文生视频只给 prompt从零生成
图生视频input_referencemedia.first_frame让一张图动起来
参考生视频media.reference_image / reference_video / reference_audio保持主体、风格或音色
首尾帧生视频media.first_frame + media.last_frame指定起止画面,中间由模型补

用官方 OpenAI SDK 传 media 要走 extra_body

media 不在 OpenAI 官方 videos.create() 的参数签名里(它只有 prompt / input_reference / model / seconds / size),直接写 media=[...] 会被 SDK 以 unexpected keyword argument 拒掉。SDK 的 extra_body 会把字段合并进请求体, 所以下面的写法是等价且可用的:

extra_body={"media": [{"type": "reference_image", "url": "..."}]}

用 curl / requests 等直接发 JSON 时,media 就是普通的顶层字段,不需要包一层。

不同模型走不同的路子,这一点最容易踩坑:

  • wan 系列按模式拆成不同模型 —— wan2.7-t2v 只做文生、wan2.7-i2v 只做图生、wan2.7-r2v 只做参考生。选错模型会 400wan3.0-video 是合一的,四种都支持。
  • minimax/h3 是一个模型做四种 —— 按你给的输入自动选,不用换模型名。
  • minimax/h3-fastminimax/h3 调用方式相同,差别见下文「参数」表:只开 1344x768 / 768x1344 两种画布、seconds 取 5~15,并支持 seed出片明显更快(5 秒片约 18 秒),适合要快速迭代的场景。

具体某个模型支持哪些模式,以模型库的详情页为准。

文生视频

generate(
    model="wan/wan3.0-video",     # 或 wan/wan2.7-t2v、wan/wan2.6-t2v
    prompt="霓虹街道,雨夜,第一人称视角向前行走",
    size="720x1280",              # 短边 720 → 720P 档;9:16 → 竖屏
    seconds="5",
    prompt_extend=False,          # ⚠️ 默认 true 会改写你的提示词
)
generate(
    model="minimax/h3",
    prompt="霓虹街道,雨夜,第一人称视角向前行走。无字幕,无文字。",
    size="768x1344",              # 9:16 竖屏,见下面的六种画布
    seconds=6,                    # 4~15 的整数
)

图生视频

generate(
    model="wan/wan2.7-i2v",       # 或 wan/wan3.0-video
    prompt="镜头缓缓拉远",
    # 链接 / base64 要放进 extra_body(原因见下方说明)
    extra_body={"input_reference": "https://example.com/first.jpg"},
    size="1280x720",              # 决定档位与单价;⚠️ 实际画幅跟随这张图
    seconds="5",
)

wan 的 input_reference 只接受公网 URL 或 data:image/...;base64,... 字符串,不接受文件上传

官方 OpenAI SDK 的 input_reference= 参数只收文件对象,直接传字符串会在本地报错。 所以链接或 base64 字符串要像上面这样放进 extra_body;用 curl / requests 直接发 JSON 时, input_reference 就是普通的顶层字段。

# 形式一:公网 URL 或 data: 内联 —— 放进 extra_body
generate(
    model="minimax/h3",
    prompt="镜头缓缓拉远,人物自然转身",
    extra_body={"input_reference": "https://example.com/first.jpg"},
    size="768x768",
    seconds=4,
)

# 形式二:直接上传本地文件(h3 支持,wan 不支持)
with open("first.jpg", "rb") as f:
    generate(
        model="minimax/h3",
        prompt="镜头缓缓拉远,人物自然转身",
        input_reference=f,
        size="768x768",
        seconds=4,
    )

参考生视频

用参考资产控制主体、风格或音色,而不是把它当作第一帧。

generate(
    model="wan/wan2.7-r2v",       # 或 wan/wan3.0-video
    prompt="同一个角色在雪地里行走",
    extra_body={
        "media": [
            {"type": "reference_image", "url": "https://example.com/character.jpg"},
        ]
    },
    size="1280x720",
    seconds="5",
)

wan2.7-r2v 没有 input_reference 参数,参考资产只能走 media

# h3 可以一次给多张参考图
generate(
    model="minimax/h3",
    prompt="这个房间里,一名女子从左侧走入并掀开箱笼",
    extra_body={
        "media": [
            {"type": "reference_image", "url": "https://example.com/room.jpg"},
            {"type": "reference_image", "url": "https://example.com/person.jpg"},
        ]
    },
    size="1344x768",
    seconds=4,
)

多张参考图之间没有具名绑定。 你可以塞进多张,但没有任何机制告诉模型"这张是某某角色" —— 跨镜头的一致性只能靠模型自己从参考图里保持,不是参数能保证的契约。

首尾帧生视频

generate(
    model="wan/wan3.0-video",
    prompt="从白天过渡到夜晚",
    extra_body={
        "media": [
            {"type": "first_frame", "url": "https://example.com/day.jpg"},
            {"type": "last_frame", "url": "https://example.com/night.jpg"},
        ]
    },
    size="1280x720",
    seconds="5",
)
generate(
    model="minimax/h3",
    prompt="人物动作自然连贯地从起始姿态过渡到结束姿态",
    extra_body={
        "media": [
            {"type": "first_frame", "url": "https://example.com/start.jpg"},
            {"type": "last_frame", "url": "https://example.com/end.jpg"},
        ]
    },
    size="1344x768",
    seconds=4,
)

h3 的首尾帧不能和参考资产混用。 first_frame / last_framereference_* 走的是模型内部两条不同的生成路径,混着传会 400。要么给首尾帧,要么给参考资产。

首尾帧最多各一个;两者在数组里的先后顺序不影响结果,网关会自动排好。

任务状态

GET /v1/videos/{video_id} 返回的 status 有四个取值,其中 completedfailed 是终态:

status含义该做什么
queued已排队,尚未开始继续轮询
in_progress正在生成继续轮询
completed已完成调下载端点取成片
failed生成失败error.code / error.message不计费

不要无限轮询。请给循环设置整体超时与最大轮询次数,超时后按失败处理并告警——否则上游异常时你的进程会一直挂着。

耗时参考:wan 5 秒 480P 实测约 1~2 分钟;h3 4 秒片实测约 1 分钟、6 秒片约 1.5 分钟;h3-fast 5 秒文生片实测约 18 秒、带参考素材约 1 分钟。轮询间隔 5 秒是合适的起点,整体超时建议留到 10 分钟以上。

画幅与时长

这一节 wan 和 h3 规则完全不同,请按你用的模型看对应一栏。

size一个参数管两件事短边决定分辨率档位(档位决定每秒单价),宽高比决定画幅

短边档位
480480P
540540P
720720P
10801080P

1920x10801080x1920 是同一档、同一个单价——算成本看短边,别看长边

画幅只有五种:16:9 横屏 · 9:16 竖屏 · 1:1 方形 · 4:3 · 3:4。推荐写法是短边写档位数字、长边按画幅配

画幅480P540P720P1080P
16:9 横屏832x480960x5401280x7201920x1080
9:16 竖屏480x832540x960720x12801080x1920
1:1 方形480x480540x540720x7201080x1080
4:3640x480720x540960x7201440x1080
3:4480x640540x720720x9601080x1440

1280x7201280*7201280×720 三种写法都接受。

不在表里的组合会报错,不会被静默换掉。

你写的结果
1024x1024800x600——短边不是档位数字400,提示短边应取的值
2560x1080——短边对上 1080P,但 21:9 不在五种画幅里400,提示可用画幅
1920x1080 发给只开了 480P 的模型400code: unsupported_value),提示该模型实际可用的档位

成片的实际宽高不等于你写的 size:上游按「档位目标总像素 × 画幅」算,且宽高必须是 16 的倍数 —— 480P 的 16:9 实际是 832×480,480P 的 1:1 是 624×624。这些实际尺寸也可以直接当 size 回传。

图生视频的画幅跟着输入图走,不跟着你的 size 走。 实测:传 512×512 的图、size832x480,成片是 624×624size 仍决定档位和单价,画幅由图决定。要横屏成片就给横屏参考图。

时长seconds 可选值随模型不同,见模型详情页。OpenAI SDK 里写成字符串如 "5"

h3 没有档位阶梯,只有六种固定画布,短边恒为 768(21:9 因像素上限压到 672)。size 必须逐位精确命中其中一种:

画幅size
21:91536x672
16:9 横屏1344x768
4:31024x768
1:1 方形768x768
3:4768x1024
9:16 竖屏768x1344

不在表里的一律 400,错误信息里会列出全部六种。不会被静默替换成相近的那一个。

六种画布单价相同 —— h3 按秒计费,与分辨率无关。

时长seconds4~15 的整数(不接受小数)。

实际成片会比你请求的秒数略长,但计费按你请求的秒数算。

模型内部的时长只能落在固定的帧数网格上,会向上吸附:请求 4 秒实际约 4.46 秒、请求 6 秒实际约 6.58 秒。多出来的部分不收费,差值最多约 0.67 秒。

seconds=8 是 4~15 里唯一正好整秒的取值(8.00 秒)。对时长有严格要求时优先用它。

h3 的成片始终带声轨,由模型一并生成,没有关闭开关 —— 这是模型结构决定的(视频与音频在同一个去噪过程里),关掉也不省算力。不需要声轨请在拿到成片后自行剥离:ffmpeg -i in.mp4 -c:v copy -an out.mp4。h3-fast 是同一套模型,声轨行为相同。

h3-fast 与 minimax/h3同一套模型,跑在另一批机器上,所以出片快得多,代价是只开两种画布

画幅size
16:9 横屏1344x768(默认)
9:16 竖屏768x1344

不带 size 时按 1344x768 出片。传其余画布(包括 h3 支持的 768x768 等四种)一律 400不会被静默替换

单价与画布无关,两种画布同价,只按秒算。

时长seconds5~15 的整数(不接受小数)。

实际成片会比你请求的秒数略长,但计费按你请求的秒数算 —— seconds=15 除外。

和 h3 一样,时长只能落在固定的帧数网格上、向上吸附:请求 5 秒实际约 5.17 秒、13 秒实际约 13.67 秒。多出来的部分不收费,差值最多约 0.67 秒。seconds=8 是唯一正好整秒的取值(8.00 秒)。

seconds=15 是唯一的例外模型本身最长只能出到 14.375 秒(帧数网格向上对齐后,再下一档就是 15.083 秒、超出模型允许的 15 秒上限),所以 15按 14 秒出片(实际约 14.38 秒)并按 14 秒计费 —— 少给的那一档不收你的钱。对时长有严格要求时请显式写 14,不要写 15

seed 只有 h3-fast 支持:同样的输入配同一个 seed,画面构图基本一致,但不保证逐像素相同。不传时每次随机。用官方 OpenAI SDK 时放进 extra_bodyextra_body={"seed": 42}

参数

不同模型开放的参数不同,传了该模型不支持的参数一律 400 并说明原因,不会被静默忽略 —— 静默忽略意味着你以为设置生效了、实际按默认值出片,钱还照付。

参数类型wan2.6/2.7-t2vwan2.7-i2vwan2.7-r2vwan3.0-videominimax/h3minimax/h3-fast
modelstring✅ 必填
promptstring
sizestring✅ 仅 1344x768 / 768x1344,默认前者
secondsint✅ 4~15 整数✅ 5~15 整数,默认 5(15 按 14 秒出片与计费)
input_referencestring / 文件✅ 仅 URL / base64✅ 仅 URL / base64✅ 也可上传文件✅ 也可上传文件
mediaarray
prompt_extendbool
watermarkbool
seedint✅ 0~2147483647
audiobool❌(恒带声轨)❌(恒带声轨)

说明:

  • prompt_extend —— ⚠️ 默认开启,会改写你的 prompt;要严格按原文出片请显式传 false
  • watermark —— 默认 false,是否加 AI 生成水印。
  • seed —— 随机种子,用于复现同一结果。minimax/h3 暂不支持,同样的输入每次产出都不同;minimax/h3-fast 支持:同样的输入配同一个 seed,画面构图基本一致,但不保证逐像素相同。用官方 OpenAI SDK 时与 media 一样放进 extra_bodyextra_body={"seed": 42}
  • audio —— 是否生成声轨,开与关单价相同

布尔参数必须是真正的布尔值:字符串 "true" 返回 400seconds / seed 同理,必须能转成整数。

未识别的字段一律不会透传到后端。 上游请求体由网关逐字段构造,只有本表里的参数会被采纳;其余字段(无论怎么传)都会被丢弃,不会影响生成结果或计费。

所以用 extra_bodymedia 是安全且必要的(见上文「四种生成模式」),但不要指望用它传本表之外的东西 —— 那些字段不会生效。另外在裸 HTTP 里显式写一个名为 extra_body 的字段会被直接 400(那条路径语义不同,见错误信息)。

media 的类型

media[{"type": "…", "url": "…"}] 数组。url 除公网地址外也接受 data:<mime>;base64,... 内联数据(图片、音频、视频都可以),这样不需要图床。注意内联会让请求体膨胀约 1/3,单个素材建议控制在几 MB 以内。

链接由模型所在的算力节点直接下载,网关不转存。所以链接必须:

  • 是公网可以直接访问的 http:// / https:// 地址,不带账号密码,不指向内网地址(否则提交时直接 400);

  • 在任务执行时仍然有效 —— 带签名的临时链接请留足有效期(排队也要算进去);

  • 响应要快,素材不要过大。

  • 优先用中国大陆境内可以稳定访问的地址(如国内对象存储、CDN)—— 算力节点访问境外站点时常超时。

下载失败时任务以失败结束,不计费minimax/h3-fast 的错误信息会指出是第几个素材出的问题(例如 media[0]: media link returned HTTP 404)。input_reference 给链接时同样适用这些规则。

type用途wan3.0-videominimax/h3
first_frame首帧图(等价于 input_reference
last_frame尾帧图
reference_image参考图(控制风格 / 主体)
reference_video参考视频
reference_audio参考音频(音色 / 风格)
file文件(≤ 100 MB)
link网页链接
first_clip起始片段
driving_audio驱动音频(口型 / 动作对齐)

上表只覆盖 wan3.0-video 与 minimax/h3,因为只有这两个的可用类型是确证过的。 其余 wan 模型(wan2.7-i2v / wan2.7-r2v 等)网关不做类型收窄,收不收由上游决定 —— 请以模型库详情页为准,并按下面这条做好失败处理。

wan 系列传了该模型不收的类型,任务会创建成功、十几秒后以 InvalidParameter 失败(失败不计费)。别依赖网关拦截。

h3 会在提交时就 400,并列出它支持的类型。

计费

成片时长计费(¥/秒),实价见模型库

单价随分辨率档位不同,档位取 size短边1920x10801080x1920 都算 1080P 档。算成本按短边对照模型详情页的档位价。

完成后 usage 带上实际产出信息,其中 duration_seconds 是计费依据:

{
  "duration_seconds": 5,      // 实际成片秒数 —— 按这个计费
  "SR": 480,                  // 实际出片的分辨率档位 —— 决定单价
  "ratio": "16:9",
  "fps": 30,
  "video_count": 1
}

档位以实际出片为准:结算读 usage.SR,不是读你请求里的 size。图生视频时上游可能按参考图调整画幅,档位仍由目标总像素决定。

单价与画布无关,六种画布同价,只按秒算。

计费秒数取你请求的 seconds,不是实际成片秒数 —— 实际成片会略长(见「画幅与时长」),多出来的部分不收费。

{
  "duration_seconds": 4,      // = 你请求的 seconds,按这个计费
  "size": "768x768",
  "inference_time_s": 54.8    // 本次生成耗时,仅供参考,不参与计费
}

单价与画布无关,两种画布同价,只按秒算。

计费秒数取你请求的 seconds,实际成片略长的部分不收费。seconds=15 按 14 秒计费 —— 模型最长只能出到约 14.38 秒,少给的那一档不收钱。

usage.duration_seconds 就是计费依据 —— 请求 seconds=15 的任务,这里会回 14

{
  "duration_seconds": 5,      // = 你请求的 seconds(请求 15 时回 14),按这个计费
  "size": "1344x768",
  "inference_time_s": 17.9    // 本次生成耗时,仅供参考,不参与计费
}

通用规则:

  • 生成失败不计费。
  • 轮询不额外计费:同一个任务只在第一次读到完成状态时结算一次,之后无论查多少次都不会重复扣费。
  • 下载端点不单独计费。

逐笔扣费金额可在控制台调用日志核对。

本页目录