Commit Β·
8ced419
1
Parent(s): 2496cac
Fix regen: CSS-hidden textboxes + remove _rtrig from outputs to fix Too many args
Browse files- Replace visible=False textboxes with CSS-hidden (elem_classes=['wf-hidden-input'])
so Gradio 5 SSR renders them to the DOM (visible=False omits them entirely,
making getElementById return null and fireRegen silently fail)
- Remove regen trigger from regen handler outputs β having a .change() component
as its own output causes SSR argument count mismatch
- Drop 4th gr.update() from all regen yield statements to match 3-output list
- Add .wf-hidden-input CSS class: position absolute off-screen, opacity 0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
app.py
CHANGED
|
@@ -1409,19 +1409,24 @@ def _make_output_slots(tab_prefix: str) -> tuple:
|
|
| 1409 |
waveforms.append(gr.HTML(
|
| 1410 |
value="<p style='color:#888;font-size:12px'>Generate audio to see waveform.</p>",
|
| 1411 |
))
|
| 1412 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1413 |
regen_triggers.append(gr.Textbox(
|
| 1414 |
value="",
|
| 1415 |
-
visible=False,
|
| 1416 |
elem_id=f"regen_trigger_{slot_id}",
|
| 1417 |
-
|
|
|
|
|
|
|
| 1418 |
))
|
| 1419 |
-
#
|
| 1420 |
-
# gr.Textbox correctly in endpoint outputs but not gr.State.
|
| 1421 |
seg_states.append(gr.Textbox(
|
| 1422 |
value="",
|
| 1423 |
-
|
| 1424 |
-
label=
|
|
|
|
| 1425 |
))
|
| 1426 |
grps.append(g)
|
| 1427 |
return grps, vids, waveforms, regen_triggers, seg_states
|
|
@@ -1487,6 +1492,18 @@ _SLOT_CSS = """
|
|
| 1487 |
max-height: 60vh !important;
|
| 1488 |
object-fit: contain;
|
| 1489 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1490 |
"""
|
| 1491 |
|
| 1492 |
_GLOBAL_JS = """
|
|
@@ -1529,35 +1546,27 @@ _GLOBAL_JS = """
|
|
| 1529 |
}
|
| 1530 |
|
| 1531 |
function fireRegen(slot_id, idx) {
|
| 1532 |
-
const
|
| 1533 |
-
|
| 1534 |
-
if (!el) { console.warn('[fireRegen] element not found:', hidden_id); return; }
|
| 1535 |
const input = el.querySelector('input, textarea');
|
| 1536 |
-
if (!input) { console.warn('[fireRegen] no input inside'
|
| 1537 |
-
|
| 1538 |
-
//
|
| 1539 |
-
//
|
| 1540 |
-
|
| 1541 |
-
|
| 1542 |
-
|
| 1543 |
-
|
| 1544 |
-
|
| 1545 |
-
nativeInputDesc.set.call(input, val);
|
| 1546 |
-
} else {
|
| 1547 |
-
input.value = val;
|
| 1548 |
-
}
|
| 1549 |
input.dispatchEvent(new Event('input', {bubbles: true}));
|
| 1550 |
input.dispatchEvent(new Event('change', {bubbles: true}));
|
| 1551 |
}
|
| 1552 |
-
|
| 1553 |
-
// Clear first so even a repeated click on the same segment fires a change
|
| 1554 |
-
setAndFire('');
|
| 1555 |
setTimeout(function() {
|
| 1556 |
-
|
| 1557 |
console.log('[fireRegen] fired', slot_id + '|' + idx);
|
| 1558 |
}, 50);
|
| 1559 |
|
| 1560 |
-
// Update status label
|
| 1561 |
const lbl = document.getElementById('wf_seglabel_' + slot_id);
|
| 1562 |
if (lbl) lbl.textContent = 'Regenerating Seg ' + (idx + 1) + '...';
|
| 1563 |
}
|
|
@@ -1662,11 +1671,11 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 1662 |
print(f"[regen TARO] trigger_val={trigger_val!r} state_json_len={len(state_json) if state_json else 0} video={video!r}")
|
| 1663 |
if not trigger_val or not state_json:
|
| 1664 |
print(f"[regen TARO] early-exit: trigger_val empty={not trigger_val} state empty={not state_json}")
|
| 1665 |
-
return gr.update(), gr.update(), gr.update(value="")
|
| 1666 |
parts = trigger_val.split("|")
|
| 1667 |
if len(parts) != 2 or parts[0] != _sid:
|
| 1668 |
print(f"[regen TARO] early-exit: parts={parts} expected slot={_sid}")
|
| 1669 |
-
return gr.update(), gr.update(), gr.update(value="")
|
| 1670 |
seg_idx = int(parts[1])
|
| 1671 |
print(f"[regen TARO] slot={_sid} seg_idx={seg_idx} β acquiring lock")
|
| 1672 |
lock = _get_slot_lock(_sid)
|
|
@@ -1677,7 +1686,7 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 1677 |
state["segments"], seg_idx, _sid,
|
| 1678 |
f"regen_trigger_{_sid}"
|
| 1679 |
)
|
| 1680 |
-
yield gr.update(), gr.update(value=pending_html), gr.update(value=state_json)
|
| 1681 |
print(f"[regen TARO] slot={_sid} seg_idx={seg_idx} β calling regen_taro_segment")
|
| 1682 |
try:
|
| 1683 |
vid, aud, new_meta_json, html = regen_taro_segment(
|
|
@@ -1688,14 +1697,13 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 1688 |
except Exception as _e:
|
| 1689 |
print(f"[regen TARO] slot={_sid} seg_idx={seg_idx} β ERROR: {_e}")
|
| 1690 |
raise
|
| 1691 |
-
yield gr.update(value=vid), gr.update(value=html), gr.update(value=new_meta_json)
|
| 1692 |
return _do
|
| 1693 |
_rtrig.change(
|
| 1694 |
fn=_make_taro_regen(_i, _slot_id),
|
| 1695 |
inputs=[_rtrig, taro_video, taro_seed, taro_cfg, taro_steps,
|
| 1696 |
taro_mode, taro_cf_dur, taro_cf_db, taro_slot_states[_i]],
|
| 1697 |
-
outputs=[taro_slot_vids[_i], taro_slot_waves[_i],
|
| 1698 |
-
taro_slot_states[_i], _rtrig],
|
| 1699 |
)
|
| 1700 |
|
| 1701 |
# ---------------------------------------------------------- #
|
|
@@ -1753,11 +1761,11 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 1753 |
print(f"[regen MMA] trigger_val={trigger_val!r} state_json_len={len(state_json) if state_json else 0} video={video!r}")
|
| 1754 |
if not trigger_val or not state_json:
|
| 1755 |
print(f"[regen MMA] early-exit: trigger_val empty={not trigger_val} state empty={not state_json}")
|
| 1756 |
-
return gr.update(), gr.update(), gr.update(value="")
|
| 1757 |
parts = trigger_val.split("|")
|
| 1758 |
if len(parts) != 2 or parts[0] != _sid:
|
| 1759 |
print(f"[regen MMA] early-exit: parts={parts} expected slot={_sid}")
|
| 1760 |
-
return gr.update(), gr.update(), gr.update(value="")
|
| 1761 |
seg_idx = int(parts[1])
|
| 1762 |
print(f"[regen MMA] slot={_sid} seg_idx={seg_idx} β acquiring lock")
|
| 1763 |
lock = _get_slot_lock(_sid)
|
|
@@ -1768,7 +1776,7 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 1768 |
state["segments"], seg_idx, _sid,
|
| 1769 |
f"regen_trigger_{_sid}"
|
| 1770 |
)
|
| 1771 |
-
yield gr.update(), gr.update(value=pending_html), gr.update(value=state_json)
|
| 1772 |
print(f"[regen MMA] slot={_sid} seg_idx={seg_idx} β calling regen_mmaudio_segment")
|
| 1773 |
try:
|
| 1774 |
vid, aud, new_meta_json, html = regen_mmaudio_segment(
|
|
@@ -1779,14 +1787,13 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 1779 |
except Exception as _e:
|
| 1780 |
print(f"[regen MMA] slot={_sid} seg_idx={seg_idx} β ERROR: {_e}")
|
| 1781 |
raise
|
| 1782 |
-
yield gr.update(value=vid), gr.update(value=html), gr.update(value=new_meta_json)
|
| 1783 |
return _do
|
| 1784 |
_rtrig.change(
|
| 1785 |
fn=_make_mma_regen(_i, _slot_id),
|
| 1786 |
inputs=[_rtrig, mma_video, mma_prompt, mma_neg, mma_seed,
|
| 1787 |
mma_cfg, mma_steps, mma_cf_dur, mma_cf_db, mma_slot_states[_i]],
|
| 1788 |
-
outputs=[mma_slot_vids[_i], mma_slot_waves[_i],
|
| 1789 |
-
mma_slot_states[_i], _rtrig],
|
| 1790 |
)
|
| 1791 |
|
| 1792 |
# ---------------------------------------------------------- #
|
|
@@ -1845,11 +1852,11 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 1845 |
print(f"[regen HF] trigger_val={trigger_val!r} state_json_len={len(state_json) if state_json else 0} video={video!r}")
|
| 1846 |
if not trigger_val or not state_json:
|
| 1847 |
print(f"[regen HF] early-exit: trigger_val empty={not trigger_val} state empty={not state_json}")
|
| 1848 |
-
return gr.update(), gr.update(), gr.update(value="")
|
| 1849 |
parts = trigger_val.split("|")
|
| 1850 |
if len(parts) != 2 or parts[0] != _sid:
|
| 1851 |
print(f"[regen HF] early-exit: parts={parts} expected slot={_sid}")
|
| 1852 |
-
return gr.update(), gr.update(), gr.update(value="")
|
| 1853 |
seg_idx = int(parts[1])
|
| 1854 |
print(f"[regen HF] slot={_sid} seg_idx={seg_idx} β acquiring lock")
|
| 1855 |
lock = _get_slot_lock(_sid)
|
|
@@ -1860,7 +1867,7 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 1860 |
state["segments"], seg_idx, _sid,
|
| 1861 |
f"regen_trigger_{_sid}"
|
| 1862 |
)
|
| 1863 |
-
yield gr.update(), gr.update(value=pending_html), gr.update(value=state_json)
|
| 1864 |
print(f"[regen HF] slot={_sid} seg_idx={seg_idx} β calling regen_hunyuan_segment")
|
| 1865 |
try:
|
| 1866 |
vid, aud, new_meta_json, html = regen_hunyuan_segment(
|
|
@@ -1871,14 +1878,13 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 1871 |
except Exception as _e:
|
| 1872 |
print(f"[regen HF] slot={_sid} seg_idx={seg_idx} β ERROR: {_e}")
|
| 1873 |
raise
|
| 1874 |
-
yield gr.update(value=vid), gr.update(value=html), gr.update(value=new_meta_json)
|
| 1875 |
return _do
|
| 1876 |
_rtrig.change(
|
| 1877 |
fn=_make_hf_regen(_i, _slot_id),
|
| 1878 |
inputs=[_rtrig, hf_video, hf_prompt, hf_neg, hf_seed,
|
| 1879 |
hf_guidance, hf_steps, hf_size, hf_cf_dur, hf_cf_db, hf_slot_states[_i]],
|
| 1880 |
-
outputs=[hf_slot_vids[_i], hf_slot_waves[_i],
|
| 1881 |
-
hf_slot_states[_i], _rtrig],
|
| 1882 |
)
|
| 1883 |
|
| 1884 |
# ---- Cross-tab video sync ----
|
|
|
|
| 1409 |
waveforms.append(gr.HTML(
|
| 1410 |
value="<p style='color:#888;font-size:12px'>Generate audio to see waveform.</p>",
|
| 1411 |
))
|
| 1412 |
+
# Regen trigger: a Textbox that is CSS-hidden (NOT visible=False).
|
| 1413 |
+
# Gradio 5 SSR omits visible=False components from the DOM entirely,
|
| 1414 |
+
# so getElementById() returns null and JS can never fire the event.
|
| 1415 |
+
# By keeping it visible=True but hiding with CSS (elem_classes),
|
| 1416 |
+
# the input element exists in the DOM and JS can write to it.
|
| 1417 |
regen_triggers.append(gr.Textbox(
|
| 1418 |
value="",
|
|
|
|
| 1419 |
elem_id=f"regen_trigger_{slot_id}",
|
| 1420 |
+
elem_classes=["wf-hidden-input"],
|
| 1421 |
+
label="",
|
| 1422 |
+
show_label=False,
|
| 1423 |
))
|
| 1424 |
+
# State textbox: also CSS-hidden for same reason.
|
|
|
|
| 1425 |
seg_states.append(gr.Textbox(
|
| 1426 |
value="",
|
| 1427 |
+
elem_classes=["wf-hidden-input"],
|
| 1428 |
+
label="",
|
| 1429 |
+
show_label=False,
|
| 1430 |
))
|
| 1431 |
grps.append(g)
|
| 1432 |
return grps, vids, waveforms, regen_triggers, seg_states
|
|
|
|
| 1492 |
max-height: 60vh !important;
|
| 1493 |
object-fit: contain;
|
| 1494 |
}
|
| 1495 |
+
/* Regen trigger and state textboxes are CSS-hidden, NOT visible=False.
|
| 1496 |
+
Gradio 5 SSR omits visible=False components from the DOM entirely,
|
| 1497 |
+
so JS can never find them. CSS-hidden components are always in the DOM. */
|
| 1498 |
+
.wf-hidden-input {
|
| 1499 |
+
position: absolute !important;
|
| 1500 |
+
left: -9999px !important;
|
| 1501 |
+
width: 1px !important;
|
| 1502 |
+
height: 1px !important;
|
| 1503 |
+
overflow: hidden !important;
|
| 1504 |
+
pointer-events: none !important;
|
| 1505 |
+
opacity: 0 !important;
|
| 1506 |
+
}
|
| 1507 |
"""
|
| 1508 |
|
| 1509 |
_GLOBAL_JS = """
|
|
|
|
| 1546 |
}
|
| 1547 |
|
| 1548 |
function fireRegen(slot_id, idx) {
|
| 1549 |
+
const el = document.getElementById('regen_trigger_' + slot_id);
|
| 1550 |
+
if (!el) { console.warn('[fireRegen] element not found: regen_trigger_' + slot_id); return; }
|
|
|
|
| 1551 |
const input = el.querySelector('input, textarea');
|
| 1552 |
+
if (!input) { console.warn('[fireRegen] no input inside regen_trigger_' + slot_id); return; }
|
| 1553 |
+
|
| 1554 |
+
// Use native setter to bypass React's controlled-input tracking.
|
| 1555 |
+
// Clear to '' first so a repeat click on the same segment still fires .change().
|
| 1556 |
+
const desc = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')
|
| 1557 |
+
|| Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value');
|
| 1558 |
+
function setNative(val) {
|
| 1559 |
+
if (desc && desc.set) desc.set.call(input, val);
|
| 1560 |
+
else input.value = val;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1561 |
input.dispatchEvent(new Event('input', {bubbles: true}));
|
| 1562 |
input.dispatchEvent(new Event('change', {bubbles: true}));
|
| 1563 |
}
|
| 1564 |
+
setNative('');
|
|
|
|
|
|
|
| 1565 |
setTimeout(function() {
|
| 1566 |
+
setNative(slot_id + '|' + idx);
|
| 1567 |
console.log('[fireRegen] fired', slot_id + '|' + idx);
|
| 1568 |
}, 50);
|
| 1569 |
|
|
|
|
| 1570 |
const lbl = document.getElementById('wf_seglabel_' + slot_id);
|
| 1571 |
if (lbl) lbl.textContent = 'Regenerating Seg ' + (idx + 1) + '...';
|
| 1572 |
}
|
|
|
|
| 1671 |
print(f"[regen TARO] trigger_val={trigger_val!r} state_json_len={len(state_json) if state_json else 0} video={video!r}")
|
| 1672 |
if not trigger_val or not state_json:
|
| 1673 |
print(f"[regen TARO] early-exit: trigger_val empty={not trigger_val} state empty={not state_json}")
|
| 1674 |
+
return gr.update(), gr.update(), gr.update(value="")
|
| 1675 |
parts = trigger_val.split("|")
|
| 1676 |
if len(parts) != 2 or parts[0] != _sid:
|
| 1677 |
print(f"[regen TARO] early-exit: parts={parts} expected slot={_sid}")
|
| 1678 |
+
return gr.update(), gr.update(), gr.update(value="")
|
| 1679 |
seg_idx = int(parts[1])
|
| 1680 |
print(f"[regen TARO] slot={_sid} seg_idx={seg_idx} β acquiring lock")
|
| 1681 |
lock = _get_slot_lock(_sid)
|
|
|
|
| 1686 |
state["segments"], seg_idx, _sid,
|
| 1687 |
f"regen_trigger_{_sid}"
|
| 1688 |
)
|
| 1689 |
+
yield gr.update(), gr.update(value=pending_html), gr.update(value=state_json)
|
| 1690 |
print(f"[regen TARO] slot={_sid} seg_idx={seg_idx} β calling regen_taro_segment")
|
| 1691 |
try:
|
| 1692 |
vid, aud, new_meta_json, html = regen_taro_segment(
|
|
|
|
| 1697 |
except Exception as _e:
|
| 1698 |
print(f"[regen TARO] slot={_sid} seg_idx={seg_idx} β ERROR: {_e}")
|
| 1699 |
raise
|
| 1700 |
+
yield gr.update(value=vid), gr.update(value=html), gr.update(value=new_meta_json)
|
| 1701 |
return _do
|
| 1702 |
_rtrig.change(
|
| 1703 |
fn=_make_taro_regen(_i, _slot_id),
|
| 1704 |
inputs=[_rtrig, taro_video, taro_seed, taro_cfg, taro_steps,
|
| 1705 |
taro_mode, taro_cf_dur, taro_cf_db, taro_slot_states[_i]],
|
| 1706 |
+
outputs=[taro_slot_vids[_i], taro_slot_waves[_i], taro_slot_states[_i]],
|
|
|
|
| 1707 |
)
|
| 1708 |
|
| 1709 |
# ---------------------------------------------------------- #
|
|
|
|
| 1761 |
print(f"[regen MMA] trigger_val={trigger_val!r} state_json_len={len(state_json) if state_json else 0} video={video!r}")
|
| 1762 |
if not trigger_val or not state_json:
|
| 1763 |
print(f"[regen MMA] early-exit: trigger_val empty={not trigger_val} state empty={not state_json}")
|
| 1764 |
+
return gr.update(), gr.update(), gr.update(value="")
|
| 1765 |
parts = trigger_val.split("|")
|
| 1766 |
if len(parts) != 2 or parts[0] != _sid:
|
| 1767 |
print(f"[regen MMA] early-exit: parts={parts} expected slot={_sid}")
|
| 1768 |
+
return gr.update(), gr.update(), gr.update(value="")
|
| 1769 |
seg_idx = int(parts[1])
|
| 1770 |
print(f"[regen MMA] slot={_sid} seg_idx={seg_idx} β acquiring lock")
|
| 1771 |
lock = _get_slot_lock(_sid)
|
|
|
|
| 1776 |
state["segments"], seg_idx, _sid,
|
| 1777 |
f"regen_trigger_{_sid}"
|
| 1778 |
)
|
| 1779 |
+
yield gr.update(), gr.update(value=pending_html), gr.update(value=state_json)
|
| 1780 |
print(f"[regen MMA] slot={_sid} seg_idx={seg_idx} β calling regen_mmaudio_segment")
|
| 1781 |
try:
|
| 1782 |
vid, aud, new_meta_json, html = regen_mmaudio_segment(
|
|
|
|
| 1787 |
except Exception as _e:
|
| 1788 |
print(f"[regen MMA] slot={_sid} seg_idx={seg_idx} β ERROR: {_e}")
|
| 1789 |
raise
|
| 1790 |
+
yield gr.update(value=vid), gr.update(value=html), gr.update(value=new_meta_json)
|
| 1791 |
return _do
|
| 1792 |
_rtrig.change(
|
| 1793 |
fn=_make_mma_regen(_i, _slot_id),
|
| 1794 |
inputs=[_rtrig, mma_video, mma_prompt, mma_neg, mma_seed,
|
| 1795 |
mma_cfg, mma_steps, mma_cf_dur, mma_cf_db, mma_slot_states[_i]],
|
| 1796 |
+
outputs=[mma_slot_vids[_i], mma_slot_waves[_i], mma_slot_states[_i]],
|
|
|
|
| 1797 |
)
|
| 1798 |
|
| 1799 |
# ---------------------------------------------------------- #
|
|
|
|
| 1852 |
print(f"[regen HF] trigger_val={trigger_val!r} state_json_len={len(state_json) if state_json else 0} video={video!r}")
|
| 1853 |
if not trigger_val or not state_json:
|
| 1854 |
print(f"[regen HF] early-exit: trigger_val empty={not trigger_val} state empty={not state_json}")
|
| 1855 |
+
return gr.update(), gr.update(), gr.update(value="")
|
| 1856 |
parts = trigger_val.split("|")
|
| 1857 |
if len(parts) != 2 or parts[0] != _sid:
|
| 1858 |
print(f"[regen HF] early-exit: parts={parts} expected slot={_sid}")
|
| 1859 |
+
return gr.update(), gr.update(), gr.update(value="")
|
| 1860 |
seg_idx = int(parts[1])
|
| 1861 |
print(f"[regen HF] slot={_sid} seg_idx={seg_idx} β acquiring lock")
|
| 1862 |
lock = _get_slot_lock(_sid)
|
|
|
|
| 1867 |
state["segments"], seg_idx, _sid,
|
| 1868 |
f"regen_trigger_{_sid}"
|
| 1869 |
)
|
| 1870 |
+
yield gr.update(), gr.update(value=pending_html), gr.update(value=state_json)
|
| 1871 |
print(f"[regen HF] slot={_sid} seg_idx={seg_idx} β calling regen_hunyuan_segment")
|
| 1872 |
try:
|
| 1873 |
vid, aud, new_meta_json, html = regen_hunyuan_segment(
|
|
|
|
| 1878 |
except Exception as _e:
|
| 1879 |
print(f"[regen HF] slot={_sid} seg_idx={seg_idx} β ERROR: {_e}")
|
| 1880 |
raise
|
| 1881 |
+
yield gr.update(value=vid), gr.update(value=html), gr.update(value=new_meta_json)
|
| 1882 |
return _do
|
| 1883 |
_rtrig.change(
|
| 1884 |
fn=_make_hf_regen(_i, _slot_id),
|
| 1885 |
inputs=[_rtrig, hf_video, hf_prompt, hf_neg, hf_seed,
|
| 1886 |
hf_guidance, hf_steps, hf_size, hf_cf_dur, hf_cf_db, hf_slot_states[_i]],
|
| 1887 |
+
outputs=[hf_slot_vids[_i], hf_slot_waves[_i], hf_slot_states[_i]],
|
|
|
|
| 1888 |
)
|
| 1889 |
|
| 1890 |
# ---- Cross-tab video sync ----
|