BoxOfColors commited on
Commit
0219f72
·
1 Parent(s): 1ccc3d0

Fix video update after regen: delay+MutationObserver to survive Svelte re-render

Browse files
Files changed (1) hide show
  1. app.py +44 -33
app.py CHANGED
@@ -1636,7 +1636,21 @@ _GLOBAL_JS = """
1636
 
1637
  // Subscribe to Gradio SSE stream for an event and apply outputs to DOM.
1638
  // For regen handlers, output[0] = video update, output[1] = waveform HTML update.
 
 
 
 
 
 
 
 
 
 
 
 
 
1639
  function _listenAndApply(eventId, slot_id, seg_idx) {
 
1640
  const es = new EventSource('/gradio_api/queue/data?session_hash=' + window.__gradio_session_hash__);
1641
  es.onmessage = function(e) {
1642
  var msg;
@@ -1648,48 +1662,26 @@ _GLOBAL_JS = """
1648
  // data[0] = video update, data[1] = waveform HTML update
1649
  var vidUpdate = out.data[0];
1650
  var waveUpdate = out.data[1];
1651
- console.log('[_listenAndApply] vidUpdate raw:', JSON.stringify(vidUpdate));
1652
- console.log('[_listenAndApply] waveUpdate type:', typeof waveUpdate,
1653
- waveUpdate && typeof waveUpdate === 'object' ? Object.keys(waveUpdate) : waveUpdate && String(waveUpdate).slice(0,80));
1654
 
1655
- // Extract video URL — Gradio 5 gr.Video outputs as {video:{url:"...",...},subtitles:null}
1656
- // or sometimes wrapped in {value:{video:{url:"..."}}}
1657
  var newSrc = null;
1658
  if (vidUpdate) {
1659
- if (vidUpdate.video && vidUpdate.video.url) {
1660
- newSrc = vidUpdate.video.url; // Gradio 5 direct
1661
- } else if (vidUpdate.value && vidUpdate.value.video && vidUpdate.value.video.url) {
1662
- newSrc = vidUpdate.value.video.url; // wrapped
1663
  } else if (vidUpdate.value && vidUpdate.value.url) {
1664
  newSrc = vidUpdate.value.url; // older style
1665
  } else if (typeof vidUpdate.value === 'string') {
1666
- newSrc = vidUpdate.value; // plain string
1667
  } else if (vidUpdate.url) {
1668
- newSrc = vidUpdate.url; // flat
1669
- }
1670
- }
1671
- console.log('[_listenAndApply] resolved video src:', newSrc);
1672
-
1673
- // Apply video: update the <video> src inside the slot_vid element
1674
- if (newSrc) {
1675
- var vidEl = document.getElementById('slot_vid_' + slot_id);
1676
- if (vidEl) {
1677
- var video = vidEl.querySelector('video');
1678
- if (video) {
1679
- // Gradio 5 uses Svelte — set src attribute AND property to force reload
1680
- video.setAttribute('src', newSrc);
1681
- video.src = newSrc;
1682
- video.load();
1683
- console.log('[_listenAndApply] set video src on', 'slot_vid_' + slot_id);
1684
- } else {
1685
- console.warn('[_listenAndApply] no <video> element found inside', 'slot_vid_' + slot_id);
1686
- }
1687
- } else {
1688
- console.warn('[_listenAndApply] no element with id slot_vid_' + slot_id);
1689
  }
1690
  }
 
1691
 
1692
- // Apply waveform HTML: update innerHTML of the slot_wave element
1693
  var waveHtml = null;
