智盒万象 Wan 3.0 视频生成
底层调用智盒万象代理的 https://www.omniboxai.cn/v1/video/generations,统一使用 Wan 3.0 模型 wan3.0-video(单一模型覆盖全部模式,靠 input.media[].type 区分):
t2v文生视频:不传mediai2v图生视频:media=[{type:first_frame, url}](指定首帧)r2v参考图生视频:media=[{type:reference_image, url}, ...](最多 10 张,prompt可用「图1」指代)video-edit视频编辑:media=[{type:video, url}, {type:reference_image,...}*]
请求协议严格遵循 videoApi.md §5(Wan 原始协议):model + input{prompt, media} + parameters。
> ⚠️ Wan 硬性互斥约束:first_frame / last_frame 与 reference_image / reference_video / file / link 不能在同一段里混用。所以链式段用 i2v(首帧接上段尾帧)保连续,首段用 r2v(参考图锁角色),两者绝不叠加——脚本已自动处理(auto 模式下有首帧优先走 i2v、有参考图走 r2v)。这点和旧 HappyHorse 的「r2v+首帧」写法不兼容。
wan3.0-video-prime 也是可选模型,把脚本顶部 MODEL = "wan3.0-video" 改掉即可切换。
> 核心原则:视频 ≤30 秒就一次性生成,不要拆成几段。
> Wan 3.0 单段支持 2~30 秒(接口文档 §5.2:无输入视频时 duration 为 2~30)。一条 30 秒以内的视频(如 15 秒竖版广告)应用一次 gen 调用、单段 t2v 直接出片:人物/手机/风格全程一致,过渡由模型自己顺接,比拆成多段再拼接更连贯、也更省 token。
> 只有当单段确实超过 30 秒,或视频本就是多镜头叙事、需要逐段确认/尾帧衔接时,才用 story 分镜分段生成再拼接。story 的自动切段阈值也已设为 30 秒——单段 ≤30s 不会被拆开。
> 平台对视频接口支持图片 base64 直传(同代理 omniboxai.cn 已实测),脚本对 ≤200KB 的 jpg/png/webp 本地图自动转 base64 data URI 直传、省掉图床上传;大图/其他格式仍上传图床拿 URL。图床默认顺序 uguu → catbox → litterbox。
何时触发
- 用户说「做个视频」「生成视频」「文生视频」「图生视频」
- 用户想用智盒万象 / Wan / wan3.0 / wan3.0-video 做视频
- 用户要做一个超过 30 秒的视频,需要分多段生成并拼接
- 用户强调「角色一致」「连续镜头」「用尾帧接下一段」
前置依赖:配置智盒万象 APIKey(必读)
本技能调用的视频生成接口是智盒万象平台(omniboxai.cn)的外部付费服务,技能本身不内置任何 APIKey。在任何机器上(包括本机首次使用、或把技能拷贝到新机器后)运行本技能,都必须先提供有效的智盒万象 APIKey,否则脚本会直接报错退出。
APIKey 三种配置方式(任选其一,优先级从上到下):
- 环境变量
ZHWX_API_KEY(推荐,最稳,尤其服务器/CI 场景)
bash export ZHWX_API_KEY=你的key - key 文件(跨机器默认位置
~/.workbuddy/secrets/zhwxkey.txt)
- 文件内容写APIKey:你的key或纯你的key均可(脚本会自动剥APIKey:前缀) - 命令行
--key
bash python happyhorse_video.py story 分镜.json --key 路径/你的key.txt
> ⚠️ 旧本机路径 D:\公司\AI聚合平台\AI工作区\.secrets\zhwxkey.txt 仍会被兼容读取,但那只是本机绝对路径,换机器不存在。部署到新机器请务必用上面三种方式之一重新提供 key,并优先用 ~/.workbuddy/secrets/zhwxkey.txt 或环境变量。
如何获取 APIKey:首次使用、找不到 Key 时,脚本会自动提示——请先到 https://www.omniboxai.cn/ 注册并申请视频生成 APIKey,拿到后按上述任一方式配置即可重试(覆盖「密钥文件不存在」和「密钥文件存在但内容为空」两种场景)。
完整工作流
-
需求收集(问用户):
- 视频主题 / 大概时长
- 是否有角色参考图、首帧图、参考视频
- 横屏(16:9)还是竖屏(9:16)
- 要不要原生声音(默认关水印、默认 720P 16:9、原生音频默认开)
- 是否先出角色/场景元素图确认(见下方「制作前元素图确认」)——含固定角色或强风格诉求时强烈建议先确认,能省大量重做 token -
提示词优化:
先用 LLM 把用户的口头需求改写成适合 Wan 3.0 的分镜描述。
优化要点:
- 每段提示词只写「动作 / 镜头运动 / 场景氛围」,不要重描角色外貌(外貌交给参考图/上一段尾帧)
- 用中文数字而非「6 个」等量词组合,避免 TTS 卡顿(如果还要做旁白)
- 视频总时长 ≤30 秒就一次性生成,不要拆段(Wan 3.0 单段支持 2~30s,见上方核心原则)。只有单段确实超过 30 秒,或天然多镜头叙事需要逐段确认/尾帧衔接,才用story分镜分段;链式分镜要保持机位固定(见下方铁律)。
- 镜头稳定不推近:链式分镜保持机位固定,不要做推近/拉远/变焦(除非收尾需要且上段已把建筑完整拍入)。推近会把人物裁出画、把背景结构(如拱桥)裁掉,导致 AI 把缺失部分误判成别的东西(如路),下一段一拉远就「凭空出现一座桥」。要拉远就匀速且保证原结构始终在画内。
- 具体台词必须写进提示词:写「角色开口说:『……』」并附原话,不要只写「角色作答/说话」;否则模型会自行编造台词、对不上剧本。
- 每段尾帧要留好下一段要锁定的全部主体(见下方「i2v 链式连续性铁律」),否则下一段锁不住。 -
输出优化后的分镜给用户确认:
- 每段 id、时长、生成模式、提示词
- 哪些段会接上一段尾帧
- 哪些图会被上传作为角色参考
- 确认后再调用脚本,不要直接生成 -
生成与拼接:
- 脚本自动上传角色参考图、上传本地首帧/尾帧
- 独立段并发跑,链式段按序跑(因为需要上一段尾帧)
- 所有段生成后自动 ffmpeg 拼接成最终成片 -
交付:
- 返回成片路径 + 总时长/分辨率/帧率
- 告诉用户各段生成任务 ID,便于排查
制作前:角色/场景元素图确认(省 token 保险)★
> 共性经验:长视频最大的浪费是「视频都生成了才发现角色/风格不对,整片重做」。一张图翻车重画,远比整段视频重生成便宜。
- 触发:视频含固定角色(人物/吉祥物/IP)、或有强风格/构图诉求时,在生成视频前先问用户要不要出元素图确认。
- 做法(本技能已内置 qwen-image-2.0 出图,无需再切到 zhwx-image):
1. 用genref子命令(单张)或story的refs_prompts字段(批量)调智盒万象 qwen-image-2.0 先生成角色立绘 + 关键场景参考图(如「爷孙站在石拱桥」的单张)。
2. 把图给用户确认角色外貌、服装、画风、场景结构是否到位。
3. 确认后再把这些图作为character_refs/ 首帧 / r2v 输入喂给视频生成(story 模式下refs_prompts生成的图会自动并入character_refs,无需手写路径)。 - 收益:角色/风格问题在「图阶段」就被拦下,避免烧掉视频 generation 的 token 才发现不对。
多分镜:逐段生成 + 逐段确认工作流(省 token + 控连续)★
> 共性经验:多分镜视频不要一次性全跑完再给看。逐段生成、逐段确认、最后拼接,既能早抓连续性 bug,又只重抽坏的那一段。
- 做法:
1. 先story --dry-run把分镜计划(每段 id/时长/模式/提示词/是否接上段尾帧)给用户过目。
2. 用gen(或自写分步驱动)只生成第 1 段,单独呈现给用户确认。
3. 用户确认 OK 后,用第 1 段尾帧作首帧生成第 2 段,拼成「1+2」再呈现确认。
4. 依此类推,已确认段绝不重抽,只重抽有问题的段(配合版本化 workdir 隔离旧产物,见下方工程坑)。 - 收益:
- 早抓问题:小孩漂移、台词不对、构图裁切等在第 1~2 段就能发现,不用等全片跑完。
- 省 token:一段不对只重生成那一段(约几 credit),而非整片重做。
- 衔接 Continuity:逐段确认时,每段尾帧都要用视觉模型核验「下一段要锁定的主体是否在画内」(见下方铁律)。
i2v 链式连续性铁律(长视频分镜必看)★
链式分镜(上一段尾帧 = 下一段首帧)最易翻车的是「主体丢失 / 漂移 / 场景突变」。三条铁律:
- 上一段尾帧必须包含下一段要锁定的全部主体。
这是最高频的根因:如果上段尾帧里某个角色已出画,下一段 i2v 锁的首帧根本没有该角色,模型只能凭空重画 → 角色变样、站位错位(如「小孩从老人身后冒出来」)。提示词再强也锁不住空——修法是让上段尾帧把下段的主体都留在画内。 - 稳机位、不推近。
推近会把人物裁出画、把背景结构(拱桥/建筑)裁掉,AI 对缺失部分只能重绘 → 下段一拉远就「凭空出现一座桥/一条路」。保持中景偏宽、主体大部分在画内、背景结构完整可见。必须拉远时匀速且保证原结构始终在画内、不得新生成建筑。 - 每段尾帧做视觉门控(必加保险)。
每段生成后抽尾帧,用视觉模型(如vision.js/ qwen3.7-plus,支持本地图 base64)核验「下一段要锁定的主体是否都在画内、场景是否正常」,不同框/异常就重生成该段,再接入下一段 i2v。 - 具体台词写进提示词,不要只写「作答/说话」(否则模型乱编,见上文提示词优化)。
脚本位置
~/.workbuddy/skills/zhwx-video/happyhorse_video.py
用托管 Python 运行:
C:\Users\wzx\.workbuddy\binaries\python\versions\3.13.12\python.exe "~/.workbuddy/skills/zhwx-video/happyhorse_video.py" story 分镜.json --workdir 工作目录 --output out.mp4
子命令速查
| 子命令 | 作用 | 示例 |
|---|---|---|
upload |
本地文件 → 公开图床 URL | python happyhorse_video.py upload 1.jpg 2.jpg |
gen |
生成单段视频 | 见下方 |
genref |
用 qwen-image-2.0 生成参考图(角色/场景概念图) | 见下方「参考图生成 genref」 |
tail |
抽视频尾帧 | python happyhorse_video.py tail a.mp4 --output tail.jpg |
concat |
拼接多段视频 | python happyhorse_video.py concat a.mp4 b.mp4 --output final.mp4 |
story |
按分镜 JSON 跑完整流水线 | 见下方 |
probe |
探测视频规格 | python happyhorse_video.py probe a.mp4 |
单段生成示例
文生视频:
python happyhorse_video.py gen --mode t2v --prompt "一只橘猫在窗台上伸懒腰,阳光洒进来" --duration 10 --output cat.mp4
图生视频(首帧):
python happyhorse_video.py gen --mode i2v --image first.jpg --prompt "镜头缓慢推近,角色微笑" --output out.mp4
参考图生视频:
python happyhorse_video.py gen --mode r2v --images ref1.jpg ref2.jpg --prompt "图1中的角色在校园林荫道上行走" --output out.mp4
参考图生成(genref,底层 qwen-image-2.0)★
视频前先用它把角色/风格定下来,比整段视频跑完才发现不对便宜得多:
# 单张角色立绘 / 场景概念图
python happyhorse_video.py genref \
--prompt "写实摄影,白发老爷爷和七岁小男孩站在古镇石拱桥上,爷爷藏青棉袄,男孩黄色羽绒服,元宵夜景红灯笼" \
--size 1280x720 --output refs/爷孙桥.png
返回本地 PNG 路径,可直接作为 character_refs / first_frame / i2v 输入喂给视频生成。
> 批量场景用 story 的 refs_prompts 字段更省事(见下方 story JSON 示例)——会按顺序自动出图并并入 character_refs,dry-run 也能先预览计划。
story 分镜 JSON 示例
{
"title": "浙理AI迎新短片",
"resolution": "720P",
"ratio": "16:9",
"watermark": false,
"audio_mode": "fade",
"fade_sec": 0.5,
"character_refs": ["D:/公司/AI聚合平台/AI工作区/理工IP形象.jpg"],
"refs_prompts": [
{"prompt": "写实摄影,机器人宝宝站在校门口,白色金属外壳,蓝色发光眼睛,卡通科技风", "size": "1024x1024", "name": "机器人宝宝"},
"一张浙理校园全景,现代教学楼,阳光,16:9"
],
"shots": [
{
"id": "s1",
"duration": 10,
"mode": "auto",
"prompt": "机器人宝宝站在校门口挥手奔跑,镜头跟拍,阳光明媚",
"chain_from_prev": false
},
{
"id": "s2",
"duration": 10,
"mode": "i2v",
"prompt": "同一个机器人宝宝在校园林荫道上继续奔跑,镜头稳定跟拍",
"chain_from_prev": true
}
]
}
字段说明:
| 字段 | 说明 |
|---|---|
title |
成片文件名基础 |
resolution |
360P/480P/720P/1080P,默认 720P |
ratio |
t2v/r2v 用,默认 16:9(i2v 跟随首帧图比例) |
watermark |
是否加水印,默认 false |
seed |
可选,全局随机种子 |
character_refs |
角色参考图本地路径列表,会传给第一段 r2v 使用 |
refs_prompts |
参考图生成清单(可选):用 qwen-image-2.0 自动出图再并入 character_refs。每项可为字符串(纯 prompt,默认 1024x1024)或对象 {prompt, size, name, output}(name 决定文件名,output 可覆盖输出路径) |
shots |
分镜数组 |
shots[].id |
段标识 |
shots[].duration |
2~30 的整数;超过 30 脚本才会自动切段(Wan 3.0 单段上限就是 30s,≤30s 一次性生成不拆) |
shots[].mode |
t2v/i2v/r2v/video-edit/auto;auto 由脚本自动选(推荐) |
shots[].prompt |
该段提示词 |
shots[].chain_from_prev |
true 表示用上一段尾帧作首帧(i2v) |
shots[].first_frame |
独立首帧本地路径或 URL(优先级高于 chain_from_prev) |
shots[].images |
本段额外参考图(1~10 张) |
shots[].use_character_refs |
是否复用全局 character_refs,默认 true |
audio_mode |
顶层可选,拼接音频模式 native/fade/bgm/first/mute,默认 native |
fade_sec |
顶层可选,fade 模式的淡入淡出时长(秒),默认 0.35 |
video_fade |
顶层可选,视频接缝交叉淡化秒数,默认 0(关闭)。链式生成建议保持 0,用硬切+去重更自然 |
bgm |
顶层可选,audio_mode=bgm 时的背景音乐本地路径 |
bgm_volume |
顶层可选,BGM / first 模式音量,默认 0.85 |
ambience_volume |
顶层可选,bgm/first 模式保留多少原环境音 0~1,默认 0 |
dedup |
顶层可选,是否自动丢弃接缝重复帧,默认 true |
角色一致性铁律
这是本技能最重要的部分:
- 第一段用 r2v + 角色参考图锁定角色(如果有参考图)。
- 后续段用 i2v + 上一段尾帧作首帧。Wan 硬性约束:首尾帧与 reference_image 互斥,所以不要试图用 r2v 同时锁首帧和参考图——
auto模式下有首帧优先走 i2v、有参考图(无首帧)走 r2v,二者不会同段叠加。 - 链式续接段建议直接用
mode: "i2v",不要叠加场景参考图:r2v 从参考图重新生成时,容易把参考图里的人物和提示词里的人物叠加,出现「两个爷爷/两个小孩」的重复角色。 - 提示词里不写外貌细节,只写动作、镜头、氛围。外貌由参考图/上一段画面决定。
- 拼接输出尺寸自动取分段真实尺寸(横竖屏都准):脚本不再靠
resolution字符串硬映射(旧版会把 720P 竖屏也拉成横屏信箱),而是probe探测各段真实宽高、取众数作为成片尺寸;探测不到才按resolution×ratio计算(已尊重ratio,竖屏 720P=720x1280)。所以横屏/竖屏/1080P 都能正确拼接,无需再像旧版那样绕开脚本自行 ffmpeg。若上传的首帧是竖图、i2v 出片就是竖片,拼接会跟随成竖片。 - 段与段之间是硬切拼接(默认
video_fade=0),因为已用尾帧接首帧,画面本身就连续,不需要转场。
拼接接缝处理(去卡顿 + 音乐连贯)★
实测发现直接硬切会出现两个问题,已由脚本自动处理:
- 卡顿/停顿:i2v 会把传入的首帧原样吐回做第 0 帧,于是「上一段尾帧」和「下一段首帧」是同一张图,硬切后这张图连续播了两帧(≈几十毫秒冻结感)。脚本在 concat 时自动检测并丢弃下一段开头与上一段尾帧重复的帧(
dedup=True默认开;阈值DUP_THRESH=2.5/FREEZE_THRESH=3.0,检测窗口最大 1 秒,能吞掉模型起手静止的几帧)。 - 前后音乐跳变:每段视频的原生音轨是模型独立生成的,旋律/节奏各不相同,硬切会听到明显跳变。用
--audio-mode解决:
| audio_mode | 行为 | 何时用 |
|---|---|---|
native |
各段原生音轨直接拼(默认,会听到跳变) | ★要保留每段模型原生对白/环境音(如角色对话)用此,不淡出原声 |
fade |
接缝处对每段音频做淡入淡出交叉过渡,把跳变变成「有意转场」 | 多段原生音轨拼接想消除突兀感时用(会轻微淡出原声,不适合纯对白) |
first |
抽取第一段音频循环铺满全片,保证音乐完全一致 | 第一段旋律好听、希望全片同一首 |
bgm |
全片静音后铺一条统一背景音乐(--bgm 指定文件) |
用户自带 BGM、想要可控配乐 |
mute |
完全去声 | 只要画面、后续自己配旁白 |
bgm / first 模式下可用 --ambience-volume(0~1,默认 0)保留少量原片环境音,让过渡更自然。
CLI 用法
# 去重(默认开) + 接缝淡入淡出(推荐)
python happyhorse_video.py concat a.mp4 b.mp4 --output final.mp4 --audio-mode fade --fade-sec 0.5
# 用第一段音频铺满全片(音乐完全连贯)
python happyhorse_video.py concat a.mp4 b.mp4 --output final.mp4 --audio-mode first
# 用自己的 BGM
python happyhorse_video.py concat a.mp4 b.mp4 --output final.mp4 --audio-mode bgm --bgm mymusic.mp3
story 模式
可在分镜 JSON 顶层写 audio_mode / fade_sec / bgm / bgm_volume / ambience_volume / dedup,或用 story 子命令的同名 --audio-mode / --fade-sec / --bgm / --bgm-volume / --ambience-volume / --no-dedup 参数覆盖(CLI 优先于 JSON)。
关键技术细节(已实测 / 协议对齐 videoApi.md §5)
- 接口真实终态包括
data.status = "SUCCESS"/"SUCCEEDED";统一查询/v1/videos/{id}也可能返回status = "completed"。脚本两种都认。 duration无输入视频时2~30秒(Wan 3.0 单段上限 30s,≤30s 一次性生成不拆段);有参考视频时 输入+输出 ≤30。build_payload会把越界值 clamp 到[2,30]。- 任务失败时,返回结构里塞的是错误文本而非 URL,脚本会识别并抛错,不会下载空文件。
- 单模型
wan3.0-video请求体 =model+input{prompt, media}+parameters: input.media[].type:first_frame/last_frame/reference_image/reference_video/videofirst_frame/last_frame与reference_*互斥,同段不能混用(Wan 硬性约束)parameters.audio(默认true,模型生成原生对白/环境音)、parameters.prompt_extend(默认false,关扩展保台词原样)、resolution、ratio、duration、watermark、seed- 图床默认顺序:
uguu→catbox→litterbox(litterbox 实测易失败,放最后兜底)。上传结果按文件 SHA256 缓存,避免重复上传。 - 拼接用 ffmpeg
concatfilter 重编码(不用-c copy的 demuxer,mp4 时间戳会乱)。 - 默认不用视频交叉淡化(
video_fade=0)。开启交叉淡化会在接缝中点产生 50% 叠化的「虚/重影」,对已经用尾帧接首帧的链式镜头反而显得不自然。 - 视频默认都带 AAC 音轨;如果某段没音轨,concat 会自动补静音。
环境依赖
- Python 3.10+
- 托管 Python 已内置
imageio_ffmpeg(完整版 ffmpeg),脚本会优先用它;找不到时回退 Remotion 自带 ffmpeg。 - 密钥读取优先级:
ZHWX_API_KEY环境变量 >--key文件 >~/.workbuddy/secrets/zhwxkey.txt(跨机器默认)> 旧本机路径D:\公司\AI聚合平台\AI工作区\.secrets\zhwxkey.txt(仅兼容回退)。新机器部署必须提供 APIKey,详见上方「前置依赖」章节。
常见坑
- 小图自动 base64 直传:脚本对 ≤200KB 的 jpg/png/webp 本地图自动转 base64 data URI 直传(已实测同代理支持),不走图床、省 token/耗时、免图床失败;大图/其他格式仍上传图床拿 URL。手动调
upload子命令则始终返回图床 URL。 - 首尾帧与参考图互斥(Wan 硬性约束):链式段用
i2v首帧、首段用r2v参考图,二者绝不能同段叠加,否则接口直接报错。auto模式已自动处理(有首帧优先 i2v,无首帧有参考图才 r2v)。 - 参考图可以锁风格/站位,不只是角色外貌:如果希望全片写实、人物固定在某侧、背景空镜,先准备一张符合预期的参考图作为
character_refs,并在每段提示词里重复强调这些约束。 - litterbox 上传后上游可能拉取失败:遇到
Failed to download https://litter.catbox.moe/...,用--backends uguu catbox换图床重试。 - r2v 多张参考图容易造成角色重复:
r2v传入含人物的参考图时,模型可能把参考图里的人物和提示词的人物各画一遍,出现两个爷爷/两个小孩。续接段直接用i2v从上一段尾帧继续即可避免。 - i2v 输出比例跟随首帧图:如果首帧是竖图,出片就是竖图,拼接时也会跟随成竖片(成片尺寸自动取分段真实尺寸),不会再被裁成 16:9。要统一横屏就确保所有首帧/参考图都是横图。
- 拼接尺寸自动识别(已修复旧隐患):旧版
story把 720P 硬编码成1280x720横屏,竖屏 720P 会被scale+pad拉成横屏信箱、人物压窄。现已改为probe探测分段真实尺寸取众数,横竖屏/1080P 都准确,无需绕开脚本自行 ffmpeg。独立concat子命令不传--width/--height时同样自动探测。 - 长视频分段别并行太多:链式段必须等上一段尾帧,独立段最多并发 2 个(默认),避免上游排队或本机 ffmpeg 冲突。
- 续跑会跳过已存在产物(resume 陷阱):脚本按
state.json的done复用已生成片段。重做不同版本(如旧版 6480 帧、新版 5754 帧)时旧中间产物仍在,会被当「已完成」跳过 → 最终把旧视频当新交付。铁律:重做版本用版本化输出目录(如v3/v4/v5)彻底隔离;或改分镜 JSON 触发_sig作废旧 state。逐段确认流程里每确认一段就复制到版本目录,避免被后续重抽覆盖。
参考:切换为 wan3.0-video-prime
若想用更高级的 wan3.0-video-prime,只需把脚本顶部常量改为:
MODEL = "wan3.0-video-prime"
其余请求协议(input/parameters/media 结构)完全一致,无需改动其它代码。