Better handling of tts_engines.json in corrupt/missing scenario

This commit is contained in:
erew123 authored and GitHub committed 2024-10-02 00:19:37 +01:00
1 parent 471c67dd3a
commit a8311005aa
2 files changed
+169 -24

No files matched your search

+107 -16
View File
@@ -123,11 +123,76 @@ update_config(config_file_path, update_config_path, downgrade_config_path)
###########################################################################################
tts_engines_path = this_dir / "system" / "tts_engines" / "tts_engines.json"
new_engines_path = this_dir / "system" / "tts_engines" / "new_engines.json"
tts_engines_backup_path = tts_engines_path.with_suffix(".backup") # Backup file path
# Load the JSON files
with open(tts_engines_path, 'r') as f:
tts_engines_data = json.load(f)
# Function to safely load JSON with error handling
def safe_load_json(file_path, backup_path=None):
try:
# Attempt to open and load the JSON file
with open(file_path, 'r') as f:
return json.load(f)
except FileNotFoundError:
# If the file is not found, handle it by either restoring from a backup or creating a default
print(f"[{branding}TTS] File not found: {file_path}")
if backup_path and os.path.exists(backup_path):
# Load the backup if it exists
print(f"[{branding}TTS] Restoring from backup: {backup_path}")
with open(backup_path, 'r') as f:
data = json.load(f)
# Save the backup data to the original path
with open(file_path, 'w') as f:
json.dump(data, f, indent=4)
print(f"[{branding}TTS] Restored {file_path} from backup.")
return data
else:
# If no backup is available, create a new default JSON structure
print(f"[{branding}TTS] No backup found, creating a new default tts_engine JSON structure.")
default_data = {
"engine_loaded": "piper",
"selected_model": "piper",
"engines_available": [
{
"name": "parler",
"selected_model": "parler - parler_tts_mini_v0.1"
},
{
"name": "piper",
"selected_model": "piper"
},
{
"name": "vits",
"selected_model": "vits - tts_models--en--vctk--vits"
},
{
"name": "xtts",
"selected_model": "xtts - xttsv2_2.0.3"
}
]
}
with open(file_path, 'w') as f:
json.dump(default_data, f, indent=4)
print(f"[{branding}TTS] Created new default file at: {file_path}")
return default_data
except json.JSONDecodeError as e:
# Handle JSON corruption
print(f"[{branding}TTS] JSON decoding error in file {file_path}: {e}")
if backup_path and os.path.exists(backup_path):
print(f"[{branding}TTS] Restoring from backup due to corrupted file: {backup_path}")
with open(backup_path, 'r') as f:
data = json.load(f)
# Save the backup data to the original path
with open(file_path, 'w') as f:
json.dump(data, f, indent=4)
print(f"[{branding}TTS] Restored {file_path} from backup.")
return data
else:
raise Exception(f"File {file_path} is corrupted and no backup is available.")
# Load tts_engines.json with retry if corrupted
tts_engines_data = safe_load_json(tts_engines_path, tts_engines_backup_path)
# Load new_engines.json
with open(new_engines_path, 'r') as f:
new_engines_data = json.load(f)
@@ -144,11 +209,15 @@ for engine in new_engines:
if engine_dir.is_dir():
# Add the new engine to the list
tts_engines_data['engines_available'].append(engine)
print(f"[{branding}TTS] \033[92mNew TTS Engines : \033[91mAdded {engine_name}\033[0m")
print(f"[{branding}TTS] \033[92mNew TTS Engine : \033[91mAdded {engine_name}\033[0m")
# Save the updated tts_engines.json
with open(tts_engines_path, 'w') as f:
json.dump(tts_engines_data, f, indent=4)
# Backup the current tts_engines.json before saving the updated file
shutil.copy(tts_engines_path, tts_engines_backup_path)
# Save the updated tts_engines.json
with open(tts_engines_path, 'w') as f:
json.dump(tts_engines_data, f, indent=4)
print(f"{tts_engines_path} updated and saved successfully.")
####################################################
# START-UP # Re-load from confignew.json to params #
@@ -1303,6 +1372,7 @@ if gradio_enabled == True:
json_file_path = os.path.join(this_dir, "system", "tts_engines", engine_name, "model_settings.json")
with open(json_file_path, "r") as f:
globals()[f"{engine_name}_model_config_data"] = json.load(f)
###########################################
# Finishing Dynamically importing Modules #
###########################################
@@ -1399,21 +1469,39 @@ if gradio_enabled == True:
# Sends request to reload the current TTS engine & set the default TTS engine loaded #
######################################################################################
def set_engine_loaded(engine_name):
global engine_loaded, selected_model, tts_engines_data, models_available, at_update_dropdowns
tts_engines_file = os.path.join(this_dir, "system", "tts_engines", "tts_engines.json")
with open(tts_engines_file, "r") as f:
tts_engines_data = json.load(f)
global engine_loaded, selected_model, tts_engines_data, models_available, tts_engines_path, tts_engines_backup_path, at_update_dropdowns
# Load the JSON with a retry mechanism
safe_load_json(tts_engines_path, tts_engines_backup_path)
# Iterate and update engines
for engine in tts_engines_data["engines_available"]:
if engine["name"] == engine_name:
tts_engines_data["engine_loaded"] = engine_name
tts_engines_data["selected_model"] = engine["selected_model"]
break
with open(tts_engines_file, "w") as f:
json.dump(tts_engines_data, f)
# Backup the existing file before writing
shutil.copy(tts_engines_path, tts_engines_backup_path)
# Write the updated JSON data safely
with open(tts_engines_path, "w") as f:
try:
json.dump(tts_engines_data, f, indent=4)
except Exception as e:
print(f"Error writing to {tts_engines_path}: {e}")
# Restore the backup if writing failed
shutil.copy(tts_engines_backup_path, tts_engines_path)
raise Exception("Failed to save tts_engines.json, restored from backup.")
engine_loaded = engine_name
selected_model = tts_engines_data["selected_model"]
# Add a brief delay to ensure file is fully written
time.sleep(0.1) # 100ms delay
# Restart the subprocess
restart_subprocess()
# Wait for the engine to be ready with error handling and retries
max_retries = 80
retry_delay = 1 # seconds
@@ -1431,15 +1519,18 @@ if gradio_enabled == True:
raise Exception("Failed to connect to the TTS engine after multiple retries.")
time.sleep(retry_delay)
models_available = at_settings["models_available"]
# Update the dropdowns directly
print(f"[{branding}ENG]")
print(f"[{branding}ENG] {branding}Server Ready")
gen_stream_value, gen_char_choices, rvcgen_char_choices, gen_narr_choices, rvcgen_narr_choices, gen_speed_interactive, gen_pitch_interactive, gen_temperature_interactive, gen_repetition_interactive, gen_lang_interactive, model_choices_gr_interactive, engine_choices_interactive = at_update_dropdowns()
return ("TTS Engine changed successfully!", # This is your output message
gen_stream_value, gen_char_choices, rvcgen_char_choices, gen_narr_choices, rvcgen_narr_choices, gen_speed_interactive, gen_pitch_interactive, gen_temperature_interactive, gen_repetition_interactive, gen_lang_interactive, model_choices_gr_interactive, engine_choices_interactive
)
gen_stream_value, gen_char_choices, rvcgen_char_choices, gen_narr_choices, rvcgen_narr_choices, gen_speed_interactive, gen_pitch_interactive, gen_temperature_interactive, gen_repetition_interactive, gen_lang_interactive, model_choices_gr_interactive, engine_choices_interactive
)
###############################
# Sends voice2rvc request off #
###############################
+62 -8
View File
@@ -2,6 +2,7 @@ import os
import sys
import json
import time
import shutil
import argparse
import librosa
import logging
@@ -247,28 +248,81 @@ async def handle_restart():
@app.post("/api/enginereload")
async def apifunction_reload(request: Request):
requested_engine = request.query_params.get("engine")
tts_engines_file = this_dir / "system" / "tts_engines" / "tts_engines.json"
tts_engines_file_backup_path = tts_engines_file.with_suffix(".backup") # Backup file path
if requested_engine not in engines_available:
return {"status": "error", "message": "Invalid TTS engine specified"}
print(f"[{branding}ENG]")
print(f"[{branding}ENG] \033[94mChanging model loaded. Please wait.\033[00m")
print(f"[{branding}ENG]")
# Update the tts_engines.json file
tts_engines_file = os.path.join(this_dir, "system", "tts_engines", "tts_engines.json")
with open(tts_engines_file, "r") as f:
tts_engines_data = json.load(f)
# Function to safely load JSON with error handling and backup restoration
def safe_load_json(file_path, backup_path=None):
try:
# Attempt to open and load the JSON file
with open(file_path, 'r') as f:
return json.load(f)
except FileNotFoundError:
# Handle missing file case, try restoring from backup or creating default
print(f"File not found: {file_path}")
if backup_path and os.path.exists(backup_path):
print(f"Restoring from backup: {backup_path}")
with open(backup_path, 'r') as f:
data = json.load(f)
# Restore the backup to the original file
with open(file_path, 'w') as f:
json.dump(data, f, indent=4)
print(f"Restored {file_path} from backup.")
return data
else:
raise Exception(f"File {file_path} is missing, and no backup is available.")
except json.JSONDecodeError as e:
# Handle JSON corruption case
print(f"JSON decoding error in file {file_path}: {e}")
if backup_path and os.path.exists(backup_path):
print(f"Restoring from backup due to corrupted file: {backup_path}")
with open(backup_path, 'r') as f:
data = json.load(f)
# Restore the backup to the original file
with open(file_path, 'w') as f:
json.dump(data, f, indent=4)
print(f"Restored {file_path} from backup.")
return data
else:
raise Exception(f"File {file_path} is corrupted, and no backup is available.")
# Load the tts_engines.json with resilience
tts_engines_data = safe_load_json(tts_engines_file, tts_engines_file_backup_path)
# Update the tts_engines_data with the requested engine
for engine in tts_engines_data["engines_available"]:
if engine["name"] == requested_engine:
tts_engines_data["engine_loaded"] = requested_engine
tts_engines_data["selected_model"] = engine["selected_model"]
break
with open(tts_engines_file, "w") as f:
json.dump(tts_engines_data, f)
#print(f"[{branding}ENG] Reloading the engine...")
# Write the updated JSON data safely and create a backup
try:
# First, create a backup of the current file before writing
shutil.copy(tts_engines_file, tts_engines_file_backup_path)
with open(tts_engines_file, "w") as f:
json.dump(tts_engines_data, f, indent=4)
# print(f"Updated and saved {tts_engines_file}. Backup created at {tts_engines_file_backup_path}.")
except Exception as e:
print(f"Error writing to {tts_engines_file}: {e}")
# If writing failed, restore the backup
shutil.copy(tts_engines_file_backup_path, tts_engines_file)
raise Exception(f"Failed to save {tts_engines_file}, restored from backup.")
# Start the restart process in the background
asyncio.create_task(handle_restart())
# Never goes past the above asyncio as the script is killed at that point
return Response(content=json.dumps({"status": "engine-success"}), media_type="application/json")
#######################################
# API Endpoint - /api/stop-generation #
#######################################