Multi-language video subtitle translation and automatic dubbing skill (supports English, Chinese, Japanese, Spanish, French, German, Korean, etc.).
Design & media
movie-subtitler
Try itDownload a foreign-language movie/video (or take a local file), transcribe and translate it to English with WhisperX, and recreate the video with English subtitles. Use on requests like "get English subs for this", "translate this movie", "subtitle this YouTube film". Fully local: yt-dlp → WhisperX → ffmpeg.
What it does
Download a foreign-language movie/video (or take a local file), transcribe and translate it to English with WhisperX, and recreate the video with English subtitles. Use on requests like "get English subs for this", "translate this movie", "subtitle this YouTube film". Fully local: yt-dlp → WhisperX → ffmpeg.
The skill document
movie-subtitler
Foreign-language video in → English-subtitled video out. One script does the whole pipeline.
Prerequisites
- ffmpeg — audio extraction and video mux/burn.
- macOS:
brew install ffmpeg - Linux:
sudo apt install ffmpeg(Debian/Ubuntu) or your distro's package manager
- macOS:
- yt-dlp — only needed if
--inputis a URL rather than a local file.- macOS:
brew install yt-dlp - Linux:
pip install -U yt-dlporsudo apt install yt-dlp
- macOS:
- whisperx — the transcribe/translate engine, on both macOS and Linux. Install it into a
Python virtualenv, e.g.:
(orpython3 -m venv ~/.venvs/whisperx source ~/.venvs/whisperx/bin/activate pip install whisperxuv pip install whisperxif you useuv.) A venv install commonly isn't left onPATH, so point the script at it withWHISPERX_BIN(see below) ifcommand -v whisperxdoesn't find it — e.g.WHISPERX_BIN=~/.venvs/whisperx/bin/whisperx. - GPU is optional. The script auto-detects
nvidia-smi: if present (Linux/Windows with an NVIDIA GPU) it uses WhisperX's CUDA default (float16); otherwise it falls back to--compute_type float32for CPU (including Apple silicon) inference. CPU-only transcription is slower but works fine — expect roughly ⅓–½ of the video's runtime (see below).
If any of whisperx/yt-dlp/ffmpeg aren't on PATH, set WHISPERX_BIN, YTDLP_BIN, or
FFMPEG_BIN to their full paths before running the script.
TL;DR — how to run it
./subtitle.sh \
--input "https://www.youtube.com/watch?v=..." \
--lang tr
--input— URL (anything yt-dlp handles) or a local file path.--lang— source language code (tr,pt,es,ja,fr, ...). Always pass it; check the video first (yt-dlp --skip-download --print "%(title)s") if unsure.- Output lands in the current directory as
.subbed.mp4+.subbed.eng.srt(srt basename matches the video so VLC auto-pairs the sidecar; override with-o/--outdirand--name). - Default is a fast soft-mux (subs as a selectable track, no re-encode — Plex/players
handle it fine). Pass
--burnonly if subs must be in the pixels (slow full re-encode). --no-translatekeeps subs in the original language.--model smalltrades accuracy for speed.
Known limitation: no speaker labels (diarization)
This skill doesn't label who's speaking. We tried bolting on whisperx --diarize (pyannote) and
it didn't hold up in practice, so it was left out. If you want to add it, here's what to know
going in:
- Why a naive bolt-on doesn't work: in translate mode, Whisper emits long (20-30s) subtitle
cues, since alignment isn't possible on translated text. That means a single
[SPEAKER_NN]label ends up stamped across an entire multi-person exchange — wrong, and a wall of text on screen. The speaker clusterer can also over-split a small cast into extra spurious IDs, especially with music or overlapping dialogue in the mix. - A design that would work better: (1) transcribe in the original language with alignment
enabled, giving short, per-word-timed cues with clean speaker turns; (2) translate each cue to
English separately (an LLM works well here) while preserving timing and speaker labels — this
step can also infer real character names from context instead of generic
SPEAKER_NNIDs. Passing--min_speakers/--max_speakersto pyannote helps when you know the cast size. Diarization is the most compute-hungry step in the whole pipeline (roughly +50% runtime on CPU), so a GPU helps a lot if you go this route. It also needspyannoteinstalled alongside whisperx and anHF_TOKENenvironment variable with access to pyannote's gated models on Hugging Face.
Run it in the background — WhisperX on CPU takes roughly ⅓–½ of the video's runtime
(a 2h movie ≈ 30–60 min; faster with a GPU). It prints [movie-subtitler] DONE: at the end.
Gotchas
- Whisper only translates into English, not between arbitrary language pairs.
- When translating, the script passes
--no_align: WhisperX's per-language alignment models can't align translated English text to foreign audio, so we keep plain Whisper timestamps. They're accurate to ~1s, fine for subtitles. - If the download 403s or fails, yt-dlp is probably stale: upgrade it (
pip install -U yt-dlporbrew upgrade yt-dlp, depending on how you installed it).
Files
subtitle.sh— the whole pipeline (download → audio extract → whisperx → mux/burn).
Related skills
Generate timecoded SRT subtitles from local video or audio files. Use when a user wants a local low-cost subtitle workflow, asks to transcribe local media in...
Fetch and use transcripts from public and local media
Turn movies and TV shows into language-learning material by analyzing subtitle files to extract vocabulary, build frequency decks, and create contextual flashcards. Use when learning a language through media immersion.
Transcribe audio and video with the DaDaScribe AI service (YouTube URLs, direct links, or local files). Supports 100+ languages, speaker diarization with named speakers, translation to up to 5 languages, and returns .txt transcripts plus .srt subtitles. Use whenever the user asks to transcribe, capt
Translate SRT, VTT, and ASS subtitles safely