BoxOfColors commited on
Commit
bdb2cf1
·
1 Parent(s): afa947b

Fix infinite retry loop in video upload transcode

Browse files

Live logs (pulled via `hf spaces logs`) showed 39 repeated failures
alternating between "SDR_Render__h264.mp4" and "SDR_Render__gr_conv.mp4"
"No such file or directory" for a single upload — an infinite loop
between _transcode_for_browser and Gradio's own internal conversion,
each re-triggering on the other's (since-cleaned-up) output via the
cross-tab _sync propagation.

_transcode_for_browser now short-circuits to a no-op gr.update() when
its input is already one of these generated filenames, or no longer
exists on disk — breaking the cycle regardless of the exact re-trigger
mechanism, since there's no longer a path for a re-triggered handler to
chase.

Files changed (1) hide show
  1. app.py +18 -1
app.py CHANGED
@@ -338,7 +338,7 @@ def strip_audio_from_video(video_path: str, output_path: str) -> None:
338
  pix_fmt="yuv420p", an=None,
339
  ).run(overwrite_output=True, quiet=True)
340
 
341
- def _transcode_for_browser(video_path: str) -> str:
342
  """Re-encode uploaded video to H.264/AAC MP4 so the browser preview widget can play it.
343
 
344
  Returns a NEW path in a fresh /tmp/gradio/ subdirectory. Gradio probes the
@@ -346,9 +346,26 @@ def _transcode_for_browser(video_path: str) -> str:
346
  slow fallback converter. The in-place overwrite approach loses the race
347
  because Gradio probes the original path at upload time before this callback runs.
348
  Only called on upload — not during generation.
 
 
 
 
 
 
 
 
 
 
349
  """
350
  if video_path is None:
351
  return video_path
 
 
 
 
 
 
 
352
  try:
353
  probe = ffmpeg.probe(video_path)
354
  has_audio = any(s["codec_type"] == "audio" for s in probe.get("streams", []))
 
338
  pix_fmt="yuv420p", an=None,
339
  ).run(overwrite_output=True, quiet=True)
340
 
341
+ def _transcode_for_browser(video_path: str):
342
  """Re-encode uploaded video to H.264/AAC MP4 so the browser preview widget can play it.
343
 
344
  Returns a NEW path in a fresh /tmp/gradio/ subdirectory. Gradio probes the
 
346
  slow fallback converter. The in-place overwrite approach loses the race
347
  because Gradio probes the original path at upload time before this callback runs.
348
  Only called on upload — not during generation.
349
+
350
+ Cross-tab sync (_sync) mirrors whatever this returns onto the other two
351
+ tabs' Video components, and each of THEIR .upload() handlers can re-fire
352
+ on that reassignment — without the guards below, that reprocesses an
353
+ already-transcoded "_h264.mp4"/"_gr_conv.mp4" file, and if it has since
354
+ been cleaned up this turns into an infinite failed-retry loop (observed
355
+ in prod: 39 repeated "No such file" failures alternating between the two
356
+ names for one upload). Both guards return gr.update() (no-op, doesn't
357
+ touch the component's value) instead of re-assigning a path, so there's
358
+ nothing for a re-triggered handler to chase.
359
  """
360
  if video_path is None:
361
  return video_path
362
+ base = os.path.basename(video_path)
363
+ if base.endswith("_h264.mp4") or base.endswith("_gr_conv.mp4"):
364
+ print(f"[transcode_for_browser] already processed ({base}), skipping")
365
+ return gr.update()
366
+ if not os.path.exists(video_path):
367
+ print(f"[transcode_for_browser] input vanished before processing: {video_path!r}")
368
+ return gr.update()
369
  try:
370
  probe = ffmpeg.probe(video_path)
371
  has_audio = any(s["codec_type"] == "audio" for s in probe.get("streams", []))