Hug0endob commited on
Commit
221df6e
·
verified ·
1 Parent(s): 6806393

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +34 -25
app.py CHANGED
@@ -185,7 +185,7 @@ def convert_to_jpeg_bytes(img_bytes: bytes, base_h: int = 480) -> bytes:
185
  w = max(1, int(img.width * (base_h / img.height)))
186
  img = img.resize((w, base_h), Image.LANCZOS)
187
  buf = BytesIO()
188
- img.save(buf, format="JPEG", quality=85)
189
  return buf.getvalue()
190
 
191
  def b64_bytes(b: bytes, mime: str = "image/jpeg") -> str:
@@ -238,7 +238,8 @@ def _get_video_info_and_timestamps(media_path: str, sample_count: int) -> Tuple[
238
  timestamps = [step * (i + 1) for i in range(actual_sample_count)]
239
 
240
  if not timestamps:
241
- timestamps = [0.5, 1.0, 2.0, 3.0, 4.0][:sample_count]
 
242
 
243
  return info, timestamps
244
 
@@ -366,10 +367,9 @@ def upload_file_to_mistral(client: Mistral, path: str, purpose: str = "batch", t
366
  if progress is not None:
367
  progress(0.5 + 0.01 * attempt, desc=f"Uploading file to model service (attempt {attempt+1}/{max_retries})...")
368
 
369
- with open(path, "rb") as fh:
370
- # Mistral client expects a file-like object directly for the 'file' parameter.
371
- # It will handle filename and mimetype internally.
372
- res = client.files.upload(file=fh, purpose=purpose)
373
  fid = getattr(res, "id", None)
374
  if not fid:
375
  raise RuntimeError(f"Mistral API upload response missing file ID: {res}")
@@ -464,6 +464,7 @@ def analyze_video_cohesive(client: Mistral, video_path: str, prompt: str, progre
464
  ]
465
  result = chat_complete(client, VIDEO_MODEL, messages, progress=progress)
466
 
 
467
  _, gallery_frame_paths = extract_frames_for_model_and_gallery(
468
  video_path, sample_count=6, gallery_base_h=1080, model_base_h=1024, progress=progress
469
  )
@@ -524,21 +525,18 @@ def _convert_video_for_preview_if_needed(path: str) -> str:
524
  print(f"Error: Could not create temporary file for video conversion from {path}.")
525
  return path
526
 
 
 
 
 
 
527
  cmd = [
528
  FFMPEG_BIN, "-y", "-i", path,
529
  "-c:v", "libx264", "-preset", "veryfast", "-crf", "28",
530
- "-c:a", " " if not _ffprobe_streams(path) or not any(s.get("codec_type") == "audio" for s in _ffprobe_streams(path).get("streams", [])) else "aac",
531
- "-b:a", "128k",
532
  "-movflags", "+faststart", out_path,
533
  "-map_metadata", "-1"
534
  ]
535
- # Remove audio options if no audio stream exists to prevent ffmpeg errors
536
- if "-c:a" in cmd and cmd[cmd.index("-c:a") + 1] == " ":
537
- cmd.pop(cmd.index("-c:a") + 1)
538
- cmd.pop(cmd.index("-c:a"))
539
- cmd.pop(cmd.index("-b:a") + 1)
540
- cmd.pop(cmd.index("-b:a"))
541
-
542
 
543
  try:
544
  subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=60)
@@ -636,7 +634,8 @@ def create_demo():
636
  with gr.Column(scale=1):
637
  preview_image = gr.Image(label="Preview Image", type="filepath", elem_classes="preview_media", visible=False)
638
  preview_video = gr.Video(label="Preview Video", elem_classes="preview_media", visible=False, format="mp4")
639
- screenshot_gallery = gr.Gallery(label="Extracted Screenshots", columns=5, rows=1, height="auto", object_fit="contain", visible=False)
 
640
  # Initially hidden, will become visible when a preview status is set
641
  preview_status_text = gr.Textbox(label="Preview Status", interactive=False, lines=1, value="", visible=False)
642
  with gr.Column(scale=2):
@@ -737,10 +736,13 @@ def create_demo():
737
  main_path_clear = ""
738
  screenshot_paths_clear = []
739
  raw_media_path_clear = ""
 
 
740
 
741
  if not url:
742
  return img_update_clear, video_update_clear, gallery_update_clear, \
743
- preview_status_clear, main_path_clear, raw_media_path_clear, screenshot_paths_clear
 
744
 
745
  temp_raw_path_for_analysis = ""
746
  try:
@@ -749,13 +751,15 @@ def create_demo():
749
  if not raw_bytes_for_analysis:
750
  return img_update_clear, video_update_clear, gallery_update_clear, \
751
  gr.update(value="Preview load failed: No media bytes fetched.", visible=True), \
752
- main_path_clear, raw_media_path_clear, screenshot_paths_clear
 
753
 
754
  temp_raw_path_for_analysis = _temp_file(raw_bytes_for_analysis, suffix=ext_from_src(url) or ".tmp")
755
  if not temp_raw_path_for_analysis:
756
  return img_update_clear, video_update_clear, gallery_update_clear, \
757
  gr.update(value="Preview load failed: Could not save raw media to temp file.", visible=True), \
758
- main_path_clear, raw_media_path_clear, screenshot_paths_clear
 
759
 
760
  progress(0.25, desc="Generating playable preview...")
761
  is_img_initial, is_vid_initial = determine_media_type(url)
@@ -768,7 +772,8 @@ def create_demo():
768
 
769
  return img_update_clear, video_update_clear, gallery_update_clear, \
770
  gr.update(value="Preview load failed: could not make content playable.", visible=True), \
771
- main_path_clear, raw_media_path_clear, screenshot_paths_clear
 
772
 
773
  ext = ext_from_src(local_playable_path)
774
  is_img_preview = ext in IMAGE_EXTENSIONS
@@ -777,11 +782,13 @@ def create_demo():
777
  if is_img_preview:
778
  return gr.update(value=local_playable_path, visible=True), gr.update(value=None, visible=False), \
779
  gallery_update_clear, gr.update(value="Image preview loaded.", visible=True), \
780
- local_playable_path, temp_raw_path_for_analysis, screenshot_paths_clear
 
781
  elif is_vid_preview:
782
  return gr.update(value=None, visible=False), gr.update(value=local_playable_path, visible=True), \
783
  gallery_update_clear, gr.update(value="Video preview loaded.", visible=True), \
784
- local_playable_path, temp_raw_path_for_analysis, screenshot_paths_clear
 
785
  else:
786
  _temp_files_to_delete.discard(local_playable_path)
787
  try: os.remove(local_playable_path)
@@ -792,7 +799,8 @@ def create_demo():
792
 
793
  return img_update_clear, video_update_clear, gallery_update_clear, \
794
  gr.update(value="Preview load failed: unknown playable format.", visible=True), \
795
- main_path_clear, raw_media_path_clear, screenshot_paths_clear
 
796
 
797
  except Exception as e:
798
  if os.path.exists(temp_raw_path_for_analysis):
@@ -802,12 +810,13 @@ def create_demo():
802
 
803
  return img_update_clear, video_update_clear, gallery_update_clear, \
804
  gr.update(value=f"Preview load failed: {type(e).__name__}: {e}", visible=True), \
805
- main_path_clear, raw_media_path_clear, screenshot_paths_clear
 
806
 
807
  url_input.change(
808
  fn=load_main_preview_and_setup_for_analysis,
809
  inputs=[url_input, main_preview_path_state, raw_media_path_state, screenshot_paths_state],
810
- outputs=[preview_image, preview_video, screenshot_gallery, preview_status_text, main_preview_path_state, raw_media_path_state, screenshot_paths_state]
811
  )
812
 
813
  def worker(url: str, prompt: str, key: str, raw_media_path: str, progress=gr.Progress()):
 
185
  w = max(1, int(img.width * (base_h / img.height)))
186
  img = img.resize((w, base_h), Image.LANCZOS)
187
  buf = BytesIO()
188
+ img.save(buf, format="JPEG", quality=90) # Increased quality from 85 to 90
189
  return buf.getvalue()
190
 
191
  def b64_bytes(b: bytes, mime: str = "image/jpeg") -> str:
 
238
  timestamps = [step * (i + 1) for i in range(actual_sample_count)]
239
 
240
  if not timestamps:
241
+ # Fallback for very short videos or if duration couldn't be determined
242
+ timestamps = [0.5, 1.0, 2.0, 3.0, 4.0, 5.0][:sample_count] # Ensure enough fallback timestamps
243
 
244
  return info, timestamps
245
 
 
367
  if progress is not None:
368
  progress(0.5 + 0.01 * attempt, desc=f"Uploading file to model service (attempt {attempt+1}/{max_retries})...")
369
 
370
+ # CHANGE: Pass the file path (str) directly, allowing the mistralai client
371
+ # to handle opening the file and inferring filename/mimetype.
372
+ res = client.files.upload(file=path, purpose=purpose)
 
373
  fid = getattr(res, "id", None)
374
  if not fid:
375
  raise RuntimeError(f"Mistral API upload response missing file ID: {res}")
 
464
  ]
