""" SRT Processing Tool - Gradio Interface Production-ready for Hugging Face Spaces """ import os import tempfile import gradio as gr from tools import process_srt_file from dotenv import load_dotenv # Load environment variables from .env if present load_dotenv(override=True) def process_srt_interface( file_path, operation, target_lang, provider, model, workers, max_chars, ): """ Process SRT file based on user inputs. Args: file_path: Path to uploaded file from Gradio operation: "translate" or "resegment" target_lang: Target language code (for translation) provider: Translation provider ("Aliyun (DashScope)", "OpenAI", "OpenRouter") model: Model name (optional) workers: Number of concurrent workers max_chars: Maximum characters per segment Returns: Tuple of (output_file_path, success_message) """ if file_path is None: return None, "❌ Please upload an SRT file first." try: # Map provider names to internal router values provider_map = { "Aliyun (DashScope)": "dashscope", "OpenAI": "openai", "OpenRouter": "openrouter", } router = provider_map.get(provider, "dashscope") # Map operation names to internal values operation_map = { "Translate only": "translate", "Resegment only": "resegment", } operation_value = operation_map.get(operation, "resegment") # Validate inputs if operation_value == "translate" and not target_lang: return None, "❌ Target language is required for translation." # Use the uploaded file path directly temp_input_path = file_path # Create temporary output file with tempfile.NamedTemporaryFile(delete=False, suffix=".srt") as temp_output: temp_output_path = temp_output.name # Process the file process_srt_file( temp_input_path, temp_output_path, operation=operation_value, max_chars=int(max_chars), target_lang=target_lang if operation_value == "translate" else None, model=model if model else None, workers=int(workers), router=router, ) # Generate output filename input_filename = os.path.splitext(os.path.basename(file_path))[0] if operation_value == "translate": output_filename = f"{input_filename}_{target_lang}.srt" else: output_filename = f"{input_filename}_resentenced.srt" # Read the output file and create download file with open(temp_output_path, "r", encoding="utf-8") as f: output_content = f.read() # Create a temporary file for download with proper name download_dir = tempfile.gettempdir() download_path = os.path.join(download_dir, output_filename) with open(download_path, "w", encoding="utf-8") as download_file: download_file.write(output_content) # Clean up temporary output file try: os.remove(temp_output_path) except Exception: pass success_msg = f"✅ Processing complete! ({operation})" return download_path, success_msg except Exception as e: # Clean up on error try: if "temp_output_path" in locals(): os.remove(temp_output_path) except Exception: pass return None, f"❌ Processing failed: {str(e)}" def create_interface(): """Create and configure the Gradio interface.""" with gr.Blocks(title="SRT Processing Tool", theme=gr.themes.Soft()) as app: gr.Markdown( """ # đŸŽŦ SRT Processing Tool Process and translate your subtitle files with AI-powered tools! **Features:** - 🔄 **Resegment** SRT files to optimize character limits per segment - 🌍 **Translate** SRT files using AI (OpenAI, Aliyun DashScope, or OpenRouter) - ⚡ **Automatic Resegmentation**: Translation automatically includes resegmentation for optimal chunk sizes """ ) with gr.Row(): with gr.Column(scale=1): gr.Markdown("### 📤 Upload & Settings") uploaded_file = gr.File( label="Upload SRT File", file_types=[".srt"], type="filepath", ) operation = gr.Radio( label="Processing Operation", choices=["Translate only", "Resegment only"], value="Translate only", info="Choose what operation to perform on the SRT file", ) with gr.Accordion("Translation Settings", open=True, visible=True) as translation_accordion: target_lang = gr.Textbox( label="Target Language Code", placeholder="e.g., fr, es, de, zh", value="zh", info="ISO language code for translation", ) provider = gr.Dropdown( label="Translation Provider", choices=["Aliyun (DashScope)", "OpenAI", "OpenRouter"], value="Aliyun (DashScope)", info="Choose the translation provider", ) model = gr.Textbox( label="Model Name", placeholder="Leave blank for default", value="qwen-max", info="Model to use (defaults: qwen-max for DashScope, gpt-4.1 for OpenAI, openai/gpt-4o for OpenRouter)", ) workers = gr.Slider( label="Concurrent Workers", minimum=1, maximum=50, value=25, step=1, info="Number of parallel translation requests", ) with gr.Accordion("Resegmentation Settings", open=True) as resegment_accordion: max_chars = gr.Slider( label="Maximum Characters per Segment", minimum=10, maximum=500, value=125, step=5, info="Controls how the SRT is resegmented before translation", ) process_btn = gr.Button("🚀 Process SRT File", variant="primary", size="lg") info_box = gr.Markdown( """ **â„šī¸ Note:** Translation automatically includes resegmentation for optimal chunk sizes. **API Keys:** Set these as secrets in Hugging Face Spaces: - `DASHSCOPE_API_KEY` for Aliyun DashScope - `OPENAI_API_KEY` for OpenAI - `OPENROUTER_API_KEY` for OpenRouter """ ) with gr.Column(scale=1): gr.Markdown("### đŸ“Ĩ Results") status_output = gr.Textbox( label="Status", interactive=False, value="Waiting for file upload...", ) output_file = gr.File( label="Download Processed SRT", visible=False, ) # Update UI visibility based on operation def update_ui(selected_operation): """Update UI components visibility based on selected operation.""" if selected_operation == "Translate only": return ( gr.update(visible=True, open=True), # translation_accordion gr.update(visible=True, open=True), # resegment_accordion gr.update(value="qwen-max"), # model default ) else: # Resegment only return ( gr.update(visible=False), # translation_accordion gr.update(visible=True, open=True), # resegment_accordion gr.update(value=""), # model empty ) operation.change( fn=update_ui, inputs=[operation], outputs=[translation_accordion, resegment_accordion, model], ) # Update model placeholder based on provider def update_model_placeholder(selected_provider): """Update model placeholder text based on provider.""" defaults = { "Aliyun (DashScope)": "qwen-max", "OpenAI": "gpt-4.1", "OpenRouter": "openai/gpt-4o", } return gr.update(value=defaults.get(selected_provider, "")) provider.change( fn=update_model_placeholder, inputs=[provider], outputs=[model], ) # Process button click handler def handle_process(file_path, op, lang, prov, mod, wrk, chars): """Handle the process button click.""" result_file, message = process_srt_interface( file_path, op, lang, prov, mod, wrk, chars ) if result_file: return ( gr.update(value=message, visible=True), gr.update(value=result_file, visible=True, label=f"Download: {os.path.basename(result_file)}") ) else: return ( gr.update(value=message, visible=True), gr.update(visible=False) ) process_btn.click( fn=handle_process, inputs=[uploaded_file, operation, target_lang, provider, model, workers, max_chars], outputs=[status_output, output_file], ) # Update status when file is uploaded uploaded_file.change( fn=lambda x: gr.update(value="✅ File uploaded! Configure settings and click 'Process SRT File'.") if x else gr.update(value="Waiting for file upload..."), inputs=[uploaded_file], outputs=[status_output], ) return app # Create the Gradio interface demo = create_interface() # For Hugging Face Spaces, expose the demo variable # For local development, launch the app if __name__ == "__main__": demo.launch( server_name="0.0.0.0", server_port=7860, share=False, )