Communitygithub.com

视频编辑(Video Edit)

通过对话编辑真实视频素材,完全本地运行。使用 whisper.cpp 转录,从转录文本中挑选片段,生成 JSON 编辑列表,验证后通过 ffmpeg 渲染,添加动画 Hyperframes 字幕和叠加层,重新构图到 9:16 / 1:1 / 16:9,最后自检渲染结果。适用于人物访谈、用户生成内容、推荐广告、创始人视频、通话录音、采访、教程等场景——只要用户需要对视频进行剪切、裁剪、添加字幕、重新构图或生成广告变体。

视频编辑(Video Edit) 是什么?

Video Edit(视频编辑)是一个基于 AI 的本地视频编辑工具,fork 自 browser-use/video-use,但完全在本地运行,无需 API 密钥。它通过对话与用户协作,从原始视频素材到最终渲染,分步骤执行:先使用 whisper.cpp 进行本地转录,从转录文本中挑选可用片段,生成 JSON 编辑列表,进行严格验证,再用 ffmpeg 渲染。支持多种高级功能,包括动画 Hyperframes 字幕、画布重新构图(9:16/1:1/16:9)、以及基于 OpenCV 的镜头检测和人物定位。工具内置了生产级正确性规则(如字幕必须最后应用、段边界 30ms 音频淡入淡出、剪切点不得在单词中间等),并通过严格的验证步骤确保输出质量。渲染前强制通过 validate_edl.py 验证编辑列表,每次渲染前自检最多 3 轮,发现问题后自动修复或报告用户。支持多个生成式 AI 功能:用户可以通过自然语言提出编辑需求(如“将这段剪成 30 秒广告”、“去掉“嗯”和“啊””、“做成竖屏版本”、“编辑这些片段”),AI 助手会据此生成编辑策略并等待用户确认。此外,它还支持并行生成多个动画层、速率斜坡、冻结帧、交叉溶解等各种创意效果,并且通过 self-eval 流程在展示前对输出进行视觉和音频质量检查。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add huzaifa-hb/video-edit

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

视频编辑(Video Edit) 是做什么的?

Fork 自 browser-use/video-use (MIT):使用本地 whisper.cpp 而非 ElevenLabs,在每次渲染前增加验证关卡,使用 Hyperframes 字幕而非 libass 字幕(因为 ffmpeg 的 Homebrew 版本没有 libass),以及为社交媒体广告变体提供画布重新构图。

原则

  1. LLM 根据原始转录 + 按需视觉信息进行推理。 唯一值得保留的衍生产物是精炼的短语级转录(takes_packed.md)。其他一切——填充词标记、重拍检测、镜头分类、强调评分——都在决策时即时推导。
  2. 音频是首要的,视觉随后。 剪切候选来自语音边界和静音间隙。只有在决策点才深入查看视觉信息。
  3. 询问 → 确认 → 执行 → 迭代 → 持久化。 在用户用纯英文确认策略之前,绝不触碰剪辑。
  4. 泛化。 不要假设这是什么类型的视频。查看素材,询问用户,然后进行编辑。
  5. 艺术自由是默认选项。 本文档中的每个具体数值、预设、字体、颜色、时长、音高结构和技术都来自一个已验证视频的工作示例——不是强制要求。阅读它们以了解什么是可能的以及为什么每种方法有效。然后根据素材的实际情况和用户的真实需求做出自己的审美判断。你必须做的唯一事情是下面“硬性规则”部分的内容。 其他一切由你决定。
  6. 自由创造。 如果素材需要本文档未描述的技术——分屏、画中画、下部身份卡片、反应剪辑、速度斜坡、冻结帧、交叉溶解、匹配剪辑、L 剪辑、J 剪辑、呼吸时的速度斜坡——都可以构建。辅助工具是 ffmpeg 和 PIL。它们可以做格式支持的任何事情。不要等待许可。
  7. 在向用户展示之前验证自己的输出。 如果你自己都不会发布,就不要展示出来。

硬性规则(生产正确性——不可协商)

