Skip to content

Commit 207de50

Browse files
committed
Refactor: centralized configuration, removed legacy code, updated CLI and docs
1 parent e75322a commit 207de50

23 files changed

Lines changed: 1597 additions & 1156 deletions

README.md

Lines changed: 76 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,23 @@
44
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
55
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
66

7-
A Python pipeline that takes a YouTube URL and spits out a dubbed/subtitled video. Feed it a link, pick a target language, and it handles the rest — downloading, transcribing, translating, synthesizing speech, and rendering the final video.
8-
9-
We built this because existing tools were either too manual, too expensive, or too locked-in. This one runs locally, stays free, and gives you full control over the output.
7+
Translate, subtitle, and dub any YouTube video automatically. Takes a URL and a target language — handles download, transcription, translation, TTS, and final render locally.
108

119
---
1210

1311
## How it works
1412

1513
```
16-
YouTube URL → Download → Transcribe (Whisper) → Chunk → Translate → TTS → Mix → Render
14+
YouTube URL → Download → Transcribe (Whisper) → Chunk → Translate (Google) → TTS → Mix → Render
1715
```
1816

19-
1. **Download**pulls video and audio via `yt-dlp`
20-
2. **Transcribe**runs Whisper ASR to get timestamped text
21-
3. **Chunk**splits transcript into natural speech segments (respects silence gaps, max 10s)
22-
4. **Translate**sends segments to Google Translate (RPC method, falls back to scraping)
23-
5. **TTS**synthesizes each segment using Edge TTS with the appropriate voice
24-
6. **Mix**overlays TTS audio on top of the original, ducked by 12dB
25-
7. **Render** — burns subtitles and/or swaps the audio track via FFmpeg
17+
1. **Download** — video + audio via `yt-dlp`
18+
2. **Transcribe** — Whisper ASR with VAD and temperature fallback
19+
3. **Chunk**groups into natural speech segments (≤10s, split at silences)
20+
4. **Translate** — Google Translate (RPC → scrape fallback)
21+
5. **TTS**Edge TTS or Qwen3-TTS with voice cloning & personas
22+
6. **Mix**tempo-aligned TTS overlaid with optional background music
23+
7. **Render** — burns subtitles and/or replaces audio via FFmpeg
2624

2725
---
2826

@@ -31,17 +29,15 @@ YouTube URL → Download → Transcribe (Whisper) → Chunk → Translate → TT
3129
### Prerequisites
3230

3331
- Python 3.10+
34-
- FFmpeg in your PATH
35-
- (Optional) CUDA for faster Whisper inference
32+
- FFmpeg in PATH
33+
- (Optional) CUDA GPU for faster Whisper
3634

3735
```bash
3836
# macOS
3937
brew install ffmpeg
40-
4138
# Ubuntu/Debian
4239
sudo apt install ffmpeg
43-
44-
# Windows — grab a build from https://ffmpeg.org/download.html
40+
# Windows — https://ffmpeg.org/download.html
4541
```
4642

