import gradio as gr import requests import json import os from pathlib import Path import uuid import fcntl import time # gr.NO_RELOAD = False # API Base URL BASE_URL = os.environ.get("BASE_URL", "") # Counter persistence file COUNTER_FILE = Path("generation_counter.json") # Example texts EXAMPLE_TEXT_ENGLISH = "Welcome to Ringg TTS! This is a text to speech system that can convert your text into natural-sounding audio. Try it out with your own content!" EXAMPLE_TEXT_HINDI = "नमस्ते! मैं रिंग टीटीएस हूँ। मैं आपके टेक्स्ट को प्राकृतिक आवाज़ में बदल सकता हूँ। कृपया अपना टेक्स्ट यहाँ लिखें और सुनें।" EXAMPLE_TEXT_MIXED = "Hello दोस्तों! Welcome to Ringg TTS. यह एक बहुत ही शानदार text to speech system है जो Hindi और English दोनों languages को support करता है।" def load_counter(): """Load universal generation counter from file (thread-safe)""" try: if COUNTER_FILE.exists(): with open(COUNTER_FILE, "r") as f: # Try to acquire shared lock for reading try: fcntl.flock(f.fileno(), fcntl.LOCK_SH) data = json.load(f) fcntl.flock(f.fileno(), fcntl.LOCK_UN) return data.get("count", 0) except: # If locking fails, just read without lock f.seek(0) data = json.load(f) return data.get("count", 0) except Exception as e: print(f"Error loading counter: {e}") return 0 def save_counter(count): """Save universal generation counter to file (thread-safe)""" try: # Use file locking to prevent race conditions with multiple users with open(COUNTER_FILE, "w") as f: try: fcntl.flock(f.fileno(), fcntl.LOCK_EX) json.dump({"count": count, "last_updated": time.time()}, f) f.flush() os.fsync(f.fileno()) fcntl.flock(f.fileno(), fcntl.LOCK_UN) except: # If locking fails, just write without lock json.dump({"count": count, "last_updated": time.time()}, f) f.flush() except Exception as e: print(f"Error saving counter: {e}") def increment_counter(): """Atomically increment and return the new counter value""" try: # Read current value, increment, and save atomically with open(COUNTER_FILE, "r+" if COUNTER_FILE.exists() else "w+") as f: try: fcntl.flock(f.fileno(), fcntl.LOCK_EX) # Read current count f.seek(0) try: data = json.load(f) current_count = data.get("count", 0) except: current_count = 0 # Increment new_count = current_count + 1 # Write back f.seek(0) f.truncate() json.dump({"count": new_count, "last_updated": time.time()}, f) f.flush() os.fsync(f.fileno()) fcntl.flock(f.fileno(), fcntl.LOCK_UN) return new_count except: # Fallback without locking f.seek(0) try: data = json.load(f) current_count = data.get("count", 0) except: current_count = 0 new_count = current_count + 1 f.seek(0) f.truncate() json.dump({"count": new_count, "last_updated": time.time()}, f) f.flush() return new_count except Exception as e: print(f"Error incrementing counter: {e}") return 0 def get_voices(): """Fetch available voices from API""" try: response = requests.get(f"{BASE_URL}/voices", timeout=10) if response.status_code == 200: voices_data = response.json().get("voices", {}) # Create a list of tuples (display_name, voice_id) voices = [] for voice_id, voice_info in voices_data.items(): name = voice_info.get("name", "Unknown") gender = voice_info.get("gender", "N/A") display_name = f"{name} ({gender})" voices.append((display_name, voice_id)) return sorted(voices, key=lambda x: x[0]) return [] except Exception as e: print(f"Error fetching voices: {e}") return [] def synthesize_speech(text, voice_id): """Synthesize speech from text""" if not text or not text.strip(): return None, "⚠️ Please enter some text", "", "", "", "", "", "" if not voice_id: return None, "⚠️ Please select a voice", "", "", "", "", "", "" # Print input text length text_length = len(text) print(f"Input text length: {text_length} characters") try: payload = {"text": text, "voice_id": voice_id} response = requests.post( f"{BASE_URL}/synthesize", headers={"Content-Type": "application/json"}, json=payload, timeout=30, ) if response.status_code == 200: result = response.json() if result.get("success"): audio_url = result.get("audio_url", "") metrics = result.get("metrics", {}) # Format metrics total_time = f"{metrics.get('t', 0):.3f}s" rtf = f"{metrics.get('rtf', 0):.4f}" wav_duration = f"{metrics.get('wav_seconds', 0):.2f}s" vocoder_time = f"{metrics.get('t_vocoder', 0):.3f}s" no_vocoder_time = f"{metrics.get('t_no_vocoder', 0):.3f}s" rtf_no_vocoder = f"{metrics.get('rtf_no_vocoder', 0):.4f}" status_msg = "✅ Audio generated successfully!" return ( audio_url, status_msg, total_time, rtf, wav_duration, vocoder_time, no_vocoder_time, rtf_no_vocoder, ) else: error_msg = result.get("message", "Unknown error") return None, f"❌ Synthesis failed: {error_msg}", "", "", "", "", "", "" else: return ( None, f"❌ API returned status code: {response.status_code}", "", "", "", "", "", "", ) except Exception as e: return None, f"❌ Error: {str(e)}", "", "", "", "", "", "" # Load initial counter value initial_counter = load_counter() # Create Gradio interface with gr.Blocks( theme=gr.themes.Base( font=[gr.themes.GoogleFont("Source Sans Pro"), "Arial", "sans-serif"] ), css=".gradio-container {max-width: none !important;}", ) as demo: # Title with Health Status with gr.Row(): with gr.Column(scale=4): audio_image = gr.HTML( value="""