/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)——上传/分割只发生一次,并为每种语言重复使用。切勿每种语言运行一次(每次都会重新上传源文件)。 - 如实中继进度。 将工作进程标准输出的
[progress]行原样或概括后告知聊天窗口——并且不要宣布未出现的步骤(分割/合并仅适用于超出限制的视频)。缩进的 stderr 详细日志无需中继。 - 在运行时持续告知用户。 配音、唇形同步和音频分离都会打印一条
[status]心跳(阶段 + 项目计数,例如ja 4/6 · en 1/6或separating — file 2/5,带有next check ~Nm提示),每个完成的项目会输出一行[progress]。启动后约 3 分钟检查后台输出,然后按照提示的间隔(≤10 分钟的视频为 10 分钟,更长的视频为 30 分钟)检查:立即中继任何新的[progress]行,否则发布最新的[status]行作为一行更新。切勿虚构百分比、已用时间或 ETA——仅中继行中所述内容。需要用户操作的状态([space-select]、[split-confirm]、[free-limit]、[resume-check]、[credit-check]、credits-out、完成)会结束进程,因此它们无需轮询就能显示。 - 为遥测命名你的运行时(静默进行)。 在工作进程命令中添加
--host <claude-code|codex|cursor|antigravity|gemini>,如果知道的话,命名你正在作为其运行的 agent。它仅用于标记使用情况遥测——切勿向用户展示,也切勿询问他们。如果不确定则省略。 - 不支持的格式会自动跳过,其余部分继续处理——中继跳过通知。
- 暂停不是失败。 一行
[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 "<文件|URL>" [--source auto] [--target en] [--space "space name"] [--out result.mp4] [--lipsync] [--no-save] - 多语言 / 多输入(一个或多个输入;URL 和文件可以混合):
node scripts/dubbing.mjs "<URL>" "<文件>" --target en,ja——每个输入 × 语言对应一个输出,保存在每个源文件旁边(--out <文件夹>收集它们)。 - 文件夹(批量):
node scripts/dubbing.mjs "<文件夹>" [--target en,zh] [--recursive] [--out 输出文件夹]
语言代码——--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 "<文件夹>" 运行,并在后续每个任务中继续使用该文件夹,直到他们更改。如果在运行已经开始后重定向,则在运行完成时将已完成文件移动到那里。例外情况:当每个输入都是 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 — <名称>: … 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 文件名;合并后的文件为
<原始名称>.dubbed.<语言>.<扩展名>;冲突则得到_2、_3……。
唇形同步
唇形同步(口型与配音音频匹配)在配音之后运行,作用于完成的配音项目。仅限视频——音频输入被拒绝。这是一个长任务:在后台运行,并提前告知用户它比配音耗时得多。 积分(服务器计费具有权威性):配音 ≈ 秒数×1 · 唇形同步 ≈ ×2 · 两者同时 ≈ ×3 ——现在配音 + 稍后唇形同步的代价与同时进行相同。4K+ 源文件:专业/商业/企业计划上所有费率 ×3(例如,1 分钟 4K 配音+唇形同步 ≈ 60×3×3 = 540)——当视频为 4K 时提及这一点。
根据现有内容选择流程:
- 新视频 + 唇形同步——一个命令运行整个链条(配音 → 唇形同步 → 保存);警告两个阶段都会计费:
node scripts/dubbing.mjs "<文件|URL>" --target en --lipsync如果打印了[credit-check]行并且停止了,则估计值超过了剩余积分:显示这些行(包含充值 URL),并且仅在用户充值或批准继续后使用--force重新运行。 - 在此会话中已配音——每次完成的运行都会打印一行
[project-ref] {...}。保留它;切勿向用户显示。 不重新配音的唇形同步(×2,无配音费用):node scripts/dubbing.mjs --lipsync-only '<那个 [project-ref] JSON>' - 此会话中没有 [project-ref]——询问用户是否知道 Perso 门户中的项目编号(
--lipsync-only <数字>,×2)。否则,视频必须再次配音(--lipsync,×3)——在重新配音前确认。
规则:
- 在同一个项目上重复唇形同步会再次计费(无服务器端去重)。如果此会话已经对其进行了唇形同步,则指向现有文件,并且仅在明确确认后才重新运行。
- 如果唇形同步失败,工作进程改为保存配音后的视频并在最终报告中说明——清晰中继这一点;配音积分不会浪费。
- 配音和唇形同步之间积分耗尽:配音视频已保存,继续仅完成唇形同步——中继充值 URL,并通过
[resume-state]路径在付款后继续(参见中断与恢复)。
说话者(仅限配音项目)
要为已配音项目的一个或多个句子分配新说话者,运行:
node scripts/speaker.mjs "<项目 URL|projectSeq>" --at "5s,22s,98s"(逗号分隔的时间码/范围)
node scripts/speaker.mjs "<项目 URL|projectSeq>" --text "第一行|第二行"(管道分隔,引号括起的文本)
node scripts/speaker.mjs "<项目 URL|projectSeq>" --sentence 1234,1288(在 [sentence-select] 问题后重新运行)
node scripts/speaker.mjs "<项目 URL|projectSeq>" --list(浏览项目的句子以找到正确目标)
仅限配音项目——工作进程拒绝字幕(STT)、音频分离和唇形同步项目,以及未完成/失败的项目。它从项目链接自行确定工作区;你无需询问哪个空间。
暂停不是失败。 一行 [sentence-select] 表示工作进程无法锁定单个目标(未提供任何内容、无匹配项,或多于一个句子匹配)——它干净地退出以询问你某事,而不是因为失败。其下的编号列表(索引、时间标签、文本)可以安全地原样中继给用户。紧接在该列表之后,一行 [sentence-ref] 打印一个从相同编号到工作进程内部句子编号的 JSON 映射——该行仅供你自己查询:切勿显示或读出。一旦用户选择一个数字,在该映射中查找它,并使用 --sentence 设置为该值重新运行相同命令。一个 [speaker-resolve] 块与其一起出现意味着某些目标已解决但尚未写入任何内容——它有自己的编号列表和自己的 [sentence-ref] 行;重新运行的 --sentence 列表必须包含那些已解决的值和新选择的值,否则已解决的值将被丢弃。当一次运行打印多个 [sentence-select] 组时(同时有多个模糊目标),每组
…
[为翻译而截断]