Spaces:
Runtime error
Runtime error
import os | |
import shutil | |
import gradio as gr | |
from . import api, files | |
def setup_event_handlers(components: dict, disable_local_saving: bool = False): | |
"""Set up all event handlers for the UI components.""" | |
def refresh_status(): | |
try: | |
is_available, voices = api.check_api_status() | |
status = "Available" if is_available else "Waiting for Service..." | |
if is_available and voices: | |
# Preserve current voice selection if it exists and is still valid | |
current_voice = components["model"]["voice"].value | |
default_voice = current_voice if current_voice in voices else voices[0] | |
return [ | |
gr.update( | |
value=f"🔄 TTS Service: {status}", | |
interactive=True, | |
variant="secondary", | |
), | |
gr.update(choices=voices, value=default_voice), | |
] | |
return [ | |
gr.update( | |
value=f"⌛ TTS Service: {status}", | |
interactive=True, | |
variant="secondary", | |
), | |
gr.update(choices=[], value=None), | |
] | |
except Exception as e: | |
print(f"Error in refresh status: {str(e)}") | |
return [ | |
gr.update( | |
value="❌ TTS Service: Connection Error", | |
interactive=True, | |
variant="secondary", | |
), | |
gr.update(choices=[], value=None), | |
] | |
def handle_file_select(filename): | |
if filename: | |
try: | |
text = files.read_text_file(filename) | |
if text: | |
preview = text[:200] + "..." if len(text) > 200 else text | |
return gr.update(value=preview) | |
except Exception as e: | |
print(f"Error reading file: {e}") | |
return gr.update(value="") | |
def handle_file_upload(file): | |
if file is None: | |
return ( | |
"" | |
if disable_local_saving | |
else [gr.update(choices=files.list_input_files())] | |
) | |
try: | |
# Read the file content | |
with open(file.name, "r", encoding="utf-8") as f: | |
text_content = f.read() | |
if disable_local_saving: | |
# When saving is disabled, put content directly in text input | |
# Normalize whitespace by replacing newlines with spaces | |
normalized_text = " ".join(text_content.split()) | |
return normalized_text | |
else: | |
# When saving is enabled, save file and update dropdown | |
filename = os.path.basename(file.name) | |
target_path = os.path.join(files.INPUTS_DIR, filename) | |
# Handle duplicate filenames | |
base, ext = os.path.splitext(filename) | |
counter = 1 | |
while os.path.exists(target_path): | |
new_name = f"{base}_{counter}{ext}" | |
target_path = os.path.join(files.INPUTS_DIR, new_name) | |
counter += 1 | |
shutil.copy2(file.name, target_path) | |
return [gr.update(choices=files.list_input_files())] | |
except Exception as e: | |
print(f"Error handling file: {e}") | |
return ( | |
"" | |
if disable_local_saving | |
else [gr.update(choices=files.list_input_files())] | |
) | |
def generate_from_text(text, voice, format, speed): | |
"""Generate speech from direct text input""" | |
is_available, _ = api.check_api_status() | |
if not is_available: | |
gr.Warning("TTS Service is currently unavailable") | |
return [None, gr.update(choices=files.list_output_files())] | |
if not text or not text.strip(): | |
gr.Warning("Please enter text in the input box") | |
return [None, gr.update(choices=files.list_output_files())] | |
# Only save text if local saving is enabled | |
if not disable_local_saving: | |
files.save_text(text) | |
result = api.text_to_speech(text, voice, format, speed) | |
if result is None: | |
gr.Warning("Failed to generate speech. Please try again.") | |
return [None, gr.update(choices=files.list_output_files())] | |
return [ | |
result, | |
gr.update( | |
choices=files.list_output_files(), value=os.path.basename(result) | |
), | |
] | |
def generate_from_file(selected_file, voice, format, speed): | |
"""Generate speech from selected file""" | |
is_available, _ = api.check_api_status() | |
if not is_available: | |
gr.Warning("TTS Service is currently unavailable") | |
return [None, gr.update(choices=files.list_output_files())] | |
if not selected_file: | |
gr.Warning("Please select a file") | |
return [None, gr.update(choices=files.list_output_files())] | |
text = files.read_text_file(selected_file) | |
result = api.text_to_speech(text, voice, format, speed) | |
if result is None: | |
gr.Warning("Failed to generate speech. Please try again.") | |
return [None, gr.update(choices=files.list_output_files())] | |
return [ | |
result, | |
gr.update( | |
choices=files.list_output_files(), value=os.path.basename(result) | |
), | |
] | |
def play_selected(file_path): | |
if file_path and os.path.exists(file_path): | |
return gr.update(value=file_path, visible=True) | |
return gr.update(visible=False) | |
def clear_files(voice, format, speed): | |
"""Delete all input files and clear UI components while preserving model settings""" | |
files.delete_all_input_files() | |
return [ | |
gr.update(value=None, choices=[]), # file_select | |
None, # file_upload | |
gr.update(value=""), # file_preview | |
None, # audio_output | |
gr.update(choices=files.list_output_files()), # output_files | |
gr.update(value=voice), # voice | |
gr.update(value=format), # format | |
gr.update(value=speed), # speed | |
] | |
def clear_outputs(): | |
"""Delete all output audio files and clear audio components""" | |
files.delete_all_output_files() | |
return [ | |
None, # audio_output | |
gr.update(choices=[], value=None), # output_files | |
gr.update(visible=False), # selected_audio | |
] | |
# Connect event handlers | |
components["model"]["status_btn"].click( | |
fn=refresh_status, | |
outputs=[components["model"]["status_btn"], components["model"]["voice"]], | |
) | |
# Connect text submit button (always present) | |
components["input"]["text_submit"].click( | |
fn=generate_from_text, | |
inputs=[ | |
components["input"]["text_input"], | |
components["model"]["voice"], | |
components["model"]["format"], | |
components["model"]["speed"], | |
], | |
outputs=[ | |
components["output"]["audio_output"], | |
components["output"]["output_files"], | |
], | |
) | |
# Only connect file-related handlers if components exist | |
if components["input"]["file_select"] is not None: | |
components["input"]["file_select"].change( | |
fn=handle_file_select, | |
inputs=[components["input"]["file_select"]], | |
outputs=[components["input"]["file_preview"]], | |
) | |
if components["input"]["file_upload"] is not None: | |
# File upload handler - output depends on disable_local_saving | |
components["input"]["file_upload"].upload( | |
fn=handle_file_upload, | |
inputs=[components["input"]["file_upload"]], | |
outputs=[ | |
components["input"]["text_input"] | |
if disable_local_saving | |
else components["input"]["file_select"] | |
], | |
) | |
if components["output"]["play_btn"] is not None: | |
components["output"]["play_btn"].click( | |
fn=play_selected, | |
inputs=[components["output"]["output_files"]], | |
outputs=[components["output"]["selected_audio"]], | |
) | |
if components["input"]["clear_files"] is not None: | |
components["input"]["clear_files"].click( | |
fn=clear_files, | |
inputs=[ | |
components["model"]["voice"], | |
components["model"]["format"], | |
components["model"]["speed"], | |
], | |
outputs=[ | |
components["input"]["file_select"], | |
components["input"]["file_upload"], | |
components["input"]["file_preview"], | |
components["output"]["audio_output"], | |
components["output"]["output_files"], | |
components["model"]["voice"], | |
components["model"]["format"], | |
components["model"]["speed"], | |
], | |
) | |
if components["output"]["clear_outputs"] is not None: | |
components["output"]["clear_outputs"].click( | |
fn=clear_outputs, | |
outputs=[ | |
components["output"]["audio_output"], | |
components["output"]["output_files"], | |
components["output"]["selected_audio"], | |
], | |
) | |
if components["input"]["file_submit"] is not None: | |
components["input"]["file_submit"].click( | |
fn=generate_from_file, | |
inputs=[ | |
components["input"]["file_select"], | |
components["model"]["voice"], | |
components["model"]["format"], | |
components["model"]["speed"], | |
], | |
outputs=[ | |
components["output"]["audio_output"], | |
components["output"]["output_files"], | |
], | |
) | |