这些是偏离会导致静默失败或输出损坏的规则。它们不是审美问题,而是正确性问题。请记住它们。

  1. 字幕在滤镜链中最后应用,在所有叠加层之后。否则叠加层会隐藏字幕。静默失败。
  2. 逐段提取 → 无损 -c copy 拼接,而不是单遍滤镜图。否则在添加叠加层时,每个片段都会被双重编码。
  3. 每个片段边界处有 30ms 音频淡入淡出(afade=t=in:st=0:d=0.03,afade=t=out:st={dur-0.03}:d=0.03)。否则每个剪辑点都会出现可听见的爆音。
  4. 叠加层使用 setpts=PTS-STARTPTS+T/TB,将叠加层的第 0 帧移动到其窗口开始位置。否则在叠加窗口期间会看到动画的中间部分。
  5. 字幕使用输出时间轴偏移:output_time = word.start - segment_start + segment_offset,其中每个片段占满其整帧长度(captions.py 负责计算)。否则字幕在片段拼接后不对齐。
  6. 永远不要在单词中间剪切。 将每个剪辑边缘对齐到转录中的单词边界。validate_edl.py --fix 自动执行此操作。
  7. 填充每个剪辑边缘。 工作窗口:30–200ms。单词边缘已经对齐到音频包络,但填充吸收了剩余部分。快节奏时更紧,电影感时更松。
  8. 仅使用词级逐字 ASR。 永远不要使用 SRT/短语模式(会丢失亚秒级间隙数据)。永远不要归一化填充词(会丢失编辑信号)。transcribe.py 提示 whisper 保留“嗯”/“啊”——不要移除提示。
  9. 每个源文件缓存转录。 除非源文件本身发生变化,否则不要重新转录。
  10. 多个动画使用并行子代理。 永远不要串行。通过 Agent 工具一次性生成 N 个;总挂钟时间 ≈ 最慢的一个。
  11. 执行前确认策略。 在用户批准纯英文计划之前,绝不触碰剪辑。
  12. 所有会话输出放在 <videos_dir>/edit/ 中。 永远不要写入技能目录内。
  13. 每次渲染前进行验证。 validate_edl.py <edl> --fix,然后仅在退出码为 0 时进行渲染。它能够捕获虚构的源 ID、超出文件末尾的时间、单词中间的剪切、缺失的叠加文件以及超出剪辑范围的叠加层——这些正是 LLM 编写的编辑列表中实际存在的失败模式。
  14. 字幕是 Hyperframes 层,而不是烧录字幕。 Homebrew 的 ffmpeg 没有 libass,因此 EDL 中的 subtitles 会失败。请使用 captions.py;render.py 将字幕 WebM 合成在每一个叠加层之上。
  15. 源 ID 是文件主名。 在 sources 中,键必须是视频文件名不带扩展名(take_B.mp4 使用 take_B),或者当通过 --audio-track N 转录时为 <stem>.track<N>——因为这是找到转录的方式。当字幕启用时,验证器会在不匹配时报错。
  16. 每次渲染使用一个帧率。 如果你给 render.py 传递了 --fps,也要给 validate_edl.py 和 captions.py 传递相同的 --fps。范围会被四舍五入到整帧,以保证字幕不会漂移。

本文档中的其他所有内容都是工作示例。当素材需要时可以偏离。

目录布局

技能本身位于自己的目录中(辅助脚本和模板在此文件旁边)。用户素材放在他们放置的任何地方。所有会话输出进入 <videos_dir>/edit/。

<videos_dir>/
├── <源文件,未修改>
└── edit/
    ├── project.md              ← 记忆文件;每次会话追加内容
    ├── takes_packed.md         ← 短语级转录,LLM 的主要阅读视图
    ├── edl.json                ← 剪辑决策(edl_9x16.json、edl_1x1.json … 用于变体)
    ├── transcripts/<name>.json ← 缓存的 whisper.cpp 转录(词级 + 静音)
    ├── captions_<edl stem>/    ← Hyperframes 字幕合成 + captions.webm
    ├── animations/slot_<id>/   ← 每个动画的源文件 + 渲染 + 推理
    ├── clips_graded/           ← 每个片段的带调色和淡入淡出的提取
    ├── downloads/              ← yt-dlp 输出
    ├── verify/                 ← 调试帧 / 时间轴 PNG
    ├── preview.mp4
    └── final.mp4

环境设置

