把 WayinVideo 接进 OpenAI 生态:一次 2API 适配实录
这两天折腾了一个小项目:把 WayinVideo 的网页视频生成功能,包成一个更容易被脚本、客户端、自动化工作流调用的 OpenAI 风格接口。
项目名字很直白:wayin-video-2api。
它不是那种“把请求转发一下就完事”的套壳。真正麻烦的地方在于:网页里按钮点一下,背后其实有模型能力表、上传流、账号态、轮询、内容兜底、错误码解释等一整套东西。把这些东西整理干净之后,客户端才可以只关心:
我给你一个 prompt,或者再给你一张图,你帮我生成视频。
而不是每次都去猜 Wayin 的内部字段应该怎么拼。
为什么要做这个
直接用网页当然也可以,但网页有几个天然限制:
- 不好接自动化:脚本、Bot、Cherry Studio 这类工具,不可能每次都模拟人去点网页。
- 接口形状不稳定:网页内部字段名和 OpenAI 常见接口差别很大。
- 模型很多,能力不一样:有些模型只能文生视频,有些必须带图,有些支持参考图/参考音频。
- 失败信息很不友好:上游经常只给一个
ERROR,不把上下文整理出来,排查起来很难受。
所以 2API 解决的问题不是“凭空创造新能力”,而是把已有网页能力整理成更稳定的调用层。
整体链路
现在客户端可以调用:
POST /v1/videos/generations
传入类似这样的请求:
{
"model": "sora-2",
"prompt": "一只橘猫在阳光下走路,电影感,高清",
"duration": 4,
"resolution": "720p"
}
如果是图生视频,则可以带:
{
"model": "Seedance 1.0 Lite",
"prompt": "让这张图里的人物张开双臂",
"input_type": "image",
"image_url": "https://example.com/frame.png"
}
2API 在中间会做这些事:
关键点一:模型能力表比想象中重要
Wayin 的模型目录里,不同模型能力差别很大。项目里把运行时 catalog 整理成了能力表,目前大致是这样:
这块最容易踩坑。比如“Seedance 1.0 Lite”看起来像一个模型,实际有文本版和图生版:
如果客户端传了图片,但仍然选择文本模型,上游就很容易失败。现在适配层会检查输入,如果发现带图,就优先路由到同系列图生模型。
关键点二:图生视频不能把本机路径直接丢给上游
这个坑很现实。
Dashboard 上传图片后,本机得到的是类似这样的临时地址:
/tmp/uploads/xxxx/frame.png
浏览器或者本机服务当然能访问它,但 Wayin 的服务器访问不到。之前图生视频失败,一个核心原因就是把这种本机临时路径直接放进了生成请求里。
后来补成了完整上传流:
POST /api/video/generate/upload
↓
PUT upload_url
↓
POST /api/external_file/refresh_url
↓
拿 data.url 去提交图生视频
对应到上游 payload,大概会变成:
{
"model": "bytedance/v1-lite-image-to-video",
"model_config": {
"ratio": "16:9",
"duration": "5",
"resolution": "720p",
"input_type": "i2v",
"image": "https://.../remote-image.png"
},
"instruction": "让她张开双臂",
"auto_prompt": true
}
这里的重点不是代码多复杂,而是边界要想清楚:
对本机可见,不等于对上游可见。
这个判断在各种“网页功能转 API”的项目里都很常见。
关键点三:生成成功以后,临时文件要删
做完图片上传之后,又冒出另一个小问题:本机临时图片会不会越堆越多?
答案是:如果不处理,会。
所以现在改成:
为什么不是上传到 Wayin 之后立刻删?因为排查失败时,有时还需要知道当时传了什么。现在的策略更保守:成功后删,失败时留一点现场。
这次排查的时间线
这次“画不了视频”的问题,最后不是一个点,而是两个问题叠在一起:
- 带图片时仍然可能走到文本模型。
- Dashboard 上传后的图片是本机临时路径,上游读不到。
看到日志里的 ERROR 时,第一反应不能是“上游坏了”。更有用的是把这些字段都留出来:
- 请求模型是什么;
- 实际路由到哪个上游模型;
- 是否带图片;
- 图片 URL 是本机路径还是远端 URL;
- 走了哪个账号;
- 上游返回的错误码是什么。
比如这类错误:
当前支持的接口形状
项目现在主要暴露这些 OpenAI 风格接口:
部署时最少需要注意两个环境变量:
API_KEY=你的本地访问密钥
WAYIN_ACCOUNTS_JSON=/path/to/accounts.json
如果要真实转发到 Wayin,还需要合法的会话来源,比如:
WAYIN_ACCESS_TOKEN=...
# 或
WAYIN_WEB_COOKIE=...
没有这些凭据时,项目会进入 dry-run,返回归一化后的上游请求预览,不会真的提交生成。
一个最小请求例子
文生视频:
curl -X POST http://127.0.0.1:3002/v1/videos/generations \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "sora-2",
"prompt": "一只橘猫在阳光下走路,电影感,高清",
"duration": 4,
"resolution": "720p"
}'
图生视频:
curl -X POST http://127.0.0.1:3002/v1/videos/generations \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "Seedance 1.0 Lite",
"prompt": "让画面里的人物自然地张开双臂",
"input_type": "image",
"image_url": "https://example.com/frame.png"
}'
查询结果:
curl -H "Authorization: Bearer $API_KEY" \
http://127.0.0.1:3002/v1/videos/<job_id>
如果 status 只返回了 fid,适配层会继续调用 content endpoint,把最终 MP4 URL 整理进 result.videos。
我觉得这个项目最有价值的地方
不是“能不能调通一次”。调通一次其实不难,难的是把这些细节变成稳定的工程边界:
- 模型能力从 catalog 读,不靠拍脑袋;
- 请求字段统一成客户端熟悉的形状;
- 上游错误码有解释;
- 图生视频自动处理上传流;
- 成功后清理本机临时文件;
- 没凭据时 dry-run,不误提交;
- 所有修复都有测试兜底。
对我来说,这类 2API 项目最怕写成“神秘脚本”:今天能跑,明天上游一变就全炸。现在至少把关键路径拆开了,后面要替换模型、补 endpoint、接新客户端,都比较容易。
项目地址
GitHub:
https://github.com/keggin-CHN/wayin-video-2api
如果你也在做类似“网页产品能力 → 本地 API 适配层”的东西,我建议优先整理三件事:
- 运行时能力表;
- 上传/文件可见性边界;
- 错误码和日志结构。
这三件事做好,后面才不会一直靠猜。