Spaces:
Sleeping
Sleeping
| 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('<div class="main-header">π¬ DubIndic - AI Audio Dubbing Pipeline</div>') | |
| gr.HTML('<div style="text-align: center; margin-bottom: 2rem; font-size: 1.1rem; color: #666;">Transform your audio into another Indian language with full editing control</div>') | |
| # Step 1: Upload & Configure | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| gr.HTML('<div class="step-card">') | |
| gr.HTML('<div class="section-header"><span class="step-number">1</span>π€ Upload & Configure</div>') | |
| 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('</div>') | |
| # Step 2: Edit & Generate (Initially Hidden) | |
| with gr.Column(visible=False, elem_classes=["step-card"]) as edit_section: | |
| gr.HTML('<div class="section-header"><span class="step-number">2</span>βοΈ Edit, Generate & Finalize Chunks</div>') | |
| # 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('<div class="section-header"><span class="step-number">3</span>π Merge Final Audio</div>') | |
| 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.") |