教程
如何在 hiapi API 上调用 happyhorse-1.1/image-to-video:curl 与 Python 指南
该端点做什么,以及何时使用
happyhorse-1.1/image-to-video 将单张静态图像转换为短视频。你向该端点发送一张首帧图像,模型便从该帧开始向前生成动画。其实际意义在于连续性:起始图像中的主体、构图和外观会延续到生成的片段中。这就是我们这里所说的身份保留——模型并非仅凭文本提示凭空创造新场景,而是在延续一个你已经用图像创作好的场景。
因此,当视觉身份比即兴创作更重要时,该端点非常合适:
- 你已经有了参考静帧——人像、产品照、角色设定图——并希望生成忠于该静帧的运动。
- 你在一个流水线中制作短片,而其中所有其他素材都是先以图像形式设计的。
- 你需要为故事板帧制作动态分镜或运动预览。
当你希望模型从零开始自由重新诠释场景,或者没有静帧来锚定输出时,它就不太适合。在这些情况下,图像优先的工作流通常意味着先生成锚定图像,然后将该结果交给此端点。
调用本身是异步的:你提交一个任务,然后轮询直到视频就绪。本指南的其余部分将端到端走完这一生命周期。
发送请求之前
首次调用前需要准备好两件事。
API 密钥
所有调用都通过 hiapi 平台进行,API 密钥作为 bearer 凭据附加。请将密钥保存在环境变量中,而非源代码管理中,这样同一份代码可以在本地开发和 CI 中通用:
export HIAPI_API_KEY="your_key_here"
在 Python 中,运行时用 os.environ["HIAPI_API_KEY"] 读取它。切勿记录原始 header 值。
可引用的首帧图像
该端点需要一张起始图像。请提前决定如何传递它——是作为平台可抓取的 URL,还是作为上传/经 base64 编码的素材,取决于你的账户和端点文档支持的方式。重要的是,该引用在提交时是稳定且可访问的。如果任务可能排队,那么五分钟后就过期的签名 URL 是个糟糕的选择。
在调用前了解你预期的输出宽高比和时长也有帮助,因为首帧通常会约束它们。提前做好这个决定可以避免之后重新提交。
密钥导出且图像引用确定后,你就可以提交了。
构建首帧输入
首帧是模型在其后生成的一切的锚点。实际上,你应当把它视为身份契约:那张图像中清晰可见的任何内容——面部、服装、标志、色彩方案、相机角度——都是模型必须依据的。如果静帧中的某个细节模糊或被遮挡,输出也会模糊。
无论具体模型参数如何,以下几条准备原则都适用:
- 选择清晰、光线良好的帧。 压缩伪影和严重噪点会减少模型可保留的内容。一张锐利、光照均匀的图像比一张戏剧化但模糊的图像是更好的锚点。
- 将主体放在你希望运动开始的位置。 在静帧中裁剪和构图比指望模型重新取景更划算。
- 保持你预期输出的宽高比。 如果最终视频是横向的,请准备横向锚定图;之后填充不会恢复丢失的构图。
- 避免矛盾的线索。 一张读起来是一种意思、却隐含暗示画面外动作的静帧会给模型不稳定的起点。
不要假设端点文档之外的模型专属参数。最稳妥的方法让图像本身承载意图,然后让请求字段只处理它们明确覆盖的内容。
用 curl 发一个可用请求
提交任务只需一次 POST。结构是:向端点发送经过身份验证的请求,JSON 请求体携带模型标识符和图像输入。
curl -sS -X POST "https://api.hiapi.ai/v1/video/generations" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-1.1/image-to-video",
"input": {
"image": "https://your-bucket.example.com/first-frame.jpg"
}
}'
逐步说明:
- 端点。 POST 目标是 hiapi API 上的视频生成路由。如果你要针对多个环境编写脚本,请把它放在变量中。
- 认证头。
Authorization: Bearer $HIAPI_API_KEY——shell 会展开你之前导出的环境变量。 - 内容类型。
application/json,因为请求体是 JSON。 - 请求体。
model选择happyhorse-1.1/image-to-video;input.image是你的首帧引用。请查阅端点参考文档,了解你账户下接受的准确图像字段名和结构。
成功的响应并不是视频本身——而是一个任务句柄。预期会得到一个标识符和一个状态字段,通常类似 queued 或 processing。记下这个 ID;你将在后续步骤中用它轮询。先运行一次命令并打印响应,这样在开始写脚本前就能看到它的结构。
用 Python 发送同一请求
Python 版本是直接移植,不是新的集成。同样的路由、同样的请求头、同样的 JSON 请求体。
import os
import requests
API_KEY = os.environ["HIAPI_API_KEY"]
URL = "https://api.hiapi.ai/v1/video/generations"
payload = {
"model": "happyhorse-1.1/image-to-video",
"input": {
"image": "https://your-bucket.example.com/first-frame.jpg"
},
}
resp = requests.post(
URL,
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json=payload,
timeout=60,
)
resp.raise_for_status()
job = resp.json()
print(job)
映射说明:
- 使用
json=payload的requests会像 curl 的-d字符串那样序列化字典——如果你使用json=,就不要再手动设置Content-Type;库会处理它。上面显式的请求头无害但多余。 raise_for_status()会在你开始轮询一个并不存在的任务之前,尽早将 HTTP 层失败转为异常。timeout只是提交调用上的客户端保护。生成在服务端运行,不受其约束。
将 payload 保存在变量中并复用。以后更改模型或图像时,你只应编辑一个字典,而不是两个平行的实现。
处理异步任务与轮询
图生视频生成比请求/响应周期耗时更长。因此 API 会立即返回一个任务引用,并期望你稍后回查。生命周期是:提交、收到 ID、轮询状态、获取结果。
从提交响应中提取任务 ID——字段名取决于该端点文档化的响应模式,所以请读一次,并把键查找硬编码,而非值:
job_id = job["id"] # check the endpoint docs for the exact field
然后按间隔轮询:
import time
poll_url = f"https://api.hiapi.ai/v1/video/generations/{job_id}"
while True:
status = requests.get(
poll_url,
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=30,
).json()
if status["status"] == "succeeded":
break
if status["status"] == "failed":
raise RuntimeError(status)
time.sleep(5)
保持轮询合理的规则:
- 按固定间隔轮询,而不是紧凑循环。 对这种规模的任务,检查之间隔几秒就足够了;猛打端点会增加负载,却不会让结果更早产生。
- 限制总等待时间。 没有上限的
while True是迟早会发生的挂起。跟踪已用时间,并在合理预算后大声失败。 - 处理两种终态。 成功和失败都是答案。将未知状态字符串视为非终态,但记录它们,以便你注意到模式变化。
- 不要为一个任务从多个并行 worker 轮询。 每个任务一个轮询器,或通过队列协调。
一旦状态为终态,就继续下载结果。
下载并存储输出
完成的视频会在已完成的任务响应中作为素材暴露——通常是一个 URL,有时带过期时间。成功轮询后的第一步是抓取该引用,并将字节复制到你控制的存储中。
video_url = status["output"]["video_url"] # inspect the real response shape
with requests.get(video_url, stream=True, timeout=120) as r:
r.raise_for_status()
with open(f"out/{job_id}.mp4", "wb") as f:
for chunk in r.iter_content(chunk_size=1 << 20):
f.write(chunk)
适用于任何流水线的存储指导:
- 对持久产物要复制,而不是链接。 签名或限时 URL 是传输方式,不是存储。持久化文件,然后在数据库中把你自己对象键与任务 ID 一起存储。
- 流式处理大文件。
iter_content保持内存平稳;将整个视频加载到变量中会让内存上限与素材大小挂钩。 - 记录来源。 将
job_id、输入图像引用和模型标识符与素材一起保存。当你需要复现片段时,该记录就是配方。 - 写入前检查 HTTP 状态。 错误响应导致的部分写入比干净的失败更糟,因为它看起来像有效文件。
文件落到磁盘或对象存储后,流水线就闭环了,你可以继续处理下一帧。
需要留意的错误与陷阱
该端点的大多数失败都是可预测的。简短清单如下:
- 首帧输入格式错误或不可达。 提交被拒最常见的原因。检查图像 URL 是否能从公网解析、格式是否为端点所接受,以及 payload 是否正确嵌套了图像字段。用 curl 调用复现,以区分认证问题和 payload 问题。
- 在任务存在之前轮询。 从错误字段读取 ID,或轮询与提交响应不匹配的路由,会产生看起来像任务失败的 404。打印一次提交响应,并据此固定键查找。
- 无界等待。 没有超时意味着卡住的任务会无限期挂起 worker。设置预算、失败并告警。
- 丢失素材链接。 生成成功,但 URL 被消费一次后再未复制。在抓取时保存字节并保留你自己的键;不要依赖返回的链接作为长期存储。
- 认证泄露。 将密钥粘贴到 notebook 中并意外提交是反复出现的问题。从环境加载,有疑虑就轮换。
- 假设端点未文档化的参数。 如果你需要某个未列出的参数,那它就不存在。只通过首帧图像和已文档化的字段来表达意图。
处理好这些,异步流程就会变得平淡无奇——这正是你在生产流水线中想要的。关于端点特定的字段名和可接受值,请在构建时保持端点参考文档打开。