daKhosa commited on
Commit
cb6f99f
·
1 Parent(s): 1efde5f

Stream WanGP events and cache uploads

Browse files
Files changed (1) hide show
  1. app.py +78 -43
app.py CHANGED
@@ -14,6 +14,7 @@ import shutil
14
  import tempfile
15
  import threading
16
  import json
 
17
  from pathlib import Path
18
 
19
  import gradio as gr
@@ -25,6 +26,7 @@ WAN2GP_ROOT = _PERSISTENT / "Wan2GP"
25
  CKPTS_DIR = WAN2GP_ROOT / "ckpts"
26
  LORAS_DIR = WAN2GP_ROOT / "loras" / "ltx2"
27
  FINETUNES_DIR = WAN2GP_ROOT / "finetunes"
 
28
 
29
  SULPHUR_ASSETS = [
30
  ("SulphurAI/Sulphur-2-base", "sulphur_distil_bf16.safetensors", CKPTS_DIR),
@@ -239,6 +241,19 @@ def _wangp_cli_args():
239
  setup()
240
 
241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  def _ensure_session():
243
  """Create WanGPSession on first call; reuse on all subsequent calls."""
244
  global _session
@@ -290,14 +305,16 @@ class _QueueWriter(io.TextIOBase):
290
 
291
 
292
  @spaces.GPU
293
- def generate_video(image, prompt, resolution, steps, guidance_scale, seconds, seed):
294
- if image is None:
295
  raise gr.Error("Please upload an image.")
296
  if not prompt.strip():
297
  raise gr.Error("Please enter a prompt.")
298
 
299
  frames = int(seconds * 25 // 8) * 8 + 1
300
- image_path = str(Path(image.strip()).resolve())
 
 
301
  out_dir = Path(tempfile.mkdtemp())
302
  out_file = out_dir / "output.mp4"
303
 
@@ -317,56 +334,67 @@ def generate_video(image, prompt, resolution, steps, guidance_scale, seconds, se
317
  "loras_multipliers": ["0.5"],
318
  }
319
 
320
- log_q = queue.Queue()
321
- result_box = [None]
322
- error_box = [None]
323
- real_out = sys.stdout
324
- real_err = sys.stderr
325
-
326
- def _run():
327
- writer = _QueueWriter(log_q, real_out)
328
- sys.stdout = writer
329
- sys.stderr = writer # tqdm and progress bars write to stderr
330
- try:
331
- print("[startup] Loading WanGP session...")
332
- session = _ensure_session()
333
- print("[startup] WanGP session ready. Starting generation...")
334
- result_box[0] = session.run_task(task)
335
- except Exception as exc:
336
- error_box[0] = exc
337
- finally:
338
- sys.stdout = real_out
339
- sys.stderr = real_err
340
- log_q.put(None) # sentinel
341
-
342
- t = threading.Thread(target=_run, daemon=True)
343
- t.start()
344
-
345
  log_lines = []
346
- while True:
347
- try:
348
- line = log_q.get(timeout=180)
349
- except queue.Empty:
350
- yield None, "\n".join(log_lines) + "\n\n[TIMEOUT]"
351
- return
352
- if line is None:
353
- break
354
- stripped = line.strip()
355
  if not stripped:
356
- continue
357
  if log_lines and ("%" in stripped or "it/s" in stripped or "step" in stripped.lower()):
358
  log_lines[-1] = stripped
359
  else:
360
  log_lines.append(stripped)
 
 
 
 
 
 
 
361
  yield None, "\n".join(log_lines[-30:])
362
 
363
- t.join(timeout=10)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
 
365
- if error_box[0]:
366
- yield None, "\n".join(log_lines) + f"\n\n[ERROR] {error_box[0]}"
 
 
 
 
367
  return
368
 
369
- result = result_box[0]
370
  output_file = None
371
 
372
  if result and result.artifacts:
@@ -393,6 +421,7 @@ def generate_video(image, prompt, resolution, steps, guidance_scale, seconds, se
393
 
394
  with gr.Blocks(title="Sulphur — Image to Video") as demo:
395
  gr.Markdown("# Sulphur — Image to Video\nUsing New 'Experimental' Lora v1")
 
396
  with gr.Row():
397
  with gr.Column(scale=1):
398
  image_in = gr.Image(type="filepath", label="Input Image")
@@ -408,9 +437,15 @@ with gr.Blocks(title="Sulphur — Image to Video") as demo:
408
  video_out = gr.Video(label="Output Video")
409
  log_out = gr.Textbox(label="Log", lines=10, interactive=False)
410
 
 
 
 
 
 
 
411
  run_btn.click(
412
  fn=generate_video,
413
- inputs=[image_in, prompt_in, resolution_dd, steps_sl, guidance_sl, seconds_sl, seed_num],
414
  outputs=[video_out, log_out],
415
  )
416
 
 
14
  import tempfile
15
  import threading
16
  import json
17
+ import time
18
  from pathlib import Path
19
 
20
  import gradio as gr
 
26
  CKPTS_DIR = WAN2GP_ROOT / "ckpts"
27
  LORAS_DIR = WAN2GP_ROOT / "loras" / "ltx2"
28
  FINETUNES_DIR = WAN2GP_ROOT / "finetunes"
29
+ UPLOADS_DIR = _PERSISTENT / "sulphur_uploads"
30
 
31
  SULPHUR_ASSETS = [
32
  ("SulphurAI/Sulphur-2-base", "sulphur_distil_bf16.safetensors", CKPTS_DIR),
 
241
  setup()
242
 
243
 
244
+ def cache_uploaded_image(image):
245
+ if not image:
246
+ return None
247
+ src = Path(str(image)).resolve()
248
+ if not src.exists():
249
+ raise gr.Error("Uploaded image is no longer available. Please upload it again.")
250
+ UPLOADS_DIR.mkdir(parents=True, exist_ok=True)
251
+ suffix = src.suffix or ".png"
252
+ dest = UPLOADS_DIR / f"input_{time.time_ns()}{suffix}"
253
+ shutil.copy2(src, dest)
254
+ return str(dest)
255
+
256
+
257
  def _ensure_session():
258
  """Create WanGPSession on first call; reuse on all subsequent calls."""
259
  global _session
 
305
 
306
 
307
  @spaces.GPU
308
+ def generate_video(image_path_cached, prompt, resolution, steps, guidance_scale, seconds, seed):
309
+ if image_path_cached is None:
310
  raise gr.Error("Please upload an image.")
311
  if not prompt.strip():
312
  raise gr.Error("Please enter a prompt.")
313
 
314
  frames = int(seconds * 25 // 8) * 8 + 1
315
+ image_path = str(Path(str(image_path_cached)).resolve())
316
+ if not Path(image_path).exists():
317
+ raise gr.Error("Uploaded image is no longer available. Please upload it again.")
318
  out_dir = Path(tempfile.mkdtemp())
319
  out_file = out_dir / "output.mp4"
320
 
 
334
  "loras_multipliers": ["0.5"],
335
  }
336
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
  log_lines = []
338
+ result = None
339
+
340
+ def _append_log(line):
341
+ stripped = str(line or "").strip()
 
 
 
 
 
342
  if not stripped:
343
+ return
344
  if log_lines and ("%" in stripped or "it/s" in stripped or "step" in stripped.lower()):
345
  log_lines[-1] = stripped
346
  else:
347
  log_lines.append(stripped)
348
+
349
+ _append_log("[startup] Loading WanGP session...")
350
+ yield None, "\n".join(log_lines[-30:])
351
+
352
+ try:
353
+ session = _ensure_session()
354
+ _append_log("[startup] WanGP session ready. Starting generation...")
355
  yield None, "\n".join(log_lines[-30:])
356
 
357
+ job = session.submit_task(task)
358
+ last_event_at = time.time()
359
+ while True:
360
+ event = job.events.get(timeout=0.5)
361
+ if event is None:
362
+ if job.done:
363
+ break
364
+ if time.time() - last_event_at > 30:
365
+ _append_log("[waiting] WanGP is still running...")
366
+ last_event_at = time.time()
367
+ yield None, "\n".join(log_lines[-30:])
368
+ continue
369
+
370
+ last_event_at = time.time()
371
+ if event.kind == "stream":
372
+ data = event.data
373
+ _append_log(getattr(data, "text", data))
374
+ elif event.kind == "progress":
375
+ progress = event.data
376
+ current = getattr(progress, "current_step", None)
377
+ total = getattr(progress, "total_steps", None)
378
+ phase = getattr(progress, "phase", "progress")
379
+ status = getattr(progress, "status", "")
380
+ if current is not None and total is not None:
381
+ _append_log(f"[{phase}] step {current}/{total} {status}")
382
+ else:
383
+ _append_log(f"[{phase}] {status}")
384
+ elif event.kind == "error":
385
+ _append_log(f"[ERROR] {event.data}")
386
+ elif event.kind == "completed":
387
+ result = event.data
388
+ break
389
 
390
+ yield None, "\n".join(log_lines[-30:])
391
+
392
+ if result is None:
393
+ result = job.result(timeout=1)
394
+ except Exception as exc:
395
+ yield None, "\n".join(log_lines) + f"\n\n[ERROR] {exc}"
396
  return
397
 
 
398
  output_file = None
399
 
400
  if result and result.artifacts:
 
421
 
422
  with gr.Blocks(title="Sulphur — Image to Video") as demo:
423
  gr.Markdown("# Sulphur — Image to Video\nUsing New 'Experimental' Lora v1")
424
+ image_state = gr.State(None)
425
  with gr.Row():
426
  with gr.Column(scale=1):
427
  image_in = gr.Image(type="filepath", label="Input Image")
 
437
  video_out = gr.Video(label="Output Video")
438
  log_out = gr.Textbox(label="Log", lines=10, interactive=False)
439
 
440
+ image_in.change(
441
+ fn=cache_uploaded_image,
442
+ inputs=[image_in],
443
+ outputs=[image_state],
444
+ )
445
+
446
  run_btn.click(
447
  fn=generate_video,
448
+ inputs=[image_state, prompt_in, resolution_dd, steps_sl, guidance_sl, seconds_sl, seed_num],
449
  outputs=[video_out, log_out],
450
  )
451