Updated firstrun better docker/colab support

In Docker or Google colab environments where people may not have access to a keyboard, just download piper (as its small) and dont wait the 60 seconds for a keypress.
This commit is contained in:
erew123 authored and GitHub committed 2024-11-25 01:35:04 +00:00
1 parent 50cafba12f
commit 856d67fe5b
2 files changed
+402 -194

No files matched your search

+115 -24
View File
@@ -1,6 +1,7 @@
import json import json
import os import os
import shutil import shutil
import time
import sys import sys
import zipfile import zipfile
import requests import requests
@@ -15,18 +16,75 @@ from config import AlltalkTTSEnginesConfig, AlltalkConfig
this_dir = Path(__file__).parent.resolve().parent.resolve().parent.resolve() this_dir = Path(__file__).parent.resolve().parent.resolve().parent.resolve()
def download_file(url, dest_path): def is_running_in_colab():
response = requests.get(url, stream=True) """Test for google colab"""
total_size = int(response.headers.get('content-length', 0)) try:
block_size = 1024 # 1 Kibibyte import google.colab
t = tqdm(total=total_size, unit='iB', unit_scale=True, desc=str(dest_path)) return True
with open(dest_path, 'wb') as file: except ImportError:
for data in response.iter_content(block_size): return False
t.update(len(data))
file.write(data) def is_running_in_docker():
t.close() """Test for google colab"""
path = '/proc/self/cgroup'
return os.path.exists('/.dockerenv') or (
os.path.exists(path) and any('docker' in line for line in open(path))
)
def download_file(url: str, dest_path: str, timeout: int = 30, retries: int = 3) -> bool:
"""
Download a file from a URL to a specified destination path with progress bar.
Args:
url (str): URL of the file to download
dest_path (str): Destination path where the file will be saved
timeout (int, optional): Connection and read timeout in seconds. Defaults to 30.
retries (int, optional): Number of retry attempts for failed downloads. Defaults to 3.
"""
progress_bar = None
for attempt in range(retries):
try:
response = requests.get(url, stream=True,
timeout=(timeout, timeout))
response.raise_for_status()
total_size = int(response.headers.get('content-length', 0))
block_size = 1024 # 1 Kibibyte
# Initialize progress bar
progress_bar = tqdm(total=total_size, unit='iB', unit_scale=True,
desc=f"Downloading {Path(dest_path).name} (Attempt {attempt + 1}/{retries})")
Path(dest_path).parent.mkdir(parents=True, exist_ok=True)
with open(dest_path, 'wb') as dwn_file:
for data in response.iter_content(block_size):
if data:
dwn_file.write(data)
progress_bar.update(len(data))
if progress_bar:
progress_bar.close()
if progress_bar.n != total_size and total_size > 0:
raise requests.exceptions.RequestException(
f"Downloaded size ({progress_bar.n} bytes) does not match expected size ({total_size} bytes)"
)
return True
except requests.exceptions.RequestException as e:
if progress_bar:
progress_bar.close()
if Path(dest_path).exists():
Path(dest_path).unlink()
if attempt == retries - 1:
raise requests.exceptions.RequestException(
f"Failed to download {url} after {retries} attempts: {str(e)}"
)
print(f"Download attempt {attempt + 1}/{retries} failed: {str(e)}")
print("Retrying in 5 seconds...")
time.sleep(5)
except Exception as e:
if progress_bar:
progress_bar.close()
raise Exception(f"Unexpected error downloading {url}: {str(e)}")
return False
def setup_piper(): def setup_piper():
"""Download base piper files"""
os.makedirs(this_dir / 'models/piper', exist_ok=True) os.makedirs(this_dir / 'models/piper', exist_ok=True)
os.makedirs(this_dir / 'models/xtts', exist_ok=True) os.makedirs(this_dir / 'models/xtts', exist_ok=True)
download_file("https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/ljspeech/high/en_US-ljspeech-high.onnx?download=true", download_file("https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/ljspeech/high/en_US-ljspeech-high.onnx?download=true",
@@ -35,6 +93,7 @@ def setup_piper():
this_dir / "models/piper/en_US-ljspeech-high.onnx.json") this_dir / "models/piper/en_US-ljspeech-high.onnx.json")
def setup_vits(): def setup_vits():
"""Download base vits files"""
os.makedirs(this_dir / 'models/vits', exist_ok=True) os.makedirs(this_dir / 'models/vits', exist_ok=True)
os.makedirs(this_dir / 'models/xtts', exist_ok=True) os.makedirs(this_dir / 'models/xtts', exist_ok=True)
zip_path = this_dir / 'models/vits/vits.zip' zip_path = this_dir / 'models/vits/vits.zip'
@@ -44,6 +103,7 @@ def setup_vits():
os.remove(zip_path) os.remove(zip_path)
def setup_xtts(): def setup_xtts():
"""Download base xtts files"""
os.makedirs(this_dir / 'models/xtts/xttsv2_2.0.3', exist_ok=True) os.makedirs(this_dir / 'models/xtts/xttsv2_2.0.3', exist_ok=True)
base_url = "https://huggingface.co/coqui/XTTS-v2/resolve/v2.0.3/" base_url = "https://huggingface.co/coqui/XTTS-v2/resolve/v2.0.3/"
files = [ files = [
@@ -56,17 +116,39 @@ def setup_xtts():
"speakers_xtts.pth", "speakers_xtts.pth",
"vocab.json" "vocab.json"
] ]
for file in files: for each_file in files:
download_file(base_url + file + "?download=true", this_dir / f"models/xtts/xttsv2_2.0.3/{file}") download_file(base_url + file + "?download=true", this_dir / f"models/xtts/xttsv2_2.0.3/{each_file}")
def update_tts_engines(engine): def update_tts_engines(engine):
AlltalkTTSEnginesConfig.get_instance().change_engine(engine).save() """Set engine to users choice that matches the download"""
try:
config_instance = AlltalkTTSEnginesConfig.get_instance()
config_instance.change_engine(engine)
config_instance.save()
except Exception as e:
print(f"[{branding}TTS] Error updating TTS engine configuration: {str(e)}")
def set_firstrun_model_false(): def set_firstrun_model_false():
config.firstrun_model = False try:
config.save() # Get a fresh instance of the config
config_fr = AlltalkConfig.get_instance()
config_fr.firstrun_model = False
# Force a flush to disk immediately
config_fr.save()
# Verify the save worked
config_path = AlltalkConfig.default_config_path()
with open(config_path, 'r', encoding="utf-8") as f:
saved_config = json.load(f)
if saved_config.get('firstrun_model', True):
print(f"[{branding}TTS] Warning: Failed to save firstrun_model=False")
# Try one more time with direct file write
saved_config['firstrun_model'] = False
with open(config_path, 'w', encoding="utf-8") as f:
json.dump(saved_config, f, indent=2)
except Exception as e:
print(f"[{branding}TTS] Error saving firstrun configuration: {str(e)}")
def warning_message(): def warning_message():
print(f"[{branding}TTS]") print(f"[{branding}TTS]")
@@ -77,7 +159,7 @@ def warning_message():
print(f"[{branding}TTS]") print(f"[{branding}TTS]")
# Load confignew.json (might be version 1 or 2): # Load confignew.json (might be version 1 or 2):
with open(AlltalkConfig.default_config_path(), 'r') as file: with open(AlltalkConfig.default_config_path(), 'r', encoding="utf-8") as file:
config_json = json.load(file) config_json = json.load(file)
branding = config_json['branding'] branding = config_json['branding']
@@ -102,19 +184,28 @@ branding = config.branding
# Check if firstrun_model is true # Check if firstrun_model is true
if config.firstrun_model: if config.firstrun_model:
# Check for Colab or Docker environment
if is_running_in_colab() or is_running_in_docker():
print(f"[{branding}TTS] Detected Colab/Docker environment - automatically selecting Piper")
setup_piper()
update_tts_engines('piper')
set_firstrun_model_false()
warning_message()
exit()
print(f"[{branding}TTS]") print(f"[{branding}TTS]")
print(f"[{branding}TTS] \033[92mThis is the first time startup.. Please download a start TTS model. Other TTS engines\033[0m") print(f"[{branding}TTS] \033[92mThis is the first time startup.. Please download a start TTS model. Other TTS engines\033[0m")
print(f"[{branding}TTS] \033[92mand TTS models can be downloaded/managed in the Gradio Interface `TTS Engines Settings`\033[0m") print(f"[{branding}TTS] \033[92mand TTS models can be downloaded/managed in the Gradio Interface `TTS Engines Settings`\033[0m")
print(f"[{branding}TTS] \033[92mtab after inital setup.\033[0m") print(f"[{branding}TTS] \033[92mtab after inital setup.\033[0m")
print(f"[{branding}TTS]") print(f"[{branding}TTS]")
# List of available models # List of available models
models = [ models = [
{"name": "piper", "model": "piper"}, {"name": "piper", "model": "piper"},
{"name": "vits", "model": "tts_models--en--vctk--vits"}, {"name": "vits", "model": "tts_models--en--vctk--vits"},
{"name": "xtts", "model": "xttsv2_2.0.3"}, {"name": "xtts", "model": "xttsv2_2.0.3"},
] ]
# Display models to the user # Display models to the user
print(f"[{branding}TTS] \033[94mAvailable First Time Start-up models:\033[0m") print(f"[{branding}TTS] \033[94mAvailable First Time Start-up models:\033[0m")
print(f"[{branding}TTS]") print(f"[{branding}TTS]")
@@ -124,8 +215,8 @@ if config.firstrun_model:
print(f"[{branding}TTS]") print(f"[{branding}TTS]")
print(f"[{branding}TTS] \033[94mIn \033[91m60 seconds\033[0m \033[94ma Piper model will be \033[91mdownloaded automatically.\033[0m") print(f"[{branding}TTS] \033[94mIn \033[91m60 seconds\033[0m \033[94ma Piper model will be \033[91mdownloaded automatically.\033[0m")
print(f"[{branding}TTS]") print(f"[{branding}TTS]")
# Auto-select model after 30 seconds # Auto-select model after 60 seconds
selected_model = models[0] # Default to the first model (piper) selected_model = models[0] # Default to the first model (piper)
try: try:
user_choice = inputimeout(prompt=f"[{branding}TTS] \033[92mEnter your choice 1-4: \033[0m ", timeout=60) user_choice = inputimeout(prompt=f"[{branding}TTS] \033[92mEnter your choice 1-4: \033[0m ", timeout=60)
@@ -167,6 +258,6 @@ if config.firstrun_model:
# Set firstrun_model to false # Set firstrun_model to false
set_firstrun_model_false() set_firstrun_model_false()
warning_message() warning_message()
exit() sys.exit()
else: else:
exit() sys.exit()
+287 -170
View File
@@ -1,170 +1,287 @@
import json import json
import os import os
import shutil import shutil
import sys import sys
import zipfile import time
import requests import zipfile
from tqdm import tqdm import requests
from inputimeout import inputimeout, TimeoutOccurred from tqdm import tqdm
from pathlib import Path from inputimeout import inputimeout, TimeoutOccurred
from pathlib import Path
# Setting the module search path: from typing import Optional, Dict, List, Any
sys.path.insert(0, str(Path(__file__).parent.parent.parent.resolve()))
# Setting the module search path:
from config import AlltalkTTSEnginesConfig, AlltalkConfig sys.path.insert(0, str(Path(__file__).parent.parent.parent.resolve()))
this_dir = Path(__file__).parent.resolve().parent.resolve().parent.resolve() from config import AlltalkTTSEnginesConfig, AlltalkConfig
def download_file(url, dest_path): this_dir = Path(__file__).parent.resolve().parent.resolve().parent.resolve()
response = requests.get(url, stream=True)
total_size = int(response.headers.get('content-length', 0)) def is_running_in_colab() -> bool:
block_size = 1024 # 1 Kibibyte """Check if the script is running in Google Colab."""
t = tqdm(total=total_size, unit='iB', unit_scale=True, desc=str(dest_path)) try:
with open(dest_path, 'wb') as file: import google.colab
for data in response.iter_content(block_size): return True
t.update(len(data)) except ImportError:
file.write(data) return False
t.close()
def is_running_in_docker() -> bool:
def setup_piper(): """Check if the script is running in a Docker container."""
os.makedirs(this_dir / 'models/piper', exist_ok=True) return os.path.exists('/.dockerenv') or (
os.makedirs(this_dir / 'models/xtts', exist_ok=True) os.path.exists('/proc/self/cgroup') and
download_file("https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/ljspeech/high/en_US-ljspeech-high.onnx?download=true", any('docker' in line for line in open('/proc/self/cgroup'))
this_dir / "models/piper/en_US-ljspeech-high.onnx") )
download_file("https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/ljspeech/high/en_US-ljspeech-high.onnx.json?download=true.json",
this_dir / "models/piper/en_US-ljspeech-high.onnx.json") def download_file(url: str, dest_path: str, timeout: int = 30) -> bool:
"""
def setup_vits(): Download a file from a URL with progress bar and error handling.
os.makedirs(this_dir / 'models/vits', exist_ok=True)
os.makedirs(this_dir / 'models/xtts', exist_ok=True) Args:
zip_path = this_dir / 'models/vits/vits.zip' url: URL to download from
download_file("https://coqui.gateway.scarf.sh/v0.6.1_models/tts_models--en--vctk--vits.zip", zip_path) dest_path: Where to save the file
with zipfile.ZipFile(zip_path, 'r') as zip_ref: timeout: Connection timeout in seconds
zip_ref.extractall(this_dir / 'models/vits')
os.remove(zip_path) Returns:
bool: True if download was successful
def setup_xtts(): """
os.makedirs(this_dir / 'models/xtts/xttsv2_2.0.3', exist_ok=True) progress_bar = None
base_url = "https://huggingface.co/coqui/XTTS-v2/resolve/v2.0.3/" try:
files = [ response = requests.get(url, stream=True, timeout=timeout)
"LICENSE.txt", response.raise_for_status()
"README.md",
"config.json", total_size = int(response.headers.get('content-length', 0))
"model.pth", block_size = 1024
"dvae.pth",
"mel_stats.pth", progress_bar = tqdm(total=total_size, unit='iB', unit_scale=True,
"speakers_xtts.pth", desc=f"Downloading {Path(dest_path).name}")
"vocab.json"
] Path(dest_path).parent.mkdir(parents=True, exist_ok=True)
for file in files:
download_file(base_url + file + "?download=true", this_dir / f"models/xtts/xttsv2_2.0.3/{file}") with open(dest_path, 'wb') as file:
for data in response.iter_content(block_size):
if data:
file.write(data)
def update_tts_engines(engine): progress_bar.update(len(data))
AlltalkTTSEnginesConfig.get_instance().change_engine(engine).save()
if progress_bar.n != total_size and total_size > 0:
def set_firstrun_model_false(): raise requests.exceptions.RequestException(
config.firstrun_model = False f"Incomplete download: {progress_bar.n}/{total_size} bytes"
config.save() )
return True
def warning_message():
print(f"[{branding}TTS]") except Exception as e:
print(f"[{branding}TTS] \033[93mIf you have you have UPGRADED from v1 ensure you have re-installed\033[0m") print(f"Error downloading {url}: {str(e)}")
print(f"[{branding}TTS] \033[93mthe requirements. Otherwise you will get failures and errors!\033[0m") if Path(dest_path).exists():
print(f"[{branding}TTS] \033[93mOn Linux ignore the \033[0m'sparse_attn requires a torch version' \033[93mand\033[0m") Path(dest_path).unlink()
print(f"[{branding}TTS] \033[0m'using untested triton version' \033[93mmessages.\033[0m") return False
print(f"[{branding}TTS]")
finally:
# Load confignew.json (might be version 1 or 2): if progress_bar:
with open(AlltalkConfig.default_config_path(), 'r') as file: progress_bar.close()
config_json = json.load(file)
def setup_model(model_name: str) -> bool:
branding = config_json['branding'] """
Set up a specific TTS model.
# Check if the config file is version 1
if "ip_address" in config_json: Args:
print(f"[{branding}TTS] Detected version 1 configuration file.") model_name: Name of the model to setup ('piper', 'vits', or 'xtts')
print(f"[{branding}TTS] Upgrading to version 2 configuration file...")
Returns:
# Backup the old configuration file bool: True if setup was successful
backup_path = AlltalkConfig.default_config_path() + ".backup" """
shutil.copyfile(AlltalkConfig.default_config_path(), backup_path) try:
if model_name == "piper":
# Copy the new configuration template to replace the old config file os.makedirs(this_dir / 'models/piper', exist_ok=True)
new_config_template_path = this_dir / 'system/config/confignew.json' os.makedirs(this_dir / 'models/xtts', exist_ok=True)
shutil.copyfile(new_config_template_path, AlltalkConfig.default_config_path()) return all([
download_file(
print(f"[{branding}TTS] Configuration file upgraded successfully.") "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/ljspeech/high/en_US-ljspeech-high.onnx?download=true",
this_dir / "models/piper/en_US-ljspeech-high.onnx"
config = AlltalkConfig.get_instance() ),
branding = config.branding download_file(
"https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/ljspeech/high/en_US-ljspeech-high.onnx.json?download=true.json",
# Check if firstrun_model is true this_dir / "models/piper/en_US-ljspeech-high.onnx.json"
if config.firstrun_model: )
print(f"[{branding}TTS]") ])
print(f"[{branding}TTS] \033[92mThis is the first time startup. Please download a start TTS model.\033[0m")
print(f"[{branding}TTS]") elif model_name == "vits":
os.makedirs(this_dir / 'models/vits', exist_ok=True)
# List of available models os.makedirs(this_dir / 'models/xtts', exist_ok=True)
models = [ zip_path = this_dir / 'models/vits/vits.zip'
{"name": "piper", "model": "piper"},
{"name": "vits", "model": "tts_models--en--vctk--vits"}, if not download_file(
{"name": "xtts", "model": "xttsv2_2.0.3"}, "https://coqui.gateway.scarf.sh/v0.6.1_models/tts_models--en--vctk--vits.zip",
] zip_path
):
# Display models to the user return False
print(f"[{branding}TTS] \033[94mAvailable First Time Start-up models:\033[0m")
print(f"[{branding}TTS]") try:
for idx, model in enumerate(models): with zipfile.ZipFile(zip_path, 'r') as zip_ref:
print(f"[{branding}TTS] \033[93m{idx + 1}. \033[94m{model['name']} - {model['model']}\033[0m") zip_ref.extractall(this_dir / 'models/vits')
print(f"[{branding}TTS] \033[93m{len(models) + 1}.\033[94m I have my own models already\033[0m") os.remove(zip_path)
print(f"[{branding}TTS]") return True
print(f"[{branding}TTS] \033[94mIn \033[91m60 seconds\033[0m \033[94ma Piper model will be \033[91mdownloaded automatically.\033[0m") except Exception as e:
print(f"[{branding}TTS]") print(f"Error extracting VITS model: {str(e)}")
return False
# Auto-select model after 30 seconds
selected_model = models[0] # Default to the first model (piper) elif model_name == "xtts":
try: os.makedirs(this_dir / 'models/xtts/xttsv2_2.0.3', exist_ok=True)
user_choice = inputimeout(prompt=f"[{branding}TTS] \033[92mEnter your choice 1-4: \033[0m ", timeout=60) base_url = "https://huggingface.co/coqui/XTTS-v2/resolve/v2.0.3/"
except TimeoutOccurred: files = [
user_choice = None "LICENSE.txt", "README.md", "config.json", "model.pth",
"dvae.pth", "mel_stats.pth", "speakers_xtts.pth", "vocab.json"
if user_choice is None: ]
print(f"[{branding}TTS]") return all(
print(f"[{branding}TTS] No input received. Proceeding with the default model (piper).") download_file(
elif user_choice.lower() == 'own' or (user_choice.isdigit() and int(user_choice) == len(models) + 1): base_url + file + "?download=true",
print(f"[{branding}TTS]") this_dir / f"models/xtts/xttsv2_2.0.3/{file}"
print(f"[{branding}TTS] If you have your own XTTS models, please move them into the") ) for file in files
print(f"[{branding}TTS] \033[93m/models/xtts/\033[0m folder") )
print(f"[{branding}TTS]")
os.makedirs('models/piper', exist_ok=True) return False
os.makedirs('models/vits', exist_ok=True)
os.makedirs('models/xtts/', exist_ok=True) except Exception as e:
update_tts_engines('piper') print(f"Error setting up {model_name}: {str(e)}")
set_firstrun_model_false() return False
warning_message()
exit() def update_tts_engines(engine: str) -> bool:
"""Update TTS engine configuration with error handling."""
if user_choice is not None and user_choice.isdigit() and 1 <= int(user_choice) <= len(models): try:
selected_model = models[int(user_choice) - 1] AlltalkTTSEnginesConfig.get_instance().change_engine(engine).save()
warning_message() return True
except Exception as e:
if user_choice is None or user_choice.lower() != 'own': print(f"Error updating TTS engine configuration: {str(e)}")
if selected_model['name'] == 'piper': return False
setup_piper()
update_tts_engines('piper') def set_firstrun_model_false() -> bool:
elif selected_model['name'] == 'vits': """Set firstrun_model to false with verification."""
setup_vits() try:
update_tts_engines('vits') # Get fresh config instance and update
elif selected_model['name'] == 'xtts': config = AlltalkConfig.get_instance()
setup_xtts() config.firstrun_model = False
update_tts_engines('xtts') config.save()
print(f"[{branding}TTS] {selected_model['name']} model downloaded and configuration updated successfully.") # Verify the save worked
# Set firstrun_model to false config_path = AlltalkConfig.default_config_path()
set_firstrun_model_false() with open(config_path, 'r') as f:
warning_message() saved_config = json.load(f)
exit() if saved_config.get('firstrun_model', True):
else: print(f"Warning: Failed to save firstrun_model=False")
exit() # Try direct file write as fallback
saved_config['firstrun_model'] = False
with open(config_path, 'w') as f:
json.dump(saved_config, f, indent=2)
return True
except Exception as e:
print(f"Error saving firstrun configuration: {str(e)}")
return False
def warning_message(branding: str):
"""Display warning messages about installation and compatibility."""
print(f"[{branding}TTS]")
print(f"[{branding}TTS] \033[93mIf you have UPGRADED from v1 ensure you have re-installed\033[0m")
print(f"[{branding}TTS] \033[93mthe requirements. Otherwise you will get failures and errors!\033[0m")
print(f"[{branding}TTS] \033[93mOn Linux ignore the \033[0m'sparse_attn requires a torch version' \033[93mand\033[0m")
print(f"[{branding}TTS] \033[0m'using untested triton version' \033[93mmessages.\033[0m")
print(f"[{branding}TTS]")
def main():
"""Main execution function."""
try:
# Load configuration
with open(AlltalkConfig.default_config_path(), 'r') as file:
config_json = json.load(file)
branding = config_json['branding']
# Handle version 1 config upgrade
if "ip_address" in config_json:
print(f"[{branding}TTS] Detected version 1 configuration file.")
print(f"[{branding}TTS] Upgrading to version 2 configuration file...")
backup_path = AlltalkConfig.default_config_path() + ".backup"
shutil.copyfile(AlltalkConfig.default_config_path(), backup_path)
new_config_template_path = this_dir / 'system/config/confignew.json'
shutil.copyfile(new_config_template_path, AlltalkConfig.default_config_path())
print(f"[{branding}TTS] Configuration file upgraded successfully.")
config = AlltalkConfig.get_instance()
branding = config.branding
if not config.firstrun_model:
return
# Auto-select Piper for Colab/Docker
if is_running_in_colab() or is_running_in_docker():
print(f"[{branding}TTS] Detected Colab/Docker environment - automatically selecting Piper")
if setup_model('piper'):
update_tts_engines('piper')
set_firstrun_model_false()
warning_message(branding)
return
# Normal interactive setup
models = [
{"name": "piper", "model": "piper"},
{"name": "vits", "model": "tts_models--en--vctk--vits"},
{"name": "xtts", "model": "xttsv2_2.0.3"},
]
print(f"[{branding}TTS]")
print(f"[{branding}TTS] \033[92mThis is the first time startup. Please download a start TTS model.\033[0m")
print(f"[{branding}TTS]")
print(f"[{branding}TTS] \033[94mAvailable First Time Start-up models:\033[0m")
print(f"[{branding}TTS]")
for idx, model in enumerate(models):
print(f"[{branding}TTS] \033[93m{idx + 1}. \033[94m{model['name']} - {model['model']}\033[0m")
print(f"[{branding}TTS] \033[93m{len(models) + 1}.\033[94m I have my own models already\033[0m")
print(f"[{branding}TTS]")
print(f"[{branding}TTS] \033[94mIn \033[91m60 seconds\033[0m \033[94ma Piper model will be \033[91mdownloaded automatically.\033[0m")
print(f"[{branding}TTS]")
try:
user_choice = inputimeout(
prompt=f"[{branding}TTS] \033[92mEnter your choice 1-4: \033[0m ",
timeout=60
)
except TimeoutOccurred:
user_choice = None
if user_choice is None:
print(f"[{branding}TTS] No input received. Proceeding with the default model (piper).")
selected_model = models[0]
elif user_choice.lower() == 'own' or (user_choice.isdigit() and int(user_choice) == len(models) + 1):
print(f"[{branding}TTS] If you have your own XTTS models, please move them into the")
print(f"[{branding}TTS] \033[93m/models/xtts/\033[0m folder")
os.makedirs('models/piper', exist_ok=True)
os.makedirs('models/vits', exist_ok=True)
os.makedirs('models/xtts/', exist_ok=True)
update_tts_engines('piper')
set_firstrun_model_false()
warning_message(branding)
return
elif user_choice.isdigit() and 1 <= int(user_choice) <= len(models):
selected_model = models[int(user_choice) - 1]
warning_message(branding)
else:
print(f"[{branding}TTS] Invalid choice. Proceeding with the default model (piper).")
selected_model = models[0]
if setup_model(selected_model['name']):
update_tts_engines(selected_model['name'])
set_firstrun_model_false()
print(f"[{branding}TTS] {selected_model['name']} model downloaded and configuration updated successfully.")
warning_message(branding)
except Exception as e:
print(f"Unexpected error: {str(e)}")
sys.exit(1)
if __name__ == "__main__":
main()