Kokoro-FastAPI/ui/lib/handlers.py

139 lines
5 KiB
Python

import gradio as gr
import os
import shutil
from . import api, files
def setup_event_handlers(components: dict):
"""Set up all event handlers for the UI components."""
def refresh_status():
is_available, voices = api.check_api_status()
status = "Available" if is_available else "Unavailable"
btn_text = f"🔄 TTS Service: {status}"
if is_available and voices:
return {
components["model"]["status_btn"]: gr.update(value=btn_text),
components["model"]["voice"]: gr.update(choices=voices, value=voices[0] if voices else None)
}
return {
components["model"]["status_btn"]: gr.update(value=btn_text),
components["model"]["voice"]: 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 gr.update(choices=files.list_input_files())
try:
# Copy file to inputs directory
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)
except Exception as e:
print(f"Error uploading file: {e}")
return gr.update(choices=files.list_input_files())
def generate_speech(text, selected_file, voice, format, speed):
is_available, _ = api.check_api_status()
if not is_available:
gr.Warning("TTS Service is currently unavailable")
return {
components["output"]["audio_output"]: None,
components["output"]["output_files"]: gr.update(choices=files.list_output_files())
}
# Use text input if provided, otherwise use file content
if text and text.strip():
files.save_text(text)
final_text = text
elif selected_file:
final_text = files.read_text_file(selected_file)
else:
gr.Warning("Please enter text or select a file")
return {
components["output"]["audio_output"]: None,
components["output"]["output_files"]: gr.update(choices=files.list_output_files())
}
result = api.text_to_speech(final_text, voice, format, speed)
if result is None:
gr.Warning("Failed to generate speech. Please try again.")
return {
components["output"]["audio_output"]: None,
components["output"]["output_files"]: gr.update(choices=files.list_output_files())
}
return {
components["output"]["audio_output"]: result,
components["output"]["output_files"]: 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)
# Connect event handlers
components["model"]["status_btn"].click(
fn=refresh_status,
outputs=[
components["model"]["status_btn"],
components["model"]["voice"]
]
)
components["input"]["file_select"].change(
fn=handle_file_select,
inputs=[components["input"]["file_select"]],
outputs=[components["input"]["file_preview"]]
)
components["input"]["file_upload"].upload(
fn=handle_file_upload,
inputs=[components["input"]["file_upload"]],
outputs=[components["input"]["file_select"]]
)
components["output"]["play_btn"].click(
fn=play_selected,
inputs=[components["output"]["output_files"]],
outputs=[components["output"]["selected_audio"]]
)
components["model"]["submit"].click(
fn=generate_speech,
inputs=[
components["input"]["text_input"],
components["input"]["file_select"],
components["model"]["voice"],
components["model"]["format"],
components["model"]["speed"]
],
outputs=[
components["output"]["audio_output"],
components["output"]["output_files"]
]
)