short-translate
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseShort Translate
Short Translate
One take in, the same take in another language out, in the creator's own voice (ElevenLabs clones it from the take itself). The image stays the original: no lip-sync. Then edits the translated take like any other, with captions in the new language.
/short-editingTalk to them in their language. : the take's slug; : the target language code (, , , , , , ...), : the source one.
<slug><lang>enesfrdeptitja<from>一次输入一条 take,即可输出同一 take 的另一语言版本,并采用创作者本人的声音(ElevenLabs 会直接从该 take 克隆声音)。画面保留原样:不做 lip-sync。随后, 会像处理其他 take 一样编辑译后的版本,并添加新语言字幕。
/short-editing用用户能理解的语言与其沟通。:take 的 slug;:目标语言代码(、、、、、、……),:源语言。
<slug><lang>enesfrdeptitja<from>1. The ElevenLabs key (once)
1. ElevenLabs API key(只需配置一次)
The key lives in , never in a chat message, a script or a commit:
video-agent/.envbash
grep -c ELEVENLABS_API_KEY video-agent/.env 2>/dev/null0- They create a key at elevenlabs.io/app/settings/api-keys (an ElevenLabs account; dubbing uses the credits of their plan, the free plan adds a watermark).
- They paste it into themselves, as one line:
video-agent/.env. Offer to create the empty file and open it. Never ask them to paste the key in the chat; if they do anyway, write it to the file and tell them they can regenerate it.ELEVENLABS_API_KEY=sk_... - must be in the project's
video-agent/.env(add the line.gitignoreif it isn't).video-agent/.env
Every command below loads it with and never prints it.
set -a; . video-agent/.env; set +aAPI key 存放在 中,绝不能出现在聊天消息、脚本或 commit 中:
video-agent/.envbash
grep -c ELEVENLABS_API_KEY video-agent/.env 2>/dev/null输出为 或文件不存在:
0- 用户需要在 elevenlabs.io/app/settings/api-keys 创建 API key(需要 ElevenLabs 账号;dubbing 会消耗其套餐 credits,免费套餐会添加水印)。
- 用户自行将 API key 粘贴到 ,格式为一行:
video-agent/.env。可以主动提出创建空文件并打开它。绝不能要求用户把 API key 粘贴到聊天中;如果用户仍然这样做,应将其写入文件,并告知用户可以重新生成。ELEVENLABS_API_KEY=sk_... - 必须位于项目的
video-agent/.env中;如果尚未加入,则添加.gitignore这一行。video-agent/.env
以下每条命令都通过 加载 API key,且绝不会将其打印出来。
set -a; . video-agent/.env; set +a2. What to translate
2. 要翻译的内容
Translate the take, not a finished video: a finished video has captions burned in the old language. Then short-editing cuts, captions and times everything on the new voice.
- A take already edited (exists): send only the kept parts, so no silence, stutter or retake is paid for. Make the plain cut with short-editing's ranges (from
video-agent/edits/<slug>/edit.json):video-agent/studio/The source isbashSEL=$(node -e 'const e=require("./public/<slug>.edit.json");console.log(e.ranges.map(r=>`between(t,${r.fromMs/1000},${r.toMs/1000})`).join("+"))') ffmpeg -y -i public/<slug>.mp4 -vf "select='$SEL',setpts=N/FRAME_RATE/TB" -af "aselect='$SEL',asetpts=N/SR/TB" -c:v libx264 -crf 18 -c:a aac -b:a 192k ../.tmp/<slug>-cut.mp4.video-agent/.tmp/<slug>-cut.mp4 - A raw take: offer to run up to its cut list first (steps 1 to 4) for the same reason. In a hurry: send the raw take as it is.
/short-editing
应翻译 take,而不是已完成的视频:成片中的字幕已经以原语言烧录进画面。随后再由 short-editing 根据新声音完成剪辑、字幕和时间轴处理。
- 已编辑的 take(存在 ):只发送保留片段,从而无需为静音、卡顿或重拍内容付费。使用 short-editing 的 ranges(来自
video-agent/edits/<slug>/edit.json)进行普通裁剪:video-agent/studio/裁剪后的源文件位于bashSEL=$(node -e 'const e=require("./public/<slug>.edit.json");console.log(e.ranges.map(r=>`between(t,${r.fromMs/1000},${r.toMs/1000})`).join("+"))') ffmpeg -y -i public/<slug>.mp4 -vf "select='$SEL',setpts=N/FRAME_RATE/TB" -af "aselect='$SEL',asetpts=N/SR/TB" -c:v libx264 -crf 18 -c:a aac -b:a 192k ../.tmp/<slug>-cut.mp4。video-agent/.tmp/<slug>-cut.mp4 - 原始 take:主动提出先运行 ,完成至其 cut list(步骤 1 到 4),原因相同。如果用户赶时间,则按原样发送原始 take。
/short-editing
3. Estimate, then send
3. 先估算,再发送
bash
ffprobe -v error -show_entries format=duration -of csv=p=0 "<source>"Show one line and wait for a yes: .
Dub <slug> fr → en · 0:52 of video · billed on your ElevenLabs credits for about 1 minute (check your plan's dubbing price on elevenlabs.io/pricing) · no lip-syncThen send it (about 10 s per minute of upload):
bash
mkdir -p video-agent/.tmp
set -a; . video-agent/.env; set +a
curl -sS --fail-with-body -X POST https://api.elevenlabs.io/v1/dubbing \
-H "xi-api-key: $ELEVENLABS_API_KEY" \
-F "file=@<source>" -F "target_lang=<lang>" -F "source_lang=<from or auto>" \
-F "num_speakers=1" -F "watermark=false" -F "name=<slug>-<lang>" \
> video-agent/.tmp/<slug>-<lang>.job.json
cat video-agent/.tmp/<slug>-<lang>.job.jsonIt returns . Keep the id: if anything stops, polling again with it costs nothing. A 401 is a wrong key; a 402 or "quota" means no credits left on their plan; refused: send it again without that field.
{"dubbing_id": "...", "expected_duration_sec": ...}watermarkbash
ffprobe -v error -show_entries format=duration -of csv=p=0 "<source>"显示一行并等待用户回复 yes:
Dub <slug> fr → en · 视频时长 0:52 · 将从 ElevenLabs credits 中扣除约 1 分钟的费用(请在 elevenlabs.io/pricing 查看套餐的 dubbing 价格)· 不做 lip-sync然后发送(上传大约每分钟素材需 10 秒):
bash
mkdir -p video-agent/.tmp
set -a; . video-agent/.env; set +a
curl -sS --fail-with-body -X POST https://api.elevenlabs.io/v1/dubbing \
-H "xi-api-key: $ELEVENLABS_API_KEY" \
-F "file=@<source>" -F "target_lang=<lang>" -F "source_lang=<from or auto>" \
-F "num_speakers=1" -F "watermark=false" -F "name=<slug>-<lang>" \
> video-agent/.tmp/<slug>-<lang>.job.json
cat video-agent/.tmp/<slug>-<lang>.job.json响应为 。保留该 id:如果流程中断,使用它再次轮询不会产生额外费用。401 表示 API key 错误;402 或“quota”表示套餐中没有剩余 credits;如果 被拒绝,则不传该字段重新发送。
{"dubbing_id": "...", "expected_duration_sec": ...}watermark4. Wait, then download
4. 等待,然后下载
Poll every 15 s until is (: show the error and stop). Usually 1 to 5 minutes for a short; run it in the background:
statusdubbedfailedbash
set -a; . video-agent/.env; set +a
curl -sS --fail-with-body -H "xi-api-key: $ELEVENLABS_API_KEY" https://api.elevenlabs.io/v1/dubbing/<dubbing_id>Then the dubbed file:
bash
set -a; . video-agent/.env; set +a
curl -sS --fail-with-body -H "xi-api-key: $ELEVENLABS_API_KEY" -o video-agent/.tmp/<slug>-<lang>.dub https://api.elevenlabs.io/v1/dubbing/<dubbing_id>/audio/<lang>
ffprobe -v error -show_entries stream=codec_type -of csv=p=0 video-agent/.tmp/<slug>-<lang>.dub- It has a stream: it is already the dubbed video, move it to
video.video-agent/takes/<slug>-<lang>.mp4 - Audio only: lay it back on the original image:
bash
ffmpeg -y -i "<source>" -i video-agent/.tmp/<slug>-<lang>.dub -map 0:v -map 1:a -c:v copy -c:a aac -b:a 192k -shortest video-agent/takes/<slug>-<lang>.mp4
Check it: same length as the source (within a second). Then ask them to listen to the first 10 seconds before editing (voice, accent, a wrong word).
每 15 秒轮询一次,直到 为 (若为 ,则显示错误并停止)。短视频通常需要 1 到 5 分钟;可在后台运行:
statusdubbedfailedbash
set -a; . video-agent/.env; set +a
curl -sS --fail-with-body -H "xi-api-key: $ELEVENLABS_API_KEY" https://api.elevenlabs.io/v1/dubbing/<dubbing_id>然后下载配音文件:
bash
set -a; . video-agent/.env; set +a
curl -sS --fail-with-body -H "xi-api-key: $ELEVENLABS_API_KEY" -o video-agent/.tmp/<slug>-<lang>.dub https://api.elevenlabs.io/v1/dubbing/<dubbing_id>/audio/<lang>
ffprobe -v error -show_entries stream=codec_type -of csv=p=0 video-agent/.tmp/<slug>-<lang>.dub- 如果包含 stream,则它已经是配音后的视频,将其移动到
video。video-agent/takes/<slug>-<lang>.mp4 - 如果只有音频,则将其重新合成到原始画面上:
bash
ffmpeg -y -i "<source>" -i video-agent/.tmp/<slug>-<lang>.dub -map 0:v -map 1:a -c:v copy -c:a aac -b:a 192k -shortest video-agent/takes/<slug>-<lang>.mp4
检查文件:时长应与源文件相同(误差不超过 1 秒)。然后让用户在编辑前试听前 10 秒(声音、口音、错误单词)。
5. Edit the translated take
5. 编辑翻译后的 take
/short-editing <slug>-<lang>- The language changed: in , set
video-agent/studio/whisper-config.mjstoWHISPER_LANGfor this transcription (English:<lang>works best; any other language:WHISPER_MODEL = "medium.en"), and put it back after. The hook card, callouts and"medium"are written inpost.md.<lang> - Same edit as the original when there is one: same hook card and callouts (translated), placed on the matching words of the new transcript.
- The ask (CTA) in : ask them once for its translation, or translate the one in voice.md and show it.
<lang>
Name everything so both versions sit side by side. Then on the account for that language.
<slug>-<lang>/publish <slug>-<lang>Delete at the end, except the job file if they may want to download the dub again.
video-agent/.tmp/<slug>-*运行 ,并向它说明:
/short-editing <slug>-<lang>- 语言已更改:在 中,将
video-agent/studio/whisper-config.mjs设为WHISPER_LANG以用于此次 transcription(英语:<lang>效果最佳;其他语言使用WHISPER_MODEL = "medium.en"),完成后恢复原值。hook card、callouts 和"medium"均使用post.md编写。<lang> - 若原版已有编辑方案,则沿用同一套方案:使用相同的 hook card 和 callouts(翻译后),并放置在新 transcript 中对应的词语上。
- 版本的 CTA:可向用户询问一次 CTA 的翻译,或直接翻译
<lang>中的 CTA 并展示。voice.md
将所有产物命名为 ,让两个版本并存。随后在该语言对应的账号上运行 。
<slug>-<lang>/publish <slug>-<lang>最后删除 ,但如果用户之后可能需要再次下载 dub,则保留 job file。
video-agent/.tmp/<slug>-*Honest limits
实际限制
- No lip-sync: the lips move in the original language. Fine with a lot of motion, a small face or quick cuts; noticeable on a long close-up.
- The voice clone comes from the take: a noisy take or music under the voice gives a worse clone.
- Machine translation: names, jargon and jokes can come out wrong. Ask them (or a native speaker) to check the transcript of the dubbed take before posting.
- Dubbing costs ElevenLabs credits per minute, and the price depends on their plan. Trimming the take first is the cheapest saving.
- 不做 lip-sync:嘴唇仍按原语言活动。画面运动较多、脸部较小或切换较快时问题不明显;在长时间特写中会比较明显。
- voice clone 来自 take 本身:take 中噪声较大或人声下方有音乐时,克隆效果会变差。
- machine translation 可能出错:名称、术语和笑话都可能被译错。发布前应让用户(或母语者)检查配音后 take 的 transcript。
- dubbing 按视频分钟数消耗 ElevenLabs credits,价格取决于用户的套餐。先裁剪 take 是最省 credits 的方法。