About video-translate
video-translate guides creators in dubbing a raw video take into another language while retaining the original image and voice character. It uses ElevenLabs voice cloning, does not lip-sync, and hands the dubbed take to /video-edit for further editing.
Key features
- 01Generate target-language dubbing with ElevenLabs
- 02Clone the voice from the original take
- 03Trim unused sections before dubbing
- 04Keep the original image without lip-sync
- 05Pass dubbed takes to /video-edit
Use cases
- 01Dub a video take into another language
- 02Trim retakes and silence to reduce dubbing use
- 03Create new captions and edits for a dubbed take
- 04Review names, jargon, and phrasing in the dub
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ melvynx/ video-agent-skills --skill video-translateAfter installing, type/video-translatein Claude Code to confirm it loaded.
Details
From the project READMEVideo 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 /video-edit edits the translated take like any other, with captions in the new language.
Talk to them in their language. <slug>: the take's slug; <lang>: the target language code (en, es, fr, de, pt, it, ja...), <from>: the source one.
1. The ElevenLabs key (once)
The key lives in video-agent/.env, never in a chat message, a script or a commit:
grep -c ELEVENLABS_API_KEY video-agent/.env 2>/dev/null
0 or no file:
- 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
video-agent/.envthemselves, as one line:ELEVENLABS_API_KEY=sk_.... 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. video-agent/.envmust be in the project's.gitignore(add the linevideo-agent/.envif it isn't).
Every command below loads it with set -a; . video-agent/.env; set +a and never prints it.
2. What to translate
Translate the take, not a finished video: a finished video has captions burned in the old language. Then video-edit cuts, captions and times everything on the new voice.
- A take already edited (
video-agent/edits/<slug>/edit.jsonexists): send only the kept parts, so no silence, stutter or retake is paid for. Make the plain cut with video-edit's ranges (fromvideo-agent/studio/):
The source isSEL=$(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.mp4video-agent/.tmp/<slug>-cut.mp4. - A raw take: offer to run
/video-editup to its cut list first (steps 1 to 4) for the same reason. In a hurry: send the raw take as it is.
3. Estimate, then send
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-sync.
Then send it (about 10 s per minute of upload):
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
It returns {"dubbing_id": "...", "expected_duration_sec": ...}. 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; watermark refused: send it again without that field.
4. Wait, then download
Poll every 15 s until status is dubbed (failed: show the error and stop). Usually 1 to 5 minutes for a short; run it in the background:
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:
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
videostream: it is already the dubbed video, move it tovideo-agent/takes/<slug>-<lang>.mp4. - Audio only: lay it back on the original image:
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).
5. Edit the translated take
/video-edit <slug>-<lang>, and tell it:
- The language changed: in
video-agent/studio/whisper-config.mjs, setWHISPER_LANGto<lang>for this transcription (English:WHISPER_MODEL = "medium.en"works best; any other language:"medium"), and put it back after. The hook card, callouts andpost.mdare written in<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
<lang>: ask them once for its translation, or translate the one in voice.md and show it.
Name everything <slug>-<lang> so both versions sit side by side. Then /video-publish <slug>-<lang> on the account for that language.
Delete video-agent/.tmp/<slug>-* at the end, except the job file if they may want to download the dub again.
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.