import os import gradio as gr import torch import spaces from TTS.api import TTS from supabase import create_client, Client import config import uuid # --- 1. Supabase Connection & Setup --- supabase = None if config.IS_CONNECTED: try: supabase: Client = create_client(config.SUPABASE_URL, config.SUPABASE_KEY) print("✅ Supabase Connected!") except Exception as e: print(f"❌ Supabase Error: {e}") # Model Setup os.environ["COQUI_TOS_AGREED"] = "1" device = "cuda" if torch.cuda.is_available() else "cpu" print(f"⏳ Loading XTTS Model on {device}...") tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(device) BUCKET_NAME = "voice-bucket" # --- Helper Functions --- def get_file_list(): """Supabase se saari audio files ki list layega""" if not supabase: return [] try: res = supabase.storage.from_(BUCKET_NAME).list() # Sirf wav/mp3 files filter karein files = [f['name'] for f in res if f['name'].endswith(('.wav', '.mp3'))] return sorted(files) except Exception as e: print(f"List Error: {e}") return [] def get_public_url(filename): """File ka direct link banayega sunne ke liye""" if not supabase: return None return f"{config.SUPABASE_URL}/storage/v1/object/public/{BUCKET_NAME}/{filename}" def download_voice(filename): """Generate karne ke liye file download karega""" local_path = f"temp_{filename}" with open(local_path, 'wb+') as f: res = supabase.storage.from_(BUCKET_NAME).download(filename) f.write(res) return local_path # --- CORE FUNCTIONS (Tabs Logic) --- # TAB 1: Generate @spaces.GPU(duration=120) def generate_audio(text, language, selected_voice): if not selected_voice: return None, "⚠️ पहले कोई आवाज़ (Sample) चुनें!" try: speaker_wav = download_voice(selected_voice) output_path = "output.wav" tts.tts_to_file( text=text, file_path=output_path, speaker_wav=speaker_wav, language=language ) return output_path, "✅ ऑडियो तैयार है!" except Exception as e: return None, f"❌ Error: {e}" # TAB 2: Save New Sample def save_new_sample(audio_file, custom_name): if not supabase: return "❌ Database connect nahi hai." if not audio_file: return "⚠️ ऑडियो फाइल नहीं मिली।" if not custom_name: return "⚠️ कृपया आवाज़ का नाम लिखें।" # Filename clean karein (spaces hata kar) clean_name = custom_name.strip().replace(" ", "_") filename = f"{clean_name}.wav" try: with open(audio_file, 'rb') as f: supabase.storage.from_(BUCKET_NAME).upload(filename, f) return f"🎉 '{filename}' सेव हो गया! अब आप इसे Home टैब में चुन सकते हैं।" except Exception as e: return f"❌ Error: शायद इस नाम की फाइल पहले से है. ({e})" # TAB 3: Manage (Delete/Rename/Preview) def update_library_view(): """Library refresh karega""" files = get_file_list() return gr.Dropdown(choices=files, value=files[0] if files else None), "📂 लिस्ट अपडेटेड" def load_file_preview(filename): """Select karte hi audio sunayega""" if not filename: return None return get_public_url(filename) def delete_sample(filename): if not supabase or not filename: return "⚠️ कुछ सेलेक्ट नहीं किया।" try: supabase.storage.from_(BUCKET_NAME).remove([filename]) return "🗑️ फाइल डिलीट हो गई!" except Exception as e: return f"❌ Delete Error: {e}" def rename_sample(old_name, new_name): if not supabase or not old_name or not new_name: return "⚠️ नाम सही नहीं है।" clean_new = new_name.strip().replace(" ", "_") if not clean_new.endswith(".wav"): clean_new += ".wav" try: supabase.storage.from_(BUCKET_NAME).move(old_name, clean_new) return f"✏️ नाम बदलकर '{clean_new}' हो गया!" except Exception as e: return f"❌ Rename Error: {e}" # --- UI LAYOUT (Tabs Style) --- with gr.Blocks(title="My Voice AI", theme=gr.themes.Soft()) as app: gr.Markdown("## 🎙️ My Hindi Voice Studio") with gr.Tabs(): # --- TAB 1: HOME (GENERATE) --- with gr.TabItem("🏠 Home (Generate)"): with gr.Row(): voice_selector = gr.Dropdown( label="1. आवाज़ चुनें (Sample Voice)", choices=get_file_list(), interactive=True ) refresh_home_btn = gr.Button("🔄 रिफ्रेश लिस्ट", size="sm") with gr.Row(): txt_input = gr.Textbox(label="2. क्या बुलवाना है? (Text)", lines=3, placeholder="नमस्ते, आप कैसे हैं?") lang_drop = gr.Dropdown(label="भाषा (Language)", choices=["hi", "en"], value="hi") gen_btn = gr.Button("🚀 आवाज़ बनाओ (Generate)", variant="primary") with gr.Row(): audio_out = gr.Audio(label="Result") status_home = gr.Textbox(label="Status") # Events refresh_home_btn.click(lambda: gr.Dropdown(choices=get_file_list()), outputs=voice_selector) gen_btn.click(generate_audio, [txt_input, lang_drop, voice_selector], [audio_out, status_home]) # --- TAB 2: ADD NEW VOICE --- with gr.TabItem("➕ Add Sample (Save)"): gr.Markdown("अपनी या किसी की भी आवाज़ सेव करें ताकि बाद में यूज़ कर सकें।") new_audio = gr.Audio(label="आवाज़ रिकॉर्ड करें या अपलोड करें", type="filepath") new_name = gr.Textbox(label="इस आवाज़ का नाम (Example: Meri_Awaz)", placeholder="Naam likhein...") save_btn = gr.Button("💾 सेव करें (Save to Cloud)", variant="primary") status_save = gr.Textbox(label="Status") save_btn.click(save_new_sample, [new_audio, new_name], status_save) # --- TAB 3: MY LIBRARY (LIST/EDIT) --- with gr.TabItem("📂 My Library"): gr.Markdown("यहाँ आपकी सारी सेव की हुई आवाज़ें हैं।") with gr.Row(): lib_dropdown = gr.Dropdown(label="फाइल चुनें", choices=get_file_list(), interactive=True) refresh_lib_btn = gr.Button("🔄 रिफ्रेश", size="sm") preview_player = gr.Audio(label="Preview (सुनें)") with gr.Accordion("🛠️ Edit / Delete Options", open=False): with gr.Row(): rename_txt = gr.Textbox(label="नया नाम (New Name)", placeholder="New_Name.wav") rename_btn = gr.Button("✏️ नाम बदलें (Rename)") delete_btn = gr.Button("🗑️ हमेशा के लिए डिलीट करें (Delete)", variant="stop") status_lib = gr.Textbox(label="Status") # Events # Select karte hi play karega lib_dropdown.change(load_file_preview, lib_dropdown, preview_player) # Buttons Logic refresh_lib_btn.click(update_library_view, None, [lib_dropdown, status_lib]) delete_btn.click(delete_sample, lib_dropdown, status_lib).then( update_library_view, None, [lib_dropdown, status_lib] ) rename_btn.click(rename_sample, [lib_dropdown, rename_txt], status_lib).then( update_library_view, None, [lib_dropdown, status_lib] ) # Login System if __name__ == "__main__": app.launch( auth=(config.APP_USER, config.APP_PASS), server_name="0.0.0.0", server_port=7860 )