File size: 44,176 Bytes
a8189eb b5e3557 a8189eb b5e3557 a8189eb b5e3557 0992c47 a8189eb a812039 a8189eb 0992c47 f5c2623 a812039 f5c2623 b5e3557 a8189eb b5e3557 a8189eb a812039 f5c2623 a8189eb a812039 0992c47 a8189eb a812039 a8189eb f5c2623 a8189eb 90ea876 a8189eb f5c2623 b5e3557 b313b80 b5e3557 b313b80 b5e3557 b313b80 b5e3557 b313b80 b5e3557 8214b7d b5e3557 1822d3c b5e3557 8214b7d b5e3557 b313b80 0613be4 e459c2b 0613be4 3282c39 0613be4 04a7213 0613be4 749bfee 0613be4 e237e73 0613be4 e459c2b a06400e b313b80 8214b7d b313b80 0613be4 96c1034 0613be4 f5c2623 b313b80 f5c2623 a8189eb |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 |
## ENVIRONMENT VARIABLES
# MODAL_VOLUME
# MODAL_TOKEN_ID
# MODAL_ENVIRONMENT
# MODAL_TOKEN_SECRET
import os
import cv2
import time
import uuid
import modal
import shutil
import logging
import gradio as gr
import numpy as np
import soundfile as sf
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
def _preprocess_audio_to_wav_pcm_mono(input_path: str) -> str:
"""
Convert the given audio file to a WAV file with PCM encoding and mono channel.
The original sampling rate is preserved (no resampling).
Returns the path to a temporary processed WAV file.
"""
try:
# Read audio with original sampling rate preserved
data, sr = sf.read(input_path, always_2d=True)
except Exception as e:
logger.error(f"Failed to read audio file '{input_path}': {e}")
raise
try:
# Downmix to mono by averaging channels (handles mono or multi-channel)
mono = data.mean(axis=1)
# Write as 16-bit PCM WAV to a temp path
out_path = f"/tmp/{uuid.uuid4().hex}.wav"
sf.write(out_path, mono, int(sr), subtype="PCM_16", format="WAV")
return out_path
except Exception as e:
logger.error(f"Failed to write processed WAV file for '{input_path}': {e}")
raise
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.
waveform_app = modal.App("Waveform-Matching")
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['WAVEFORM_MODAL_VOLUME']
processing_id = str(int(time.time()))
# Preprocess audio files: WAV format, PCM encoding, mono, preserve original sampling rate
try:
processed_original = _preprocess_audio_to_wav_pcm_mono(original_audio_path)
processed_dubbed = _preprocess_audio_to_wav_pcm_mono(dubbed_audio_path)
except Exception as e:
logger.error(f"Error preprocessing audio files: {e}")
return "Error preprocessing audio files."
try:
bsodtv_storage = modal.Volume.from_name(modal_volume)
with bsodtv_storage.batch_upload() as batch:
batch.put_file(processed_original, f"/{processing_id}/original_audio.wav")
batch.put_file(processed_dubbed, f"/{processing_id}/dubbed_audio.wav")
except Exception as e:
logger.error(f"Error uploading audio files to Modal Storage: {e}")
return "Error uploading audio files to Cloud Storage."
finally:
# Cleanup temporary processed files
for p in [processed_original, processed_dubbed]:
try:
if p and os.path.exists(p):
os.remove(p)
except Exception:
pass
# 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="/{}/dubbed_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."
def process_video(video_path, notes, email, company_name) -> str:
"""
Process the input video for content moderation using Modal.
Steps:
1. Upload the provided video to the configured Modal Volume.
2. Obtain the video dimensions (width, height).
3. Call the Content-Moderation reception_function via Modal (synchronously with .remote).
4. Download the processed video returned by the function to /tmp with a random UUID filename.
5. Return the local path to the downloaded video.
"""
# Validate inputs
if not video_path or not os.path.exists(video_path):
logger.error("Invalid video path provided to process_video.")
return "Invalid video path."
# Helper to obtain width and height
def _get_video_dimensions(path: str):
try:
# type: ignore
cap = cv2.VideoCapture(path)
if cap.isOpened():
width = int(cap.get(3))
height = int(cap.get(4))
cap.release()
except Exception as e:
logger.debug(f"OpenCV not available or failed to read video dimensions: {e}")
return width, height
try:
# 1. Setup Modal app and volume
_ = os.environ.get('MODAL_TOKEN_ID') # Read to ensure environment readiness (kept for parity with process_audio)
_ = os.environ.get('MODAL_TOKEN_SECRET')
_ = os.environ.get('MODAL_ENVIRONMENT')
modal_volume_name = os.environ['MODERATION_MODAL_VOLUME']
# Unique processing folder and paths
processing_id = str(int(time.time()))
ext = os.path.splitext(video_path)[1]
remote_input_path = f"/{processing_id}/input_video{ext}"
# 2. Upload video to Modal Volume
volume = modal.Volume.from_name(modal_volume_name)
try:
with volume.batch_upload() as batch:
batch.put_file(video_path, remote_input_path)
except Exception as e:
logger.error(f"Error uploading video to Modal Storage: {e}")
return "Error uploading video to Cloud Storage."
# 3. Obtain video dimensions
width, height = _get_video_dimensions(video_path)
# 4. Call Modal function synchronously
try:
moderation_function = modal.Function.from_name("Content-Moderation", "professional_reception_function")
moderation_function.spawn(
input_text=str(notes) if notes is not None else "",
video_path=remote_input_path,
size=(int(width), int(height)),
email=email,
company_name=company_name
)
except Exception as e:
logger.error(f"Error calling Modal reception_function: {e}")
return "Error calling Outpost to trigger processing."
return "Video Request Obtained"
except Exception as e:
logger.error(f"Unexpected error in process_video: {e}")
return "Unexpected error during video processing."
# 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: 100%;
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: 300px;
max-height: 100px;
transition: transform 0.3s ease;
display: block; /* Ensure it's a block element */
margin: 0 auto; /* This will center a block element within its flex container */
}
.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"):
# Header section
with gr.Column(elem_classes="header"):
gr.HTML("""
<img src="data:image/png;base64,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" class="logo" alt="Logo">
<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 to start the QC process.
</p>
""")
# Main content with specified layout
with gr.Tabs():
with gr.Tab("Dub Quality Control"):
# First Row: Half Original Audio Input, Half Dubbed Audio Input
with gr.Row(elem_classes="input-section"):
with gr.Column(scale=1):
original_audio = gr.Audio(type="filepath", label="Original .wav file", sources=['upload'],format="wav")
with gr.Column(scale=1):
dubbed_audio = gr.Audio(type="filepath", label="Dubbed .wav file", sources=['upload'],format="wav")
# Second Row: 2/3 Email Input 1/3 Company Name Input
with gr.Row(elem_classes="input-section"):
with gr.Column(scale=2):
_email = gr.Textbox(label="Email")
with gr.Column(scale=1):
_company_name = gr.Textbox(label="Company Name")
# Third Row: Tolerance Percentage
with gr.Row(elem_classes="input-section"):
_tolerance = gr.Slider(0, 100, value=5, label="Tolerance Percentage",
info="Set the tolerance for audio comparison.")
# Fourth Row: Processing Status
with gr.Row(elem_classes="output-section"):
output = gr.Text(label="Processing Status")
with gr.Row():
submit_btn = gr.Button("Process Audio", variant="primary")
with gr.Row():
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")
with gr.Tab("Content Moderation"):
# First Row: Left Department Notes (Textbox), Right Video input
with gr.Row(elem_classes="input-section"):
with gr.Column(scale=1):
cm_notes = gr.Textbox(label="Department Notes", lines=6, placeholder="Enter notes for the moderation team...")
with gr.Column(scale=1):
cm_video_in = gr.Video(label="Video Input", sources=["upload"], interactive=True)
# Second Row: Email and Company Name (2/3 and 1/3 columns)
with gr.Row(elem_classes="input-section"):
with gr.Column(scale=2):
cm_email = gr.Textbox(label="Email")
with gr.Column(scale=1):
cm_company_name = gr.Textbox(label="Company Name")
# Third Row: Single Video Output
with gr.Row(elem_classes="output-section"):
cm_video_out = gr.Textbox(label="Output")
# Final Row: Process button
with gr.Row():
cm_process_btn = gr.Button("Process", variant="primary")
# Set up the processing function
submit_btn.click(
fn=process_audio,
inputs=[original_audio, dubbed_audio, _email, _company_name, _tolerance],
outputs=output
)
# Wire Content Moderation processing
cm_process_btn.click(
fn=process_video,
inputs=[cm_video_in, cm_notes, cm_email, cm_company_name],
outputs=cm_video_out
)
if __name__ == "__main__":
# To run this file locally, you'll need to install gradio and requests:
# pip install gradio requests
demo.launch()
|