/dubbing
一个通过 Perso AI Dubbing API 自动为视频配音的技能。
核心规则(必须遵守)
- 只有工作进程能看到原始密钥。 切勿使用
Read读取、回显或将其作为命令行参数传递。工作进程仅在XP-API-KEY标头中发送它。 - 一个命令 = 整个任务。
scripts/dubbing.mjs自行处理上传 · 分割 · 翻译 · 合并 · 保存。在此之前切勿运行prepare_input.mjs或probe_split.mjs—— 它们是调试工具,而probe_split.mjs会执行一次真正的上传(视频会被上传两次)。 - 始终在后台运行
dubbing.mjs。 任务需要几分钟到几小时;前台 shell 超时会中途终止运行。 - 未指定语言 → 源语言
auto,目标语言en。 - 多种语言放在一个命令中(
--target en,zh,ja)—— 上传/分割只进行一次,并针对每种语言重用。切勿每种语言运行一次(每次都会重新上传源文件)。 - 忠实转发进度。 将工作进程的 stdout
[progress]行逐字或摘要呈现到聊天中 —— 并且不要宣布未出现的步骤(仅对超限视频存在分割/合并)。不需要转发缩进的 stderr 详细日志。 - 运行时持续告知用户。 配音、口型同步和音频分离都会打印
[status]心跳(阶段 + 项目计数,例如ja 4/6 · en 1/6或separating — file 2/5,带有next check ~Nm提示),每个完成的项目会以[progress]行形式流出。启动后约 3 分钟检查后台输出,然后按提示间隔(媒体 ≤10 分钟时每 10 分钟,更长时每 30 分钟):立即转发任何新的[progress]行,否则将最新的[status]行发布为一行更新。切勿虚构百分比、已用时间或预计时间 —— 仅转发行中所说的内容。需要用户介入的暂停([space-select]、[split-confirm]、[free-limit]、[resume-check]、[credit-check]、额度不足、完成)会结束进程,因此它们会直接出现,无需轮询。 - 为遥测命名您的运行时(静默)。 如果能够确定,向工作进程命令添加
--host <claude-code|codex|cursor|antigravity|gemini>,命名您所代表的代理。它仅用于标记使用情况遥测 —— 切勿向用户显示,也切勿询问他们。如果不确定则省略。 - 不支持的格式会自动跳过,其余部分继续处理 —— 转发跳过通知。
- 暂停不是失败。
[space-select]、[split-confirm]、[free-limit]、[credit-check]或[resume-check]行意味着运行已停止以询问您某些事 —— 它以 0 退出(因此看起来不像错误),但任务未完成。每当出现这些行之一时,切勿报告运行已完成:转发它并采取行动。这些行是信号,而不是退出代码。
设置(懒惰 —— 无需预先步骤)
- API 密钥 —— 不是您首先运行的步骤。 工作进程在需要 Perso API 的命令运行时自行进行门控:在第一次此类运行时,它会打开一个浏览器页面(登录,一键操作 —— 密钥被加密存储),或者如果无法打开浏览器则打印基于文件的说明 —— 将这些转发给用户。在无头/SSH 机器上,工作进程完全跳过浏览器流程,并从文件/
--import注册开始(浏览器页面只能将密钥传递到其运行的机器上)—— 以相同方式转发这些说明。切勿自行触发密钥注册:工作进程会在需要时生成connect.mjs/resolve_key.mjs --watch,因此不要运行它们,也不要运行resolve_key.mjs --check作为预先步骤(仅在用户明确询问是否已注册密钥时 —— 密钥缺失不是错误)。切勿将密钥粘贴到聊天中。获取密钥:https://developers.perso.ai/api-keys - ffmpeg/ffprobe —— 仅在视频超过计划限制且必须分割时自动安装(如果请求权限则批准)。手动检查:
node scripts/check_deps.mjs。
运行
收集输入(本地路径或 URL —— 如果缺失则重新询问)并在后台运行 —— 工作进程在首次需要时自行执行密钥门控;如果注册开始,只需转发其打印的说明:
- 单个:
node scripts/dubbing.mjs "<file|URL>" [--source auto] [--target en] [--space "space name"] [--out result.mp4] [--lipsync] [--no-save] - 多语言/多输入(一个或多个输入;URL 和文件可以混合):
node scripts/dubbing.mjs "<URL>" "<file>" --target en,ja—— 每个输入 × 语言一个输出,保存在每个源文件旁边(--out <folder>收集它们)。 - 文件夹(批量):
node scripts/dubbing.mjs "<folder>" [--target en,zh] [--recursive] [--out output-folder]
语言代码 —— --target 接受语言代码(en、ja)或区域标签。区域变体仅适用于英语(en = 美国默认,en-GB = 英国)、葡萄牙语(pt = 巴西默认,pt-PT = 葡萄牙)和西班牙语(es = 墨西哥默认,es-ES = 西班牙);en-US、pt-BR 和 es-MX 是这些默认值的可接受拼写。每种其他语言只有一个变体,并且 --source 只需要语言(那里的区域标签被视为纯语言)。
- 请求中指定了区域 → 使用该确切标签。没有变体的区域(例如澳大利亚英语)→ 告知用户仅存在列出的选项,并在运行前让他们选择。
- 两个字母的令牌是语言代码,而不是国家:
uk是乌克兰语 —— 英式英语是en-GB。ca(加泰罗尼亚语)、be(白俄罗斯语)、eu(巴斯克语)也有同样陷阱。如果用户的措辞可能意味着两者,请在运行前询问他们的意思。 - 未指定区域的语言 → 默认变体。对于这三种语言,在交付说明中指定应用的默认值(例如“西班牙语(墨西哥)—— 对于西班牙使用
es-ES”)。
保存位置 —— --out 接受一个文件夹(现有目录,或以 / 结尾的路径),结果将收集在其中,或者对于单个输入,接受一个文件路径(如果路径没有扩展名,则附加实际扩展名)。默认是每个源文件旁边;不要询问。在会话的第一个任务中,在启动行中说明它(例如“结果将保存在源文件旁边 —— 如果您需要不同的文件夹,请立即告诉我”)并立即开始,无需等待回答。如果用户指定了文件夹 —— 在这次请求或本会话中稍后的任何时间 —— 使用 --out "<folder>" 运行,并在他们更改之前,为每个后续任务继续使用该文件夹。如果在运行开始后他们重定向,则在运行完成时将完成文件移动过去。例外情况:当每个输入都是 URL(没有本地源文件夹)时,在开始前询问保存位置。
空间选择 —— 如果有多个工作区,工作进程会先停止,打印 [space-select] 行(名称 | (计划) | 剩余信用)并停止。仅向用户显示这些选项(没有内部编号),询问哪一个,然后使用 --space "<space name>" 重新运行。一个空间 → 无需询问;PERSO_SPACE_SEQ 固定它。
分割确认 —— 如果输入超过长度或大小限制,并且必须自动分割和合并(配音、配音+口型同步或音频分离),工作进程会停止并打印 [split-confirm] 行。转发它们并询问用户:它超过了长度/大小限制,因此需要自动分割 → 处理 → 合并,这可能会比用户自己分割的结果差一些 —— 是否自动继续?如果是,使用相同的命令并添加 --allow-split 重新运行(直到此时都不计费,因此重新运行是免费的)。批量运行:--allow-split 授权运行中的所有分割。
不保存(仅服务器) —— 当用户想要视频配音但不保存为本地文件时,添加 --no-save:工作进程将结果保留在用户的 Perso 工作区中并跳过下载,打印 Kept on server, not saved: … → project <seq>([project-ref] 行仍然发出,因此配音可以在以后进行口型同步或检索)。仅限单个/未分割视频 —— 分割视频的合并文件需要本地下载,因此它会正常保存,并且工作进程会这样说。不能与 --lipsync 结合使用(口型同步的视频必须下载)。
免费计划(结果保留在 Perso 上) —— 免费工作区可以正常进行配音、口型同步和分离,但服务器拒绝每次下载,因此工作进程永远不会在那里保存文件。它会打印 Free plan — <name>: … ready on Perso → <url> (downloads need a paid plan) —— 将该链接转发为已交付的结果:运行完成,没有失败,也没有需要恢复的内容。如果用户想要实际文件,则需要付费计划 —— 提供升级路径(请参阅计划升级和信用),切勿重新运行。
免费计划 [free-limit] 暂停 —— 两个,都在任何计费之前:
- 30 秒预览。 超过 30 秒的配音/口型同步输入会停止并显示
[free-limit]行:在免费计划中,只有前 30 秒会被配音,其余部分被排除在外。转发问题,如果是,使用相同的命令并添加--allow-preview重新运行(工作进程本地修剪前 30 秒,仅提交该部分)。Perso 导入的平台链接无法修剪 —— 那里的答案是本地文件、更短的剪辑或升级。 - 超出计划限制。 超出计划长度/大小限制的媒体在免费计划上不会得到分割提议(
--allow-split不适用):[free-limit]行说明更短的文件或升级是唯一的方法。转发它们并停止。
运行时(用于解释等待):
- 分割:首先上传整个文件;只有计划限制拒绝才会安装 ffmpeg 并无损分割。外部 URL(YouTube·TikTok·Drive·Vimeo)在服务器端处理。
- 队列:所有输入 × 部分 × 语言共享一个池;完整队列每 5 分钟重新检查一次。某个部分上的引擎错误会取消该部分的其他语言;静默部分会直接通过原样内容;空闲守卫防止永远挂起。
- 保存:各部分合并回每个(输入 × 语言)的一个文件。未分割的输出保留 Perso 文件名;合并的输出是
<original-name>.dubbed.<lang>.<ext>;冲突会得到_2、_3……。
口型同步
口型同步(嘴巴与配音音频匹配)在配音之后、对完成的配音项目运行。仅限视频 —— 音频输入被拒绝。这是一个长时间的任务:在后台运行,并预先告知用户这比配音花费的时间长得多。 信用(服务器计费是权威的):配音 ≈ 秒 ×1 · 口型同步 ≈ ×2 · 两者 ≈ ×3 —— 现在配音 + 稍后口型同步的费用与同时进行相同。4K+ 源:所有费率在专业/商业/企业计划上 ×3(例如,一个 1 分钟的 4K 配音+口型同步 ≈ 60×3×3 = 540)—— 当视频为 4K 时提及这一点。
根据存在的内容选择流程:
- 新视频 + 口型同步 —— 一个命令运行整个链(配音 → 口型同步 → 保存);警告这两个阶段都会计费:
node scripts/dubbing.mjs "<file|URL>" --target en --lipsync如果打印了[credit-check]行并且停止,则估计值超过了剩余信用:显示这些行(包括充值 URL),并且仅在用户充值或批准无论如何继续后,使用--force重新运行。 - 在本会话中已配音 —— 每次完成的运行都会打印一行
[project-ref] {...}。保留它;切勿向用户显示。 不重新配音的口型同步(×2,无配音费用):node scripts/dubbing.mjs --lipsync-only '<that [project-ref] JSON>' - 本会话中没有 [project-ref] —— 询问用户是否知道来自 Perso 门户的项目编号(
--lipsync-only <number>,×2)。否则必须再次对视频进行配音(--lipsync,×3)—— 在重新配音前确认。
规则:
- 对同一项目重复进行口型同步会再次计费(没有服务器端去重)。如果此会话已经对其进行了口型同步,则指向现有文件,并且仅在明确确认后重新运行。
- 如果口型同步失败,工作进程会转而保存配音视频并在最终报告中说明 —— 清晰转发该信息;配音信用不会浪费。
- 配音和口型同步之间信用耗尽:配音视频会被保存,并且仅完成口型同步的剩余部分 —— 转发充值 URL,并在付款后通过
[resume-state]路径继续(请参阅中断和恢复)。
说话人(仅限配音项目)
要为已配音项目的一个或多个句子分配新说话人,运行:
node scripts/speaker.mjs "<project URL|projectSeq>" --at "5s,22s,98s"(逗号分隔的时间码/范围)
node scripts/speaker.mjs "<project URL|projectSeq>" --text "first line|second line"(竖线分隔的引号文本)
node scripts/speaker.mjs "<project URL|projectSeq>" --sentence 1234,1288(在 [sentence-select] 问题后重新运行)
node scripts/speaker.mjs "<project URL|projectSeq>" --list(浏览项目的句子以找到正确的目标)
仅限配音项目 —— 工作进程会拒绝字幕(STT)、音频分离和口型同步项目,以及未完成/失败的项目。它会根据项目链接自行确定工作区;您无需询问哪个空间。
暂停不是失败。 [sentence-select] 行意味着工作进程无法确定单个目标(没有给出任何内容,没有匹配项,或者有多个句子匹配)—— 它会干净退出以询问您某些事,而不是因为失败。其下的编号列表(索引、时间标签、文本)可以安全地按原样转发给用户。在该列表之后立即,一行 [sentence-ref] 会打印相同数字到工作进程内部句子编号的 JSON 映射 —— 该行是仅供您自己查找的:切勿显示它或大声读出它。一旦用户选择了一个数字,就在该映射中查找它,并使用设置为该值的 --sentence 重新运行相同的命令。旁边的 [speaker-resolve] 块意味着某些目标已经解析但尚未写入任何内容 —— 它有自己编号的列表和它自己的 [sentence-ref] 行;重新运行的 --sentence 列表必须同时包含那些已解析的值和新选择的值,否则已解析的将被丢弃。当一个运行打印多个 [sentence-select] 组(同时有多个模糊目标)时,每个组
……
[翻译已截断]