rough-cut:为 Premiere Pro 自动粗剪
一次性设置:参见套件根目录下的 INSTALL.md。对于插件,设置行是
bash "${CLAUDE_PLUGIN_ROOT}/install.sh" --data "${CLAUDE_PLUGIN_DATA}";对于手动安装(将四个 skills/ 文件夹并排复制到 ~/.claude/skills/),从套件根目录运行 bash install.sh。脚本位于此技能的 scripts/ 文件夹内。
范围
- 是:一份 Final Cut Pro 7 XML(XMEML,
.xml),其时间线已移除重拍、假起始、口吃、填充词和长静音;一份 Markdown 报告;对需要决定的部分显示红色标记。 - 否:修改剪辑、转码、导出视频、调色、混音、在明显改述之外的两个镜头中选择(那是剪辑师的决定)。
核心规则:原始剪辑永不修改
该技能仅在剪辑旁写入:
<clip>_Derush_v1.xml:要导入的编辑(FCP7 / XMEML);<clip>_Derush_v1.report.md,<clip>_Derush_v1.log.txt;<clip>_Derush_v1_work/:工作文件夹,保留(转录、中间剪辑)。
XML 通过绝对路径(file://localhost/...)引用剪辑:Premiere 仅引用它。新的一次运行会创建 v2、v3……而不会覆盖任何内容。
用户操作(3 步)
- 在 Premiere 中:导入剪辑,运行转录(文本面板 > 转录),保存项目。转录随后位于
.prproj内部:永远不要要求用户导出它。 - 给 Claude 剪辑的路径,以及视频的参考脚本(如果有的话)(纯文本、Markdown、Google 文档、Notion 页面,任何可读格式)。脚本仅用于判断重复是否是故意的。没有脚本,流水线仍能工作;故意的重复随后被标记为标志而非直接剪切。
- 导入
.xml(文件 > 导入)。出现一个Derush_vN序列,已经编辑好:剪切从时间线中移除(剪辑首尾相接,无需闭合间隙),存疑部分作为带有红色标记的活动剪辑保留。
不要询问其他内容:语言、剪辑类型和选项会被推断(见下文),只有在剪辑类型真正模糊时才值得提问。
语言
所有语言相关的内容(填充词、假起始短语、拍板模式、孤立连接词、要剪掉的开场白、耳语幻觉、标记的理由文本)都位于 languages/<code>.json 中,而非代码中。内置了法语(fr)和英语(en)。语言通过 --lang <code> 选择,否则使用转录中记录的语言,否则使用 en。详情及如何添加新语言:references/languages.md。
流水线 v2:镜头选择(正常路线)
仅检测缺陷(下面的 v1)的召回率最多在 57% 左右,因为 Whisper 会静默地去重重复的镜头:其反循环解码器会将逐字重复的句子视为幻觉并仅转录一次。这无法通过配置修复。Premiere 的转录会逐字保留每个镜头,并且其单词开始时间在长静音后不会漂移(Whisper 会)。因此:
| 步骤 | 工具 | 角色 |
|---|---|---|
| 1 | extract_prproj_transcript.py project.prproj words_premiere.json [--rush CLIP.MOV] | .prproj 到 words_premiere.json(主要来源)。如果项目没有转录,退出码为 2:要求用户运行转录并保存项目。后备:在手动 JSON 导出上运行 parse_premiere_transcript.py |
| 2 | extract_audio.py + transcribe.py | Whisper = 二次意见(交叉检查文本,从不检查边界) |
| 2b | calibrate_premiere_times.py | 将 Premiere 的转录与音频重新对齐。在每个粗剪后立即运行(当转录按时时,它不进行任何修改)。通过互相关、逐窗口测量偏移;在 21 分钟剪辑的末尾观察到 1.8 秒的漂移。如果忘记,症状:silence_cuts.py 报告数十个“可疑噪音” |
| 3 | Claude 在上下文中 | 语义分析:通过将 Premiere 的转录与脚本和 Whisper 进行比较,发现重拍、假起始和口吃。规则如下 |
| 4 | silence_cuts.py | Premiere 单词之间 >= 1.0 秒的静音,单词结束重新对齐到声音电平,枚举保护(两个短块之间的短暂停 = 故意节奏),噪音区域:标记或剪切(见下文)。默认值针对已完成的剪辑进行了校准:未经重新测量不要更改它们(参见 references/lessons.md,“测量”) |
| 5 | apply_takes.py <work> | 步骤 3 中决定的镜头写入 takes.json(单词索引;可选的手动设置边界或以秒为单位的剪切)。脚本将边界放置在能量谷中,与静音合并,融合两次剪切之间留下的静音片段,将每个剪辑边缘重新对齐到真实声音,并标记剪辑边缘处小于 0.25 秒的孤立声音,写入 cuts_final.json。仅当用户要求时 protect(保留静音) |
| 6 | build_premiere_xml.py | XMEML。选项 meta["audio_file"]:将音频指向完整长度的处理过的 WAV(参见已知限制)。多个剪辑:每个剪辑一个 XML,然后 merge_xml.py output.xml "name" a.xml b.xml = 一个单一的序列,首尾相接 |
| 6b | 编辑,再次转录 | 端到端检查:连接 audio.wav 的保留区间,在上面运行 Whisper,并使用剪切点 阅读 文本。捕获其他检查遗漏的内容:编辑中留下的静音(3 个单词的 8 秒剪辑)、被剪切的单词、幸存的失败镜头 |
| 7 | qc_repeat_scan.py | 确定性检查:如果编辑中保留有在 30 秒内重复的 5-gram,或者如果在暂停或剪切之后的两个单词在早些时候的 8 秒编辑内已经说过(一个带有变体的重复镜头),则退出码为 2。每个命中都对照脚本和通过耳朵(hear.py)检查:幸存的重复镜头(保留最后一个)或故意的修辞(首语重复、枚举) |
| 7b | montage_qc.py <work> | 编辑的音频被逐剪辑转录和阅读,Premiere 单词(P)对比 Whisper(W):空白的时长 > 1.6 秒或片段 < 1 秒时退出码为 2。可疑的边缘用 hear.py audio.wav 12.3-14.0 ...(在隔离片段上的 Whisper)检查 |
| 8 | report_v2.py | 报告:编辑和剪辑的时长、镜头、疑虑;--notes 用于“故意保留”和触及脚本敏感点的段落 |
步骤 3,决策规则
硬性规则:
- 重复文本:保留 最后一个 镜头(即使它不如较早的镜头干净);
- 口吃:保留 最长的连续片段;
- 疑虑:
flag,永远不要disable。低切总是优于高切:添加一个剪切很容易,恢复一个缺失的段落是地狱。 - 剪切边界对齐到 Premiere 单词,在保留的第一个单词之前留出大约 0.15 秒的空隙。
从真实编辑中学习的规则:
- 任何在两次镜头剪切之间被夹在中间的、时长小于 2 秒的孤岛都会被监听(
hear.py)。几乎总是假起始。永远不要仅凭 Premiere 的文本来判断:一次呼吸可以在 Premiere 中承载单词,而“感谢观看”类别的幻听意味着根本没有单词。 - 带有噪音但非脚本句子的静音(旁白、“开始了”、操作相机)被剪切,而非标记。
- 句子开头的填充性开场白(语言文件的“openers”列表,例如“So,”或“Well,”)被剪切。
- 句子内的逐字口吃(“It's it's”)被禁用。但是在一个句号两侧重复的名字(“less documented than Nikon. Nikon has plenty of docs”)不是口吃:Premiere 可能将其写为双份。在任何单字剪切之前用
hear.py检查该片段。 - 孤立连接词(一个孤立的“And / So / But”)紧接在剪切静音之前,包含在剪切中。对于 任何在剪切和暂停之间被夹在中间的孤立假起始单词,也是如此。
- 在 Premiere 转录中的无意义单词,且没有 Whisper 对应物,在冗余旁白中,是一个口吃:至少标记它。
- 不携带信息的填充短语,重复了整个下一句:仅标记(这是编辑性的)。
- 一个“重拍”可能是前一句的结尾,被 Premiere 错误计时(一个单词持续 5 秒)。编辑的 Whisper 文本会显示这一点。
- Premiere 有时将一个句子分成两半:一个两个单词的“假起始”可能是下一句的开头。
- Premiere 会漏掉小的嘟囔词:剪切一个重复的句子可能留下一个孤立的引导词。首先用
hear.py检查该片段;手动设置边界(takes.json中条目的第 6 个元素)。 - 一个单一的双字重复(“the the”):边界可能会吃掉它前面的单词,因为 Premiere 将两者都标注在同一个呼吸中。收听连接处(先
ffmpeg atrim两个保留片段,然后hear.py)。 - 两个剪辑之间的接缝:剪辑 1 的结尾和剪辑 2 的开头被一起收听。一个在剪辑 1 结尾开始并在剪辑 2 开头重复的句子从其第一个单词开始被剪切。
- 对相机说给剪辑师听的指令(“这是给你的,在这里放些图形”)作为指令被剪切,并在报告中列出,因为它们指示了在哪里进行说明。
- 在剪切一个镜头后,收听保留镜头的攻击(当 Premiere 有两个单词时,第一个单词可能被吃掉)。
Premiere 转录位于 .prproj 内部
.prproj 是 gzipped XML,包含一个块
<ExternallyProvidedTranscriptDocument><TranscriptData Encoding="base64">,其有效载荷是一个 FlatBuffers 缓冲区。extract_prproj_transcript.py 解码它:
Segment -> f0 i64 start_ticks, f3 vector<Word>, f4 string language
Word -> f0 i64 start_ticks, f1 i64 duration_ticks, f2 string text, f3 f32 confidence
Premiere ticks = 254016000000 每秒
空文本的项是 Premiere 标记的暂停(其持续时间覆盖整个静音)。脚本会丢弃它们;否则单词之间的每个间隙都是 0,silence_cuts.py 找不到任何内容(症状:“gaps > 1s: 0”并且单词长达 39 秒)。
一个项目中的多个剪辑:.prproj 为每个剪辑保存一个转录。--rush NAME.MOV 会选择与剪辑时长匹配的那个(没有它,会选择最长的一个,并带有警告)。它比较最后一个单词的开始时间,漂移余量为时长的 0.3%。检查两个剪辑之间的单词数量是否不同。每个剪辑一个工作文件夹,然后 merge_xml.py。
这个转录中的 两个陷阱:
- 它可能相对于文件漂移:步骤 2b。剪切是基于文件时间的(XML 读取的就是这个),所以单词会被校正,而不是反过来。
- 单词时长可能会吞掉其后的静音:静音不再存在于单词之间,也不会被剪切。
silence_cuts.py将这些单词恢复到其真实结束位置(trim_overflow)。永远不要信任 Premiere 单词的结束点;只有开始点是可靠的。
更多陷阱(在静音内计时的单词、静音片段、微小孤岛):references/lessons.md。
镜头剪辑边界:位于单词开始点的最近 能量谷,在保留单词的攻击之前留出 0.12 秒的空隙。通过 单词索引 描述剪切,而非秒数:它们在重新对齐后仍然有效。
下面的 v1 流水线在 没有 Premiere 转录时作为后备(警告:逐字重拍可能被错过)。
机器要求
在启动时检查;如果有缺失,指向 INSTALL.md(仅关于 Whisper 的部分在 references/whisper-setup.md 中)。
brew install whisper-cpp ffmpeg
mkdir -p ~/Models/whisper
curl -L -o ~/Models/whisper/ggml-large-v3-turbo.bin \
https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo.bin
Python 3,带有 numpy(没有其他依赖)。无需 API 密钥,无需账户,模型下载完成后没有任何东西离开机器。
流水线 v1(后备,8 个阶段)
由 scripts/derush.py 运行。每个阶段写入 <clip>_Derush_vN_work/,位于剪辑旁,永不删除(可以恢复和检查)。
| # | 脚本 | 角色 |
|---|---|---|
| 1 | derush.py(入口点) | ffprobe 元数据,版本号 |
| 2 | extract_audio.py | ffmpeg 转为 audio.wav 16 kHz 单声道 |
| 3 | transcribe.py | whisper.cpp 转为 transcript.json |
| 4 | mechanical_cuts.py | 语言文件的单词列表和模式转为 cuts_mechanical.json |
| 5 | 语义分析(Claude) | 转为 cuts_semantic.json(见下文) |
| 6 | merge_cuts.py | 合并,对齐到单词,得分转为 cuts_final.json |
| 7 | build_premiere_xml.py | 转为 .xml(XMEML / FCP7) |
| 8 | build_report.py | 转为 .report.md |
阶段 5:语义分析
脚本写入 prompt_semantic.txt 并停止。这是正常路线:运行技能的 Claude 然后执行以下操作。
- 完整读取
prompt_semantic.txt; - 按照提示中的规则分析转录;
- 将 JSON 写入
cuts_semantic.json(路径在提示中给出); - 恢复:
derush.py <clip> --resume-from=6 --work-dir=<work>。
如果 cuts_semantic.json 已存在,则跳过此阶段。
三级决策(技能的理念)
每个提议的剪切都有 0 到 1 之间的置信度。
disable:该段落 从时间线中移除,而非停用。build_spine_clips()通过减法构建序列:仅保留的区间保留,首尾相接,序列时长是保留区间的总和。相当于“波纹删除”已应用。阈值:机械剪切 >= 0.85,语义剪切 >= 0.92(LLM 经常超出其边界)。flag(0.6 到 0.85):不剪切。该段落作为 活动剪辑 保留,带有一个红色序列标记(“DOUBT <类型> (conf X):原因”)在正确的位置。由用户决定。ignore(< 0.6):不处理,以避免淹没时间线。
永远不要反转这些语义。交付停用、灰色的剪辑让用户手动删除,会将工作转移到用户身上,而非移除它:承诺的是一个可直接使用的序列。
剪切类型
filler:填充词(“um”、“uh”……)和句子开头的填充性开场白。