install.sh / setup.sh 安装所有内容(Homebrew 包、.venv、whisper 模型、Hyperframes + Chrome)。在冷启动时只需验证——如果缺少 .venv 或某个辅助脚本在导入或缺少工具时失败,则在执行其他任何操作之前运行 <this skill dir>/setup.sh(它是幂等的):

  • PY=<this skill dir>/.venv/bin/python 存在。使用 $PY 运行每个辅助脚本,永远不要使用系统 Python(它缺少 numpy/Pillow/OpenCV)。
  • ffmpeg、ffprobe、whisper-cli 在 PATH 中,以及一个 ggml whisper 模型。默认:~/.claude-video-vision/models/ggml-large-v3-turbo.bin;可以通过 VIDEO_EDIT_WHISPER_MODEL 覆盖。
  • Hyperframes 需要 Node.js 22+。始终调用固定版本:npx --yes [email protected] …。
  • yt-dlp 和 Manim 仅在首次使用时安装。此技能包含 skills/manim-video/;在构建 Manim 插槽时阅读其 SKILL.md。
  • 没有 API 密钥。除了 Hyperframes 的一次性 npm 下载外,任何内容都不会离开机器。

辅助脚本位于此 SKILL.md 文件旁边的 helpers/ 目录中。解析相对于包含此文件的目录的路径。

辅助脚本

