BoxOfColors commited on
Commit
3f11a0e
Β·
1 Parent(s): 7cacbd0

Replace server-side slot-visibility updates with client-side JS

Browse files

Gradio 5.25's client rejects gr.Group visibility outputs with "Too many
arguments provided for the endpoint" (500 on /queue/join), even when
isolated in their own .then() step β€” confirmed via fn_index tracing
that this wasn't session staleness but a real, reproducible bug tied
specifically to Group components as event outputs.

Toggling display on the slot Group's DOM element (by elem_id) via a
fn=None/js= event needs no server round-trip, so there's nothing left
for that validation to reject.

Files changed (1) hide show
  1. app.py +38 -21
app.py CHANGED
@@ -2474,7 +2474,7 @@ def _make_output_slots(tab_prefix: str) -> tuple:
2474
  grps, vids, waveforms = [], [], []
2475
  for i in range(MAX_SLOTS):
2476
  slot_id = f"{tab_prefix}_{i}"
2477
- with gr.Group(visible=(i == 0)) as g:
2478
  vids.append(gr.Video(label=f"Generation {i+1} β€” Video",
2479
  elem_id=f"slot_vid_{slot_id}",
2480
  show_download_button=False))
@@ -2532,11 +2532,6 @@ def _on_video_upload_taro(video_file, num_steps, crossfade_s):
2532
  return gr.update(maximum=max_s, value=min(1, max_s))
2533
 
2534
 
2535
- def _update_slot_visibility(n):
2536
- n = int(n)
2537
- return [gr.update(visible=(i < n)) for i in range(MAX_SLOTS)]
2538
-
2539
-
2540
  def _btn_generating():
2541
  """Disable a Generate button and show a busy label. Chained as the first
2542
  .click() step so it fires immediately, before the (possibly slow) generate
@@ -2586,6 +2581,22 @@ _GLOBAL_JS = """
2586
  if (window._wf_global_listener) return; // already registered
2587
  window._wf_global_listener = true;
2588
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2589
  // ── ZeroGPU quota attribution ──
2590
  // HF Spaces run inside an iframe on huggingface.co. Gradio's own JS client
2591
  // gets ZeroGPU auth headers (x-zerogpu-token, x-zerogpu-uuid) by sending a
@@ -3017,7 +3028,7 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
3017
  taro_btn = gr.Button("Generate", variant="primary")
3018
 
3019
  with gr.Column(scale=1):
3020
- (taro_slot_grps, taro_slot_vids,
3021
  taro_slot_waves) = _make_output_slots("taro")
3022
 
3023
  # Hidden regen plumbing β€” render=False so no DOM element is created,
@@ -3034,9 +3045,10 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
3034
  outputs=[taro_samples],
3035
  )
3036
  taro_samples.change(
3037
- fn=_update_slot_visibility,
3038
  inputs=[taro_samples],
3039
- outputs=taro_slot_grps,
 
3040
  )
3041
 
3042
  def _run_taro(video, seed, cfg, steps, mode, cf_dur, cf_db, n):
@@ -3065,9 +3077,10 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
3065
  taro_cf_dur, taro_cf_db, taro_samples],
3066
  outputs=taro_slot_vids + taro_slot_waves,
3067
  ).then(
3068
- fn=_update_slot_visibility,
3069
  inputs=[taro_samples],
3070
- outputs=taro_slot_grps,
 
3071
  ).then(
3072
  fn=_btn_ready,
3073
  inputs=None,
@@ -3101,7 +3114,7 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
3101
  mma_btn = gr.Button("Generate", variant="primary")
3102
 
3103
  with gr.Column(scale=1):
3104
- (mma_slot_grps, mma_slot_vids,
3105
  mma_slot_waves) = _make_output_slots("mma")
3106
 
3107
  # Hidden regen plumbing β€” render=False so no DOM element is created,
@@ -3110,9 +3123,10 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
3110
  mma_regen_state = gr.Textbox(value="", render=False)
3111
 
3112
  mma_samples.change(
3113
- fn=_update_slot_visibility,
3114
  inputs=[mma_samples],
3115
- outputs=mma_slot_grps,
 
3116
  )
3117
 
3118
  def _run_mmaudio(video, prompt, neg, seed, cfg, steps, cf_dur, cf_db, n):
@@ -3129,9 +3143,10 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
3129
  mma_cfg, mma_steps, mma_cf_dur, mma_cf_db, mma_samples],
