本地视频编排工作台。一支视频从想法到成片的全部状态——设计、素材、生成作业、花费、编译产物、审阅记录——收在一个项目目录里;人、内置 agent、外部 MCP 客户端在同一份状态上工作;编译结果逐字节可复现。
不做服务端、不做账号、不做协作后端。出站的只有你自己配置的生成 provider(imagine / colab / 自定义 CLI)。
仓库自带一份 agent 技能,装一次之后任何 agent 都能按同一套规矩干活:
filmbench install # 装到 ~/.agents/skills/filmbench 与 ~/.claude/skills/filmbench
filmbench install --check # 看装没装
filmbench install --dir DIR # 装到指定目录
filmbench install --force # 覆盖技能内容:skills/filmbench/SKILL.md(真相模型、常用流程、升级顺序、提示词四条硬规则、controlnet 用法、
出错处理、钱与边界)。它和 docs/method.md 是同一套判断,前者给 agent 读,后者给人读。
遇到画面不达标时按顺序升级:改静帧 → 改提示词 → 上 ControlNet 灰模 → 换构图 → 换素材。 "东西长什么样"改静帧,"东西在哪、往哪动"上 controlnet——纯靠提示词硬拗空间关系是这几轮返工的根因。
硬规则:静帧先出再让它动;画面里不许有字;背景必须有自己的运动;动作要写接触关系;每镜分前景/中景/远景写。
四条能机器判的已经进 filmbench prompt lint(signage-leak / static-background / physics / layering)。
完整版:docs/method.md;给 agent 的硬边界:AGENTS.md。
前置:Bun ≥ 1.2、ffmpeg / ffprobe(brew install ffmpeg)。
cd filmbench
bun install
bun test # 24 例:哈希链 / 幂等 / 预算 / 真实 ffmpeg 出片 / MCP 客户端
bun run demo # 在 ./demo-project 造一个 4 镜项目并跑完作业
(cd packages/cli && bun link) # 把 filmbench 装进 PATH(~/.bun/bin),只需一次
cd demo-project
filmbench status # 没做 bun link 的话用 bun ../packages/cli/src/cli.ts status
filmbench status --json # 结构化状态(DSH 看板读的就是它)
bun ../packages/cli/src/cli.ts build # build/final.mp4
bun ../packages/cli/src/cli.ts storyboard # build/storyboard.html(直接双击打开)
bun ../packages/cli/src/cli.ts ledger桌面端(Electron):
bun run dev # = bash scripts/dev.sh:自检依赖 → 备好 demo 项目 → 起界面
bash scripts/dev.sh --check # 只做环境自检
bash scripts/dev.sh --reset # 重建 demo 项目
bash scripts/dev.sh --project /path/to/project如果 bun install 之后 Electron 报 Electron failed to install correctly(二进制没下全),补一次:
cd node_modules/.bun/electron@*/node_modules/electron
rm -rf dist path.txt
curl -fsSL -o /tmp/electron.zip https://github.com/electron/electron/releases/download/v33.4.11/electron-v33.4.11-darwin-arm64.zip
mkdir -p dist && unzip -q /tmp/electron.zip -d dist
printf 'Electron.app/Contents/MacOS/Electron' > path.txtintegrations/dsh/ 是一个 DSH 插件:在 DSH 侧栏加一个 filmbench 图标,点开是只读看板——
项目列表、镜头/作业状态、预算、编译就绪与否。数据全部来自 filmbench status --json,
面板不写 journal、不触发付费作业。
- 安装:用 DSH 的插件管理器装本地目录
integrations/dsh;装一次,重启 DSH 后侧栏出现入口。 - 只读:出片、审片、付费作业仍然走 CLI 或 agent(付费要人批)。
- Host 半边要能起
bun。找不到时在插件 Config 里给bunPath;CLI 不在仓库里时给cliPath。 - 数据契约就在
filmbench status --json里,CLI 与面板读的是同一份状态。
imagine 的凭据放在 ~/.imagine/azure.env,显式给 engine:
export FILMBENCH_IMAGINE_ENV_FILE=~/.imagine/azure.env # dev.sh 会自动带上并打印
filmbench init /tmp/night --name night --title "夜行试片" --width 1344 --height 768 --budget 3
cd /tmp/night
filmbench shot add --id shot-river --title "空镜 · 江边步道" --frames 120 \
--provider imagine --task image \
--prompt "cinematic still: empty riverside promenade at night, wet asphalt, teal and amber" \
--param model=MAI-Image-2.6-Flash --param width=1344 --param height=768
filmbench jobs submit --all
filmbench jobs run # 真调 imagine(约 13s,价格未知会如实记账)
filmbench select --shot shot-river && filmbench review --shot shot-river
filmbench build # 1344×768 / 210 帧 / 7.000s试过的结论:
MAI-Image-2.6-Flash(Azure)可用,height 必须 ≥ 768,所以 16:9 取 1344×768。doubao-seedream-4/5、doubao-seedance-*在这台机器的 ARK 账号上 404(无端点/无权限),不是配置问题。- 图片尺寸和成片不一致是允许的(编译期会 scale+pad);视频尺寸不一致会直接报
spec-mismatch。 - imagine 没有价格数据,账本记
priceUnknown而不是 0;要定价在镜头参数里给unitPrice。
内置 agent 已可用(pi 1.4.0):filmbench agent "读一下 project_state,告诉我还能不能出片"。
它会通过同一张工具表调用引擎,写操作以 actor=agent 落事件。
filmbench script set --file script.md [--logline …] | show filmbench cover --shot ID filmbench info filmbench check filmbench doctor 一次性体检:ffmpeg / ffprobe / electron / pi / colab(ADC) / imagine(已配置模型数、凭据来源)。
把工具接到别的 agent(Claude Code / Codex / pi):
{ "mcpServers": { "filmbench": { "command": "bun",
"args": ["<repo>/packages/cli/src/cli.ts", "mcp"],
"cwd": "<你的项目目录>" } } }filmbench/remotion 接的是剪辑形态的 Remotion 工程(不是生成):交叉溶解与滑入、
下三分之一字幕、片名与结尾压字,全部 React 动画,由 provider 渲染成镜头产物。
provider 会用 ffprobe 实测产物尺寸与帧数再登记,composition 尺寸写错会被编译期的 spec-mismatch
拦下。跑通的参考工程在 ~/github/filmbench-demo/remotion-edit(12 秒,附 4 小节 scorekit 配乐,
花费 0,filmbench check 全绿)。
接进 filmbench 只要四个参数(cwd 支持绝对路径):
filmbench shot add --id edit --title '剪辑' --frames 360 \
--provider filmbench/remotion --task render \
--param cwd=/abs/path/to/remotion-edit --param entry=src/index.ts --param composition=Editexamples/python-kid-film/ 是给小学三年级看的 Python 启蒙片工程:一个叫小蟒的角色,
用六个生活类比讲六个概念(变量 / 列表 / 字典 / 条件 / 循环 / 函数),全程中文旁白 + 大字幕。
画面由 Remotion + Three.js 渲染,旁白本地 Qwen3-TTS,配乐 scorekit 循环并由编译链自动避让。
成片规格见该目录 README(1920×1080 · 30fps · 约 3 分 08 秒,全部本地 0 CU)。
cd examples/python-kid-film
filmbench status # 结构化状态也可用 --json(DSH 看板读的就是它)
filmbench build && filmbench checkDSH 侧栏插件会把工作区里带 journal/ 的工程列出来(含 demo-project/ 与这个例子)。
| 最初的要求 | 现状 | 位置 |
|---|---|---|
| 一个项目,两套协议合并 | ✅ 单一命名空间 filmbench/v1:Film 意图 / Event 历史 / Ledger 账本,共用一个 schema 包与一个时间基 |
packages/protocol |
| 去掉网络性质(服务端形态) | ✅ 无 Worker / 无 D1 / 无 R2 / 无 SSO;项目目录即全部。出站只剩 provider | docs/architecture.md |
| Electron | ✅ 主进程 → Bun engine 子进程 → 渲染层(IPC,contextIsolation),单实例 | apps/desktop |
| 成片预览 | ✅ 「成片预览」直接播最近一次渲染;③ 出片完成后自动打开;总览的成片记录可点播 | apps/desktop |
| 界面支持编排 | ✅ 缩略图网格 · 项目切换 · 分镜详情抽屉 · 一键提交/执行/出片/故事板 · 总览→时间轴拖动行即可重排镜头(pointer 事件,拖动 4px 起算,未拖就是点击开详情) | apps/desktop/src/renderer |
| 分镜详情(vflow 式) | ✅ 意图 / 绑定 / 主体版本 / 参考 / 候选与选定 / 产物与路径 / 依赖 / 检查 / 花费 / 时间轴 / 作业历史 / 事件;内置预览(视频/图片直接播,可接着截图标注) | packages/core/src/detail.ts |
| 影片总览与剧本 | ✅ filmbench script set/info,界面「总览」看封面、剧本、镜头表、渠道策略与三步流程 |
packages/core · apps/desktop |
| 片名卡 / 封面 | ✅ filmbench/title 四层排版(kicker/title/rule/subtitle/credits)+ 三个预设 + 剧照压暗/暗角/颗粒;filmbench cover --shot 设封面 |
packages/providers · 设计规范 |
| 渠道策略 | ✅ .filmbench/policy.json 限制 agent 只能用白名单 provider;视频渠道默认需人工 --approve |
packages/core/src/policy.ts |
| 一键验收 | ✅ filmbench check:事件链/剧本/产物/选定/审阅/提示词/编译/审计/预算/封面,exit 1 即不通过 |
packages/core/src/check.ts |
| agent 唤起界面 | ✅ filmbench ui --shot <id>;MCP 工具 open_ui;单实例聚焦并定位镜头 |
packages/tools |
| 截图 + 框选标注 | ✅ ⌘⇧S 截图 → 框选/矩形/箭头/文字 → 复制图片 或 保存到 review/ 并复制路径 |
apps/desktop |
| 调用 imagine 生成素材 | ✅ 图片:MAI-Image-2.6-Flash 真跑通;🎬 视频:provider + 首帧 + 视频提示词已接好,付费云视频需显式 IMAGINE_SPEND_OK=1 授权 |
packages/providers |
| 配乐 | ✅ filmbench/scorekit:scene.yaml → WAV,作为 role: music 的音轨混入成片(响度归一 + 淡入淡出) |
packages/providers |
| 调用 colab | ✅ 可复用会话:装一次环境/模型,之后复用;30 分钟空闲自动停(本地 reap + VM watchdog) | workflows/comfy-h3 · packages/providers/src/colab.ts |
| 内置 pi agent 核心 | ✅ pi 1.4.0 已装,真会话跑通(调用 project_state 并正确汇总) |
packages/agent |
| 消耗统计 | ✅ 账本 = 事件投影:按 provider / 计量 / 镜头聚合,未知价格单列 | packages/core/src/cost.ts |
| 全功能 stdio MCP | ✅ 与 agent 同一张工具表;真 MCP 客户端测试覆盖读写 | packages/mcp |
| sqlite + 本地文件 | ✅ 文件事件日志是真相,sqlite 是索引,删库可重建 | packages/core/src/{journal,db}.ts |
| 独立审计故事板 | ✅ 单文件 HTML:网格铺开 + 顺序连线 + 逐镜审计 | packages/storyboard-audit |
- 真相是事件日志,不是 yaml 也不是 sqlite。 每次改动都是一个带 actor /
operationId/baseRevision的 typed 事件;filmbench verify能检出任何篡改;删掉.filmbench/index.db可从事件重建全部状态。 - 只有一个写入路径。 GUI、CLI、MCP、内置 agent 都调同一个
Service;不存在第二条写逻辑。 - 时间基只有整数帧。 秒只在展示与 ffmpeg 参数边界出现;产物帧数与镜头声明不一致,编译阶段就拒绝。
- 选片与审阅是独立的一步。 有产物不等于能出片:
select之后还要review批准,未经审阅的候选进不了成片。
bun run dev 打开后:
- 左栏:项目卡(点开切换项目 / 打开目录,非项目可直接初始化)、镜头数/帧数/就位/花费、作业队列、快捷键提示
- 中间:镜头网格,作业完成后自动抽首帧做缩略图;点任意卡打开右侧详情
- 详情抽屉:顶部「候选画面」是横向缩略图队列(本镜生成出来的版本,点图即切换进片版本,+ 生成新一版);下面一条动作栏按"下一步"给出唯一主按钮(提交 / 选定这一版 / 批准 / 再生成);再往下是「参考图」输入槽位(≤5,带用途与缩略图)、意图与双提示词、绑定、检查、产物、时间轴与依赖、作业历史、事件。候选是输出,参考是输入,两个数量本来就不一样
- 顶部动作:① 提交待办(排队,不花钱)/ ② 执行队列(调用模型,会花钱)/ ③ 出片(本地 ffmpeg,不花钱)/ 成片预览 / 验收 / 总览 / 审计故事板 / 截图。下方常驻一条流程条,实时显示「待生成 N 镜 → 队列 N 个 → 就绪 N/M 镜」,点击任一步就是对应动作
- 截图标注:
⌘⇧S→ 框选(可裁剪)、矩形、箭头、文字,四种颜色,撤销;「复制图片」进剪贴板,「保存到项目」落到<project>/review/markup-*.png并把路径复制到剪贴板,可以直接甩给 agent - 从 agent 唤起:
filmbench ui --shot shot-003,或让 agent 调open_ui工具;已在运行时不会开第二个窗口,而是聚焦并跳到该镜头
filmbench colab up --workflow comfy-h3 # 建会话(或复用)+ 上传工作流包 + setup(persist 命中则跳过)
filmbench colab status # 会话、存活、空闲分钟数、CU 余额
filmbench colab reap # 空闲 ≥ 30 分钟就停
filmbench colab down # 立即停- 工作流在
workflows/comfy-h3/:workflow.json(GPU/依赖/模型/持久化)、setup.py(幂等安装)、run.py(起 ComfyUI → 提交图 → 取结果)、watchdog.py(VM 内 30 分钟空闲自动unassign)、comfy/workflow.json(ComfyUI API 图)。 - 只装一次:
workflow.json的哈希写进 persist 目录的 marker;模型文件存在就不重下。persist 建议指向 Google Drive(colab drivemount /content/drive),VM 被回收也不重装。 - 只建一次会话:作业之间复用;只有工作流文件变了才会 stop + 重建。
- 30 分钟空闲自动停:engine 每 60 秒 reap 一次;即使 filmbench 关掉,VM 里的 watchdog 也会自己 unassign。
作业里指定工作流:--param workflow=comfy-h3;换 GPU:filmbench colab up --gpu A100。
状态(在本机实测):会话创建、上传、解包、幂等 setup(clone ComfyUI + 模型检查 + watchdog)、心跳、状态文件、空闲停止全部跑通;
workflows/comfy-h3里的models[].url还是占位地址,没有真实权重就不会出视频——把地址换成你的 ckpt(或先手工放进 persist 目录)后即可。 另:colab exec的退出码恒为 0,远端脚本必须自己打印__FB_OK__/__FB_FAIL__,filmbench 靠这个判断成败。 长任务(装环境、下模型、推理)一律后台执行 + 轮询(launch.py/check.py),否则 colab exec 的长单元格会掉连接。
workflows/t2v-diffusers(ModelScope T2V 1.7B,diffusers)在 A100 上端到端跑通:
filmbench colab up --workflow t2v-diffusers --gpu A100 # 会话 + 模型缓存
filmbench shot add --id t2v --title A100生成 --frames 60 --provider colab --task video \
--prompt "a courier places a small parcel on a wet doormat at night, cinematic" \
--param workflow=t2v-diffusers --param numFrames=16 --param steps=16
filmbench jobs submit --shot t2v && filmbench jobs run # 后台生成 → 回传 → 内容寻址
filmbench colab down实测:A100(5.30 CU/hr),16 帧 / 16 步 → 2 秒 1344×768 H.264,单次作业端到端约 2 分钟(模型已缓存时)。
输出原生 256×256,run.py 直接放大 + 补边到成片规格(会看到左右黑边),不适合最终交付画质,适合"先用起来"。
音乐不是"背景音随便垫一下",它和画面一样是可审计的产物:
filmbench shot add --id score --title "配乐 · 雨夜铺底" --frames 600 --role music \
--provider filmbench/scorekit --task music \
--param scene=music/score.yaml --param volume=0.9 --param fadeIn=2 --param fadeOut=3
filmbench jobs submit --shot score && filmbench jobs run
filmbench select --shot score && filmbench review --shot score
filmbench build # 混入成片:AAC 48kHz 立体声scene.yaml用 scorekit 的场景 DSL(key/tempo/bars/harmony/tracks/clips/story/performance);需要精确写谱时用clips(pitched 给绝对音高、percussion 给 GM 鼓件),按拍位落音;- 改了 scene.yaml 会让音轨过期:被引用文件(scene / cues / 脚本)的内容哈希也算进输入,改文件必须重跑;
- 音轨镜头
role: music不占画面时间,帧数统计与故事板卡片都会把它排除,只出现在音轨泳道; - 混音链:
loudnorm(I=-18,TP=-2)→volume→ 淡入/淡出,多个音轨走amix; - 产物同样走内容寻址、选定、审阅,
filmbench check会把它算进"每镜有产物"。
filmbench shot add --id vo-open --title "旁白 · 没有地址" --frames 90 --role voice \
--provider filmbench/qwentts --task voice --narration "这一单,没有地址。" \
--param startFrame=60 --param voice=Serena --param emotion=low- 本地 Qwen3-TTS 生成(qwentts skill),产物是音频,时长以实际生成为准;
- 男声独白走 base 克隆:
--param model=base --param referenceAudio=<speaker.mp3>(默认用 skill 里的「锅哥男声」), 不传则走 CustomVoice 预置音色(--param voice=Serena); startFrame决定入点,必须在剪辑点上(与音乐卡点同一套校验),否则build拒绝;- 有旁白时音乐自动避让:
sidechaincompress用旁白做侧链压音乐,旁白 -16 LUFS / 音乐 -18 LUFS; build会顺带写出build/narration.srt(按句切分、按字数分配时间,近似字幕,不做强制对齐)。
音乐不是"铺上去就行",剪辑点要落在乐句边界上:
filmbench shot param --id score --set fit=exact \
--set cues=music/score.cues.json --set tolerance=0.06
filmbench cues # 打印每个剪辑点最近的小节边界与偏差- 校验对象是每个画面剪辑点:必须在容差内落在某个段落边界,否则
build直接拒绝(cues-misaligned); filmbench check里也有一项「音乐卡点」,能过就说明音画同步是可证明的,不是听感;- 小节的秒数由
tempo × beatsPerBar算出,所以改 tempo/bars 或改镜头时长都会立刻被这项检查抓出来。
filmbench init <dir> --name demo --width 1920 --height 1080 --fps 30 --budget 5
filmbench shot add --id s1 --title 开场 --frames 90 --provider filmbench/colorcard --task clip --prompt "..."
filmbench plan # 每镜状态 + 下一步该做什么
filmbench jobs submit --all # 过预算闸门
filmbench jobs run # 执行队列(串行)
filmbench select --shot s1 # 选定(默认取最新产物)
filmbench review --shot s1 # 批准;--reject 否决
filmbench build # 编译 + ffmpeg 出片
filmbench storyboard [--embed] # 审计故事板 HTML(--embed 内联预览图,可单文件外发)
filmbench ledger --json
filmbench colab up|status|down|reap
filmbench ui [--shot ID] # 打开桌面界面(单实例)
filmbench script set --file script.md [--logline …] | show
filmbench cover --shot ID
filmbench info
filmbench check
filmbench doctor
filmbench status | verify | reindex | export
filmbench mcp # stdio MCP(给外部 agent)
filmbench agent "<任务描述>" # 内置 pi agent
filmbench demo [dir]- 架构 —— 模块、真相与投影、单写路径、编译、预算、provider 契约、验收面与非目标
- 工作流与 provider 契约 —— 声明式 workflow(defaults/capabilities)、H3 i2v/ref2v、Three.js 录制、Remotion/HyperFrames
- 片名卡与封面设计 —— 四层结构、三个预设、怎么根据剧情写标题
- 参考图与 prompt 规范 —— 5 张上限、槽位语义、prompt 结构、机器判定规则
- 审计故事板 —— 投影 schema、检查登记表、网格与连线规则
时间轴拖拽编辑;分支/提案(协议留了 ref,当前只有 main);imagine / colab 的真实凭据端到端验证;Electron 打包与签名。
MIT