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.Markdown(f"**Error Details:** {str(e)}") gr.Markdown("Please check your connection and try again.") 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 select a target language.", None, "", "", None, "", gr.update(visible=False), 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 ( f"✅ {result[0]}", result[2], result[3], result[4], result[5], result[6], gr.update(visible=True), gr.update(visible=True) ) except Exception as e: logger.error("Error in start_processing: %s", e, exc_info=True) return ( f"❌ Error: {str(e)}", None, "", "", None, "", gr.update(visible=False), gr.update(visible=False) ) def navigate_chunk(transcription, translation, direction): 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) return result[1], result[2], result[3], result[4], result[5] except Exception as e: logger.error("Error navigating chunks: %s", e, exc_info=True) return None, f"❌ Navigation error: {str(e)}", "", None, "" def generate_dubbed_chunk(transcription, translation): if not transcription and not translation: return 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 except Exception as e: logger.error("Error generating dubbed chunk: %s", e, exc_info=True) return 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 f"✅ {progress}" except Exception as e: logger.error("Error finalizing chunk: %s", e, exc_info=True) return f"❌ Error finalizing: {str(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 f"🎉 {final_status}", final_audio except Exception as e: logger.error("Error merging audio files: %s", e, exc_info=True) return f"❌ Merge error: {str(e)}", None # Create custom theme with enhanced styling custom_theme = gr_themes.Soft( primary_hue="orange", secondary_hue="red", neutral_hue="slate" ) # Enhanced CSS with creative design elements enhanced_css = """ /* Main container styling */ .gradio-container { background: linear-gradient(135deg, #fff8e6 0%, #fef3e2 50%, #fff8e6 100%); font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; min-height: 100vh; } /* Header styling with animated gradient */ .main-header { background: linear-gradient(45deg, #f97316, #ef4444, #f97316); background-size: 300% 300%; animation: gradientShift 6s ease infinite; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; text-align: center; font-weight: 800; font-size: 2.5rem; margin-bottom: 1rem; } @keyframes gradientShift { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } /* Card-like sections */ .step-card { background: rgba(255, 255, 255, 0.9); border: 2px solid rgba(249, 115, 22, 0.2); border-radius: 16px; padding: 24px; margin: 16px 0; box-shadow: 0 8px 32px rgba(249, 115, 22, 0.1); backdrop-filter: blur(8px); transition: all 0.3s ease; } .step-card:hover { transform: translateY(-2px); box-shadow: 0 12px 48px rgba(249, 115, 22, 0.15); border-color: rgba(249, 115, 22, 0.4); } /* Enhanced primary buttons */ .gr-button[variant="primary"] { background: linear-gradient(45deg, #f97316, #ef4444) !important; border: none !important; color: white !important; font-weight: 600 !important; padding: 12px 24px !important; border-radius: 12px !important; font-size: 1rem !important; transition: all 0.3s ease !important; box-shadow: 0 4px 16px rgba(249, 115, 22, 0.3) !important; } .gr-button[variant="primary"]:hover { background: linear-gradient(45deg, #ea580c, #dc2626) !important; transform: translateY(-2px) !important; box-shadow: 0 8px 24px rgba(239, 68, 68, 0.4) !important; } /* Enhanced secondary buttons */ .gr-button[variant="secondary"] { background: rgba(255, 248, 230, 0.9) !important; border: 2px solid #f97316 !important; color: #f97316 !important; font-weight: 600 !important; padding: 10px 20px !important; border-radius: 10px !important; transition: all 0.3s ease !important; } .gr-button[variant="secondary"]:hover { background: linear-gradient(45deg, #f97316, #ef4444) !important; color: white !important; transform: translateY(-1px) !important; box-shadow: 0 4px 12px rgba(249, 115, 22, 0.3) !important; } /* Navigation buttons */ .nav-button { background: linear-gradient(45deg, #f97316, #ef4444) !important; border: none !important; color: white !important; font-weight: 600 !important; padding: 8px 16px !important; border-radius: 8px !important; font-size: 0.9rem !important; min-width: 120px !important; } /* Input styling */ .gr-textbox, .gr-dropdown { border: 2px solid rgba(249, 115, 22, 0.3) !important; border-radius: 12px !important; background: rgba(255, 255, 255, 0.95) !important; transition: all 0.3s ease !important; font-size: 1rem !important; } .gr-textbox:focus, .gr-dropdown:focus { border-color: #ef4444 !important; box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.1) !important; outline: none !important; } /* Audio player styling */ .gr-audio { border: 2px solid rgba(249, 115, 22, 0.3) !important; border-radius: 12px !important; background: rgba(255, 255, 255, 0.95) !important; padding: 12px !important; } /* Progress indicators */ .progress-text { background: linear-gradient(45deg, #f97316, #ef4444); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; font-weight: 600; } /* Step numbers */ .step-number { background: linear-gradient(45deg, #f97316, #ef4444); color: white; border-radius: 50%; width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center; font-weight: bold; margin-right: 8px; } /* Section headers */ .section-header { color: #f97316; font-weight: 700; font-size: 1.25rem; margin-bottom: 16px; display: flex; align-items: center; } /* Responsive design */ @media (max-width: 768px) { .main-header { font-size: 2rem; } .step-card { padding: 16px; } } /* Loading animation */ .loading { animation: pulse 2s infinite; } @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.7; } 100% { opacity: 1; } } """ # Define the enhanced Gradio interface with gr.Blocks(theme=custom_theme, title="DubIndic - AI Audio Dubbing", css=enhanced_css) as demo: # Main header gr.HTML('
🎬 DubIndic - AI Audio Dubbing Pipeline
') gr.HTML('
Transform your audio into another Indian language with full editing control
') # Step 1: Upload & Configure with gr.Row(): with gr.Column(scale=2): gr.HTML('
') gr.HTML('
1📤 Upload & Configure
') with gr.Row(): with gr.Column(scale=1): audio_input = gr.Audio( sources=["upload"], type="filepath", label="🎵 Upload Audio File", elem_classes=["upload-audio"] ) with gr.Column(scale=1): lang_dropdown = gr.Dropdown( choices=["Assamese", "Bengali", "Gujarati", "Hindi", "Kannada", "Malayalam", "Marathi", "Odia", "Punjabi", "Tamil", "Telugu"], label="🌐 Target Language", value=None, elem_classes=["language-select"] ) process_btn = gr.Button( "🎯 Start Processing", variant="primary", size="lg", elem_classes=["process-button"] ) step1_output = gr.Textbox( label="📊 Processing Status", interactive=False, elem_classes=["status-output"] ) gr.HTML('
') # Step 2: Edit & Generate (Initially Hidden) with gr.Column(visible=False, elem_classes=["step-card"]) as edit_section: gr.HTML('
2✏️ Edit, Generate & Finalize Chunks
') # Navigation controls with gr.Row(): with gr.Column(scale=1): prev_btn = gr.Button("◀️ Previous", elem_classes=["nav-button"]) with gr.Column(scale=1): next_btn = gr.Button("Next ▶️", elem_classes=["nav-button"]) # Audio and text editing with gr.Row(): with gr.Column(scale=1): original_audio = gr.Audio( label="🎵 Original Chunk Audio", type="filepath", interactive=False ) with gr.Column(scale=1): transcription_text = gr.Textbox( label="📝 Transcription (edit if needed)", lines=3, interactive=True, placeholder="Original transcription will appear here..." ) translation_text = gr.Textbox( label="🔄 Translation (edit if needed)", lines=3, interactive=True, placeholder="Translation will appear here..." ) # Generate and finalize controls with gr.Row(): with gr.Column(scale=1): generate_btn = gr.Button("🔊 Generate Dubbed Chunk", variant="secondary") with gr.Column(scale=1): finalize_btn = gr.Button("✔️ Finalize Chunk", variant="secondary") dubbed_audio = gr.Audio( label="🎤 Dubbed Chunk Audio", type="filepath", interactive=False ) progress_text = gr.Textbox( label="📈 Progress", interactive=False, elem_classes=["progress-text"] ) # Step 3: Final Merge (Initially Hidden) with gr.Column(visible=False, elem_classes=["step-card"]) as merge_section: gr.HTML('
3🏁 Merge Final Audio
') merge_btn = gr.Button( "🚀 Merge All Finalized Chunks", variant="primary", size="lg" ) with gr.Row(): with gr.Column(scale=1): final_output = gr.Textbox( label="🎉 Final Results", interactive=False, elem_classes=["final-status"] ) with gr.Column(scale=1): 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] ) 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] ) 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] ) generate_btn.click( fn=generate_dubbed_chunk, inputs=[transcription_text, translation_text], outputs=[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, favicon_path=None, show_tips=True ) else: logger.error("Failed to create the Gradio interface.")