Hug0endob commited on
Commit
f532fe5
·
verified ·
1 Parent(s): f56a823

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +119 -17
app.py CHANGED
@@ -416,7 +416,8 @@ def create_demo():
416
  with gr.Row():
417
  with gr.Column(scale=1):
418
  preview_image = gr.Image(label="Preview Image", type="pil", elem_classes="preview_media", visible=False)
419
- preview_video = gr.Video(label="Preview Video", elem_classes="preview_media", visible=False)
 
420
  with gr.Column(scale=2):
421
  url_input = gr.Textbox(label="Image / Video URL", placeholder="https://...", lines=1)
422
  with gr.Accordion("Prompt (optional)", open=False):
@@ -426,54 +427,155 @@ def create_demo():
426
  with gr.Row():
427
  submit_btn = gr.Button("Submit")
428
  clear_btn = gr.Button("Clear")
 
429
  output_md = gr.Markdown("")
430
  status_state = gr.State("idle")
431
 
 
 
 
432
  def load_preview(url: str):
433
  if not url:
434
- return gr.update(value=None, visible=False), gr.update(value=None, visible=False)
 
435
  if is_remote(url) and any(url.lower().endswith(ext) for ext in VIDEO_EXTS):
436
- return gr.update(value=None, visible=False), gr.update(value=url, visible=True)
 
437
  try:
438
  if is_remote(url):
439
- r = safe_get(url)
440
- img_bytes = r.content
 
 
 
 
 
 
 
 
 
 
 
 
 
441
  else:
442
  with open(url, "rb") as f:
443
  img_bytes = f.read()
444
  img = Image.open(BytesIO(img_bytes))
445
  if getattr(img, "is_animated", False):
446
  img.seek(0)
447
- return gr.update(value=img.convert("RGB"), visible=True), gr.update(value=None, visible=False)
448
- except Exception:
449
- return gr.update(value=None, visible=False), gr.update(value=None, visible=False)
 
 
450
 
451
- url_input.change(fn=load_preview, inputs=[url_input], outputs=[preview_image, preview_video])
452
 
453
  def clear_all():
454
- return "", None, None, "idle", ""
455
-
456
- clear_btn.click(fn=clear_all, inputs=[], outputs=[url_input, preview_image, preview_video, status_state, output_md])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
457
 
458
  def worker(url: str, prompt: str, key: str, progress=gr.Progress()):
459
  try:
460
  if not url:
461
  return ("error", "**Error:** No URL provided.")
462
  progress(0.01, desc="Starting processing...")
463
- fut = run_blocking_in_thread(process_media, url, prompt or "", key or "", progress)
464
- res = fut.result()
465
- if isinstance(res, str) and res.lower().startswith("error"):
466
- return ("error", f"**Error:** {res}")
467
- return ("done", res if isinstance(res, str) else str(res))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
468
  except Exception as e:
469
  return ("error", f"Unexpected worker error: {e}")
470
 
 
471
  submit_btn.click(fn=worker, inputs=[url_input, custom_prompt, api_key], outputs=[status_state, output_md], queue=True)
472
 
473
  def btn_label_from_state(s):
474
  return _btn_label_for_status(s)
475
 
476
  status_state.change(fn=btn_label_from_state, inputs=[status_state], outputs=[submit_btn])
 
 
 
 
477
 
478
  return demo
479
 
 
416
  with gr.Row():
417
  with gr.Column(scale=1):
418
  preview_image = gr.Image(label="Preview Image", type="pil", elem_classes="preview_media", visible=False)
419
+ preview_video = gr.Video(label="Preview Video", elem_classes="preview_media", visible=False, format="mp4")
420
+ preview_status = gr.Textbox(label="Preview status", interactive=False, lines=1, value="", visible=True)
421
  with gr.Column(scale=2):
422
  url_input = gr.Textbox(label="Image / Video URL", placeholder="https://...", lines=1)
423
  with gr.Accordion("Prompt (optional)", open=False):
 
427
  with gr.Row():
428
  submit_btn = gr.Button("Submit")
429
  clear_btn = gr.Button("Clear")