3130
  outputs=mma_slot_vids + mma_slot_waves,
3131
  ).then(
3132
- fn=_update_slot_visibility,
3133
  inputs=[mma_samples],
3134
- outputs=mma_slot_grps,
 
3135
  ).then(
3136
  fn=_btn_ready,
3137
  inputs=None,
@@ -3164,7 +3179,7 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
3164
  hf_btn = gr.Button("Generate", variant="primary")
3165
 
3166
  with gr.Column(scale=1):
3167
- (hf_slot_grps, hf_slot_vids,
3168
  hf_slot_waves) = _make_output_slots("hf")
3169
 
3170
  # Hidden regen plumbing β€” render=False so no DOM element is created,
@@ -3173,9 +3188,10 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
3173
  hf_regen_state = gr.Textbox(value="", render=False)
3174
 
3175
  hf_samples.change(
3176
- fn=_update_slot_visibility,
3177
  inputs=[hf_samples],
3178
- outputs=hf_slot_grps,
 
3179
  )
3180
 
3181
  def _run_hunyuan(video, prompt, neg, seed, guidance, steps, size, cf_dur, cf_db, n):
@@ -3192,9 +3208,10 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
3192
  hf_guidance, hf_steps, hf_size, hf_cf_dur, hf_cf_db, hf_samples],
3193
  outputs=hf_slot_vids + hf_slot_waves,
