denizaybey's picture
Revamp UI with custom theme, CSS, and Blocks interface for improved design and usability
0613be4
raw
history blame
9.67 kB
## 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("""
<h1 style="margin-top: 0;">BSOD.tv - Dub QC Demo</h1>
<p style="font-size: 1.1rem; line-height: 1.618;">
Professional audio synchronization verification for media localization.
<br>Upload original and dubbed .wav files (under 30 minutes) to start the QC process.
</p>
""")
# Main content with Golden ratio proportions
with gr.Row():
# Input section (larger according to Golden ratio)
with gr.Column(scale=1.618, elem_classes="input-section"):
original_audio = gr.Audio(type="filepath", label="Original .wav file")
dubbed_audio = gr.Audio(type="filepath", label="Dubbed .wav file")
email = gr.Textbox(label="Email")
company_name = gr.Textbox(label="Company Name")
tolerance = gr.Slider(0, 100, value=5, label="Tolerance Percentage",
info="Set the tolerance for audio comparison.")
submit_btn = gr.Button("Process Audio", variant="primary")
# Output section (smaller according to Golden ratio)
with gr.Column(scale=1, elem_classes="output-section"):
output = gr.Text(label="Processing Status")
gr.Markdown("### Results")
gr.Markdown("Once processing is complete, results will be emailed to the address provided.")
# Footer
with gr.Row(elem_classes="footer"):
gr.Markdown("© BSOD.tv - Professional Dub Quality Control")
# Set up the processing function
submit_btn.click(
fn=process_audio,
inputs=[original_audio, dubbed_audio, email, company_name, tolerance],
outputs=output
)
if __name__ == "__main__":
# To run this file locally, you'll need to install gradio and requests:
# pip install gradio requests
demo.launch()