4743
### Install
@@ -50,15 +46,16 @@ sudo apt install ffmpeg
5046
pip install youtube-auto-dub
5147
```
5248

53-
Or install from source:
49+
Or from source:
5450

5551
```bash
5652
git clone https://github.com/mangodxd/youtube-auto-dub.git
5753
cd youtube-auto-dub
5854
pip install .
5955
```
6056

61-
For GPU support:
57+
For GPU:
58+
6259
```bash
6360
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
6461
```
@@ -74,20 +71,17 @@ youtube-auto-dub "https://youtube.com/watch?v=VIDEO_ID"
7471
# Or via python -m
7572
python -m youtube_auto_dub "https://youtube.com/watch?v=VIDEO_ID"
7673

77-
# Or legacy entry point (still works)
78-
python main.py "https://youtube.com/watch?v=VIDEO_ID"
79-
80-
# Just subtitles, in Spanish
81-
youtube-auto-dub "https://youtube.com/watch?v=VIDEO_ID" --mode sub --lang es
74+
# Just subtitles, Spanish
75+
youtube-auto-dub "https://youtube.com/watch?v=VIDEO_ID" -m sub -l es
8276

8377
# Dubbing only, French, female voice
84-
youtube-auto-dub "https://youtube.com/watch?v=VIDEO_ID" --mode dub --lang fr --gender female
78+
youtube-auto-dub "https://youtube.com/watch?v=VIDEO_ID" -m dub -l fr -g female
8579

8680
# Different languages for subs and dub
87-
youtube-auto-dub "https://youtube.com/watch?v=VIDEO_ID" --mode both --lang_sub en --lang_dub vi
81+
youtube-auto-dub "https://youtube.com/watch?v=VIDEO_ID" -m both -s en -d vi
8882

89-
# Age-restricted or private video — pull cookies from your browser
90-
youtube-auto-dub "https://youtube.com/watch?v=VIDEO_ID" --lang es --browser chrome
83+
# Age-restricted — pull cookies from browser
84+
youtube-auto-dub "https://youtube.com/watch?v=VIDEO_ID" -b chrome
9185
```
9286

9387
### All options
@@ -97,17 +91,35 @@ youtube-auto-dub "https://youtube.com/watch?v=VIDEO_ID" --lang es --browser chro
9791
| `url` | | YouTube URL (required) |
9892
| `--mode` | `-m` | `sub`, `dub`, or `both` (default: `both`) |
9993
| `--lang` | `-l` | Target language for both sub and dub |
100-
| `--lang_sub` | `-ls` | Override subtitle language |
101-
| `--lang_dub` | `-ld` | Override dubbing language |
94+
| `--sub-lang` | `-s` | Override subtitle language |
95+
| `--dub-lang` | `-d` | Override dubbing language |
10296
| `--gender` | `-g` | `male` or `female` voice (default: `female`) |
103-
| `--whisper_model` | `-wm` | `tiny`, `base`, `small`, `medium` |
97+
| `--model` | | Whisper model: `tiny`, `base`, `small`, `medium`, `large` |
10498
| `--browser` | `-b` | Cookie source: `chrome`, `edge`, `firefox` |
99+
| `--tts-engine` | `-e` | `edge` or `qwen` (default: `edge`) |
100+
| `--voice` | | Qwen3-TTS persona: `narrator-m`, `young-f`, etc. |
101+
| `--voice-clone` | | Auto-clone voice from source audio |
102+
| `--no-tempo` | | Disable tempo alignment |
103+
| `--no-vad` | | Disable voice-activity detection |
104+
| `--bg-music` | | Mix original background audio into dub |
105+
| `--output-dir` | `-o` | Output directory (default: `./output`) |
106+
| `--version` | | Show version |
105107

106108
---
107109

108-
## Language & voice config
110+
## Configuration
111+
112+
All defaults are centralized in `youtube_auto_dub/models.py` and can be overridden via environment variables:
109113

110-
Voices are mapped in `language_map.json` inside the package. Edit it to change defaults or add new languages:
114+
| Variable | Default | Description |
115+
|----------|---------|-------------|
116+
| `YAD_CACHE_DIR` | `./.cache` | Download cache |
117+
| `YAD_TEMP_DIR` | `./temp` | Intermediates |
118+
| `YAD_OUTPUT_DIR` | `./output` | Final videos |
119+
| `YAD_SAMPLE_RATE` | `24000` | TTS / export sample rate |
120+
| `YAD_AMBIENT_GAIN` | `0.15` | Background music volume |
121+
122+
Voices are mapped in `youtube_auto_dub/language_map.json`. Edit to add languages or change voices:
111123

112124
```json
113125
{
@@ -121,103 +133,71 @@ Voices are mapped in `language_map.json` inside the package. Edit it to change d
121133
}
122134
```
123135