465
  result = chat_complete(client, VIDEO_MODEL, messages, progress=progress)
466
 
467
+ # Always extract frames for gallery, even if full analysis worked
468
  _, gallery_frame_paths = extract_frames_for_model_and_gallery(
469
  video_path, sample_count=6, gallery_base_h=1080, model_base_h=1024, progress=progress
470
  )
 
525
  print(f"Error: Could not create temporary file for video conversion from {path}.")
526
  return path
527
 
528
+ audio_codec_args = []
529
+ video_info = _ffprobe_streams(path)
530
+ if video_info and any(s.get("codec_type") == "audio" for s in video_info.get("streams", [])):
531
+ audio_codec_args = ["-c:a", "aac", "-b:a", "128k"]
532
+
533
  cmd = [
534
  FFMPEG_BIN, "-y", "-i", path,
535
  "-c:v", "libx264", "-preset", "veryfast", "-crf", "28",
536
+ *audio_codec_args, # Unpack the list
 
537
  "-movflags", "+faststart", out_path,
538
  "-map_metadata", "-1"
539
  ]
 
 
 
 
 
 
 
540
 
541
  try:
542
  subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=60)
 
634
  with gr.Column(scale=1):
635
  preview_image = gr.Image(label="Preview Image", type="filepath", elem_classes="preview_media", visible=False)