1694
  if (waveUpdate) {
1695
  if (typeof waveUpdate === 'string') {
@@ -1701,19 +1693,38 @@ _GLOBAL_JS = """
1701
  if (waveHtml) {
1702
  var waveEl = document.getElementById('slot_wave_' + slot_id);
1703
  if (waveEl) {
1704
- // Find the inner content div (Gradio wraps HTML component)
1705
  var inner = waveEl.querySelector('.prose') || waveEl.querySelector('div');
1706
  if (inner) inner.innerHTML = waveHtml;
1707
  else waveEl.innerHTML = waveHtml;
1708
  }
1709
  }
1710
  }
 
1711
  if (msg.msg === 'process_completed') {
1712
  es.close();
1713
  var lbl = document.getElementById('wf_seglabel_' + slot_id);
1714
  if (lbl) lbl.textContent = msg.output && msg.output.error ?
1715
  'Error regenerating' : 'Done';
1716
  console.log('[fireRegen] completed for', slot_id, 'success:', msg.success);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1717
  }
1718
  }
1719
  if (msg.msg === 'close_stream') { es.close(); }
 
1636
 
1637
  // Subscribe to Gradio SSE stream for an event and apply outputs to DOM.
1638
  // For regen handlers, output[0] = video update, output[1] = waveform HTML update.
1639
+ function _applyVideoSrc(slot_id, newSrc) {
1640
+ var vidEl = document.getElementById('slot_vid_' + slot_id);
1641
+ if (!vidEl) return false;
1642
+ var video = vidEl.querySelector('video');
1643
+ if (!video) return false;
1644
+ if (video.getAttribute('src') === newSrc) return true; // already correct
1645
+ video.setAttribute('src', newSrc);
1646
+ video.src = newSrc;
1647
+ video.load();
1648
+ console.log('[_applyVideoSrc] applied src to', 'slot_vid_' + slot_id, 'src:', newSrc.slice(-40));
1649
+ return true;
1650
+ }
1651
+
1652
  function _listenAndApply(eventId, slot_id, seg_idx) {
1653
+ var _pendingVideoSrc = null; // track the latest resolved video URL
1654
  const es = new EventSource('/gradio_api/queue/data?session_hash=' + window.__gradio_session_hash__);
1655
  es.onmessage = function(e) {
1656
  var msg;
 
1662
  // data[0] = video update, data[1] = waveform HTML update
1663
  var vidUpdate = out.data[0];
1664
  var waveUpdate = out.data[1];
 
 
 
1665
 
1666
+ // Extract video URL — Gradio 5 wraps final output as
1667
+ // {value:{video:{url:"..."},subtitles:null},__type__:"update"}
1668
  var newSrc = null;
1669
  if (vidUpdate) {
1670
+ if (vidUpdate.value && vidUpdate.value.video && vidUpdate.value.video.url) {
1671
+ newSrc = vidUpdate.value.video.url; // final update shape
1672
+ } else if (vidUpdate.video && vidUpdate.video.url) {
1673
+ newSrc = vidUpdate.video.url; // direct shape
1674
  } else if (vidUpdate.value && vidUpdate.value.url) {
1675
  newSrc = vidUpdate.value.url; // older style
1676
  } else if (typeof vidUpdate.value === 'string') {
1677
+ newSrc = vidUpdate.value;
1678
  } else if (vidUpdate.url) {
1679
+ newSrc = vidUpdate.url;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1680
  }
1681
  }
1682
+ if (newSrc) _pendingVideoSrc = newSrc;
1683
 
1684
+ // Apply waveform HTML immediately (waveform is plain HTML, no Svelte reactivity)
1685
  var waveHtml = null;
1686
  if (waveUpdate) {
1687
  if (typeof waveUpdate === 'string') {
 
1693
  if (waveHtml) {
1694
  var waveEl = document.getElementById('slot_wave_' + slot_id);
1695
  if (waveEl) {
 
1696
  var inner = waveEl.querySelector('.prose') || waveEl.querySelector('div');
1697
  if (inner) inner.innerHTML = waveHtml;
1698
  else waveEl.innerHTML = waveHtml;
1699
  }
1700
  }
1701
  }
1702
+
1703
  if (msg.msg === 'process_completed') {
1704
  es.close();
1705
  var lbl = document.getElementById('wf_seglabel_' + slot_id);
1706
  if (lbl) lbl.textContent = msg.output && msg.output.error ?
1707
  'Error regenerating' : 'Done';
1708
  console.log('[fireRegen] completed for', slot_id, 'success:', msg.success);
1709
+
1710
+ // Apply video src AFTER Gradio/Svelte finishes its own re-render cycle.
1711
+ // We try at 50ms, 300ms, and 800ms to catch whenever Svelte settles.
1712
+ var src = _pendingVideoSrc;
1713
+ if (src) {
1714
+ _applyVideoSrc(slot_id, src);
1715
+ setTimeout(function() { _applyVideoSrc(slot_id, src); }, 50);
1716
+ setTimeout(function() { _applyVideoSrc(slot_id, src); }, 300);
1717
+ setTimeout(function() { _applyVideoSrc(slot_id, src); }, 800);
1718
+ // Also install a MutationObserver for 2s to re-apply if Svelte resets src
1719
+ var vidEl = document.getElementById('slot_vid_' + slot_id);
1720
+ if (vidEl) {
1721
+ var obs = new MutationObserver(function() {
1722
+ _applyVideoSrc(slot_id, src);
1723
+ });
1724
+ obs.observe(vidEl, {subtree: true, attributes: true, attributeFilter: ['src'], childList: true});
1725
+ setTimeout(function() { obs.disconnect(); }, 2000);
1726
+ }
1727
+ }
1728
  }
1729
  }
1730
  if (msg.msg === 'close_stream') { es.close(); }