Spaces:
Running on Zero
Running on Zero
Commit ·
42b7d7f
1
Parent(s): 4aa0f7d
app.py
CHANGED
|
@@ -1290,47 +1290,79 @@ def _build_waveform_html(audio_path: str, segments: list, slot_id: str,
|
|
| 1290 |
plugins = [regionsPlugin];
|
| 1291 |
}}
|
| 1292 |
|
| 1293 |
-
|
| 1294 |
-
|
| 1295 |
-
|
| 1296 |
-
|
| 1297 |
-
|
| 1298 |
-
|
| 1299 |
-
|
| 1300 |
-
|
| 1301 |
-
|
| 1302 |
-
|
| 1303 |
-
|
| 1304 |
-
|
| 1305 |
-
|
| 1306 |
-
|
| 1307 |
-
|
| 1308 |
-
|
|
|
|
| 1309 |
ws.setVolume(0);
|
| 1310 |
-
|
| 1311 |
-
|
| 1312 |
-
|
| 1313 |
-
|
| 1314 |
-
|
| 1315 |
-
|
| 1316 |
-
|
| 1317 |
-
|
| 1318 |
-
|
| 1319 |
-
|
| 1320 |
-
|
| 1321 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1322 |
}});
|
| 1323 |
-
|
| 1324 |
-
|
| 1325 |
-
|
| 1326 |
-
|
| 1327 |
-
|
| 1328 |
-
}}
|
| 1329 |
-
|
| 1330 |
-
|
| 1331 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1332 |
|
| 1333 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1334 |
}}
|
| 1335 |
|
| 1336 |
// Load WaveSurfer + Regions scripts once (idempotent)
|
|
@@ -1442,7 +1474,15 @@ def _update_slot_visibility(n):
|
|
| 1442 |
# GRADIO UI #
|
| 1443 |
# ================================================================== #
|
| 1444 |
|
| 1445 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1446 |
gr.Markdown(
|
| 1447 |
"# Generate Audio for Video\n"
|
| 1448 |
"Choose a model and upload a video to generate synchronized audio.\n\n"
|
|
|
|
| 1290 |
plugins = [regionsPlugin];
|
| 1291 |
}}
|
| 1292 |
|
| 1293 |
+
// Wait until the container has a non-zero width before creating WaveSurfer,
|
| 1294 |
+
// otherwise it renders blank (container is 0px when Gradio first injects HTML).
|
| 1295 |
+
const wfContainer = document.getElementById('wf_{slot_id}');
|
| 1296 |
+
function createWS() {{
|
| 1297 |
+
const ws = WaveSurfer.create({{
|
| 1298 |
+
container: '#wf_{slot_id}',
|
| 1299 |
+
waveColor: '#4a9eff',
|
| 1300 |
+
progressColor: '#1a5fa8',
|
| 1301 |
+
height: 80,
|
| 1302 |
+
barWidth: 2,
|
| 1303 |
+
barGap: 1,
|
| 1304 |
+
barRadius: 2,
|
| 1305 |
+
url: '{data_uri}',
|
| 1306 |
+
plugins: plugins,
|
| 1307 |
+
}});
|
| 1308 |
+
|
| 1309 |
+
// Silence immediately — waveform is display-only, video plays the audio
|
| 1310 |
ws.setVolume(0);
|
| 1311 |
+
ws.on('ready', function() {{
|
| 1312 |
+
ws.setVolume(0);
|
| 1313 |
+
// Prevent WaveSurfer from auto-playing (it shouldn't, but be safe)
|
| 1314 |
+
ws.pause();
|
| 1315 |
+
const segments = {segs_json};
|
| 1316 |
+
const colors = {json.dumps(colors)};
|
| 1317 |
+
if (regionsPlugin) {{
|
| 1318 |
+
segments.forEach(function(seg, idx) {{
|
| 1319 |
+
regionsPlugin.addRegion({{
|
| 1320 |
+
id: 'seg_' + idx,
|
| 1321 |
+
start: seg[0],
|
| 1322 |
+
end: seg[1],
|
| 1323 |
+
color: colors[idx % colors.length],
|
| 1324 |
+
drag: false,
|
| 1325 |
+
resize: false,
|
| 1326 |
+
content: 'Seg ' + (idx + 1),
|
| 1327 |
+
}});
|
| 1328 |
}});
|
| 1329 |
+
regionsPlugin.on('region-clicked', function(region, e) {{
|
| 1330 |
+
e.stopPropagation();
|
| 1331 |
+
const idx = parseInt(region.id.replace('seg_', ''));
|
| 1332 |
+
showPopup(idx, e.clientX, e.clientY);
|
| 1333 |
+
}});
|
| 1334 |
+
}}
|
| 1335 |
+
attachVideoSync(ws);
|
| 1336 |
+
}});
|
| 1337 |
+
|
| 1338 |
+
// Also silence if WaveSurfer tries to play for any reason
|
| 1339 |
+
ws.on('play', function() {{ ws.pause(); ws.setVolume(0); }});
|
| 1340 |
+
|
| 1341 |
+
window["_wf_ws_{slot_id}"] = ws;
|
| 1342 |
+
}}
|
| 1343 |
|
| 1344 |
+
// Use ResizeObserver: fire createWS as soon as the container has width
|
| 1345 |
+
if (wfContainer && wfContainer.offsetWidth > 0) {{
|
| 1346 |
+
createWS();
|
| 1347 |
+
}} else if (wfContainer) {{
|
| 1348 |
+
const ro = new ResizeObserver(function(entries) {{
|
| 1349 |
+
for (const entry of entries) {{
|
| 1350 |
+
if (entry.contentRect.width > 0) {{
|
| 1351 |
+
ro.disconnect();
|
| 1352 |
+
createWS();
|
| 1353 |
+
break;
|
| 1354 |
+
}}
|
| 1355 |
+
}}
|
| 1356 |
+
}});
|
| 1357 |
+
ro.observe(wfContainer);
|
| 1358 |
+
// Fallback: also try after a short delay in case ResizeObserver fires late
|
| 1359 |
+
setTimeout(function() {{
|
| 1360 |
+
if (!window["_wf_ws_{slot_id}"]) {{ ro.disconnect(); createWS(); }}
|
| 1361 |
+
}}, 800);
|
| 1362 |
+
}} else {{
|
| 1363 |
+
// Container not in DOM yet — wait for it
|
| 1364 |
+
setTimeout(loadWS, 200);
|
| 1365 |
+
}}
|
| 1366 |
}}
|
| 1367 |
|
| 1368 |
// Load WaveSurfer + Regions scripts once (idempotent)
|
|
|
|
| 1474 |
# GRADIO UI #
|
| 1475 |
# ================================================================== #
|
| 1476 |
|
| 1477 |
+
_SLOT_CSS = """
|
| 1478 |
+
/* Cap generated videos so the waveform below is never pushed out of view */
|
| 1479 |
+
.gradio-video video {
|
| 1480 |
+
max-height: 340px;
|
| 1481 |
+
object-fit: contain;
|
| 1482 |
+
}
|
| 1483 |
+
"""
|
| 1484 |
+
|
| 1485 |
+
with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS) as demo:
|
| 1486 |
gr.Markdown(
|
| 1487 |
"# Generate Audio for Video\n"
|
| 1488 |
"Choose a model and upload a video to generate synchronized audio.\n\n"
|