124-
Common codes: `es` · `fr` · `de` · `it` · `pt` · `ja` · `ko` · `zh` · `ar` · `hi` · `ru` · `vi` · `th`
136+
Common codes: `es` `fr` `de` `it` `pt` `ja` `ko` `zh` `ar` `hi` `ru` `vi` `th`
125137

126138
---
127139

128140
## Project structure
129141

130142
```
131-
youtube-auto-dub/
132-
├── pyproject.toml # Package config, dependencies, CLI entry
133-
├── main.py # Legacy entry point (thin wrapper)
134-
├── language_map.json # (root copy kept for reference)
135-
└── youtube_auto_dub/ # Installable Python package
136-
├── __init__.py # Package version
137-
├── __main__.py # python -m youtube_auto_dub support
138-
├── cli.py # Thin CLI adapter (argparse → pipeline)
139-
├── pipeline.py # Pipeline orchestration logic
140-
├── models.py # Dataclasses (SubtitleSegment, ProjectContext)
141-
├── youtube.py # yt-dlp wrapper, audio extraction
142-
├── media.py # Chunking, SRT, mixing, FFmpeg render
143-
├── googlev4.py # Google Translate (RPC + scrape fallback)
144-
├── tts.py # Edge TTS synthesis with retry
145-
├── transcriber.py # Whisper transcription + device manager
146-
├── renderer.py # FFmpeg helper utilities
147-
├── segmenter.py # Smart segmentation (dynamic chunking)
148-
├── config.py # Config manager, language data loading
149-
├── exceptions.py # Custom exception hierarchy
150-
├── utils.py # General-purpose utilities
151-
├── engine.py # Legacy AI Engine (preserved for BC)
152-
└── language_map.json # Language → voice mappings (package data)
153-
├── tests/
154-
│ ├── test_models.py # Model dataclass tests
155-
│ ├── test_googlev4.py # Translation shortcut tests (no network)
156-
│ └── test_tts.py # Voice lookup tests
157-
.tests/
158-
├── .github/workflows/
159-
│ └── ci.yml # Ruff lint + pytest on 3.10, 3.11, 3.12
160-
├── .cache/ # Downloaded videos (persists between runs)
161-
├── output/ # Final rendered videos
162-
└── temp/ # Intermediate files (cleared each run)
143+
youtube_auto_dub/
144+
├── __init__.py # Package version
145+
├── __main__.py # python -m support
146+
├── cli.py # argparse with global-standard flags
147+
├── core.py # Pipeline orchestrator (7-step run)
148+
├── models.py # Dataclasses + all centralized constants
149+
├── audio.py # Chunking, mixing, tempo align, loudness, render
150+
├── speech.py # Whisper ASR with VAD & metadata prompt
151+
├── subs.py # SRT parsing, resplit, refinement
152+
├── voice.py # Edge TTS + Qwen3-TTS synthesis
153+
├── googlev4.py # Google Translate (RPC + scrape)
154+
├── youtube.py # yt-dlp wrapper with metadata
155+
├── ui.py # Rich console helpers
156+
└── language_map.json # Language → voice mappings
157+
tests/
158+
├── test_models.py # Dataclass tests
159+
├── test_googlev4.py # Translation logic tests (no network)
160+
└── test_voice.py # Voice lookup tests
161+
.github/workflows/
162+
└── ci.yml # Ruff lint + pytest
163163
```
164164

165165
---
166166

167-
## Known issues & workarounds
168-
169-
**FFmpeg not found**
170-
Add FFmpeg to your system PATH. Run `ffmpeg -version` to verify.
167+
## Known issues
171168

172-
**CUDA out of memory**
173-
Switch to a smaller model: `--whisper_model tiny` or `--whisper_model base`. The pipeline auto-selects `base` on CPU and `small` on GPU if you don't specify.
169+
**FFmpeg not found** — add FFmpeg to PATH.
174170

