把一句话故事拆成 N 个 10 秒镜头,批量抽卡生成候选,自动选片、终审、 二次剪辑,最后拼成一支成片。面向“同风格多镜头短片”这个具体需求: 锁定人物、场景与视觉风格,镜头之间用接力方式衔接,避免每个镜头各拍各的。
本仓库是独立开源重构版:根据公开的工作流描述从零实现,不是 从任何现有产品或插件实例中导出的代码,仓库内不含任何用户真实项目 数据。两者工作流对标,但实现、代码与运行环境完全独立。
关键区别:本仓库不自带视频生成模型。默认的 mock 后端只用 ffmpeg 生成占位片段,保证全流程不依赖真实视频模型也能跑通;要出 真正可用的片子,需要按下面“接入真实视频后端”一节接自己的供应商。
环境要求:Python 3.12+、ffmpeg / ffprobe 在 PATH 中。零 pip 依赖, 全部只用 Python 标准库 + SQLite + ffmpeg。
python3 server.py --port 8000
# 打开 http://127.0.0.1:8000数据默认写到 ./data/(SQLite + 生成的视频),该目录已 gitignore,
换机器或清数据直接删掉重跑即可。
跑测试:
python3 -m unittest discover -s tests -v # 轮次规则 + 渲染单测
python3 demo_test.py # 端到端实测(起真实服务器 + mock 生成)- 项目:一句话故事(idea)、类型、横/竖屏、每镜候选数(1–6)、 自动模式。项目从空开始。
- 分镜规划:把故事拆成 N 个 10 秒镜头,每个镜头有运镜、画面、 台词、起始画面、结束画面。默认是模板化规划(见“已知限制”), 规划结果可在页面上改,分镜提示词随后按锁定重建。
- 风格圣经:人物锁定 / 场景锁定 / 视觉锁定三段。组装提示词时 三段锁定逐字放进每个镜头的完整提示词前面,再加真实感母版 (电影感摄影、柔和均匀光线、同一光源同一色温、真实皮肤不磨皮、 表情放松不摆拍、真实环境声无配乐、画面内不烧录水印字幕文字、 背景不出现可读文字)与接力规则(第 2 镜起从上一镜尾帧接续)。
- 抽卡派发与自动续跑:每轮最多 6 条成功视频(失败不计数), 超出的排队;本轮最后一条回报到即自动派下一轮;派发失败回排队 (最多 3 次,之后标为失败);30 分钟无动静的僵死轮次由下一轮 接管(看门狗每分钟巡查,启动时也会回收未回报条目);成片后不 再自动生成多余候选;每镜可抽 1–6 条候选。
- 选片:上报每条候选的文件、规格检查注、接缝分与真实感分; 自动模式下第一条完成的候选即为选定,后续更高分不自动替换。
- 终审:对每条选定片抽首 / 中 / 尾帧,按 15 类假信号检查 (塑料皮肤、表情僵、光线色温漂移、服装发型跳变、身份漂移、 接缝机位跳变、招牌乱码、道具消失、口型不清等),每镜给 pass / warn / fail 判定、真实感分、接缝分与具体剪辑修法 (含裁剪秒数)。规格检查(时长 / 分辨率 / 音频)与真实感 分开计分,规格通过不等于真实感通过;对白逐字核对明确标注为 “抽帧无法核口型、未验证”。
- 二次剪辑方案:按终审自动生成可执行精剪方案(每镜裁剪点、 接缝处硬切或 2–3 帧叠化、全片统一色温曝光、统一响度、轻颗粒 破塑料感);剪不了的镜头不硬剪,列入再抽清单;方案可人工编辑。
- 执行与定稿:按方案用 ffmpeg 真剪出完善版;用户点“定稿” 才算完;不通过镜头可一键再抽并重新补审。
- 发布文案:自动生成,内含“本片全部画面由 AI 生成,非实拍” 的 AI 标识;画面内不烧录任何水印或文字。
- 播放与导入:已成候选可播放、新生成自动收进、成片区整片 连播;支持从磁盘导入已有视频作为候选。
server.py 入口:HTTP 服务、看门狗线程、启动恢复
batch_plugin/
db.py SQLite 表结构与线程安全封装
planner.py 模板化分镜规划(可替换为模型规划)
prompts.py 锁定 + 真实感母版 + 接力规则的提示词组装
adapters.py 视频后端接口 + mock 适配器 + 供应商模板
engine.py 轮次引擎:6 条上限、自动续跑、回排队、僵死接管
review.py 终审:15 类假信号、规格与真实感分开计分
editplan.py 精剪方案生成(fail 镜头进再抽清单)
assemble.py ffmpeg 两段式渲染:裁剪 → 接缝链 → 颗粒
publish.py 发布文案(含 AI 标识)
httpapi.py JSON API 与静态页面
web/ 原生 JS 前端(无构建步骤)
数据表:projects(项目与三段锁定)、shots(分镜与完整提示词)、
takes(候选:状态 / 文件 / 评分 / 尝试次数)、rounds(轮次与
成功数)、reviews(终审判定)、edit_plans(精剪方案 JSON)、
final_videos(成片与定稿标记)。
| 方法与路径 | 说明 |
|---|---|
POST /api/projects |
创建项目(idea、genre、orientation、takesPerShot、autoMode) |
POST /api/projects/:id/plan |
分镜规划(shotCount、beats,可选每镜画面/对白) |
POST /api/projects/:id/locks |
写入三段锁定并重建全部镜头提示词 |
POST /api/projects/:id/launch |
派发抽卡(自动续跑直至成片或队列为空) |
POST /api/projects/:id/takes/:takeId/select |
手动选定某条候选 |
POST /api/projects/:id/review |
自动终审(首/中/尾帧 + 规格检查) |
POST /api/projects/:id/review-manual |
人工终审(覆盖判定、信号、裁剪点) |
POST /api/projects/:id/edit-plan |
按终审生成精剪方案 |
PUT /api/projects/:id/edit-plan |
人工编辑精剪方案 |
POST /api/projects/:id/polish |
按方案 ffmpeg 渲染完善版 |
POST /api/projects/:id/roughcut |
选定片硬切粗剪预览 |
POST /api/projects/:id/approve |
定稿某支成片 |
POST /api/projects/:id/reroll-failed |
一键再抽全部不通过镜头 |
POST /api/projects/:id/import/:shotId |
从磁盘导入候选(multipart,字段名 file) |
GET /media/... |
播放候选与成片 |
在 batch_plugin/adapters.py 中实现 VideoBackend.generate:
class MyVendorAdapter(VideoBackend):
def generate(self, prompt, out_path, orientation="landscape",
duration=10.0, meta=None):
# 调用供应商生成 10 秒视频,写到 out_path(mp4,H.264 + AAC)
# 成功返回 {"ok": True};失败返回 {"ok": False, "error": "..."}
# 失败时引擎会把该条目回排队,不计入本轮 6 条上限
...然后在 server.py 里把 MockAdapter() 换成你的适配器。
仓库里附带的 VendorAdapterTemplate 是填空模板,含必须遵守的
约定:时长 10 秒、方向一致、只写一个完整文件、失败时删掉残缺
输出、画面内不烧录水印字幕文字。
本插件的设计立场写死在两处,改动请三思:
- 画面内:不烧录任何水印、台标、字幕或文字,保持纯电影画面。
- 画面外:项目信息与发布文案中保留 AI 生成标识,发布文案 固定包含“本片全部画面由 AI 生成,非实拍。”写实风格的 AI 片子不标等于当实拍发,本插件不提供这种用法。
- 6 条 / 轮上限是参照原插件的派发约束,不是视频模型本身的 限制;真实供应商的配额与并发请以供应商文档为准。
- 分镜规划是模板化的:按开端 / 发展 / 转折 / 收尾的通用节拍
生成分镜骨架,提示词需要人工细化。
planner.py的plan_shots签名就是替换点,接模型规划时只换这一处。 - 终审的自动信号只有帧级启发式:亮度漂移、接缝差异、镜内 场景跳变、曝光突变。其余 11 类信号(身份、服装、道具、招牌 等)没有 OCR 与唇读模型,只能靠人工复核入口标记——这也是 页面上保留“人工标为不通过”的原因。
- 对白逐字核对做不了:抽帧无法核口型,终审报告里会明确写 “未验证”;发布前请人工听一遍确认台词清楚。
- 响度统一是线性归一(loudnorm 单次),色温统一是全片同一 档 eq,不是逐镜调色。
- 单机本地工具:SQLite + 本地文件,无多用户、无鉴权,不要 直接暴露到公网。
- mock 占位片画面是均匀的:故意让帧级自动检查通过,只用于 验证流程;它不是视频模型,也不能代表真实生成质量。
MIT,见 LICENSE。