Commit ·
afa947b
1
Parent(s): 3bede42
Add client-side watchdog so a crashed generation can't wedge Generate
Browse filesThe button-reset step (_btn_ready) only fires via Gradio's .then()
chain, which requires the preceding step's SSE stream to report SOME
terminal status. A dropped connection (container crash-restart, HTTP/2
protocol error — both seen in this app's logs) delivers no status at
all, so the chain stalls forever and Generate stays disabled with no
way to retry.
Arm a client-side timeout (10 min, well past any normal generation)
right after the button disables, that force-resets it if still stuck.
Token-gated per click so a stale watchdog can't clobber a newer,
still-in-flight generation.
app.py
CHANGED
|
@@ -2645,6 +2645,31 @@ _GLOBAL_JS = """
|
|
| 2645 |
}
|
| 2646 |
};
|
| 2647 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2648 |
// ── ZeroGPU quota attribution ──
|
| 2649 |
// HF Spaces run inside an iframe on huggingface.co. Gradio's own JS client
|
| 2650 |
// gets ZeroGPU auth headers (x-zerogpu-token, x-zerogpu-uuid) by sending a
|
|
@@ -3073,7 +3098,7 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 3073 |
taro_cf_dur = gr.Slider(label="Crossfade Duration (s)", minimum=0, maximum=4, value=2, step=0.1, elem_id="taro_cf_dur")
|
| 3074 |
taro_cf_db = gr.Textbox(label="Crossfade Boost (dB)", value="3", elem_id="taro_cf_db")
|
| 3075 |
taro_samples = gr.Slider(label="Generations", minimum=1, maximum=MAX_SLOTS, value=1, step=1)
|
| 3076 |
-
taro_btn = gr.Button("Generate", variant="primary")
|
| 3077 |
|
| 3078 |
with gr.Column(scale=1):
|
| 3079 |
(_, taro_slot_vids,
|
|
@@ -3119,6 +3144,11 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 3119 |
fn=_btn_generating,
|
| 3120 |
inputs=None,
|
| 3121 |
outputs=[taro_btn],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3122 |
).then(
|
| 3123 |
fn=_run_taro,
|
| 3124 |
inputs=[taro_video, taro_seed, taro_cfg, taro_steps, taro_mode,
|
|
@@ -3159,7 +3189,7 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 3159 |
mma_cf_dur = gr.Slider(label="Crossfade Duration (s)", minimum=0, maximum=4, value=2, step=0.1, elem_id="mma_cf_dur")
|
| 3160 |
mma_cf_db = gr.Textbox(label="Crossfade Boost (dB)", value="3", elem_id="mma_cf_db")
|
| 3161 |
mma_samples = gr.Slider(label="Generations", minimum=1, maximum=MAX_SLOTS, value=1, step=1)
|
| 3162 |
-
mma_btn = gr.Button("Generate", variant="primary")
|
| 3163 |
|
| 3164 |
with gr.Column(scale=1):
|
| 3165 |
(_, mma_slot_vids,
|
|
@@ -3185,6 +3215,11 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 3185 |
fn=_btn_generating,
|
| 3186 |
inputs=None,
|
| 3187 |
outputs=[mma_btn],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3188 |
).then(
|
| 3189 |
fn=_run_mmaudio,
|
| 3190 |
inputs=[mma_video, mma_prompt, mma_neg, mma_seed,
|
|
@@ -3224,7 +3259,7 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 3224 |
hf_cf_dur = gr.Slider(label="Crossfade Duration (s)", minimum=0, maximum=4, value=2, step=0.1, elem_id="hf_cf_dur")
|
| 3225 |
hf_cf_db = gr.Textbox(label="Crossfade Boost (dB)", value="3", elem_id="hf_cf_db")
|
| 3226 |
hf_samples = gr.Slider(label="Generations", minimum=1, maximum=MAX_SLOTS, value=1, step=1)
|
| 3227 |
-
hf_btn = gr.Button("Generate", variant="primary")
|
| 3228 |
|
| 3229 |
with gr.Column(scale=1):
|
| 3230 |
(_, hf_slot_vids,
|
|
@@ -3250,6 +3285,11 @@ with gr.Blocks(title="Generate Audio for Video", css=_SLOT_CSS, js=_GLOBAL_JS) a
|
|
| 3250 |
fn=_btn_generating,
|
| 3251 |
inputs=None,
|
| 3252 |
outputs=[hf_btn],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3253 |
).then(
|
| 3254 |
fn=_run_hunyuan,
|
| 3255 |
inputs=[hf_video, hf_prompt, hf_neg, hf_seed,
|
|
|
|
| 2645 |
}
|
| 2646 |
};
|
| 2647 |
|
| 2648 |
+
// Force-reset a stuck "Generating…" button after a generous timeout.
|
| 2649 |
+
// The normal reset path is a .then() step chained after the (possibly
|
| 2650 |
+
// crash-prone) generate call — Gradio only fires .then() steps when the
|
| 2651 |
+
// preceding step's SSE stream reports SOME terminal status. A dropped
|
| 2652 |
+
// connection (container restart, HTTP/2 protocol error — both observed in
|
| 2653 |
+
// this app) never delivers one, so the chain stalls forever and the button
|
| 2654 |
+
// never re-enables. This watchdog is a client-side safety net that doesn't
|
| 2655 |
+
// depend on the chain completing at all. Token-gated so a stale watchdog
|
| 2656 |
+
// from an earlier click can't clobber a newer, still-in-flight generation.
|
| 2657 |
+
window._armGenerateWatchdog = function(btnId, timeoutMs) {
|
| 2658 |
+
const root = document.getElementById(btnId);
|
| 2659 |
+
if (!root) return;
|
| 2660 |
+
const btn = root.tagName === 'BUTTON' ? root : root.querySelector('button');
|
| 2661 |
+
if (!btn) return;
|
| 2662 |
+
btn._genToken = (btn._genToken || 0) + 1;
|
| 2663 |
+
const token = btn._genToken;
|
| 2664 |
+
setTimeout(function() {
|
| 2665 |
+
if (btn._genToken === token && btn.disabled) {
|
| 2666 |
+
btn.disabled = false;
|
| 2667 |
+
btn.textContent = 'Generate';
|
| 2668 |
+
console.log('[watchdog] ' + btnId + ' stuck disabled past ' + timeoutMs + 'ms — force re-enabled');
|
| 2669 |
+
}
|
| 2670 |
+
}, timeoutMs);
|
| 2671 |
+
};
|
| 2672 |
+
|
| 2673 |
// ── ZeroGPU quota attribution ──
|
| 2674 |
// HF Spaces run inside an iframe on huggingface.co. Gradio's own JS client
|
| 2675 |
// gets ZeroGPU auth headers (x-zerogpu-token, x-zerogpu-uuid) by sending a
|
|
|
|
| 3098 |
taro_cf_dur = gr.Slider(label="Crossfade Duration (s)", minimum=0, maximum=4, value=2, step=0.1, elem_id="taro_cf_dur")
|
| 3099 |
taro_cf_db = gr.Textbox(label="Crossfade Boost (dB)", value="3", elem_id="taro_cf_db")
|
| 3100 |
taro_samples = gr.Slider(label="Generations", minimum=1, maximum=MAX_SLOTS, value=1, step=1)
|
| 3101 |
+
taro_btn = gr.Button("Generate", variant="primary", elem_id="taro_btn")
|
| 3102 |
|
| 3103 |
with gr.Column(scale=1):
|
| 3104 |
(_, taro_slot_vids,
|
|
|
|
| 3144 |
fn=_btn_generating,
|
| 3145 |
inputs=None,
|
| 3146 |
outputs=[taro_btn],
|
| 3147 |
+
).then(
|
| 3148 |
+
fn=None,
|
| 3149 |
+
inputs=None,
|
| 3150 |
+
outputs=[],
|
| 3151 |
+
js="() => window._armGenerateWatchdog('taro_btn', 600000)",
|
| 3152 |
).then(
|
| 3153 |
fn=_run_taro,
|
| 3154 |
inputs=[taro_video, taro_seed, taro_cfg, taro_steps, taro_mode,
|
|
|
|
| 3189 |
mma_cf_dur = gr.Slider(label="Crossfade Duration (s)", minimum=0, maximum=4, value=2, step=0.1, elem_id="mma_cf_dur")
|
| 3190 |
mma_cf_db = gr.Textbox(label="Crossfade Boost (dB)", value="3", elem_id="mma_cf_db")
|
| 3191 |
mma_samples = gr.Slider(label="Generations", minimum=1, maximum=MAX_SLOTS, value=1, step=1)
|
| 3192 |
+
mma_btn = gr.Button("Generate", variant="primary", elem_id="mma_btn")
|
| 3193 |
|
| 3194 |
with gr.Column(scale=1):
|
| 3195 |
(_, mma_slot_vids,
|
|
|
|
| 3215 |
fn=_btn_generating,
|
| 3216 |
inputs=None,
|
| 3217 |
outputs=[mma_btn],
|
| 3218 |
+
).then(
|
| 3219 |
+
fn=None,
|
| 3220 |
+
inputs=None,
|
| 3221 |
+
outputs=[],
|
| 3222 |
+
js="() => window._armGenerateWatchdog('mma_btn', 600000)",
|
| 3223 |
).then(
|
| 3224 |
fn=_run_mmaudio,
|
| 3225 |
inputs=[mma_video, mma_prompt, mma_neg, mma_seed,
|
|
|
|
| 3259 |
hf_cf_dur = gr.Slider(label="Crossfade Duration (s)", minimum=0, maximum=4, value=2, step=0.1, elem_id="hf_cf_dur")
|
| 3260 |
hf_cf_db = gr.Textbox(label="Crossfade Boost (dB)", value="3", elem_id="hf_cf_db")
|
| 3261 |
hf_samples = gr.Slider(label="Generations", minimum=1, maximum=MAX_SLOTS, value=1, step=1)
|
| 3262 |
+
hf_btn = gr.Button("Generate", variant="primary", elem_id="hf_btn")
|
| 3263 |
|
| 3264 |
with gr.Column(scale=1):
|
| 3265 |
(_, hf_slot_vids,
|
|
|
|
| 3285 |
fn=_btn_generating,
|
| 3286 |
inputs=None,
|
| 3287 |
outputs=[hf_btn],
|
| 3288 |
+
).then(
|
| 3289 |
+
fn=None,
|
| 3290 |
+
inputs=None,
|
| 3291 |
+
outputs=[],
|
| 3292 |
+
js="() => window._armGenerateWatchdog('hf_btn', 600000)",
|
| 3293 |
).then(
|
| 3294 |
fn=_run_hunyuan,
|
| 3295 |
inputs=[hf_video, hf_prompt, hf_neg, hf_seed,
|