Hug0endob commited on
Commit
bebf085
·
verified ·
1 Parent(s): 0ca9425

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +135 -103
app.py CHANGED
@@ -581,111 +581,143 @@ def create_demo():
581
  return False
582
 
583
  # --- Convert only if not browser-playable
584
- def _convert_video_for_preview_if_needed(path: str) -> str:
585
- try:
586
- if _is_browser_playable(path):
587
- return path
588
- except Exception:
589
- pass
590
- return _convert_video_for_preview(path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
591
 
592
- # Worker now returns (status_state, output_md, preview_path_state)
593
- def worker(url: str, prompt: str, key: str, progress=gr.Progress()):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
594
  try:
595
- if not url:
596
- return ("error", "**Error:** No URL provided.", "")
597
-
598
- progress(0.01, desc="Starting processing...") # Initial processing
599
- progress(0.03, desc="Checking URL / content-type...") # URL check
600
- is_img, is_vid = determine_media_type(url, progress=progress)
601
- progress(0.06, desc=f"Determined media type: image={is_img}, video={is_vid}")
602
-
603
- client = get_client(key)
604
- preview_local = None
605
-
606
- if is_vid:
607
- progress(0.08, desc="Fetching video bytes (may take a while)...")
608
- # Ensure video bytes fetching has proper error handling
609
- raw = fetch_bytes(url, timeout=120, progress=progress)
610
- if raw is None:
611
- return ("error", "Failed to fetch video bytes.", "")
612
-
613
- tmp = save_bytes_to_temp(raw, suffix=ext_from_src(url) or ".mp4")
614
- progress(0.18, desc="Saved video to temp; converting for preview if needed...")
615
- preview_tmp = _convert_video_for_preview(tmp)
616
- preview_local = preview_tmp if os.path.exists(preview_tmp) else tmp
617
-
618
- progress(0.25, desc="Starting video analysis...")
619
- res = analyze_video_cohesive(client, tmp, prompt or "", progress=progress)
620
- progress(0.90, desc="Finalizing results...")
621
-
622
- elif is_img:
623
- progress(0.08, desc="Fetching image bytes...")
624
- raw = fetch_bytes(url, progress=progress)
625
-
626
- try:
627
- preview_fd, preview_path = tempfile.mkstemp(suffix=".jpg")
628
- os.close(preview_fd)
629
- with open(preview_path, "wb") as fh:
630
- fh.write(convert_to_jpeg_bytes(raw, base_h=1024))
631
- preview_local = preview_path
632
- except Exception:
633
- preview_local = None
634
-
635
- progress(0.18, desc="Analyzing image...")
636
- try:
637
- res = analyze_image_structured(client, raw, prompt or "", progress=progress)
638
- except UnidentifiedImageError:
639
- return ("error", "Error: provided file is not a valid image.", preview_local or "")
640
-
641
- progress(0.98, desc="Finalizing result...")
642
-
643
- else:
644
- progress(0.07, desc="Unknown media type — fetching bytes for heuristics...")
645
- raw = fetch_bytes(url, timeout=120, progress=progress)
646
-
647
- try:
648
- progress(0.15, desc="Attempting to interpret as image...")
649
- Image.open(BytesIO(raw))
650
- progress(0.20, desc="Image detected — analyzing...")
651
- res = analyze_image_structured(client, raw, prompt or "", progress=progress)
652
-
653
- try:
654
- preview_fd, preview_path = tempfile.mkstemp(suffix=".jpg")
655
- os.close(preview_fd)
656
- with open(preview_path, "wb") as fh:
657
- fh.write(convert_to_jpeg_bytes(raw, base_h=1024))
658
- preview_local = preview_path
659
- except Exception:
660
- preview_local = None
661
-
662
- except Exception:
663
- fd, tmp = tempfile.mkstemp(suffix=ext_from_src(url) or ".mp4")
664
- os.close(fd)
665
- with open(tmp, "wb") as fh:
666
- fh.write(raw)
667
-
668
- try:
669
- progress(0.30, desc="Saved fallback video file; analyzing...")
670
- preview_tmp = _convert_video_for_preview(tmp)
671
- preview_local = preview_tmp if os.path.exists(preview_tmp) else tmp
672
- res = analyze_video_cohesive(client, tmp, prompt or "", progress=progress)
673
- finally:
674
- try:
675
- os.remove(tmp)
676
- except Exception:
677
- pass
678
-
679
- # Determine final status and return
680
- status = "done" if not (isinstance(res, str) and res.lower().startswith("error")) else "error"
681
- return (status, res if isinstance(res, str) else str(res), preview_local or "")
682
-
683
- except Exception as e:
684
- return ("error", f"Unexpected worker error: {e}", "")
685
-
686
 
687
- # UI wiring
688
- submit_btn.click(fn=lambda: "busy", inputs=[], outputs=[status_state])
 
 
 
 
689
 
690
  submit_btn.click(
691
  fn=worker,
@@ -695,7 +727,7 @@ def create_demo():
695
  show_progress="full",
696
  show_progress_on=progress_md,
697
  )
698
-
699
  def _btn_label_for_status(s):
700
  labels = {
701
  "idle": "Submit",
 
581
  return False
582
 
583
  # --- Convert only if not browser-playable
584
+ def _convert_video_for_preview_if_needed(path: str) -> str:
585
+ """
586
+ Return a path that the Gradio video component can play.
587
+ If the original file is already MP4 with H.264 (or another browser‑compatible codec),
588
+ the original path is returned unchanged.
589
+ Otherwise the file is re‑encoded to MP4 (H.264 + AAC) and the new path is returned.
590
+ """
591
+ if not FFMPEG_BIN or not os.path.exists(path):
592
+ return path
593
+
594
+ # Quick check: extension + ffprobe for codecs
595
+ if path.lower().endswith((".mp4", ".m4v", ".mov")):
596
+ info = _ffprobe_streams(path)
597
+ if info:
598
+ codecs = {s.get("codec_name") for s in info.get("streams", []) if s.get("codec_type") == "video"}
599
+ if "h264" in codecs or "h265" in codecs:
600
+ return path # already playable
601
+
602
+ # Need conversion → write to a new temp MP4
603
+ out_fd, out_path = tempfile.mkstemp(suffix=".mp4")
604
+ os.close(out_fd)
605
+ cmd = [
606
+ FFMPEG_BIN, "-y", "-i", path,
607
+ "-c:v", "libx264", "-preset", "veryfast", "-crf", "28",
608
+ "-c:a", "aac", "-movflags", "+faststart", out_path,
609
+ ]
610
+ try:
611
+ subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=60)
612
+ return out_path
613
+ except Exception:
614
+ # If conversion fails, fall back to the original (Gradio will show its own warning)
615
+ try: os.remove(out_path)
616
+ except Exception: pass
617
+ return path
618
 
619
+ def worker(url: str, prompt: str, key: str, progress=gr.Progress()):
620
+ """Return (status, markdown_output, preview_path)."""
621
+ try:
622
+ if not url:
623
+ return "error", "**Error:** No URL provided.", ""
624
+
625
+ # ------------------------------------------------------------------
626
+ # 1️⃣ Determine media type – use a *single* call to avoid duplicate work
627
+ # ------------------------------------------------------------------
628
+ progress(0.02, desc="Checking URL / content‑type")
629
+ is_img, is_vid = determine_media_type(url, progress=progress)
630
+
631
+ client = get_client(key)
632
+ preview_path = ""
633
+
634
+ # ------------------------------------------------------------------
635
+ # 2️⃣ Helper to write a temp file with a *correct* suffix
636
+ # ------------------------------------------------------------------
637
+ def _temp_file(data: bytes, suffix: str) -> str:
638
+ fd, p = tempfile.mkstemp(suffix=suffix)
639
+ os.close(fd)
640
+ with open(p, "wb") as f:
641
+ f.write(data)
642
+ return p
643
+
644
+ # ------------------------------------------------------------------
645
+ # 3️⃣ VIDEO PATH
646
+ # ------------------------------------------------------------------
647
+ if is_vid:
648
+ progress(0.05, desc="Downloading video")
649
+ raw = fetch_bytes(url, timeout=120, progress=progress)
650
+ if not raw:
651
+ return "error", "Failed to download video bytes.", ""
652
+
653
+ # write with a proper video extension
654
+ tmp_video = _temp_file(raw, suffix=ext_from_src(url) or ".mp4")
655
+ progress(0.15, desc="Preparing preview")
656
+ preview_path = _convert_video_for_preview_if_needed(tmp_video)
657
+
658
+ progress(0.25, desc="Running full‑video analysis")
659
+ result = analyze_video_cohesive(client, tmp_video, prompt, progress=progress)
660
+
661
+ # clean‑up the *raw* temp file (preview may be a different file)
662
+ try: os.remove(tmp_video)
663
+ except Exception: pass
664
+
665
+ # ------------------------------------------------------------------
666
+ # 4️⃣ IMAGE PATH
667
+ # ------------------------------------------------------------------
668
+ elif is_img:
669
+ progress(0.05, desc="Downloading image")
670
+ raw = fetch_bytes(url, progress=progress)
671
+
672
+ # preview image (always JPEG for consistency)
673
+ preview_path = _temp_file(convert_to_jpeg_bytes(raw, base_h=1024), suffix=".jpg")
674
+
675
+ progress(0.20, desc="Running image analysis")
676
+ result = analyze_image_structured(client, raw, prompt, progress=progress)
677
+
678
+ # ------------------------------------------------------------------
679
+ # 5️⃣ FALLBACK – try image first, then video
680
+ # ------------------------------------------------------------------
681
+ else:
682
+ progress(0.07, desc="Downloading unknown media")
683
+ raw = fetch_bytes(url, timeout=120, progress=progress)
684
+
685
+ # try to open as image
686
  try:
687
+ Image.open(BytesIO(raw)).verify()
688
+ is_img = True
689
+ except Exception:
690
+ is_img = False
691
+
692
+ if is_img:
693
+ preview_path = _temp_file(convert_to_jpeg_bytes(raw, base_h=1024), suffix=".jpg")
694
+ result = analyze_image_structured(client, raw, prompt, progress=progress)
695
+ else:
696
+ tmp_vid = _temp_file(raw, suffix=ext_from_src(url) or ".mp4")
697
+ preview_path = _convert_video_for_preview_if_needed(tmp_vid)
698
+ result = analyze_video_cohesive(client, tmp_vid, prompt, progress=progress)
699
+ try: os.remove(tmp_vid)
700
+ except Exception: pass
701
+
702
+ # ------------------------------------------------------------------
703
+ # 6️⃣ Final status
704
+ # ------------------------------------------------------------------
705
+ status = "done" if not (isinstance(result, str) and result.lower().startswith("error")) else "error"
706
+ return status, result if isinstance(result, str) else str(result), preview_path or ""
707
+
708
+ except Exception as exc:
709
+ return "error", f"Unexpected worker error: {exc}", ""
710
+
711
+ def _start_processing(url, prompt, key):
712
+ # set state to busy and launch the worker in the same call
713
+ return "busy", None, None # temporary values; the real result will replace them
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
714
 
715
+ submit_btn.click(
716
+ fn=_start_processing,
717
+ inputs=[url_input, custom_prompt, api_key],
718
+ outputs=[status_state, output_md, preview_path_state],
719
+ queue=False, # this tiny wrapper runs instantly
720
+ )
721
 
722
  submit_btn.click(
723
  fn=worker,
 
727
  show_progress="full",
728
  show_progress_on=progress_md,
729
  )
730
+
731
  def _btn_label_for_status(s):
732
  labels = {
733
  "idle": "Submit",