joeseesun/qiaomu-cut
GitHub: joeseesun/qiaomu-cut
一款纯前端的在线视频编辑器,在浏览器本地即可完成多轨剪辑与MP4导出,确保素材数据绝不上传。
Stars: 0 | Forks: 0
# 乔木剪辑 Qiaomu Cut
**中文** | [English](#english)




**在线体验:** [cut.qiaomu.ai](https://cut.qiaomu.ai)(已上线)
**已验证:** `npm run build` ✅ · `npm run test` 36/36 ✅ · E2E 全流程(导入→剪辑→导出)+ ffprobe 校验 ✅(见「实测验证」)
## 这是什么
一个纯前端、可自部署的在线视频编辑器(类似剪映网页版的核心剪辑链路)。
全部剪辑、预览、渲染都在浏览器本地完成:没有账号体系、没有云存储、没有任何素材上传。
## 为什么值得用
| 痛点 | 乔木剪辑的做法 |
|---|---|
| 剪个短片要装几百 MB 的客户端 | 打开网页即用,零安装 |
| 网页版剪辑工具要登录、要上传素材 | 零登录;素材全程不离开浏览器(可在 DevTools Network 面板自证) |
| 公司电脑装软件要审批、内部录屏不敢上云 | 纯本地处理,IT 无可指摘 |
| SaaS 剪辑器不可控 | MIT 开源,静态文件可部署到任意服务器 |
## 核心能力
| 能力 | 用户得到什么 |
|---|---|
| 素材导入(视频/音频/图片) | 拖入即解析:时长、宽高、首帧缩略图,自动保存到 IndexedDB,刷新不丢 |
| 多轨时间轴 | 1 文字轨 + 2 视频轨 + 1 音频轨;拖拽移动、裁剪出入点、分割、删除、波纹删除、吸附对齐、缩放、右键菜单 |
| 剪辑操作 | 撤销/重做(50 步)、快捷键(Space 播放、S 分割、⌫ 删除、⌘Z 撤销、←/→ 逐帧) |
| 实时预览 | 主时钟驱动多元素同步(漂移 >80ms 自动重同步),Canvas 合成画面与文字层 |
| 文字叠加 | 样式预设、字号/颜色/描边/透明度/对齐、画布上直接拖动位置、双击改内容 |
| 本地导出 MP4 | ffmpeg.wasm 浏览器端重编码(H.264 + AAC,1080P/720P),进度与取消;文字经 canvas 预渲染 PNG 烧录,与预览逐像素一致 |
| 隐私与离线 | 无上传请求;引擎与素材缓存后第二次访问可离线使用 |
## 快速开始
### 最快路径
git clone https://github.com/joeseesun/qiaomu-cut.git
cd qiaomu-cut
npm install # postinstall 会把 ffmpeg 引擎文件(约 31MB)复制到 public/ffmpeg*
npm run dev # 打开 http://localhost:5173
打开编辑器后点「载入示例素材」,10 秒体验完整流程:入轨 → 分割 → 加文字 → 播放 → 导出 MP4。
## 产品巡游
| 界面 | 说明 |
|---|---|
|  | 多轨时间轴:视频胶片缩略图、音频轨、胶片级裁剪手柄 |
|  | 文字叠加:画布选中框可拖动,双击直接改内容 |
|  | 浏览器端导出 MP4:进度、取消、本机完成不上传 |
## 工作流与架构
UI 层(React 19) 页面与组件,不含业务规则
状态层(zustand + immer) 单一 project JSON,编辑操作全为纯函数 action
媒体层 主时钟(performance.now) 驱动 video/audio seek,Canvas 合成
导出层(ffmpeg.wasm) tracks JSON → filter_complex 生成器(纯函数可单测)→ 重编码
持久层(IndexedDB) 工程 JSON + 素材 blob 自动保存
设计决策与产品规格全部公开:[`PRD.md`](PRD.md)(11 章产品需求)、[`DESIGN.md`](DESIGN.md)(设计系统)。
### 关键技术点
- **文字烧录**:ffmpeg.wasm 官方 core 不含 drawtext(依赖 freetype)。改用浏览器 canvas
预渲染全尺寸透明 PNG(与预览同一绘制函数),再 `overlay + enable` 窗口贴合——中文用系统字体,
无字体文件依赖,烧录与预览逐像素一致。
- **多线程挂死兜底**:core-mt 在部分环境(无头/旧 Safari)会静默挂死。内置看门狗:
45s 无进度自动终止并降级单线程 core 重试;也可用 `localStorage.qmcut:coreFlavor = 'st'` 强制单线程。
- **主时钟规则**:预览不允许多个媒体元素自由播放后漂移,全部跟随唯一主时钟,偏差 >80ms 立即重同步。
## 项目结构
src/
types/project.ts # 工程数据模型(version 化)
store/projectStore.ts # zustand + immer:全部编辑 action + 撤销/重做
lib/
timeline.ts # 吸附/插入推开/波纹删除等纯函数
importMedia.ts # 导入管线(校验/解析/缩略图)
persistence.ts # IndexedDB 工程与素材 blob
preview/engine.ts # 主时钟、媒体池、Canvas 文字绘制
export/filterBuilder.ts # tracks JSON → filter_complex(纯函数)
export/exporter.ts # wasm FS 管理、进度、挂死看门狗、ST 降级
components/editor/ # 顶栏/素材面板/预览/属性/时间轴/导出弹窗
scripts/e2e.mjs # puppeteer 端到端验收
## 部署
静态站点,可部署到任意静态服务器。**必须配置两条跨域隔离响应头**(SharedArrayBuffer 前提):
add_header Cross-Origin-Opener-Policy same-origin always;
add_header Cross-Origin-Embedder-Policy require-corp always;
npm run build # 产物在 dist/,含 ffmpeg 引擎文件(public/ffmpeg* 已并入)
缺头时编辑器会整页提示「服务器未配置跨域隔离头」,导出按钮禁用,其余功能可用。
## 实测验证
- `npm run test`:36/36 通过(时间轴吸附/插入/波纹、导入校验、预览分层、filter 图生成)
- E2E(`scripts/e2e.mjs`,本机 Chrome):载入示例 → 入轨 → 5s 分割删除 → 加文字 →
播放预览 → 导出 720P,产物经 ffprobe 验证 `h264 1280x720 + aac, duration 15.000s`,
抽帧目检中文文字烧录位置正确
- 导出速度参考:10s 720P 单片段,单线程 core 约 17.5s(Apple Silicon 实测)
## 限制与边界
- 单项目成片 ≤ 5 分钟、单素材建议 ≤ 500MB(浏览器 wasm 内存约束,超出会明确提示)
- 仅桌面端 Chrome / Edge / Safari 最新版本;移动端为只读引导屏
- 无账号、无云同步:工程保存在当前浏览器 IndexedDB,换设备不会带走(工程文件导出在路线图中)
- 不做:关键帧动画、转场、滤镜、抠像、多人协作(见 PRD 第九章优先级)
## Troubleshooting
- **导出长时间 0% 或失败**:看门狗会自动降级单线程 core 重试;仍失败可在 Console 执行
`localStorage.setItem('qmcut:coreFlavor','st')` 后刷新重试
- **导出按钮禁用**:当前环境未开启跨域隔离(检查 COOP/COEP 响应头或更换浏览器)
- **mov 文件导入失败**:浏览器无法解码该编码,请先转码为 H.264 的 mp4
## 关于
由 [向阳乔木](https://qiaomu.ai/) 开发与维护。
- Blog: [blog.qiaomu.ai](https://blog.qiaomu.ai)
- 推荐: [tuijian.qiaomu.ai](https://tuijian.qiaomu.ai)
- X: [@vista8](https://x.com/vista8)
- GitHub: [@joeseesun](https://github.com/joeseesun)
- 微信公众号:向阳乔木推荐看
问题与建议欢迎开 Issue。当前为 v0.1 首发版本,暂无正式贡献流程。
## License
[MIT](LICENSE) © 向阳乔木
# Qiaomu Cut
Edit videos right in your browser — a CapCut-web-style core editing flow, fully client-side.

**What you get**
- Import video/audio/image locally; metadata + thumbnails; autosaved to IndexedDB
- Multi-track timeline (1 text + 2 video + 1 audio): drag, trim, split, ripple delete, snap, zoom, undo/redo (50 steps)
- Real-time preview driven by a single master clock (media elements re-synced when drift > 80ms), canvas-composited text layer
- Text overlays: presets, size/color/stroke/opacity/align, drag on canvas, double-click to edit
- In-browser MP4 export (H.264 + AAC, 1080P/720P) via ffmpeg.wasm, with progress and cancel;
text is burned in via canvas-pre-rendered transparent PNGs (pixel-identical to preview, no font files needed)
- Privacy-first: no accounts, no uploads — media never leaves the browser
**Quick start**
git clone https://github.com/joeseesun/qiaomu-cut.git
cd qiaomu-cut
npm install
npm run dev # http://localhost:5173
**Verified**: `npm run build` ✅ · `npm run test` 36/36 ✅ · Puppeteer E2E (import → edit → export)
with ffprobe validation (`h264 1280x720 + aac, 15.000s`) ✅
**Requirements**: latest desktop Chrome / Edge / Safari. Export requires SharedArrayBuffer —
your static host must send `Cross-Origin-Opener-Policy: same-origin` and
`Cross-Origin-Embedder-Policy: require-corp` (see 部署/Deploy section above).
**Limits**: project ≤ 5 min, asset ≤ 500MB recommended; no cloud sync (projects stay in local IndexedDB);
no keyframes/transitions/filters yet (roadmap in `PRD.md`).
Docs: product spec [`PRD.md`](PRD.md) · design system [`DESIGN.md`](DESIGN.md).
Maintained by [向阳乔木 (Qiaomu)](https://qiaomu.ai/) · [MIT License](LICENSE).
手动配置与常用命令
npm run build # 类型检查 + 产物构建(dist/) npm run preview # 本地预览构建产物 npm run test # vitest 单元测试(时间轴/导入/预览/导出纯函数) node scripts/e2e.mjs # 端到端验收(需要本机装有 Chrome) node scripts/debug-export.mts # 用本机 ffmpeg 复现导出命令,排查 filter 图标签:ffmpeg.wasm, React, Syscalls, WebAssembly, 本地渲染, 自动化攻击, 视频编辑器