import os import logging import gradio as gr from gradio_client import Client, handle_file import gradio.themes as gr_themes # Set up logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # It's recommended to set the HUGGINGFACE_TOKEN as an environment variable token = os.getenv("HUGGINGFACE_TOKEN") def create_DubIndic_interface(): """ Creates and configures the Gradio interface for the DubIndic application. """ try: # Connect to the Gradio client on Hugging Face Spaces client = Client("Tamiloneto8/Test1", hf_token=token, verbose=True) logger.info("Successfully connected to Gradio client.") except Exception as e: logger.error("Error connecting to the private space: %s", e, exc_info=True) # Create a fallback interface to show the connection error with gr.Blocks() as demo: gr.Markdown("# 🎬 DubIndic - Connection Error") gr.Textbox( value=f"Failed to connect to the Hugging Face Space. Please ensure the Space is running and the token is correct. Error: {e}", label="Error", interactive=False ) return demo # Define wrapper functions to call the API endpoints def start_processing(audio_file, target_language): if not audio_file or not target_language: return ( "Please provide both an audio file and a target language.", None, "", "", None, "", gr.update(visible=False), gr.update(visible=False), None, gr.update(visible=False), None, gr.update(visible=False) ) try: logger.info("Calling /process_audio_pipeline_step1 with file: %s", audio_file) result = client.predict( audio_file=handle_file(audio_file), target_lang=target_language, api_name="/process_audio_pipeline_step1" ) logger.info("Received result from step 1: %s", result) # API returns a 7-element tuple, we map it to our UI outputs # [status, internal_val, orig_audio, trans, transl, dubbed_audio, progress] return ( result[0], result[2], result[3], result[4], result[5], result[6], gr.update(visible=True), gr.update(visible=True), # Make edit and merge sections visible result[2] if result[2] else None, gr.update(visible=True if result[2] else False), result[5] if result[5] else None, gr.update(visible=True if result[5] else False) ) except Exception as e: logger.error("Error in start_processing: %s", e, exc_info=True) return ( f"Error starting the process: {e}", None, "", "", None, "", gr.update(visible=False), gr.update(visible=False), None, gr.update(visible=False), None, gr.update(visible=False) ) def navigate_chunk(transcription, translation, direction): # The API uses different endpoints for next/previous navigation # We assume /lambda is for previous and /lambda_1 is for next api_to_call = "/lambda" if direction == "prev" else "/lambda_1" try: logger.info("Calling %s to navigate.", api_to_call) result = client.predict( t=transcription, tr=translation, api_name=api_to_call ) logger.info("Received result from navigation: %s", result) # API returns a 6-element tuple # [internal_val, orig_audio, trans, transl, dubbed_audio, progress] return (result[1], result[2], result[3], result[4], result[5], result[1] if result[1] else None, result[4] if result[4] else None) except Exception as e: logger.error("Error navigating chunks: %s", e, exc_info=True) return None, f"Error navigating chunks: {e}", "", None, "", None, None def generate_dubbed_chunk(transcription, translation): if not transcription and not translation: return None, None try: logger.info("Calling /generate_dubbed_chunk.") dubbed_path = client.predict( transcription=transcription, translation=translation, api_name="/generate_dubbed_chunk" ) logger.info("Received dubbed chunk: %s", dubbed_path) return dubbed_path, gr.update(value=dubbed_path, visible=True) if dubbed_path else gr.update(visible=False) except Exception as e: logger.error("Error generating dubbed chunk: %s", e, exc_info=True) return None, None def finalize_current_chunk(): try: logger.info("Calling /finalize_current_chunk.") progress = client.predict(api_name="/finalize_current_chunk") logger.info("Received finalization progress: %s", progress) return progress except Exception as e: logger.error("Error finalizing chunk: %s", e, exc_info=True) return f"Error finalizing chunk: {e}" def merge_all_chunks(): try: logger.info("Calling /merge_audio_files.") final_status, final_audio = client.predict(api_name="/merge_audio_files") logger.info("Received final merged audio.") return final_status, final_audio except Exception as e: logger.error("Error merging audio files: %s", e, exc_info=True) return f"Error merging audio: {e}", None # Create custom theme with orange-red gradient colors custom_theme = gr_themes.Soft( primary_hue="orange", secondary_hue="red", neutral_hue="gray" ).set( button_primary_background_fill="linear-gradient(45deg, #f97316, #ef4444)", button_primary_background_fill_hover="linear-gradient(45deg, #ea580c, #dc2626)", button_primary_text_color="white", block_background_fill="rgba(255, 255, 255, 0.05)", block_border_color="rgba(249, 115, 22, 0.2)", input_background_fill="rgba(255, 255, 255, 0.9)", input_border_color="rgba(249, 115, 22, 0.3)", input_border_color_focus="rgba(239, 68, 68, 0.6)" ) # Define the Gradio Interface using Blocks for a custom layout with gr.Blocks(theme=custom_theme, title="DubIndic - AI Audio Dubbing", css=""" .gradio-container { background: linear-gradient(135deg, rgba(249, 115, 22, 0.1), rgba(239, 68, 68, 0.1)); } .gr-button[variant="primary"] { background: linear-gradient(45deg, #f97316, #ef4444) !important; border: none !important; color: white !important; font-weight: bold !important; } .gr-button[variant="primary"]:hover { background: linear-gradient(45deg, #ea580c, #dc2626) !important; transform: translateY(-1px); box-shadow: 0 4px 8px rgba(239, 68, 68, 0.3); } .gr-button[variant="secondary"] { background: linear-gradient(45deg, rgba(249, 115, 22, 0.1), rgba(239, 68, 68, 0.1)) !important; border: 2px solid #f97316 !important; color: #f97316 !important; font-weight: bold !important; } .gr-button[variant="secondary"]:hover { background: linear-gradient(45deg, #f97316, #ef4444) !important; color: white !important; } h1 { background: linear-gradient(45deg, #f97316, #ef4444); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; font-weight: bold; } .gr-textbox { border: 2px solid rgba(249, 115, 22, 0.3) !important; } .gr-textbox:focus { border-color: #ef4444 !important; box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1) !important; } """) as demo: gr.Markdown("# 🎬 DubIndic - AI Audio Dubbing Pipeline") gr.Markdown("Transform your audio into another Indian language with full editing control.") with gr.Row(): with gr.Column(scale=1): gr.Markdown("### 📤 Step 1: Upload & Configure") audio_input = gr.Audio(sources=["upload"], type="filepath", label="🎵 Upload Audio File") lang_dropdown = gr.Dropdown( choices=["Assamese", "Bengali", "Gujarati", "Hindi", "Kannada", "Malayalam", "Marathi", "Odia", "Punjabi", "Tamil", "Telugu"], label="🌐 Target Language" ) process_btn = gr.Button("🎯 Start Processing", variant="primary") step1_output = gr.Textbox(label="📊 Processing Status", interactive=False) with gr.Column(visible=False) as edit_section: gr.Markdown("### ✏️ Step 2: Edit, Generate & Finalize Chunks") with gr.Row(): prev_btn = gr.Button("◀️ Previous") next_btn = gr.Button("Next ▶️") with gr.Row(): with gr.Column(): original_audio = gr.Audio(label="🎧 Original Chunk Audio", type="filepath", interactive=False) download_original_audio = gr.File(label="💾 Download Original Chunk", visible=False) with gr.Column(): dubbed_audio = gr.Audio(label="🔊 Dubbed Chunk Audio", type="filepath", interactive=False) download_dubbed_audio = gr.File(label="💾 Download Dubbed Chunk", visible=False) transcription_text = gr.Textbox(label="Transcription (edit if needed)", lines=2, interactive=True) translation_text = gr.Textbox(label="Translation (edit if needed)", lines=2, interactive=True) with gr.Row(): generate_btn = gr.Button("🔊 Generate Dubbed Chunk") finalize_btn = gr.Button("✔️ Finalize Chunk", variant="secondary") progress_text = gr.Textbox(label="Progress", interactive=False) with gr.Row(visible=False) as merge_section: gr.Markdown("### 🏁 Step 3: Merge Final Audio") merge_btn = gr.Button("🚀 Merge All Finalized Chunks", variant="primary") final_output = gr.Textbox(label="🎉 Final Results", interactive=False) output_audio = gr.Audio(label="🔊 Final Dubbed Audio", type="filepath", interactive=False) # Connect functions to UI components process_btn.click( fn=start_processing, inputs=[audio_input, lang_dropdown], outputs=[step1_output, original_audio, transcription_text, translation_text, dubbed_audio, progress_text, edit_section, merge_section, download_original_audio, download_original_audio, download_dubbed_audio, download_dubbed_audio] ) prev_btn.click( fn=lambda t, tr: navigate_chunk(t, tr, "prev"), inputs=[transcription_text, translation_text], outputs=[original_audio, transcription_text, translation_text, dubbed_audio, progress_text, download_original_audio, download_dubbed_audio] ) next_btn.click( fn=lambda t, tr: navigate_chunk(t, tr, "next"), inputs=[transcription_text, translation_text], outputs=[original_audio, transcription_text, translation_text, dubbed_audio, progress_text, download_original_audio, download_dubbed_audio] ) generate_btn.click( fn=generate_dubbed_chunk, inputs=[transcription_text, translation_text], outputs=[dubbed_audio, download_dubbed_audio] ) finalize_btn.click( fn=finalize_current_chunk, inputs=[], outputs=[progress_text] ) merge_btn.click( fn=merge_all_chunks, inputs=[], outputs=[final_output, output_audio] ) return demo if __name__ == "__main__": DubIndic_interface = create_DubIndic_interface() if DubIndic_interface: DubIndic_interface.launch(show_error=True, share=False, server_name="0.0.0.0", server_port=7860) else: logger.error("Failed to create the Gradio interface.")