## ENVIRONMENT VARIABLES # MODAL_VOLUME # MODAL_TOKEN_ID # MODAL_ENVIRONMENT # MODAL_TOKEN_SECRET import os import time import wave import modal import contextlib import gradio as gr def process_audio(original_audio_path, dubbed_audio_path, email, company_name, tolerance): """ This function processes the audio files, handling the logic for duration check, file upload to presigned URLs, and triggering the processing. """ # 1. Check the duration of both audio files. modal_token_id = os.environ['MODAL_TOKEN_ID'] modal_token_secret = os.environ['MODAL_TOKEN_SECRET'] modal_environment = os.environ['MODAL_ENVIRONMENT'] modal_volume = os.environ['MODAL_VOLUME'] processing_id = str(int(time.time())) try: with contextlib.closing(wave.open(original_audio_path, 'r')) as f: frames = f.getnframes() rate = f.getframerate() original_duration = frames / float(rate) with contextlib.closing(wave.open(dubbed_audio_path, 'r')) as f: frames = f.getnframes() rate = f.getframerate() dubbed_duration = frames / float(rate) if original_duration > 1800 or dubbed_duration > 1800: return "Error: Audio duration exceeds 30 minutes." except Exception as e: return f"Error reading audio files: {e}" # 2. Upload Audio Files to Modal Storage try: bsodtv_storage = modal.Volume.from_name(modal_volume) with bsodtv_storage.batch_upload() as batch: batch.put_file(original_audio_path, "/{}/original_audio.wav".format(processing_id)) batch.put_file(dubbed_audio_path, "/{}/original_audio.wav".format(processing_id)) bsodtv_storage.commit() except: return "Error uploading audio files to Cloud Storage." # 3. Call modal to trigger processing try: waveform_matching_function = modal.Function.from_name("Waveform-Matching", "reception_handler") waveform_matching_function.spawn( processing_id=processing_id, original_file="/{}/original_audio.wav".format(processing_id), dubbed_file="/{}/original_audio.wav".format(processing_id), email=email, company_name=company_name, tolerance_percentage=tolerance ) except: return "Error calling Outpost to trigger processing." return "Processing started. Results will be emailed to you shortly." # Create a professional Gradio interface using the Golden ratio (1.618) for proportions # Define custom CSS for a professional look css = """ :root { --main-bg-color: #111827; --primary-color: #3B82F6; --secondary-color: #60A5FA; --text-color: #F9FAFB; --text-secondary: #9CA3AF; --card-bg: #1F2937; --border-color: #374151; --accent-blue: #3B82F6; --accent-yellow: #FBBF24; --accent-red: #EF4444; --accent-green: #22C55E; --border-radius: 8px; --golden-ratio: 1.618; --font-header: 'Barlow', sans-serif; --font-body: 'Work Sans', sans-serif; } body { font-family: var(--font-body); background-color: var(--main-bg-color); color: var(--text-color); } .container { max-width: 1200px; margin: 0 auto; padding: calc(20px * var(--golden-ratio)); background-color: var(--main-bg-color); border-radius: calc(var(--border-radius) * var(--golden-ratio)); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); } .logo-container { display: flex; justify-content: center; margin-bottom: calc(20px * var(--golden-ratio)); padding: 15px; background-color: var(--card-bg); border-radius: var(--border-radius); border: 1px solid var(--border-color); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2); } .logo { max-width: 200px; max-height: 100px; transition: transform 0.3s ease; } .logo:hover { transform: scale(1.05); } .header { text-align: center; margin-bottom: calc(30px * var(--golden-ratio)); padding: calc(15px * var(--golden-ratio)); background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%); color: white; border-radius: var(--border-radius); box-shadow: 0 4px 10px rgba(59, 130, 246, 0.3); } .header h1 { color: white; font-family: var(--font-header); font-size: calc(1.5rem * var(--golden-ratio)); margin-bottom: calc(0.5rem * var(--golden-ratio)); text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); font-weight: 600; } .header p { color: rgba(255, 255, 255, 0.9); font-size: 1rem; max-width: calc(600px * var(--golden-ratio)); margin: 0 auto; } .input-section, .output-section { background-color: var(--card-bg); border: 1px solid var(--border-color); border-radius: var(--border-radius); padding: calc(20px * var(--golden-ratio)); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2); margin-bottom: 20px; transition: all 0.3s ease; } .input-section:hover, .output-section:hover { box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3); border-color: var(--primary-color); } .input-section { flex: var(--golden-ratio); } .output-section { flex: 1; } .footer { text-align: center; margin-top: calc(30px * var(--golden-ratio)); padding: 15px; color: var(--text-secondary); font-size: 0.9rem; border-top: 1px solid var(--border-color); } /* Improve form elements */ .gradio-slider input[type=range] { accent-color: var(--primary-color); } .gradio-textbox input, .gradio-textbox textarea { background-color: var(--main-bg-color) !important; border: 1px solid var(--border-color) !important; border-radius: var(--border-radius) !important; padding: 10px !important; color: var(--text-color) !important; transition: all 0.3s ease !important; } .gradio-textbox input:focus, .gradio-textbox textarea:focus { border-color: var(--primary-color) !important; box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3) !important; } .gradio-button { background-color: var(--primary-color) !important; color: white !important; border-radius: var(--border-radius) !important; padding: calc(10px * var(--golden-ratio)) calc(20px * var(--golden-ratio)) !important; font-weight: 600 !important; font-family: var(--font-header) !important; transition: all 0.3s ease !important; box-shadow: 0 4px 6px rgba(59, 130, 246, 0.3) !important; border: none !important; } .gradio-button:hover { background-color: var(--secondary-color) !important; transform: translateY(-2px); box-shadow: 0 6px 12px rgba(59, 130, 246, 0.4) !important; } /* Golden ratio spacing for elements */ .gradio-row { margin-bottom: calc(16px * var(--golden-ratio)) !important; } /* Additional dark theme adjustments */ .gradio-container { background-color: var(--main-bg-color) !important; } .gradio-form { background-color: var(--card-bg) !important; border: 1px solid var(--border-color) !important; } /* Labels and text styling */ label { color: var(--text-color) !important; font-family: var(--font-body) !important; } /* Responsive adjustments */ @media (max-width: 768px) { .container { padding: 15px; } .input-section, .output-section { padding: 15px; } } """ # Create a Blocks interface for more customization with gr.Blocks(css=css, theme=gr.themes.Soft(primary_hue="indigo", secondary_hue="purple")) as demo: with gr.Column(elem_classes="container"): # Logo section with gr.Row(elem_classes="logo-container"): logo = gr.Image("logo.png", elem_id="logo") # Header section with gr.Column(elem_classes="header"): gr.HTML("""
Professional audio synchronization verification for media localization.
Upload original and dubbed .wav files (under 30 minutes) to start the QC process.