-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathebookconvert.py
More file actions
269 lines (225 loc) · 7.75 KB
/
ebookconvert.py
File metadata and controls
269 lines (225 loc) · 7.75 KB
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
#!/usr/bin/env python3
"""
ebookconvert.py - Main orchestration script for text-to-speech conversion
Validates inputs, coordinates other modules, and manages the conversion pipeline
"""
import argparse
import os
import sys
from pathlib import Path
# Import our modules
try:
from dependencies import check_dependencies
from config_check import validate_configurations
from process_audio import AudioProcessor
from create_audio_out import concatenate_audio_segments
except ImportError as e:
print(f"Error importing modules: {e}")
print("Please ensure all module files are in the same directory:")
print(" - dependencies.py")
print(" - config_check.py")
print(" - process_audio.py")
print(" - create_audio_out.py")
sys.exit(1)
def validate_file_exists(filepath, description):
"""Validate that a file exists"""
if not os.path.exists(filepath):
print(f"Error: {description} not found: {filepath}")
return False
if not os.path.isfile(filepath):
print(f"Error: {description} is not a file: {filepath}")
return False
return True
def validate_output_path(filepath):
"""Validate output path and create directory if needed"""
output_dir = os.path.dirname(filepath)
# If no directory specified, use current directory
if not output_dir:
return True
# Create directory if it doesn't exist
if not os.path.exists(output_dir):
try:
os.makedirs(output_dir, exist_ok=True)
print(f"Created output directory: {output_dir}")
return True
except Exception as e:
print(f"Error creating output directory {output_dir}: {e}")
return False
# Check if directory is writable
if not os.access(output_dir, os.W_OK):
print(f"Error: Output directory is not writable: {output_dir}")
return False
return True
def parse_arguments():
"""Parse and validate command line arguments"""
parser = argparse.ArgumentParser(
description='Convert marked-up text to speech using XTTS-v2',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s --voices-config voices.json --sfx-defaults sfx.json \\
--input story.txt --output-audio output.wav
%(prog)s -v voices.json -s sfx.json -i input.txt -o output.wav
"""
)
parser.add_argument(
'--voices-config', '-v',
type=str,
required=True,
help='Path to JSON file containing voice character definitions',
metavar='FILE'
)
parser.add_argument(
'--sfx-defaults', '-s',
type=str,
required=True,
help='Path to JSON file containing sound effect definitions and default pause/break durations',
metavar='FILE'
)
parser.add_argument(
'--input', '-i',
type=str,
required=True,
help='Path to input text file with markup',
metavar='FILE'
)
parser.add_argument(
'--output-audio', '-o',
type=str,
required=True,
help='Path to output audio file (WAV format)',
metavar='FILE'
)
parser.add_argument(
'--temp-dir',
type=str,
default='./temp_audio_segments',
help='Directory for temporary audio segments (default: ./temp_audio_segments)',
metavar='DIR'
)
parser.add_argument(
'--keep-temp',
action='store_true',
help='Keep temporary audio segment files after processing'
)
return parser.parse_args()
def validate_arguments(args):
"""Validate all command line arguments"""
print("Validating input arguments...")
valid = True
# Validate input files exist
if not validate_file_exists(args.voices_config, "Voices configuration file"):
valid = False
if not validate_file_exists(args.sfx_defaults, "SFX defaults configuration file"):
valid = False
if not validate_file_exists(args.input, "Input text file"):
valid = False
# Validate output path
if not validate_output_path(args.output_audio):
valid = False
# Validate temp directory
if not validate_output_path(os.path.join(args.temp_dir, "dummy")):
valid = False
if not valid:
print("\nValidation failed. Please check the errors above.")
return False
print("✓ All input arguments validated")
return True
def main():
"""Main entry point for the ebook conversion process"""
print("=" * 70)
print("XTTS-v2 Text-to-Speech Conversion System")
print("=" * 70)
print()
# Parse arguments
args = parse_arguments()
# Validate arguments
if not validate_arguments(args):
sys.exit(1)
print()
print("Configuration:")
print(f" Voices Config: {args.voices_config}")
print(f" SFX Defaults: {args.sfx_defaults}")
print(f" Input File: {args.input}")
print(f" Output File: {args.output_audio}")
print(f" Temp Directory: {args.temp_dir}")
print()
# Step 1: Check dependencies
print("Step 1/5: Checking dependencies...")
if not check_dependencies():
print("Error: Dependency check failed")
sys.exit(1)
print("✓ Dependencies validated")
print()
# Step 2: Validate configuration files
print("Step 2/5: Validating configuration files...")
voices_config, sfx_config = validate_configurations(
args.voices_config,
args.sfx_defaults
)
if voices_config is None or sfx_config is None:
print("Error: Configuration validation failed")
sys.exit(1)
print("✓ Configurations validated")
print()
# Step 3: Initialize audio processor
print("Step 3/5: Initializing audio processor...")
try:
processor = AudioProcessor(
voices_config=voices_config,
sfx_config=sfx_config,
temp_dir=args.temp_dir
)
print("✓ Audio processor initialized")
except Exception as e:
print(f"Error initializing audio processor: {e}")
sys.exit(1)
print()
# Step 4: Process input text and generate audio segments
print("Step 4/5: Processing text and generating audio...")
try:
segment_files = processor.process_text_file(args.input)
print(f"✓ Generated {len(segment_files)} audio segments")
except Exception as e:
print(f"Error processing text file: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
print()
# Step 5: Concatenate audio segments into final output
print("Step 5/5: Creating final audio file...")
try:
concatenate_audio_segments(segment_files, args.output_audio)
print(f"✓ Final audio saved to: {args.output_audio}")
except Exception as e:
print(f"Error creating final audio: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
print()
# Cleanup temporary files if requested
if not args.keep_temp:
print("Cleaning up temporary files...")
import shutil
try:
shutil.rmtree(args.temp_dir)
print("✓ Temporary files removed")
except Exception as e:
print(f"Warning: Could not remove temporary directory: {e}")
else:
print(f"Temporary audio segments saved in: {args.temp_dir}")
print()
print("=" * 70)
print("Conversion completed successfully!")
print("=" * 70)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\nOperation cancelled by user")
sys.exit(1)
except Exception as e:
print(f"\n\nUnexpected error: {e}")
import traceback
traceback.print_exc()
sys.exit(1)