React + MCP 实战:为开源视频编辑器添加 AI Agent 可操作的时间线标记
开发视频编辑器时,播放、裁剪、字幕和导出往往最先得到关注。真正开始剪长视频后,却会频繁遇到另一类需求:
“这里开始第二章。”
“这个镜头要对齐音乐重拍。”
“这一段保留,先记下修改意见。”
最近,我在开源项目 Timeline Studio 中补齐了时间线标记功能,并进一步接入 CLI、MCP 和 Agent Skill。用户可以在浏览器里操作标记,AI Agent 也能通过命令读取、添加和修改它们。
本文结合这次实现,介绍从 React 交互到项目数据,再到 Agent 命令接口的完整设计,以及区间吸附、重复 ID、浮点精度等容易踩坑的细节。
项目入口:
Timeline Studio 使用 React 和 Vite 构建,是一个本地优先的浏览器视频编辑器。本文涉及的 Agent 标记能力已随 Skill v1.0.7 发布。
先定义标记需要表达什么
这次实现支持四种标记:
| 类型 | 用途 | 时间字段 |
|---|---|---|
marker | 节拍、动作点、切镜头提示 | time |
chapter | 章节或主题起点 | time |
range | 待处理或待复查区间 | time、endTime |
note | 与时间关联的修改意见 | time |
它们共享同一个数据模型:
{
"id": "product-review",
"type": "range",
"time": 5,
"endTime": 8,
"title": "产品展示",
"notes": "检查字幕出现时机",
"color": "violet"
}
时间统一使用项目时间轴上的秒数,标题和备注支持中文等 Unicode 文本。
这些数据保存在项目的 timelineMarkers 中,随可移植的 .timeline 项目一起保存。
数据层有一个需要提前确定的约束:标记不参与媒体时长计算。
例如,一个 60 秒的视频,在 90 秒处添加规划标记,不能让导出视频延长到 90 秒。章节标记也不会自动生成画面标题或 MP4 容器章节。
这让标记成为独立的项目注释。Agent 只需要整理章节或补充备注时,直接生成新的项目文件即可,无需重新渲染视频。
React 交互:默认紧凑,需要时展开
时间线的垂直空间很宝贵。如果标记始终占据独立轨道,长标题和区间会持续挤压素材轨道的可见区域。
最终采用的交互是:
- 默认将标记合并到刻度轴,显示为紧凑旗标。
- 点击工具栏旁的展开按钮,显示标题和区间跨度。
- 按
M在播放头位置快速添加标记。 - 按
Shift + M打开标记管理面板。
拖动过程中,组件使用临时预览状态展示位置变化,松开指针后再提交项目修改。用户按下 Escape 或发生取消事件时,可以丢弃这次拖动。
这样的状态划分,让连续的鼠标移动与最终的项目修改拥有清楚的边界。
吸附阈值应该跟随屏幕距离
如果使用固定的时间阈值,例如“距离目标小于 0.2 秒就吸附”,不同缩放级别下的体验会差很多。
时间线放大后,0.2 秒可能占据很宽的屏幕距离;缩小后,它又可能几乎不可见。
因此,实现中先定义像素阈值,再换算为秒数:
const thresholdSeconds =
10 / railWidth * timelineDuration;
其中:
10是吸附距离,单位为像素。railWidth是时间轨道宽度。timelineDuration是该轨道对应的时间跨度。
标记复用时间线已有的吸附点集合,可以对齐播放头、素材边界和其他标记,并显示共同的对齐辅助线。按住 Alt 可以临时绕过吸附。
这个处理能让用户在缩放时间线后,仍然获得接近一致的拖动手感。
区间移动,需要同时考虑两个端点
区间吸附比单点标记多一个约束:整体移动时必须保留长度。
假设一个区间原本是 5–8 秒,长度为 3 秒。如果结束端吸附到 12 秒,新的范围应该是 9–12 秒。
核心计算可以简化为:
const nextStart =
movingEdge === "end"
? targetTime - duration
: targetTime;
const nextEnd = nextStart + duration;
实际实现会分别计算起点、终点的吸附候选,选择距离更近的结果。
如果只修正一个端点,就容易将“整体移动”意外变成“缩短或拉长区间”。因此,整体平移和调整区间终点需要分别处理。
让 Agent 使用同一套命令逻辑
浏览器交互完成后,还需要解决 Agent 如何操作的问题。
这次没有为 MCP 再实现一套标记增删改,而是采用下面的结构:
Agent Skill
↓
CLI / MCP
↓
共享项目命令引擎
↓
新的 .timeline 项目
各层职责分别是:
| 层级 | 职责 |
|---|---|
| Skill | 描述任务流程、时间依据与验证要求 |
| CLI | 提供本地命令入口 |
| MCP | 将已有命令能力暴露给 Agent |
| 命令引擎 | 执行校验、修改、版本检查和差异计算 |
MCP 适配层调用现有 CLI 运行器,实际编辑逻辑集中在共享命令引擎中。后续修复校验规则时,两种调用方式可以共同受益。
读取项目和标记的命令如下:
npm run agent -- project.inspect /projects/input.timeline
npm run agent -- marker.inspect /projects/input.timeline
也可以查询单个标记:
npm run agent -- marker.inspect \
/projects/input.timeline product-review
MCP 对应提供只读工具:
timeline_marker_inspect
写入操作包括:
marker.add
marker.update
marker.delete
一个完整的 Agent 修改示例
假设项目检查结果显示当前版本为 0,已有一个 ID 为 product-review、范围为 5–8 秒的标记。
用户希望将它移动到 9 秒,并在 11 秒添加一条结尾修改意见,可以生成下面的计划:
{
"schemaVersion": 1,
"project": "/projects/input.timeline",
"baseRevision": 0,
"operations": [
{
"id": "move-product-review-v1",
"type": "marker.update",
"markerId": "product-review",
"time": 9
},
{
"id": "add-ending-note-v1",
"type": "marker.add",
"markerId": "ending-note",
"markerType": "note",
"time": 11,
"title": "结尾节奏",
"notes": "用户意见:结尾多留一点呼吸",
"color": "rose"
}
],
"output": {
"project": "/projects/output-marked.timeline"
}
}
实际使用时,路径、版本号和标记 ID 都应替换为检查得到的值,备注也应来自真实需求。
这里有两个容易混淆的地方:
id标识一次操作,markerId标识项目中的标记。type表示操作类型,markerType表示新增标记的类型。
更新区间时只提供 time,会保持区间长度。上面的操作因此将 5–8 秒移动为 9–12 秒。
准备好计划后,先检查结构,再预览差异:
node skills/edit-timeline-studio/scripts/validate_edit_plan.mjs \
/projects/markers-plan.json
npm run agent -- project.diff /projects/markers-plan.json
确认结果符合预期,再执行:
npm run agent -- project.run /projects/markers-plan.json
npm run agent -- marker.inspect \
/projects/output-marked.timeline
语义差异会列出新增、删除和修改的标记,并提供修改前后的数据。对于只移动区间的任务,预期变化应该集中在标记时间,而不是素材轨道。
版本、事务和文件保护需要落实到代码
Skill 会要求 Agent 先检查项目、预览差异,再应用操作。命令引擎则负责实际保护:
baseRevision不匹配时返回REVISION_CONFLICT。- 已执行的操作 ID 再次提交时,不重复应用。
- 一批操作中出现失败时,不写出部分修改结果。
- 输出路径不能覆盖输入项目或已有文件。
这种分工能让工作流程和执行约束相互配合。
例如,Agent 收到版本冲突后,应重新读取项目并审查计划,而不是直接把版本号改成最新值后继续执行。
两个在批量操作中暴露的问题
第一个问题是旧项目中的重复 ID。
假设导入数据里有三个标记都叫 x,读取时将它们规范化为:
x
x-2
x-3
如果每执行一次操作都重新生成 ID,那么删除第一个标记后,后续标记的身份可能发生变化。接着执行“更新 x-2”,就可能修改错误的对象。
修复方式是在事务开始时,对项目副本统一规范化一次,并固定这组 ID。后续操作按固定身份查找,不再根据数组位置重新分配。
生成重复 ID 的后缀时,也需要给后缀预留长度,避免规范化后的结果超过字段长度限制。
第二个问题是浮点精度。
一个位于 1000 秒附近、长度为 1 毫秒的区间,经过减法计算后,结果可能略小于 0.001。如果直接严格比较,就会把合法移动误判为区间过短。
这里需要处理浮点误差,同时继续拒绝真正无效的区间输入。单纯放宽所有时间校验,会带来新的数据问题。
Agent 的时间点必须有依据
浏览器拖动可以使用像素吸附,CLI 和 MCP 接收的则是精确秒数。
Agent 应从项目检查结果、片段边界或经过验证的音频分析中获得时间信息。
如果使用源视频里的事件时间,还需要考虑裁剪与变速。恒定倍速下可以这样换算:
项目时间 =
片段在项目中的起点
+(事件源时间 - 源裁剪起点)/ 播放倍速
启用速度曲线后,需要使用对应的源时间映射,不能简单除以平均速度。
音乐标记也遵循相同原则:当前标记命令负责保存时间点,本身不执行自动节拍检测。生成节拍提示前,仍然需要可靠的节拍起点、速度信息或分析结果。
验证结果与使用入口
这次验证覆盖了 CLI、MCP 实际调用,以及一个完整的 Agent 工作流:
将“产品展示”从 5–8 秒移到 9–12 秒,在 11 秒新增中文备注,并保存为新项目。
验证内容包括:
- 区间长度保持 3 秒。
- 中文备注完整保存。
- 其他标记保持不变。
- 原文件哈希和归档媒体字节保持不变。
- 媒体时长与渲染计划保持不变。
此外,还检查了重复执行、事务回滚、异常 ID 和最小区间边界,相关实现通过了类型检查和生产构建。
如果你希望实际体验,可以打开 Timeline Studio 在线编辑器,尝试添加标记、拖动区间以及播放头吸附。
如果你正在开发 React 编辑器、MCP 工具或 Agent 文件操作接口,可以到 GitHub 仓库 查看实现。欢迎 Star 关注更新,也欢迎通过 Issue 分享实际剪辑需求。
给 Codex 安装这一版 Skill,可以使用:
gh skill install MartinDelophy/ai-video-editor edit-timeline-studio \
--pin v1.0.7 --agent codex --scope user
使用本地命令仍需要准备 Timeline Studio 仓库和相应 Node 依赖。完整说明见 v1.0.7 Release 和时间线标记工作流文档。
更多推荐


所有评论(0)