175-
**Translation splitting wrong**
176-
The batch translator joins segments with a delimiter and splits on it after. If the translated text collapses the delimiter, it falls back to translating each segment individually. Slower but accurate.
171+
**CUDA OOM** — use `--model tiny` or `--model base`.
177172

178-
**YouTube rate-limited or auth errors**
179-
Close your browser fully before running with `--browser chrome`. If that still fails, export a `cookies.txt` file via a browser extension and pass it with `yt-dlp`'s `--cookies` option directly (you'd need to patch `youtube.py` for now — PRs welcome).
180-
181-
**TTS file too small / silent audio**
182-
Usually a bad language code or a voice that's region-restricted. Double check your `language_map.json` entry and try the other gender.
173+
**YouTube auth errors** — close browser fully before `--browser chrome`.
183174

184175
---
185176

186177
## What we're working on
187178

188-
- **Speaker diarization** — using `pyannote.audio` to detect multiple speakers and assign them distinct voices
189-
- **Background music separation**`Demucs` integration to preserve BGM while replacing only the vocals
190-
- **Voice conversion (RVC)** — optional post-processing to match the original speaker's voice characteristics
191-
192-
On the backlog:
193-
- Batch mode for playlists/channels
194-
- Local LLM translation (Llama 3 / Mistral) for offline/private use
195-
- Web UI via Gradio or Streamlit
196-
- Better lip-sync timing (stretch/compress TTS clips to fit original segment duration)
179+
- Speaker diarization (`pyannote.audio`)
180+
- Background music separation (Demucs)
181+
- Voice conversion (RVC) post-processing
182+
- Local LLM translation (offline)
183+
- Web UI (Gradio / Streamlit)
197184

198185
---
199186

200187
## Contributing
201188

202-
Issues and PRs are open. If you're adding a new language to `language_map.json`, please include both a male and female voice where Edge TTS supports it.
203-
204189
```bash
205-
# Dev install
206190
pip install -e ".[dev]"
207-
208-
# Run tests
209191
pytest tests/ -v
210-
211-
# Lint
212192
ruff check .
213193
```
214194

195+
Issues and PRs welcome. Include both male and female voices when adding a language to `language_map.json`.
196+
215197
---
216198

217199
## License
218200

219201
MIT. See [LICENSE](LICENSE).
220202

221-
---
222-
223203
*Built by Nguyen Cong Thuan Huy ([@mangodxd](https://github.com/mangodxd))*

tests/test_tts.py

Lines changed: 0 additions & 29 deletions
This file was deleted.

tests/test_voice.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""Tests for voice lookup functions (no network calls)."""
2+
3+
import pytest
4+
5+
from youtube_auto_dub.voice import pick_voice
6+
7+
8+
class TestPickVoice:
9+
def test_returns_string(self):
10+
voice = pick_voice("en", gender="female")
11+
assert isinstance(voice, str)
12+
assert len(voice) > 0
13+
14+
def test_male(self):
15+
voice = pick_voice("en", gender="male")
16+
assert isinstance(voice, str)
17+
18+
def test_fallback_gender(self):
19+
voice = pick_voice("am", gender="male")
20+
assert isinstance(voice, str)
21+
22+
def test_unknown_language(self):
23+
with pytest.raises(ValueError, match="not found|not in map"):
24+
pick_voice("zz", gender="female")

youtube_auto_dub/__init__.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
"""YouTube Auto Dub — Automated Video Translation and Dubbing."""
1+
"""YouTube Auto Dub — translate, subtitle, and dub any video automatically."""
22

3-
__version__ = "1.0.0"
4-
__author__ = "Nguyen Cong Thuan Huy (mangodxd)"
5-
6-
__all__ = [
7-
"__version__",
8-
"__author__",
9-
]
3+
__version__ = "2.0.0"

0 commit comments

Comments
 (0)