3194
  ).then(
3195
- fn=_update_slot_visibility,
3196
  inputs=[hf_samples],
3197
- outputs=hf_slot_grps,
 
3198
  ).then(
3199
  fn=_btn_ready,
3200
  inputs=None,
 
2474
  grps, vids, waveforms = [], [], []
2475
  for i in range(MAX_SLOTS):
2476
  slot_id = f"{tab_prefix}_{i}"
2477
+ with gr.Group(visible=(i == 0), elem_id=f"slot_grp_{slot_id}") as g:
2478
  vids.append(gr.Video(label=f"Generation {i+1} β€” Video",
2479
  elem_id=f"slot_vid_{slot_id}",
2480
  show_download_button=False))
 
2532
  return gr.update(maximum=max_s, value=min(1, max_s))
2533
 
2534
 
 
 
 
 
 
2535
  def _btn_generating():
2536
  """Disable a Generate button and show a busy label. Chained as the first
2537
  .click() step so it fires immediately, before the (possibly slow) generate
 
2581
  if (window._wf_global_listener) return; // already registered
2582
  window._wf_global_listener = true;
2583
 
2584
+ // Toggle output-slot visibility by directly flipping display on the slot
2585
+ // Group's DOM element. This used to be a server round-trip (a .then() that
2586
+ // returned gr.update(visible=...) for each Group), but Gradio 5.25's client
2587
+ // rejects Group-visibility outputs with "Too many arguments provided for
2588
+ // the endpoint" (500 on /queue/join) even when isolated in their own step β€”
2589
+ // doing it client-side sidesteps that bug entirely, no server call needed.
2590
+ window._updateSlotVisibility = function(prefix, n) {
2591
+ let i = 0;
2592
+ while (true) {
2593
+ const el = document.getElementById('slot_grp_' + prefix + '_' + i);
2594
+ if (!el) break;
2595
+ el.style.display = (i < n) ? '' : 'none';
2596
+ i++;
2597
+ }
2598
+ };
2599
+
2600
  // ── ZeroGPU quota attribution ──
2601
  // HF Spaces run inside an iframe on huggingface.co. Gradio's own JS client
2602
  // gets ZeroGPU auth headers (x-zerogpu-token, x-zerogpu-uuid) by sending a
 
3028
  taro_btn = gr.Button("Generate", variant="primary")
3029
 
3030
  with gr.Column(scale=1):
3031
+ (_, taro_slot_vids,
3032
  taro_slot_waves) = _make_output_slots("taro")
3033
 
3034
  # Hidden regen plumbing β€” render=False so no DOM element is created,
 
3045
  outputs=[taro_samples],
3046
  )
3047
  taro_samples.change(
3048
+ fn=None,
3049
  inputs=[taro_samples],
3050
+ outputs=[],
3051
+ js="(n) => window._updateSlotVisibility('taro', n)",
3052
  )
3053
 
3054
  def _run_taro(video, seed, cfg, steps, mode, cf_dur, cf_db, n):
 
3077
  taro_cf_dur, taro_cf_db, taro_samples],
3078
  outputs=taro_slot_vids + taro_slot_waves,
3079
  ).then(
3080
+ fn=None,
3081
  inputs=[taro_samples],
3082
+ outputs=[],
3083
+ js="(n) => window._updateSlotVisibility('taro', n)",
3084
  ).then(
3085
  fn=_btn_ready,
3086
  inputs=None,
 
3114
  mma_btn = gr.Button("Generate", variant="primary")
3115
 
3116
  with gr.Column(scale=1):
3117
+ (_, mma_slot_vids,
3118
  mma_slot_waves) = _make_output_slots("mma")
3119
 
3120
  # Hidden regen plumbing β€” render=False so no DOM element is created,
 
3123
  mma_regen_state = gr.Textbox(value="", render=False)
3124
 
3125
  mma_samples.change(
3126
+ fn=None,
3127
  inputs=[mma_samples],
3128
+ outputs=[],
3129
+ js="(n) => window._updateSlotVisibility('mma', n)",
3130
  )
3131
 
3132
  def _run_mmaudio(video, prompt, neg, seed, cfg, steps, cf_dur, cf_db, n):
 
3143
  mma_cfg, mma_steps, mma_cf_dur, mma_cf_db, mma_samples],
3144
  outputs=mma_slot_vids + mma_slot_waves,
3145
  ).then(
3146
+ fn=None,
3147
  inputs=[mma_samples],
3148
+ outputs=[],
3149
+ js="(n) => window._updateSlotVisibility('mma', n)",
3150
  ).then(
3151
  fn=_btn_ready,
3152
  inputs=None,
 
3179
  hf_btn = gr.Button("Generate", variant="primary")
3180
 
3181
  with gr.Column(scale=1):
3182
+ (_, hf_slot_vids,
3183
  hf_slot_waves) = _make_output_slots("hf")
3184
 
3185
  # Hidden regen plumbing β€” render=False so no DOM element is created,
 
3188
  hf_regen_state = gr.Textbox(value="", render=False)
3189
 
3190
  hf_samples.change(
3191
+ fn=None,
3192
  inputs=[hf_samples],
3193
+ outputs=[],
3194
+ js="(n) => window._updateSlotVisibility('hf', n)",
3195
  )
3196
 
3197
  def _run_hunyuan(video, prompt, neg, seed, guidance, steps, size, cf_dur, cf_db, n):
 
3208
  hf_guidance, hf_steps, hf_size, hf_cf_dur, hf_cf_db, hf_samples],
3209
  outputs=hf_slot_vids + hf_slot_waves,
3210
  ).then(
3211
+ fn=None,
3212
  inputs=[hf_samples],
3213
+ outputs=[],
3214
+ js="(n) => window._updateSlotVisibility('hf', n)",
3215
  ).then(
3216
  fn=_btn_ready,
3217
  inputs=None,