636
  preview_video = gr.Video(label="Preview Video", elem_classes="preview_media", visible=False, format="mp4")
637
+ # CHANGE: Set columns to 6 to display all 6 extracted frames without scrolling
638
+ screenshot_gallery = gr.Gallery(label="Extracted Screenshots", columns=6, rows=1, height="auto", object_fit="contain", visible=False)
639
  # Initially hidden, will become visible when a preview status is set
640
  preview_status_text = gr.Textbox(label="Preview Status", interactive=False, lines=1, value="", visible=False)
641
  with gr.Column(scale=2):
 
736
  main_path_clear = ""
737
  screenshot_paths_clear = []
738
  raw_media_path_clear = ""
739
+ progress_markdown_update_clear = gr.update(value="Idle")
740
+
741
 
742
  if not url:
743
  return img_update_clear, video_update_clear, gallery_update_clear, \
744
+ preview_status_clear, main_path_clear, raw_media_path_clear, \
745
+ screenshot_paths_clear, progress_markdown_update_clear
746
 
747
  temp_raw_path_for_analysis = ""
748
  try:
 
751
  if not raw_bytes_for_analysis:
752
  return img_update_clear, video_update_clear, gallery_update_clear, \
753
  gr.update(value="Preview load failed: No media bytes fetched.", visible=True), \
754
+ main_path_clear, raw_media_path_clear, screenshot_paths_clear, \
755
+ gr.update(value="Preview load failed (Error)")
756
 
757
  temp_raw_path_for_analysis = _temp_file(raw_bytes_for_analysis, suffix=ext_from_src(url) or ".tmp")
758
  if not temp_raw_path_for_analysis:
759
  return img_update_clear, video_update_clear, gallery_update_clear, \
760
  gr.update(value="Preview load failed: Could not save raw media to temp file.", visible=True), \
761
+ main_path_clear, raw_media_path_clear, screenshot_paths_clear, \
762
+ gr.update(value="Preview load failed (Error)")
763
 
764
  progress(0.25, desc="Generating playable preview...")
765
  is_img_initial, is_vid_initial = determine_media_type(url)
 
772
 
773
  return img_update_clear, video_update_clear, gallery_update_clear, \
774
  gr.update(value="Preview load failed: could not make content playable.", visible=True), \
775
+ main_path_clear, raw_media_path_clear, screenshot_paths_clear, \
776
+ gr.update(value="Preview load failed (Error)")
777
 
778
  ext = ext_from_src(local_playable_path)
779
  is_img_preview = ext in IMAGE_EXTENSIONS
 
782
  if is_img_preview:
783
  return gr.update(value=local_playable_path, visible=True), gr.update(value=None, visible=False), \
784
  gallery_update_clear, gr.update(value="Image preview loaded.", visible=True), \
785
+ local_playable_path, temp_raw_path_for_analysis, screenshot_paths_clear, \
786
+ gr.update(value="Preview ready")
787
  elif is_vid_preview:
788
  return gr.update(value=None, visible=False), gr.update(value=local_playable_path, visible=True), \
789
  gallery_update_clear, gr.update(value="Video preview loaded.", visible=True), \
790
+ local_playable_path, temp_raw_path_for_analysis, screenshot_paths_clear, \
791
+ gr.update(value="Preview ready")
792
  else:
793
  _temp_files_to_delete.discard(local_playable_path)
794
  try: os.remove(local_playable_path)
 
799
 
800
  return img_update_clear, video_update_clear, gallery_update_clear, \
801
  gr.update(value="Preview load failed: unknown playable format.", visible=True), \
802
+ main_path_clear, raw_media_path_clear, screenshot_paths_clear, \
803
+ gr.update(value="Preview load failed (Error)")
804
 
805
  except Exception as e:
806
  if os.path.exists(temp_raw_path_for_analysis):
 
810
 
811
  return img_update_clear, video_update_clear, gallery_update_clear, \
812
  gr.update(value=f"Preview load failed: {type(e).__name__}: {e}", visible=True), \
813
+ main_path_clear, raw_media_path_clear, screenshot_paths_clear, \
814
+ gr.update(value="Preview load failed (Error)")
815
 
816
  url_input.change(
817
  fn=load_main_preview_and_setup_for_analysis,
818
  inputs=[url_input, main_preview_path_state, raw_media_path_state, screenshot_paths_state],
819
+ outputs=[preview_image, preview_video, screenshot_gallery, preview_status_text, main_preview_path_state, raw_media_path_state, screenshot_paths_state, progress_markdown] # Added progress_markdown to outputs
820
  )
821
 
822
  def worker(url: str, prompt: str, key: str, raw_media_path: str, progress=gr.Progress()):