430
+ progress_md = gr.Markdown("Idle")
431
  output_md = gr.Markdown("")
432
  status_state = gr.State("idle")
433
 
434
+ def _set_preview_text(text: str):
435
+ return gr.update(value=text)
436
+
437
  def load_preview(url: str):
438
  if not url:
439
+ return gr.update(value=None, visible=False), gr.update(value=None, visible=False), gr.update(value="")
440
+ # If remote and looks like a video by extension, show video immediately and try async conversion for browser compatibility
441
  if is_remote(url) and any(url.lower().endswith(ext) for ext in VIDEO_EXTS):
442
+ return gr.update(value=None, visible=False), gr.update(value=url, visible=True), gr.update(value="Remote video detected — showing preview if browser-playable; converting if needed...")
443
+ # Try fetching as image first (quick check). Provide verbose status messages.
444
  try:
445
  if is_remote(url):
446
+ head = safe_head(url)
447
+ if head:
448
+ ctype = (head.headers.get("content-type") or "").lower()
449
+ if ctype.startswith("video/"):
450
+ return gr.update(value=None, visible=False), gr.update(value=url, visible=True), gr.update(value=f"Remote video (content-type={ctype}) — showing preview if browser-playable.")
451
+ if ctype.startswith("image/"):
452
+ r = safe_get(url, timeout=10)
453
+ img_bytes = r.content
454
+ else:
455
+ # try GET anyway
456
+ r = safe_get(url, timeout=10)
457
+ img_bytes = r.content
458
+ else:
459
+ r = safe_get(url, timeout=10)
460
+ img_bytes = r.content
461
  else:
462
  with open(url, "rb") as f:
463
  img_bytes = f.read()
464
  img = Image.open(BytesIO(img_bytes))
465
  if getattr(img, "is_animated", False):
466
  img.seek(0)
467
+ return gr.update(value=img.convert("RGB"), visible=True), gr.update(value=None, visible=False), gr.update(value="Image preview loaded")
468
+ except Exception as e:
469
+ # Fallback: if file might be video but extension missing, show helpful message
470
+ msg = f"Preview load failed: {e}. If this is a video, ensure URL ends with a video extension or upload a playable mp4/webm."
471
+ return gr.update(value=None, visible=False), gr.update(value=None, visible=False), gr.update(value=msg)
472
 
473
+ url_input.change(fn=load_preview, inputs=[url_input], outputs=[preview_image, preview_video, preview_status])
474
 
475
  def clear_all():
476
+ return "", None, None, "idle", "Idle", ""
477
+
478
+ clear_btn.click(fn=clear_all, inputs=[], outputs=[url_input, preview_image, preview_video, status_state, progress_md, output_md])
479
+
480
+ def _convert_video_for_preview(path: str, progress_cb=None) -> str:
481
+ if not FFMPEG_BIN or not os.path.exists(path):
482
+ return path
483
+ # ensure mp4 with h264 for browser playability
484
+ out_fd, out_path = tempfile.mkstemp(suffix=".mp4")
485
+ os.close(out_fd)
486
+ cmd = [
487
+ FFMPEG_BIN, "-nostdin", "-y", "-i", path,
488
+ "-c:v", "libx264", "-preset", "veryfast", "-crf", "28",
489
+ "-c:a", "aac", "-movflags", "+faststart", out_path
490
+ ]
491
+ try:
492
+ subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=60)
493
+ return out_path
494
+ except Exception:
495
+ try: os.remove(out_path)
496
+ except Exception: pass
497
+ return path
498
 
499
  def worker(url: str, prompt: str, key: str, progress=gr.Progress()):
500
  try:
501
  if not url:
502
  return ("error", "**Error:** No URL provided.")
503
  progress(0.01, desc="Starting processing...")
