drewThomasson commited on
Commit
518cdf9
1 Parent(s): 757d0bf

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +701 -0
app.py ADDED
@@ -0,0 +1,701 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ print("starting...")
2
+
3
+ import os
4
+ import shutil
5
+ import subprocess
6
+ import re
7
+ from pydub import AudioSegment
8
+ import tempfile
9
+ from pydub import AudioSegment
10
+ import os
11
+ import nltk
12
+ from nltk.tokenize import sent_tokenize
13
+ import sys
14
+ import torch
15
+ from TTS.api import TTS
16
+ from TTS.tts.configs.xtts_config import XttsConfig
17
+ from TTS.tts.models.xtts import Xtts
18
+ from tqdm import tqdm
19
+ import gradio as gr
20
+ from gradio import Progress
21
+ import urllib.request
22
+ import zipfile
23
+ nltk.download('punkt_tab')
24
+
25
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
26
+ print(f"Device selected is: {device}")
27
+
28
+ #nltk.download('punkt') # Make sure to download the necessary models
29
+
30
+
31
+ def download_and_extract_zip(url, extract_to='.'):
32
+ try:
33
+ # Ensure the directory exists
34
+ os.makedirs(extract_to, exist_ok=True)
35
+
36
+ zip_path = os.path.join(extract_to, 'model.zip')
37
+
38
+ # Download with progress bar
39
+ with tqdm(unit='B', unit_scale=True, miniters=1, desc="Downloading Model") as t:
40
+ def reporthook(blocknum, blocksize, totalsize):
41
+ t.total = totalsize
42
+ t.update(blocknum * blocksize - t.n)
43
+
44
+ urllib.request.urlretrieve(url, zip_path, reporthook=reporthook)
45
+ print(f"Downloaded zip file to {zip_path}")
46
+
47
+ # Unzipping with progress bar
48
+ with zipfile.ZipFile(zip_path, 'r') as zip_ref:
49
+ files = zip_ref.namelist()
50
+ with tqdm(total=len(files), unit="file", desc="Extracting Files") as t:
51
+ for file in files:
52
+ if not file.endswith('/'): # Skip directories
53
+ # Extract the file to the temporary directory
54
+ extracted_path = zip_ref.extract(file, extract_to)
55
+ # Move the file to the base directory
56
+ base_file_path = os.path.join(extract_to, os.path.basename(file))
57
+ os.rename(extracted_path, base_file_path)
58
+ t.update(1)
59
+
60
+ # Cleanup: Remove the ZIP file and any empty folders
61
+ os.remove(zip_path)
62
+ for root, dirs, files in os.walk(extract_to, topdown=False):
63
+ for name in dirs:
64
+ os.rmdir(os.path.join(root, name))
65
+ print(f"Extracted files to {extract_to}")
66
+
67
+ # Check if all required files are present
68
+ required_files = ['model.pth', 'config.json', 'vocab.json_']
69
+ missing_files = [file for file in required_files if not os.path.exists(os.path.join(extract_to, file))]
70
+
71
+ if not missing_files:
72
+ print("All required files (model.pth, config.json, vocab.json_) found.")
73
+ else:
74
+ print(f"Missing files: {', '.join(missing_files)}")
75
+
76
+ except Exception as e:
77
+ print(f"Failed to download or extract zip file: {e}")
78
+
79
+
80
+
81
+ def is_folder_empty(folder_path):
82
+ if os.path.exists(folder_path) and os.path.isdir(folder_path):
83
+ # List directory contents
84
+ if not os.listdir(folder_path):
85
+ return True # The folder is empty
86
+ else:
87
+ return False # The folder is not empty
88
+ else:
89
+ print(f"The path {folder_path} is not a valid folder.")
90
+ return None # The path is not a valid folder
91
+
92
+ def remove_folder_with_contents(folder_path):
93
+ try:
94
+ shutil.rmtree(folder_path)
95
+ print(f"Successfully removed {folder_path} and all of its contents.")
96
+ except Exception as e:
97
+ print(f"Error removing {folder_path}: {e}")
98
+
99
+
100
+
101
+
102
+ def wipe_folder(folder_path):
103
+ # Check if the folder exists
104
+ if not os.path.exists(folder_path):
105
+ print(f"The folder {folder_path} does not exist.")
106
+ return
107
+
108
+ # Iterate over all the items in the given folder
109
+ for item in os.listdir(folder_path):
110
+ item_path = os.path.join(folder_path, item)
111
+ # If it's a file, remove it and print a message
112
+ if os.path.isfile(item_path):
113
+ os.remove(item_path)
114
+ print(f"Removed file: {item_path}")
115
+ # If it's a directory, remove it recursively and print a message
116
+ elif os.path.isdir(item_path):
117
+ shutil.rmtree(item_path)
118
+ print(f"Removed directory and its contents: {item_path}")
119
+
120
+ print(f"All contents wiped from {folder_path}.")
121
+
122
+
123
+ # Example usage
124
+ # folder_to_wipe = 'path_to_your_folder'
125
+ # wipe_folder(folder_to_wipe)
126
+
127
+
128
+ def create_m4b_from_chapters(input_dir, ebook_file, output_dir):
129
+ # Function to sort chapters based on their numeric order
130
+ def sort_key(chapter_file):
131
+ numbers = re.findall(r'\d+', chapter_file)
132
+ return int(numbers[0]) if numbers else 0
133
+
134
+ # Extract metadata and cover image from the eBook file
135
+ def extract_metadata_and_cover(ebook_path):
136
+ try:
137
+ cover_path = ebook_path.rsplit('.', 1)[0] + '.jpg'
138
+ subprocess.run(['ebook-meta', ebook_path, '--get-cover', cover_path], check=True)
139
+ if os.path.exists(cover_path):
140
+ return cover_path
141
+ except Exception as e:
142
+ print(f"Error extracting eBook metadata or cover: {e}")
143
+ return None
144
+ # Combine WAV files into a single file
145
+ def combine_wav_files(chapter_files, output_path):
146
+ # Initialize an empty audio segment
147
+ combined_audio = AudioSegment.empty()
148
+
149
+ # Sequentially append each file to the combined_audio
150
+ for chapter_file in chapter_files:
151
+ audio_segment = AudioSegment.from_wav(chapter_file)
152
+ combined_audio += audio_segment
153
+ # Export the combined audio to the output file path
154
+ combined_audio.export(output_path, format='wav')
155
+ print(f"Combined audio saved to {output_path}")
156
+
157
+ # Function to generate metadata for M4B chapters
158
+ def generate_ffmpeg_metadata(chapter_files, metadata_file):
159
+ with open(metadata_file, 'w') as file:
160
+ file.write(';FFMETADATA1\n')
161
+ start_time = 0
162
+ for index, chapter_file in enumerate(chapter_files):
163
+ duration_ms = len(AudioSegment.from_wav(chapter_file))
164
+ file.write(f'[CHAPTER]\nTIMEBASE=1/1000\nSTART={start_time}\n')
165
+ file.write(f'END={start_time + duration_ms}\ntitle=Chapter {index + 1}\n')
166
+ start_time += duration_ms
167
+
168
+ # Generate the final M4B file using ffmpeg
169
+ def create_m4b(combined_wav, metadata_file, cover_image, output_m4b):
170
+ # Ensure the output directory exists
171
+ os.makedirs(os.path.dirname(output_m4b), exist_ok=True)
172
+
173
+ ffmpeg_cmd = ['ffmpeg', '-i', combined_wav, '-i', metadata_file]
174
+ if cover_image:
175
+ ffmpeg_cmd += ['-i', cover_image, '-map', '0:a', '-map', '2:v']
176
+ else:
177
+ ffmpeg_cmd += ['-map', '0:a']
178
+
179
+ ffmpeg_cmd += ['-map_metadata', '1', '-c:a', 'aac', '-b:a', '192k']
180
+ if cover_image:
181
+ ffmpeg_cmd += ['-c:v', 'png', '-disposition:v', 'attached_pic']
182
+ ffmpeg_cmd += [output_m4b]
183
+
184
+ subprocess.run(ffmpeg_cmd, check=True)
185
+
186
+
187
+
188
+ # Main logic
189
+ chapter_files = sorted([os.path.join(input_dir, f) for f in os.listdir(input_dir) if f.endswith('.wav')], key=sort_key)
190
+ temp_dir = tempfile.gettempdir()
191
+ temp_combined_wav = os.path.join(temp_dir, 'combined.wav')
192
+ metadata_file = os.path.join(temp_dir, 'metadata.txt')
193
+ cover_image = extract_metadata_and_cover(ebook_file)
194
+ output_m4b = os.path.join(output_dir, os.path.splitext(os.path.basename(ebook_file))[0] + '.m4b')
195
+
196
+ combine_wav_files(chapter_files, temp_combined_wav)
197
+ generate_ffmpeg_metadata(chapter_files, metadata_file)
198
+ create_m4b(temp_combined_wav, metadata_file, cover_image, output_m4b)
199
+
200
+ # Cleanup
201
+ if os.path.exists(temp_combined_wav):
202
+ os.remove(temp_combined_wav)
203
+ if os.path.exists(metadata_file):
204
+ os.remove(metadata_file)
205
+ if cover_image and os.path.exists(cover_image):
206
+ os.remove(cover_image)
207
+
208
+ # Example usage
209
+ # create_m4b_from_chapters('path_to_chapter_wavs', 'path_to_ebook_file', 'path_to_output_dir')
210
+
211
+
212
+
213
+
214
+
215
+
216
+ #this code right here isnt the book grabbing thing but its before to refrence in ordero to create the sepecial chapter labeled book thing with calibre idk some systems cant seem to get it so just in case but the next bit of code after this is the book grabbing code with booknlp
217
+ import os
218
+ import subprocess
219
+ import ebooklib
220
+ from ebooklib import epub
221
+ from bs4 import BeautifulSoup
222
+ import re
223
+ import csv
224
+ import nltk
225
+
226
+ # Only run the main script if Value is True
227
+ def create_chapter_labeled_book(ebook_file_path):
228
+ # Function to ensure the existence of a directory
229
+ def ensure_directory(directory_path):
230
+ if not os.path.exists(directory_path):
231
+ os.makedirs(directory_path)
232
+ print(f"Created directory: {directory_path}")
233
+
234
+ ensure_directory(os.path.join(".", 'Working_files', 'Book'))
235
+
236
+ def convert_to_epub(input_path, output_path):
237
+ # Convert the ebook to EPUB format using Calibre's ebook-convert
238
+ try:
239
+ subprocess.run(['ebook-convert', input_path, output_path], check=True)
240
+ except subprocess.CalledProcessError as e:
241
+ print(f"An error occurred while converting the eBook: {e}")
242
+ return False
243
+ return True
244
+
245
+ def save_chapters_as_text(epub_path):
246
+ # Create the directory if it doesn't exist
247
+ directory = os.path.join(".", "Working_files", "temp_ebook")
248
+ ensure_directory(directory)
249
+
250
+ # Open the EPUB file
251
+ book = epub.read_epub(epub_path)
252
+
253
+ previous_chapter_text = ''
254
+ previous_filename = ''
255
+ chapter_counter = 0
256
+
257
+ # Iterate through the items in the EPUB file
258
+ for item in book.get_items():
259
+ if item.get_type() == ebooklib.ITEM_DOCUMENT:
260
+ # Use BeautifulSoup to parse HTML content
261
+ soup = BeautifulSoup(item.get_content(), 'html.parser')
262
+ text = soup.get_text()
263
+
264
+ # Check if the text is not empty
265
+ if text.strip():
266
+ if len(text) < 2300 and previous_filename:
267
+ # Append text to the previous chapter if it's short
268
+ with open(previous_filename, 'a', encoding='utf-8') as file:
269
+ file.write('\n' + text)
270
+ else:
271
+ # Create a new chapter file and increment the counter
272
+ previous_filename = os.path.join(directory, f"chapter_{chapter_counter}.txt")
273
+ chapter_counter += 1
274
+ with open(previous_filename, 'w', encoding='utf-8') as file:
275
+ file.write(text)
276
+ print(f"Saved chapter: {previous_filename}")
277
+
278
+ # Example usage
279
+ input_ebook = ebook_file_path # Replace with your eBook file path
280
+ output_epub = os.path.join(".", "Working_files", "temp.epub")
281
+
282
+
283
+ if os.path.exists(output_epub):
284
+ os.remove(output_epub)
285
+ print(f"File {output_epub} has been removed.")
286
+ else:
287
+ print(f"The file {output_epub} does not exist.")
288
+
289
+ if convert_to_epub(input_ebook, output_epub):
290
+ save_chapters_as_text(output_epub)
291
+
292
+ # Download the necessary NLTK data (if not already present)
293
+ #nltk.download('punkt')
294
+
295
+ def process_chapter_files(folder_path, output_csv):
296
+ with open(output_csv, 'w', newline='', encoding='utf-8') as csvfile:
297
+ writer = csv.writer(csvfile)
298
+ # Write the header row
299
+ writer.writerow(['Text', 'Start Location', 'End Location', 'Is Quote', 'Speaker', 'Chapter'])
300
+
301
+ # Process each chapter file
302
+ chapter_files = sorted(os.listdir(folder_path), key=lambda x: int(x.split('_')[1].split('.')[0]))
303
+ for filename in chapter_files:
304
+ if filename.startswith('chapter_') and filename.endswith('.txt'):
305
+ chapter_number = int(filename.split('_')[1].split('.')[0])
306
+ file_path = os.path.join(folder_path, filename)
307
+
308
+ try:
309
+ with open(file_path, 'r', encoding='utf-8') as file:
310
+ text = file.read()
311
+ # Insert "NEWCHAPTERABC" at the beginning of each chapter's text
312
+ if text:
313
+ text = "NEWCHAPTERABC" + text
314
+ sentences = nltk.tokenize.sent_tokenize(text)
315
+ for sentence in sentences:
316
+ start_location = text.find(sentence)
317
+ end_location = start_location + len(sentence)
318
+ writer.writerow([sentence, start_location, end_location, 'True', 'Narrator', chapter_number])
319
+ except Exception as e:
320
+ print(f"Error processing file {filename}: {e}")
321
+
322
+ # Example usage
323
+ folder_path = os.path.join(".", "Working_files", "temp_ebook")
324
+ output_csv = os.path.join(".", "Working_files", "Book", "Other_book.csv")
325
+
326
+ process_chapter_files(folder_path, output_csv)
327
+
328
+ def sort_key(filename):
329
+ """Extract chapter number for sorting."""
330
+ match = re.search(r'chapter_(\d+)\.txt', filename)
331
+ return int(match.group(1)) if match else 0
332
+
333
+ def combine_chapters(input_folder, output_file):
334
+ # Create the output folder if it doesn't exist
335
+ os.makedirs(os.path.dirname(output_file), exist_ok=True)
336
+
337
+ # List all txt files and sort them by chapter number
338
+ files = [f for f in os.listdir(input_folder) if f.endswith('.txt')]
339
+ sorted_files = sorted(files, key=sort_key)
340
+
341
+ with open(output_file, 'w', encoding='utf-8') as outfile: # Specify UTF-8 encoding here
342
+ for i, filename in enumerate(sorted_files):
343
+ with open(os.path.join(input_folder, filename), 'r', encoding='utf-8') as infile: # And here
344
+ outfile.write(infile.read())
345
+ # Add the marker unless it's the last file
346
+ if i < len(sorted_files) - 1:
347
+ outfile.write("\nNEWCHAPTERABC\n")
348
+
349
+ # Paths
350
+ input_folder = os.path.join(".", 'Working_files', 'temp_ebook')
351
+ output_file = os.path.join(".", 'Working_files', 'Book', 'Chapter_Book.txt')
352
+
353
+
354
+ # Combine the chapters
355
+ combine_chapters(input_folder, output_file)
356
+
357
+ ensure_directory(os.path.join(".", "Working_files", "Book"))
358
+
359
+
360
+ #create_chapter_labeled_book()
361
+
362
+
363
+
364
+
365
+ import os
366
+ import subprocess
367
+ import sys
368
+ import torchaudio
369
+
370
+ # Check if Calibre's ebook-convert tool is installed
371
+ def calibre_installed():
372
+ try:
373
+ subprocess.run(['ebook-convert', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
374
+ return True
375
+ except FileNotFoundError:
376
+ print("Calibre is not installed. Please install Calibre for this functionality.")
377
+ return False
378
+
379
+
380
+ import os
381
+ import torch
382
+ from TTS.api import TTS
383
+ from nltk.tokenize import sent_tokenize
384
+ from pydub import AudioSegment
385
+ # Assuming split_long_sentence and wipe_folder are defined elsewhere in your code
386
+
387
+ default_target_voice_path = "default_voice.wav" # Ensure this is a valid path
388
+ default_language_code = "en"
389
+ def combine_wav_files(input_directory, output_directory, file_name):
390
+ # Ensure that the output directory exists, create it if necessary
391
+ os.makedirs(output_directory, exist_ok=True)
392
+
393
+ # Specify the output file path
394
+ output_file_path = os.path.join(output_directory, file_name)
395
+
396
+ # Initialize an empty audio segment
397
+ combined_audio = AudioSegment.empty()
398
+
399
+ # Get a list of all .wav files in the specified input directory and sort them
400
+ input_file_paths = sorted(
401
+ [os.path.join(input_directory, f) for f in os.listdir(input_directory) if f.endswith(".wav")],
402
+ key=lambda f: int(''.join(filter(str.isdigit, f)))
403
+ )
404
+
405
+ # Sequentially append each file to the combined_audio
406
+ for input_file_path in input_file_paths:
407
+ audio_segment = AudioSegment.from_wav(input_file_path)
408
+ combined_audio += audio_segment
409
+
410
+ # Export the combined audio to the output file path
411
+ combined_audio.export(output_file_path, format='wav')
412
+
413
+ print(f"Combined audio saved to {output_file_path}")
414
+
415
+ # Function to split long strings into parts
416
+ def split_long_sentence(sentence, max_length=249, max_pauses=10):
417
+ """
418
+ Splits a sentence into parts based on length or number of pauses without recursion.
419
+
420
+ :param sentence: The sentence to split.
421
+ :param max_length: Maximum allowed length of a sentence.
422
+ :param max_pauses: Maximum allowed number of pauses in a sentence.
423
+ :return: A list of sentence parts that meet the criteria.
424
+ """
425
+ parts = []
426
+ while len(sentence) > max_length or sentence.count(',') + sentence.count(';') + sentence.count('.') > max_pauses:
427
+ possible_splits = [i for i, char in enumerate(sentence) if char in ',;.' and i < max_length]
428
+ if possible_splits:
429
+ # Find the best place to split the sentence, preferring the last possible split to keep parts longer
430
+ split_at = possible_splits[-1] + 1
431
+ else:
432
+ # If no punctuation to split on within max_length, split at max_length
433
+ split_at = max_length
434
+
435
+ # Split the sentence and add the first part to the list
436
+ parts.append(sentence[:split_at].strip())
437
+ sentence = sentence[split_at:].strip()
438
+
439
+ # Add the remaining part of the sentence
440
+ parts.append(sentence)
441
+ return parts
442
+
443
+ """
444
+ if 'tts' not in locals():
445
+ tts = TTS(selected_tts_model, progress_bar=True).to(device)
446
+ """
447
+ from tqdm import tqdm
448
+
449
+ # Convert chapters to audio using XTTS
450
+
451
+ def convert_chapters_to_audio_custom_model(chapters_dir, output_audio_dir, target_voice_path=None, language=None, custom_model=None):
452
+
453
+ if target_voice_path==None:
454
+ target_voice_path = default_target_voice_path
455
+
456
+ if custom_model:
457
+ print("Loading custom model...")
458
+ config = XttsConfig()
459
+ config.load_json(custom_model['config'])
460
+ model = Xtts.init_from_config(config)
461
+ model.load_checkpoint(config, checkpoint_path=custom_model['model'], vocab_path=custom_model['vocab'], use_deepspeed=False)
462
+ model.device
463
+ print("Computing speaker latents...")
464
+ gpt_cond_latent, speaker_embedding = model.get_conditioning_latents(audio_path=[target_voice_path])
465
+ else:
466
+ selected_tts_model = "tts_models/multilingual/multi-dataset/xtts_v2"
467
+ tts = TTS(selected_tts_model, progress_bar=False).to(device)
468
+
469
+ if not os.path.exists(output_audio_dir):
470
+ os.makedirs(output_audio_dir)
471
+
472
+ for chapter_file in sorted(os.listdir(chapters_dir)):
473
+ if chapter_file.endswith('.txt'):
474
+ match = re.search(r"chapter_(\d+).txt", chapter_file)
475
+ if match:
476
+ chapter_num = int(match.group(1))
477
+ else:
478
+ print(f"Skipping file {chapter_file} as it does not match the expected format.")
479
+ continue
480
+
481
+ chapter_path = os.path.join(chapters_dir, chapter_file)
482
+ output_file_name = f"audio_chapter_{chapter_num}.wav"
483
+ output_file_path = os.path.join(output_audio_dir, output_file_name)
484
+ temp_audio_directory = os.path.join(".", "Working_files", "temp")
485
+ os.makedirs(temp_audio_directory, exist_ok=True)
486
+ temp_count = 0
487
+
488
+ with open(chapter_path, 'r', encoding='utf-8') as file:
489
+ chapter_text = file.read()
490
+ sentences = sent_tokenize(chapter_text, language='italian' if language == 'it' else 'english')
491
+ for sentence in tqdm(sentences, desc=f"Chapter {chapter_num}"):
492
+ fragments = split_long_sentence(sentence, max_length=249 if language == "en" else 213, max_pauses=10)
493
+ for fragment in fragments:
494
+ if fragment != "":
495
+ print(f"Generating fragment: {fragment}...")
496
+ fragment_file_path = os.path.join(temp_audio_directory, f"{temp_count}.wav")
497
+ if custom_model:
498
+ out = model.inference(fragment, language, gpt_cond_latent, speaker_embedding, temperature=0.7)
499
+ torchaudio.save(fragment_file_path, torch.tensor(out["wav"]).unsqueeze(0), 24000)
500
+ else:
501
+ speaker_wav_path = target_voice_path if target_voice_path else default_target_voice_path
502
+ language_code = language if language else default_language_code
503
+ tts.tts_to_file(text=fragment, file_path=fragment_file_path, speaker_wav=speaker_wav_path, language=language_code)
504
+ temp_count += 1
505
+
506
+ combine_wav_files(temp_audio_directory, output_audio_dir, output_file_name)
507
+ wipe_folder(temp_audio_directory)
508
+ print(f"Converted chapter {chapter_num} to audio.")
509
+
510
+
511
+
512
+ def convert_chapters_to_audio_standard_model(chapters_dir, output_audio_dir, target_voice_path=None, language=None):
513
+ selected_tts_model = "tts_models/multilingual/multi-dataset/xtts_v2"
514
+ tts = TTS(selected_tts_model, progress_bar=False).to(device)
515
+
516
+ if not os.path.exists(output_audio_dir):
517
+ os.makedirs(output_audio_dir)
518
+
519
+ for chapter_file in sorted(os.listdir(chapters_dir)):
520
+ if chapter_file.endswith('.txt'):
521
+ match = re.search(r"chapter_(\d+).txt", chapter_file)
522
+ if match:
523
+ chapter_num = int(match.group(1))
524
+ else:
525
+ print(f"Skipping file {chapter_file} as it does not match the expected format.")
526
+ continue
527
+
528
+ chapter_path = os.path.join(chapters_dir, chapter_file)
529
+ output_file_name = f"audio_chapter_{chapter_num}.wav"
530
+ output_file_path = os.path.join(output_audio_dir, output_file_name)
531
+ temp_audio_directory = os.path.join(".", "Working_files", "temp")
532
+ os.makedirs(temp_audio_directory, exist_ok=True)
533
+ temp_count = 0
534
+
535
+ with open(chapter_path, 'r', encoding='utf-8') as file:
536
+ chapter_text = file.read()
537
+ sentences = sent_tokenize(chapter_text, language='italian' if language == 'it' else 'english')
538
+ for sentence in tqdm(sentences, desc=f"Chapter {chapter_num}"):
539
+ fragments = split_long_sentence(sentence, max_length=249 if language == "en" else 213, max_pauses=10)
540
+ for fragment in fragments:
541
+ if fragment != "":
542
+ print(f"Generating fragment: {fragment}...")
543
+ fragment_file_path = os.path.join(temp_audio_directory, f"{temp_count}.wav")
544
+ speaker_wav_path = target_voice_path if target_voice_path else default_target_voice_path
545
+ language_code = language if language else default_language_code
546
+ tts.tts_to_file(text=fragment, file_path=fragment_file_path, speaker_wav=speaker_wav_path, language=language_code)
547
+ temp_count += 1
548
+
549
+ combine_wav_files(temp_audio_directory, output_audio_dir, output_file_name)
550
+ wipe_folder(temp_audio_directory)
551
+ print(f"Converted chapter {chapter_num} to audio.")
552
+
553
+
554
+
555
+ # Define the functions to be used in the Gradio interface
556
+ def convert_ebook_to_audio(ebook_file, target_voice_file, language, use_custom_model, custom_model_file, custom_config_file, custom_vocab_file, custom_model_url=None, progress=gr.Progress()):
557
+ ebook_file_path = ebook_file.name
558
+ target_voice = target_voice_file.name if target_voice_file else None
559
+ custom_model = None
560
+
561
+
562
+ working_files = os.path.join(".", "Working_files", "temp_ebook")
563
+ full_folder_working_files = os.path.join(".", "Working_files")
564
+ chapters_directory = os.path.join(".", "Working_files", "temp_ebook")
565
+ output_audio_directory = os.path.join(".", 'Chapter_wav_files')
566
+ remove_folder_with_contents(full_folder_working_files)
567
+ remove_folder_with_contents(output_audio_directory)
568
+
569
+ if use_custom_model and custom_model_file and custom_config_file and custom_vocab_file:
570
+ custom_model = {
571
+ 'model': custom_model_file.name,
572
+ 'config': custom_config_file.name,
573
+ 'vocab': custom_vocab_file.name
574
+ }
575
+ if use_custom_model and custom_model_url:
576
+ print(f"Received custom model URL: {custom_model_url}")
577
+ download_dir = os.path.join(".", "Working_files", "custom_model")
578
+ download_and_extract_zip(custom_model_url, download_dir)
579
+ custom_model = {
580
+ 'model': os.path.join(download_dir, 'model.pth'),
581
+ 'config': os.path.join(download_dir, 'config.json'),
582
+ 'vocab': os.path.join(download_dir, 'vocab.json_')
583
+ }
584
+
585
+ try:
586
+ progress(0, desc="Starting conversion")
587
+ except Exception as e:
588
+ print(f"Error updating progress: {e}")
589
+
590
+ if not calibre_installed():
591
+ return "Calibre is not installed."
592
+
593
+
594
+ try:
595
+ progress(0.1, desc="Creating chapter-labeled book")
596
+ except Exception as e:
597
+ print(f"Error updating progress: {e}")
598
+
599
+ create_chapter_labeled_book(ebook_file_path)
600
+ audiobook_output_path = os.path.join(".", "Audiobooks")
601
+
602
+ try:
603
+ progress(0.3, desc="Converting chapters to audio")
604
+ except Exception as e:
605
+ print(f"Error updating progress: {e}")
606
+
607
+ if use_custom_model:
608
+ convert_chapters_to_audio_custom_model(chapters_directory, output_audio_directory, target_voice, language, custom_model)
609
+ else:
610
+ convert_chapters_to_audio_standard_model(chapters_directory, output_audio_directory, target_voice, language)
611
+
612
+ try:
613
+ progress(0.9, desc="Creating M4B from chapters")
614
+ except Exception as e:
615
+ print(f"Error updating progress: {e}")
616
+
617
+ create_m4b_from_chapters(output_audio_directory, ebook_file_path, audiobook_output_path)
618
+
619
+ # Get the name of the created M4B file
620
+ m4b_filename = os.path.splitext(os.path.basename(ebook_file_path))[0] + '.m4b'
621
+ m4b_filepath = os.path.join(audiobook_output_path, m4b_filename)
622
+
623
+ try:
624
+ progress(1.0, desc="Conversion complete")
625
+ except Exception as e:
626
+ print(f"Error updating progress: {e}")
627
+ print(f"Audiobook created at {m4b_filepath}")
628
+ return f"Audiobook created at {m4b_filepath}", m4b_filepath
629
+
630
+
631
+ def list_audiobook_files(audiobook_folder):
632
+ # List all files in the audiobook folder
633
+ files = []
634
+ for filename in os.listdir(audiobook_folder):
635
+ if filename.endswith('.m4b'): # Adjust the file extension as needed
636
+ files.append(os.path.join(audiobook_folder, filename))
637
+ return files
638
+
639
+ def download_audiobooks():
640
+ audiobook_output_path = os.path.join(".", "Audiobooks")
641
+ return list_audiobook_files(audiobook_output_path)
642
+
643
+
644
+ language_options = [
645
+ "en", "es", "fr", "de", "it", "pt", "pl", "tr", "ru", "nl", "cs", "ar", "zh-cn", "ja", "hu", "ko"
646
+ ]
647
+
648
+ theme = gr.themes.Soft(
649
+ primary_hue="blue",
650
+ secondary_hue="blue",
651
+ neutral_hue="blue",
652
+ text_size=gr.themes.sizes.text_md,
653
+ )
654
+
655
+ # Gradio UI setup
656
+ with gr.Blocks(theme=theme) as demo:
657
+ gr.Markdown(
658
+ """
659
+ # eBook to Audiobook Converter
660
+
661
+ Transform your eBooks into immersive audiobooks with optional custom TTS models.
662
+ """
663
+ )
664
+
665
+ with gr.Row():
666
+ with gr.Column(scale=3):
667
+ ebook_file = gr.File(label="eBook File")
668
+ target_voice_file = gr.File(label="Target Voice File (Optional)")
669
+ language = gr.Dropdown(label="Language", choices=language_options, value="en")
670
+
671
+ with gr.Column(scale=3):
672
+ use_custom_model = gr.Checkbox(label="Use Custom Model")
673
+ custom_model_file = gr.File(label="Custom Model File (Optional)", visible=False)
674
+ custom_config_file = gr.File(label="Custom Config File (Optional)", visible=False)
675
+ custom_vocab_file = gr.File(label="Custom Vocab File (Optional)", visible=False)
676
+ custom_model_url = gr.Textbox(label="Custom Model Zip URL (Optional)", visible=False)
677
+
678
+ convert_btn = gr.Button("Convert to Audiobook", variant="primary")
679
+ output = gr.Textbox(label="Conversion Status")
680
+ audio_player = gr.Audio(label="Audiobook Player", type="filepath")
681
+ download_btn = gr.Button("Download Audiobook Files")
682
+ download_files = gr.File(label="Download Files", interactive=False)
683
+
684
+ convert_btn.click(
685
+ convert_ebook_to_audio,
686
+ inputs=[ebook_file, target_voice_file, language, use_custom_model, custom_model_file, custom_config_file, custom_vocab_file, custom_model_url],
687
+ outputs=[output, audio_player]
688
+ )
689
+
690
+ use_custom_model.change(
691
+ lambda x: [gr.update(visible=x)] * 4,
692
+ inputs=[use_custom_model],
693
+ outputs=[custom_model_file, custom_config_file, custom_vocab_file, custom_model_url]
694
+ )
695
+
696
+ download_btn.click(
697
+ download_audiobooks,
698
+ outputs=[download_files]
699
+ )
700
+
701
+ demo.launch(share=True)