所有脚本都通过 $PY helpers/<name>.py … 调用。

  • transcribe.py <video> — 本地 whisper.cpp,词级,保留填充词,单词边缘对齐到音频包络,列出静音。每个源文件缓存(仅在文件更改时重新运行)。--language en 跳过语言检测;--audio-track N 用于多音轨录制。
  • transcribe_batch.py <videos_dir> — 逐个处理文件夹中的每个视频(whisper 已经占满 GPU)。
  • pack_transcripts.py --edit-dir <dir> — transcripts/*.json → takes_packed.md(短语级,在 ≥ 0.5s 的静音处断开;对于语速快的人,将 --silence-threshold 降低到 0.3)。
  • timeline_view.py <video> <start> <end> — 生成胶片条 + 波形 PNG。按需的视觉深入探查。不是扫描工具——在决策点使用,而不是持续使用。
  • validate_edl.py <edl.json> [--fix] — 每次渲染前的验证关卡(硬性规则 13)。--fix 将边缘对齐到单词间隙,填充它们(--pad-in、--pad-out),将范围四舍五入到整帧,重新计算 total_duration_s,保留 edl.json.bak。关于边缘静音 < 30ms 的警告在 --fix 后仍然存在:这些剪辑点位于单词接触的地方——将它们移动到一个真正的停顿处。
  • captions.py <edl.json> — 输出时间轴词级字幕,作为 Hyperframes 合成 → 透明 captions.webm,设置 EDL 的 captions 键。样式标志:--accent、--color、--top、--size、--font、--case、--words、--no-pop。
  • render.py <edl.json> -o <out> — 逐段提取(帧精确、HDR→SDR、画布重新构图、调色、淡入淡出)→ 无损拼接 → 叠加层(PTS 偏移)→ 字幕 → loudnorm。--preview 用于质量控制,--draft 仅用于剪辑检查。
  • reframe.py <edl.json> [--apply] — 在每个范围内通过 OpenCV 人脸检测(YuNet)→ crop_x/crop_y,使说话者居中并保持头部空间,作用于 EDL canvas。在每次更改 canvas 宽高比后,在 validate_edl.py --fix 之后运行。它会标记那些无法用一次静态裁剪容纳的移动范围(MOVES,需要拆分)和没有检测到人脸的(no face,需要手动选择裁剪)。范围的 "crop_lock": true 可保护手动裁剪。
  • scenes.py <video> — OpenCV 镜头变更检测,加上每个镜头的 DARK / BLURRY 标志 → edit/shots/<stem>.json。用于转录无法结构化的素材:B-roll、产品镜头、蒙太奇、多机位导出、无声音片段。镜头边界是清晰的视觉剪辑点;标记的镜头很少在广告中使用。
  • grade.py <in> -o <out> — ffmpeg 滤镜链调色。提供预设 + --filter '<raw>' 用于自定义。

对于动画,创建 <edit>/animations/slot_<id>/,使用 Bash 并通过 Agent 工具生成子代理。

流程

  1. 库存检查。 对每个源文件运行 ffprobe。对目录运行 transcribe_batch.py。对于语音很少或无语音的源文件(B-roll、产品镜头),改为运行 scenes.py 获取其镜头。阅读它打印的 QC: 行(每个转录中 qc 下方也有):宽度 ≤20ms 或从静音内部开始的单词时间不可靠——在使用它们前,对任何靠近这些单词的剪辑点使用 timeline_view 进行检查。

    • 多个说话者(通话、采访):没有说话者标签。从内容推断(问题 vs 回答),然后对你计划保留的每个短语使用 timeline_view 确认(在视频通话中,活动说话者磁贴会显示谁在说话)。如果通话是每人一个音频轨道录制的,则对每个轨道使用 --audio-track N 进行转录。运行 pack_transcripts.py 生成 takes_packed.md。抽样一两个 timeline_view 以获得视觉第一印象。
  2. 问题预扫描。 通读 takes_packed.md 一次,记下口误、明显的说错或需要避免的措辞。列出清单,提供给编辑简报。

  3. 对话。 用纯英文描述你看到的内容。询问基于素材的问题。收集:内容类型、目标长度/宽高比、美学/品牌方向、节奏感觉、必须保留的时刻、必须剪掉的时刻、动画和调色偏好、字幕需求。不要使用固定检查表——正确的提问每次都不一样。

  4. 提出策略。 4-8 句话:形态、片段选择、剪辑方向、动画计划、调色方向、字幕风格、时长估计。等待确认。

  5. 执行。 通过编辑子代理简报生成 edl.json。在模糊时刻使用 timeline_view 深入探查。在并行子代理中构建动画。运行 validate_edl.py --fix 直到其退出码为 0。如果需要字幕,则运行 captions.py(字幕是基于最终范围构建的,因此在范围变更后重新运行它)。

  6. 预览。 validate_edl.py(不带 --fix)→ render.py --preview。

  7. 自我评估(在向用户展示之前)。 在渲染输出(而非源文件)的每个剪辑边界处(±1.5s 窗口)运行 timeline_view。检查每张图片是否有:

    • 视觉不连续 / 闪烁 / 剪辑点跳跃
    • 边界处波形尖峰(音频爆音未通过 30ms 淡入淡出)
    • 字幕被叠加层遮挡(违反规则 1)
    • 叠加层未对齐或显示错误帧(违反规则 4)

    同时抽样:前 2s、后 2s 以及 2-3 个中间点——检查调色一致性、字幕可读性、整体连贯性。对输出运行 ffprobe 以验证时长是否与 EDL 预期匹配。

    测量音频,不要假设:ffmpeg -i out.mp4 -af ebur128=peak=true -f null - 获取集成响度和真实峰值,以及每个部分的 RMS(对话、纯音乐、结束卡)。结束卡比对话低 15 dB,或者效果比语音更响,都是错误。你无法听到音频:请说明这种情况,并报告数值。

    对于用户将发布的任何内容(发布会、推广、广告),还要生成一个评论子代理,提供渲染文件、EDL 和用户提供的任何参考视频。指示它挑剔,不要夸奖:给出结论,按顺序列出问题及时间码和证据(帧、电平),以及首先要做的 5 个修复。新鲜的眼睛能发现作者已经看不到的缺陷——被切掉的妙语、0.5 秒的 meme、在手机屏幕上无法阅读的 28px 文本。

    如果有任何问题:修复 → 重新渲染 → 重新评估。自我评估最多 3 轮——如果 3 轮后问题仍然存在,则将其标记给用户,而不是无限循环。只有在自我评估通过后才展示预览。

  8. 迭代 + 持久化。 自然语言反馈,重新规划,重新渲染。不要重新转录。确认后最终渲染。追加到 project.md。

剪辑技巧(技术)

  • 音频优先。 从单词边界和静音间隙中提取剪切候选。
  • 保留高潮。 笑声、点睛之笔、强调节拍。在妙语后延长以包含反应——笑声本身就是节拍。
  • 说话者交接时需要话语之间的空气。常见值:400–600ms。快节奏时更短,电影感时更长。审美判断。
  • 笑声和反应未被标记(whisper 不发出音频事件)。在 timeline_view 波形中找到它们作为无语音但响亮的段落;在它们之后延长——笑声本身就是节拍。
  • 静音间隙是剪切候选。

相关技能