drewThomasson commited on
Commit
408d3f8
·
verified ·
1 Parent(s): 0e4f3d0

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +237 -0
app.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import os
3
+ import regex as re
4
+ import socket
5
+ import subprocess
6
+ import sys
7
+ import unidic
8
+
9
+ from lib.conf import *
10
+ from lib.lang import language_mapping, default_language_code
11
+
12
+ # To download the voices folder
13
+ import download_github_folder
14
+ # Call the main function to execute the downloading process
15
+ download_github_folder.main()
16
+
17
+ def check_python_version():
18
+ current_version = sys.version_info[:2] # (major, minor)
19
+ if current_version < min_python_version or current_version > max_python_version:
20
+ error = f'''********** Error: Your OS Python version is not compatible! (current: {current_version[0]}.{current_version[1]})
21
+ Please create a virtual python environment verrsion {min_python_version[0]}.{min_python_version[1]} or {max_python_version[0]}.{max_python_version[1]}
22
+ with conda or python -v venv **********'''
23
+ print(error)
24
+ return False
25
+ else:
26
+ return True
27
+
28
+ def check_and_install_requirements(file_path):
29
+ if not os.path.exists(file_path):
30
+ print(f'Warning: File {file_path} not found. Skipping package check.')
31
+ try:
32
+ from importlib.metadata import version, PackageNotFoundError
33
+ with open(file_path, 'r') as f:
34
+ contents = f.read().replace('\r', '\n')
35
+ packages = [pkg.strip() for pkg in contents.splitlines() if pkg.strip()]
36
+
37
+ missing_packages = []
38
+ for package in packages:
39
+ # Extract package name without version specifier
40
+ pkg_name = re.split(r'[<>=]', package)[0].strip()
41
+ try:
42
+ installed_version = version(pkg_name)
43
+ except PackageNotFoundError:
44
+ print(f'{package} is missing.')
45
+ missing_packages.append(package)
46
+ pass
47
+
48
+ if missing_packages:
49
+ print('\nInstalling missing packages...')
50
+ try:
51
+ subprocess.check_call([sys.executable, '-m', 'pip', 'install', '--upgrade', 'pip'] + missing_packages)
52
+ except subprocess.CalledProcessError as e:
53
+ print(f'Failed to install packages: {e}')
54
+ return False
55
+
56
+ return True
57
+ except Exception as e:
58
+ raise(f'An error occurred: {e}')
59
+
60
+ def check_dictionary():
61
+ unidic_path = unidic.DICDIR
62
+ dicrc = os.path.join(unidic_path, 'dicrc')
63
+ if not os.path.exists(dicrc) or os.path.getsize(dicrc) == 0:
64
+ try:
65
+ print('UniDic dictionary not found or incomplete. Downloading now...')
66
+ subprocess.run(['python', '-m', 'unidic', 'download'], check=True)
67
+ except subprocess.CalledProcessError as e:
68
+ print(f'Failed to download UniDic dictionary. Error: {e}')
69
+ raise SystemExit('Unable to continue without UniDic. Exiting...')
70
+ return True
71
+
72
+ def is_port_in_use(port):
73
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
74
+ return s.connect_ex(('0.0.0.0', port)) == 0
75
+
76
+ def main():
77
+ global is_gui_process
78
+
79
+ # Convert the list of languages to a string to display in the help text
80
+ lang_list_str = ', '.join(list(language_mapping.keys()))
81
+
82
+ # Argument parser to handle optional parameters with descriptions
83
+ parser = argparse.ArgumentParser(
84
+ description='Convert eBooks to Audiobooks using a Text-to-Speech model. You can either launch the Gradio interface or run the script in headless mode for direct conversion.',
85
+ epilog='''
86
+ Example usage:
87
+ Windows:
88
+ headless:
89
+ ebook2audiobook.cmd --headless --ebook 'path_to_ebook'
90
+ Graphic Interface:
91
+ ebook2audiobook.cmd
92
+ Linux/Mac:
93
+ headless:
94
+ ./ebook2audiobook.sh --headless --ebook 'path_to_ebook'
95
+ Graphic Interface:
96
+ ./ebook2audiobook.sh
97
+ ''',
98
+ formatter_class=argparse.RawTextHelpFormatter
99
+ )
100
+ options = [
101
+ '--script_mode', '--share', '--headless',
102
+ '--session', '--ebook', '--ebooks_dir',
103
+ '--voice', '--language', '--device', '--custom_model',
104
+ '--temperature', '--length_penalty', '--repetition_penalty',
105
+ '--top_k', '--top_p', '--speed',
106
+ '--enable_text_splitting', '--fine_tuned',
107
+ '--version', '--help'
108
+ ]
109
+ parser.add_argument(options[0], type=str,
110
+ help='Force the script to run in NATIVE or DOCKER_UTILS')
111
+ parser.add_argument(options[1], action='store_true',
112
+ help='Enable a public shareable Gradio link. Default to False.')
113
+ parser.add_argument(options[2], nargs='?', const=True, default=False,
114
+ help='Run in headless mode. Default to True if the flag is present without a value, False otherwise.')
115
+ parser.add_argument(options[3], type=str,
116
+ help='Session to reconnect in case of interruption (headless mode only)')
117
+ parser.add_argument(options[4], type=str,
118
+ help='Path to the ebook file for conversion. Required in headless mode.')
119
+ parser.add_argument(options[5], nargs='?', const='default', type=str,
120
+ help=f'Path to the directory containing ebooks for batch conversion. Default to "{os.path.basename(ebooks_dir)}" if "default" is provided.')
121
+ parser.add_argument(options[6], type=str, default=None,
122
+ help='Path to the target voice file for TTS. Optional, must be 24khz for XTTS and 16khz for fairseq models, uses a default voice if not provided.')
123
+ parser.add_argument(options[7], type=str, default=default_language_code,
124
+ help=f'Language for the audiobook conversion. Options: {lang_list_str}. Default to English (eng).')
125
+ parser.add_argument(options[8], type=str, default='cpu', choices=['cpu', 'gpu'],
126
+ help=f'Type of processor unit for the audiobook conversion. If not specified: check first if gpu available, if not cpu is selected.')
127
+ parser.add_argument(options[9], type=str,
128
+ help=f'Path to the custom model (.zip file containing {default_model_files}). Required if using a custom model.')
129
+ parser.add_argument(options[10], type=float, default=0.65,
130
+ help='Temperature for the model. Default to 0.65. Higher temperatures lead to more creative outputs.')
131
+ parser.add_argument(options[11], type=float, default=1.0,
132
+ help='A length penalty applied to the autoregressive decoder. Default to 1.0. Not applied to custom models.')
133
+ parser.add_argument(options[12], type=float, default=2.5,
134
+ help='A penalty that prevents the autoregressive decoder from repeating itself. Default to 2.5')
135
+ parser.add_argument(options[13], type=int, default=50,
136
+ help='Top-k sampling. Lower values mean more likely outputs and increased audio generation speed. Default to 50')
137
+ parser.add_argument(options[14], type=float, default=0.8,
138
+ help='Top-p sampling. Lower values mean more likely outputs and increased audio generation speed. Default to 0.8')
139
+ parser.add_argument(options[15], type=float, default=1.0,
140
+ help='Speed factor for the speech generation. Default to 1.0')
141
+ parser.add_argument(options[16], action='store_true',
142
+ help='Enable splitting text into sentences. Default to False.')
143
+ parser.add_argument(options[17], type=str, default=default_fine_tuned,
144
+ help='Name of the fine tuned model. Optional, uses the standard model according to the TTS engine and language.')
145
+ parser.add_argument(options[18], action='version',version=f'ebook2audiobook version {version}',
146
+ help='Show the version of the script and exit')
147
+
148
+ for arg in sys.argv:
149
+ if arg.startswith('--') and arg not in options:
150
+ print(f'Error: Unrecognized option "{arg}"')
151
+ sys.exit(1)
152
+
153
+ args = vars(parser.parse_args())
154
+
155
+ # Check if the port is already in use to prevent multiple launches
156
+ if not args['headless'] and is_port_in_use(interface_port):
157
+ print(f'Error: Port {interface_port} is already in use. The web interface may already be running.')
158
+ sys.exit(1)
159
+
160
+ args['script_mode'] = args['script_mode'] if args['script_mode'] else NATIVE
161
+ args['share'] = args['share'] if args['share'] else False
162
+
163
+ if args['script_mode'] == NATIVE:
164
+ check_pkg = check_and_install_requirements(requirements_file)
165
+ if check_pkg:
166
+ if not check_dictionary():
167
+ sys.exit(1)
168
+ else:
169
+ print('Some packages could not be installed')
170
+ sys.exit(1)
171
+
172
+ from lib.functions import web_interface, convert_ebook
173
+
174
+ # Conditions based on the --headless flag
175
+ if args['headless']:
176
+ args['is_gui_process'] = False
177
+ args['audiobooks_dir'] = audiobooks_cli_dir
178
+
179
+ # Condition to stop if both --ebook and --ebooks_dir are provided
180
+ if args['ebook'] and args['ebooks_dir']:
181
+ print('Error: You cannot specify both --ebook and --ebooks_dir in headless mode.')
182
+ sys.exit(1)
183
+
184
+ # Condition 1: If --ebooks_dir exists, check value and set 'ebooks_dir'
185
+ if args['ebooks_dir']:
186
+ new_ebooks_dir = None
187
+ if args['ebooks_dir'] == 'default':
188
+ print(f'Using the default ebooks_dir: {ebooks_dir}')
189
+ new_ebooks_dir = os.path.abspath(ebooks_dir)
190
+ else:
191
+ # Check if the directory exists
192
+ if os.path.exists(args['ebooks_dir']):
193
+ new_ebooks_dir = os.path.abspath(args['ebooks_dir'])
194
+ else:
195
+ print(f'Error: The provided --ebooks_dir "{args["ebooks_dir"]}" does not exist.')
196
+ sys.exit(1)
197
+
198
+ if os.path.exists(new_ebooks_dir):
199
+ for file in os.listdir(new_ebooks_dir):
200
+ # Process files with supported ebook formats
201
+ if any(file.endswith(ext) for ext in ebook_formats):
202
+ full_path = os.path.join(new_ebooks_dir, file)
203
+ print(f'Processing eBook file: {full_path}')
204
+ args['ebook'] = full_path
205
+ progress_status, audiobook_file = convert_ebook(args)
206
+ if audiobook_file is None:
207
+ print(f'Conversion failed: {progress_status}')
208
+ sys.exit(1)
209
+ else:
210
+ print(f'Error: The directory {new_ebooks_dir} does not exist.')
211
+ sys.exit(1)
212
+
213
+ elif args['ebook']:
214
+ progress_status, audiobook_file = convert_ebook(args)
215
+ if audiobook_file is None:
216
+ print(f'Conversion failed: {progress_status}')
217
+ sys.exit(1)
218
+
219
+ else:
220
+ print('Error: In headless mode, you must specify either an ebook file using --ebook or an ebook directory using --ebooks_dir.')
221
+ sys.exit(1)
222
+ else:
223
+ args['is_gui_process'] = True
224
+ passed_arguments = sys.argv[1:]
225
+ allowed_arguments = {'--share', '--script_mode'}
226
+ passed_args_set = {arg for arg in passed_arguments if arg.startswith('--')}
227
+ if passed_args_set.issubset(allowed_arguments):
228
+ web_interface(args)
229
+ else:
230
+ print('Error: In non-headless mode, no option or only --share can be passed')
231
+ sys.exit(1)
232
+
233
+ if __name__ == '__main__':
234
+ if not check_python_version():
235
+ sys.exit(1)
236
+ else:
237
+ main()