1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
#!/usr/bin/env python3
"""
Qwen-Based Audiobook Converter
Converts TXT, PDF and EPUB files into audiobooks using a local Qwen3-TTS server.
Edit converter/config.py to change voice and processing settings.
License: MIT
"""
import argparse
import sys
import traceback
# Fix Windows console encoding for unicode output
if sys.platform == "win32":
try:
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
except AttributeError:
pass
from converter import config
from converter.converter import AudiobookConverter, setup_directories, setup_logging
def main() -> None:
"""Entry point with argparse."""
parser = argparse.ArgumentParser(
description="Convert books to audiobooks using the Qwen3-TTS voice model",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Use custom voice (default - Vivian speaker)
python audiobook_converter.py
# Use voice cloning with reference audio
python audiobook_converter.py --voice-clone --voice-sample path/to/reference.wav
"""
)
parser.add_argument(
"--voice-clone",
action="store_true",
help="Use voice cloning mode instead of custom voice (requires --voice-sample)"
)
parser.add_argument(
"--voice-sample",
type=str,
help="Path to reference audio file for voice cloning (WAV format)."
)
parser.add_argument(
"--voice-sample-text",
type=str,
default=None,
help=("Transcript of the reference audio for in-context cloning (recommended for "
"highest quality). If omitted, a local Whisper backend is used if installed; "
"otherwise the converter falls back to x-vector-only mode.")
)
parser.add_argument(
"--no-transcription",
action="store_true",
help=("Skip automatic transcription of the reference audio (use x-vector-only "
"cloning). Ignored when --voice-sample-text is provided.")
)
parser.add_argument(
"--speed",
type=float,
default=1.0,
help="Playback speed factor for the final audiobook (1.0 = normal). Pitch-preserving."
)
parser.add_argument(
"--format",
choices=list(config.AUDIO_FORMATS),
default=config.AUDIO_FORMAT,
help=f"Output container format (default: {config.AUDIO_FORMAT}). m4b uses AAC audio."
)
parser.add_argument(
"--single-file",
action="store_true",
help=("Combine all chapters into a single mp3. By default books with "
"chapters (e.g. EPUB) are converted to one mp3 per chapter. "
"Ignored for m4b, which is always a single file.")
)
args = parser.parse_args()
if args.speed <= 0:
parser.error(f"--speed must be a positive number (got {args.speed:g})")
if args.voice_clone:
if not args.voice_sample:
print("[ERROR] --voice-clone requires --voice-sample")
print('Usage: python audiobook_converter.py --voice-clone --voice-sample <path> [--voice-sample-text "..."]')
sys.exit(1)
elif args.voice_sample or args.voice_sample_text or args.no_transcription:
print("[WARNING] --voice-sample/--voice-sample-text/--no-transcription "
"are ignored without --voice-clone")
setup_logging()
setup_directories()
try:
converter = AudiobookConverter(
voice_mode=config.VOICE_MODE_CLONE if args.voice_clone else config.VOICE_MODE_CUSTOM,
voice_clone_ref_audio=args.voice_sample if args.voice_clone else None,
voice_clone_ref_text=args.voice_sample_text if args.voice_clone else None,
skip_transcription=args.no_transcription,
speed=args.speed,
single_file=args.single_file,
output_format=args.format,
)
ok = converter.run()
except KeyboardInterrupt:
print("\n[WARNING] Shutdown requested by user")
sys.exit(130)
except Exception as exc:
print(f"[FATAL] Fatal error: {exc}")
traceback.print_exc()
sys.exit(1)
sys.exit(0 if ok else 1)
if __name__ == "__main__":
main()
|