video-dubbing · diff
git:20260618.b8c6e9a to git:20260925.32ce6c0
102 added, 109 removed. Audit A to A.
---
name: video-dubbing
- description: >
- Translate video/audio content into another language with dubbed voice and synchronized subtitles.
- Use this skill whenever the user asks to: translate a video, dub a video, generate foreign-language voiceover for a video,
- add translated subtitles with dubbed audio, or any workflow involving ASR → translate → TTS → video composition.
+ description: "Translate video/audio into another language with dubbed voice and synchronized subtitles. 完整流水线:抽音轨(16kHz) -> ASR -> AI 翻译 -> 分段 TTS 配音 -> 合并音轨 -> 烧录硬字幕,翻译字幕与配音共用原始时间戳,保证音画同步。触发词:视频翻译、视频配音、给视频配音、翻译字幕、生成外语配音、双语字幕、字幕同步、dub a video、translate a video、add foreign-language voiceover、dubbed subtitles。Use when the user asks to translate a video, dub a video, generate foreign-language voiceover, or add translated subtitles with dubbed audio. Do NOT use when:只需转录不需要翻译/配音(改用 video-minutes 或 whisper)、只需本机文字转语音出音频(改用 mlx-tts)、要求保留原背景音并做专业混音(需额外的 Demucs 人声分离,本技能默认不做)。"
---
- # Video Dubbing & Subtitle Sync
-
- Complete workflow: **ASR → translate → TTS → merge audio → burn subtitles**
-
- ## Pipeline
-
- ```
- 视频 → 抽音轨(16kHz) → ASR(whisper) → AI翻译 → TTS(dub_segments) → 合并音轨 → 烧录硬字幕
- ```
-
- ## Workflow
-
- ### 0. Prerequisite Check
-
- Ensure whisper + moviepy + mlx-audio are installed:
+ # video-dubbing: 视频翻译配音与字幕同步
- ```bash
- pip install openai-whisper moviepy 2>/dev/null
- # mlx_audio for TTS:
- pip install mlx-audio 2>/dev/null # Apple Silicon
- ```
+ ## Purpose
- ### 1. Extract Audio & Transcribe (ASR)
+ 把一段视频整体本地化为另一种语言:识别原语音、翻译、用 TTS 生成目标语言配音,在**保持原始
+ 时间戳**的前提下合成音轨并把译文硬字幕烧进画面。产出是可直接发布的单文件视频,配音与字幕
+ 天然同步,不需要二次对轴。
- Extract 16kHz mono audio, then run whisper for **accurate per-segment timestamps**:
+ ## When to Use
- ```bash
- ffmpeg -y -i input.mp4 -ar 16000 -ac 1 -c:a pcm_s16le audio16k.wav
+ - 用户要求把视频翻译成另一种语言,并要配音(不只是字幕)。
+ - 需要给视频加目标语言配音 + 同步字幕(双语或纯译文)。
+ - 触发词:视频翻译、视频配音、翻译字幕、生成外语配音、双语字幕、dub a video、translate a video、
+ foreign-language voiceover。
+ - 任何 ASR -> 翻译 -> TTS -> 视频合成的工作流。
- whisper audio16k.wav \
- --model large-v3-turbo \
- --language <source_lang> \
- --output_format srt \
- --output_dir .
- ```
+ ## When NOT to Use
- Output: `audio16k.srt` with native timestamps matching the video.
+ - 只需转录、不需要翻译或配音 -> 交给 `video-minutes` 或直接 whisper。
+ - 只需把文字合成为音频、不涉及视频 -> 交给 `mlx-tts`。
+ - 需要保留原背景音乐/音效并做专业混音(人声分离)-> 本技能默认不做 Demucs,需另行处理。
+ - 视频无音轨或音质极差到无法识别。
- > Use `large-v3-turbo` for best accuracy. If you want even better Chinese recognition, install Qwen3-ASR (`pip install qwen-asr`) and use `scripts/qwen3_asr.py` instead.
+ ## Workflow
- ### 2. Translate Subtitles (AI does this)
+ 端到端推荐直接用编排脚本 `<skill>/scripts/dub.py`(见 `## Scripts`),它会依次执行下面各步并
+ 在翻译处暂停等待 AI 翻译。手动执行时按以下步骤:
- Read the SRT, translate each segment's text into the target language. **Output one line per SRT segment**, preserving order exactly.
+ 1. **抽音轨(16kHz 单声道)**:
+ `ffmpeg -y -i input.mp4 -ar 16000 -ac 1 -c:a pcm_s16le audio16k.wav`
+ 2. **ASR 转录**(要**逐段准确时间戳**):
+ `whisper audio16k.wav --model large-v3-turbo --language <源语言> --output_format srt --output_dir .`
+ 产出 `audio16k.srt`,时间戳与原视频天然对齐。想要更好的中文识别可改装 Qwen3-ASR
+ (`pip install qwen-asr`)并用 `<skill>/scripts/qwen3_asr.py`。
+ 3. **翻译字幕(由 AI 完成)**:读 SRT,逐段翻译。**每个 SRT 段输出一行译文,严格保持顺序**。
+ 先数清段数(如 27 段 -> 27 行),存为 `translated.txt`,继续前核对行数一致。
+ 4. **生成配音**:
+ `python3 <skill>/scripts/dub_segments.py audio16k.srt translated.txt dubbing.wav subtitle_synced.srt --lang <目标语言>`
+ 脚本会:用首段作音色参考保证**音色一致**;**保留原始时间戳**让字幕与画面同步;只对溢出槽位
+ 的段做变速(atempo 0.88–1.20);把相邻小于 1s 的间隔桥接起来让听感自然。
+ 5. **合并配音音轨到视频**:
- - Count SRT segments first (e.g. 27 segments → 27 translated lines)
- - Save as `translated.txt` in the working directory
- - Verify line count matches SRT segment count before proceeding
+ ```bash
+ ffmpeg -y -i input.mp4 -i dubbing.wav \
+ -map 0:v:0 -map 1:a:0 \
+ -c:v copy -c:a aac -b:a 192k -shortest output_temp.mp4
+ ```
- ### 3. Generate Dubbed Audio
+ 然后确认音轨已被替换:
+ `ffprobe -v error -show_entries stream=codec_type -of csv=p=0 output_temp.mp4`(应显示 `video, audio`)。
+ 6. **烧录硬字幕**:
+ `python3 <skill>/scripts/burn_subtitles.py output_temp.mp4 subtitle_synced.srt output_final.mp4`
+ 把译文字幕永久渲染进画面。
+ 7. **清理(可选)**:删除 `audio16k.wav`、`audio16k.srt`、`translated.txt`、`dubbing.wav`、
+ `subtitle_synced.srt`、`output_temp.mp4` 等中间产物。
- ```bash
- python3 scripts/dub_segments.py audio16k.srt translated.txt dubbing.wav subtitle_synced.srt --lang <target_lang>
- ```
+ ## Decision Rules
- What it does:
- - Generates TTS per segment with **voice consistency** (first segment = voice reference)
- - **Preserves original timestamps** — subtitles stay synced with video
- - Smart speed adjustment: only adjusts segments that overflow their slot (atempo 0.88–1.20)
- - Bridges adjacent gaps <1s for natural flow
+ - 想一键跑完 -> 用 `dub.py`;只想重跑某一步(如已有 SRT)-> 用 `dub.py --srt existing.srt` 跳步。
+ - ASR 引擎:默认 `--asr whisper`(`large-v3-turbo`);中文识别要求高 -> `--asr qwen3`。
+ - 不需要硬字幕 -> `dub.py --no-burn`(只到音轨合并)。
+ - 目标语言由 `--target-lang` 决定(默认 `zh`);`dub_segments.py` 的 TTS 语言由 `--lang` 决定。
+ - 翻译必须由 AI 完成且与 SRT 段严格 1:1;行数不符时脚本直接报错退出,不要绕过。
+ - 字幕没出现/中文变方框 -> 用 `burn_subtitles.py`(moviepy 渲染,自动找中文字体),不要依赖
+ ffmpeg 的 `subtitles=`(libass 缺失时会静默失败)。
+ - 配音时长溢出槽位 -> 依赖脚本的分段 atempo,不要对整条音轨做全局变速。
- ### 4. Merge Dubbed Audio into Video
+ ## Constraints
- ```bash
- ffmpeg -y \
- -i input.mp4 \
- -i dubbing.wav \
- -map 0:v:0 -map 1:a:0 \
- -c:v copy -c:a aac -b:a 192k \
- -shortest \
- output_temp.mp4
- ```
+ - **时间戳必须复用 ASR 原始结果**,禁止对配音重新 ASR 取时间。
+ - **译文行数必须等于 SRT 段数**(1:1),否则中止。
+ - **合并音轨必须显式 `-map 0:v:0 -map 1:a:0`**,否则会保留原音轨。
+ - ASR 必须用 `large-v3-turbo`(或 `turbo`),不要退回 `base`/`tiny`。
+ - 变速只在段级别进行,范围 0.88–1.20x;禁止全局 atempo。
+ - 默认不做背景音乐人声分离;用户要求时应显式说明需要额外处理。
- Verify audio replaced:
- ```bash
- ffprobe -v error -show_entries stream=codec_type -of csv=p=0 output_temp.mp4
- # Should show: video, audio
- ```
+ ## Verification
- ### 5. Burn Hard Subtitles into Video
+ 交付前逐项确认:
- ```bash
- python3 scripts/burn_subtitles.py output_temp.mp4 subtitle_synced.srt output_final.mp4
- ```
+ - [ ] `translated.txt` 行数与 SRT 段数完全一致。
+ - [ ] `dubbing.wav` 与 `subtitle_synced.srt` 均已生成。
+ - [ ] `ffprobe` 显示最终视频同时含 video 与 audio 流,且音频确实被替换。
+ - [ ] 字幕已硬烧进画面(不是外部软字幕),中文无方框。
+ - [ ] 配音与画面在若干随机点抽查时间戳对齐。
+ - [ ] 未删除用户提供的源文件。
- This renders translated subtitles permanently into the video frame (hardcoded).
+ ## Output
- ### 6. Cleanup (Optional)
+ - **Outcome**:一个本地化后的视频文件(默认 `output_final.mp4`,`dub.py` 下为
+ `<视频名>_<目标语言>.mp4`),含目标语言配音与硬字幕;另可保留 `subtitle_synced.srt`。
+ - **Done when**:音轨已替换、字幕已烧录、音画在抽查点同步、ffprobe 校验通过。
+ - **Evidence**:最终文件路径;SRT 段数与译文行数;ffprobe 的流清单;抽查的同步时间点。
- ```bash
- rm -f audio16k.wav audio16k.srt translated.txt dubbing.wav subtitle_synced.srt output_temp.mp4
- ```
+ ## References
- ## Output Files
+ 按需加载,不要预先全读:
- | File | Description |
- |------|-------------|
- | `output_final.mp4` | Final video with dubbed audio + hardcoded subtitles |
- | `subtitle_synced.srt` | Synced subtitles (original timestamps + translated text) |
- | `dubbing.wav` | Per-segment aligned dubbing track |
+ - `references/pipeline-design.md` — 需要解释或调整流水线行为(为何保留时间戳、分段变速、
+ 默认不做人声分离、产物与依赖清单)时读。
+ - `references/troubleshooting.md` — 实际报错、音画不同步、字幕没出现或中文变方框时读。
- ## Key Design Decisions
+ ## Scripts
- | Principle | Why |
- |-----------|-----|
- | **Preserve original ASR timestamps** | They match the video's visual cues natively. Re-ASR on dubbing drifts. |
- | **Per-segment speed adjustment** (0.88–1.20x) | Global atempo on the whole dubbing sounds robotic. Per-segment is natural. |
- | **Hard subtitles (burned in)** | Soft subtitles don't work on all platforms. Burn them so they always show. |
- | **16kHz mono for ASR** | Whisper expects this. Higher sample rate wastes compute without improving accuracy. |
- | **Voice consistency via first segment reference** | mlx-tts supports `--ref_audio`. First segment sets the voice for all others. |
- | **No Demucs by default** | For typical narration/speech videos, background music removal is unnecessary overhead. |
- | **large-v3-turbo model** | Best accuracy/speed tradeoff. Significantly better than `base` for proper nouns and fast speech. |
+ - `<skill>/scripts/dub.py` — 端到端编排。用法
+ `python3 <skill>/scripts/dub.py input.mp4 --source-lang en --target-lang zh [--asr whisper|qwen3] [--srt existing.srt] [-o out.mp4] [--font-size 28] [--no-burn] [--keep-temp]`。
+ 运行到翻译步时会生成 `to_translate.txt` 并要求 AI 翻译成 `translated.txt`,然后重跑继续;
+ 返回最终视频路径与字幕路径。
+ - `<skill>/scripts/dub_segments.py` — 分段 TTS 配音 + 时间戳对齐。用法
+ `python3 <skill>/scripts/dub_segments.py original.srt translated.txt out_dub.wav out_synced.srt [--lang zh] [--min-atempo 0.88] [--max-atempo 1.20] [--gap-bridge 1.0] [--keep-temp]`。
+ 返回对齐后的 `dubbing.wav` 与 `subtitle_synced.srt`;译文行数不匹配时以退出码 1 中止。
+ - `<skill>/scripts/burn_subtitles.py` — 用 moviepy 烧录硬字幕。用法
+ `python3 <skill>/scripts/burn_subtitles.py input.mp4 subtitles.srt output.mp4 [--font <path>] [--font-size 28]`。
+ 返回烧好字幕的视频;`--font` 缺省时自动探测中文字体。
+ - `<skill>/scripts/qwen3_asr.py` — whisper 兼容的 Qwen3-ASR CLI,中文识别更佳。用法
+ `python3 <skill>/scripts/qwen3_asr.py audio.wav [--model 0.6B|1.7B] [--language zh] [-f srt|vtt|txt|json|tsv|all] [-o 输出目录] [--device cuda:0|mps|cpu] [--dtype float16|bfloat16|float32]`。
+ 返回指定格式的字幕/文本文件。模型别名:`turbo/tiny/base/small -> Qwen3-ASR-0.6B`,
+ `medium/large -> Qwen3-ASR-1.7B`。
- ## Dependencies
+ ## Troubleshooting
- | Component | Install | Purpose |
- |-----------|---------|---------|
- | `ffmpeg` | `brew install ffmpeg` | Audio extraction, merging |
- | `openai-whisper` | `pip install openai-whisper` | Speech-to-text (ASR) |
- | `moviepy` | `pip install moviepy` | Burn subtitles into video |
- | `mlx-audio` | `pip install mlx-audio` | TTS on Apple Silicon |
- | `qwen-asr` | `pip install qwen-asr` (optional) | Better Chinese ASR |
+ - 配音没被替换 / 还是原声 -> 合并 ffmpeg 时漏了 `-map 0:v:0 -map 1:a:0`。
+ - 字幕没出现或中文方框 -> 改用 `burn_subtitles.py`,并用 `--font` 指定中文字体。
+ - 译文行数与 SRT 段数不符 -> 脚本会报错退出,先补齐到 1:1。
+ - 完整错误清单见 `references/troubleshooting.md`。
- ## Common Mistakes to Avoid
+ ## Related Skills
- | Mistake | Why It Fails | Correct Approach |
- |---------|-------------|------------------|
- | ffmpeg without `-map` | Picks original audio (video source's audio stream), dubbing ignored | Always use `-map 0:v:0 -map 1:a:0` |
- | Global atempo on entire dubbing | All speech slows uniformly (EN→ZH ~0.8x), sounds robotic | Per-segment alignment (dub_segments.py) |
- | Re-ASR on dubbed audio for timestamps | Timestamps drift from visual cues | Reuse original Step 1 timestamps |
- | Translation line count ≠ SRT segments | `dub_segments.py` requires strict 1:1 mapping | Count SRT segments first, match exactly |
- | Use whisper `base`/`tiny` model | Proper nouns wrong, fast speech missed | Use `large-v3-turbo` or `turbo` |
- | Burn subtitles without checking FFmpeg libass | `subtitles=` filter silently fails | Use `scripts/burn_subtitles.py` (moviepy) |
- | Translate segments out of order | Subtitles play at wrong times | Keep segment order: 1 translated line per SRT segment |
- | Skip verification | Audio may not have been replaced | Run ffprobe to check both audio + video streams exist |
+ - `mlx-tts` — 提供底层 `mlx_audio.tts.generate`,本技能的配音节依赖它。
+ - `video-minutes` — 视频转结构化纪要;只需转录/总结时改用它。