504
+ # Fetch header to decide media type early for verbose feedback
505
+ progress(0.03, desc="Checking URL / content-type...")
506
+ is_img, is_vid = determine_media_type(url, progress=progress)
507
+ progress(0.06, desc=f"Determined media type: image={is_img}, video={is_vid}")
508
+ if is_vid:
509
+ progress(0.08, desc="Fetching video bytes (this may take a while)...")
510
+ raw = fetch_bytes(url, timeout=120, progress=progress)
511
+ tmp = save_bytes_to_temp(raw, suffix=ext_from_src(url) or ".mp4")
512
+ progress(0.15, desc="Saved video to temp file; preparing for preview/analysis...")
513
+ # Convert for preview to ensure browser-playable format (non-destructive)
514
+ try:
515
+ preview_tmp = _convert_video_for_preview(tmp, progress_cb=progress)
516
+ except Exception:
517
+ preview_tmp = tmp
518
+ # Update progress text for preview step (front-end preview will show converted file path)
519
+ progress(0.25, desc="Video ready for preview and analysis. Starting analysis...")
520
+ res = analyze_video_cohesive(get_client(key), tmp, prompt or "", progress=progress)
521
+ progress(0.98, desc="Finalizing result...")
522
+ try:
523
+ if preview_tmp != tmp:
524
+ try: os.remove(preview_tmp)
525
+ except Exception: pass
526
+ finally:
527
+ try: os.remove(tmp)
528
+ except Exception: pass
529
+ status = "done" if not (isinstance(res, str) and res.lower().startswith("error")) else "error"
530
+ return (status, res if isinstance(res, str) else str(res))
531
+ elif is_img:
532
+ progress(0.08, desc="Fetching image bytes...")
533
+ raw = fetch_bytes(url, progress=progress)
534
+ progress(0.18, desc="Analyzing image...")
535
+ res = analyze_image_structured(get_client(key), raw, prompt or "", progress=progress)
536
+ progress(0.98, desc="Finalizing result...")
537
+ status = "done" if not (isinstance(res, str) and res.lower().startswith("error")) else "error"
538
+ return (status, res if isinstance(res, str) else str(res))
539
+ else:
540
+ # Unknown: try fetch and try image first, then video fallback
541
+ progress(0.07, desc="Unknown media type — fetching bytes for heuristic check...")
542
+ raw = fetch_bytes(url, timeout=120, progress=progress)
543
+ # try image
544
+ try:
545
+ progress(0.15, desc="Attempting to interpret as image...")
546
+ _ = Image.open(BytesIO(raw))
547
+ progress(0.2, desc="Image detected — analyzing...")
548
+ res = analyze_image_structured(get_client(key), raw, prompt or "", progress=progress)
549
+ status = "done" if not (isinstance(res, str) and res.lower().startswith("error")) else "error"
550
+ return (status, res if isinstance(res, str) else str(res))
551
+ except Exception:
552
+ # Treat as video
553
+ fd, tmp = tempfile.mkstemp(suffix=ext_from_src(url) or ".mp4")
554
+ os.close(fd)
555
+ with open(tmp, "wb") as fh:
556
+ fh.write(raw)
557
+ try:
558
+ progress(0.3, desc="Saved fallback video file; analyzing...")
559
+ res = analyze_video_cohesive(get_client(key), tmp, prompt or "", progress=progress)
560
+ status = "done" if not (isinstance(res, str) and res.lower().startswith("error")) else "error"
561
+ return (status, res if isinstance(res, str) else str(res))
562
+ finally:
563
+ try: os.remove(tmp)
564
+ except Exception: pass
565
  except Exception as e:
566
  return ("error", f"Unexpected worker error: {e}")
567
 
568
+ # submit returns (status_state, output_md)
569
  submit_btn.click(fn=worker, inputs=[url_input, custom_prompt, api_key], outputs=[status_state, output_md], queue=True)
570
 
571
  def btn_label_from_state(s):
572
  return _btn_label_for_status(s)
573
 
574
  status_state.change(fn=btn_label_from_state, inputs=[status_state], outputs=[submit_btn])
575
+ # Show verbose progress messages in progress_md by listening to a lightweight helper that maps state to text.
576
+ def status_to_progress_text(s):
577
+ return {"idle":"Idle","busy":"Processing…","done":"Completed","error":"Error — see output"}.get(s, s)
578
+ status_state.change(fn=status_to_progress_text, inputs=[status_state], outputs=[progress_md])
579
 
580
  return demo
581