Hug0endob commited on
Commit
0e1399c
·
verified ·
1 Parent(s): 9618450

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +11 -63
app.py CHANGED
@@ -1,6 +1,4 @@
1
  #!/usr/bin/env python3
2
- # app.py - Gradio UI wrapper for your media analysis functions
3
-
4
  import os
5
  from io import BytesIO
6
  from typing import Tuple
@@ -8,17 +6,6 @@ from typing import Tuple
8
  import gradio as gr
9
  from PIL import Image
10
 
11
- # Import or copy your existing helpers here:
12
- # - process_media(src, custom_prompt, api_key, progress=gr.Progress())
13
- # - fetch_bytes, determine_media_type, ext_from_src, is_remote, safe_head, safe_get
14
- # - IMAGE_EXTS, VIDEO_EXTS
15
- # For example, if they are in analysis.py:
16
- # from analysis import process_media, fetch_bytes, determine_media_type, ext_from_src, is_remote, safe_head, safe_get, IMAGE_EXTS, VIDEO_EXTS
17
-
18
- # --- Begin: Minimal adapters if you want to inline small helpers (optional) ---
19
- # If you already have those functions in another module, import them instead of duplicating.
20
- # --- End adapters ---
21
-
22
  css = """
23
  .preview_media img, .preview_media video { max-width: 100%; height: auto; border-radius: 6px; }
24
  .top_preview { display:flex; gap:12px; align-items:flex-start; flex-direction:column; }
@@ -34,7 +21,6 @@ def _btn_label_for_status(status: str) -> str:
34
  }.get(status or "idle", "Submit")
35
 
36
 
37
- # preview loader returns image/video updates (use gr.update for component props)
38
  def load_preview(url: str):
39
  empty_img = gr.update(value=None, visible=False)
40
  empty_vid = gr.update(value=None, visible=False)
@@ -74,52 +60,37 @@ def load_preview(url: str):
74
  return empty_img, empty_vid, gr.update(visible=False)
75
 
76
 
77
- # Create the Blocks app
78
  def create_demo():
79
  with gr.Blocks(css=css, title="Media Analysis") as demo:
80
- # Top preview column
81
  with gr.Row():
82
  with gr.Column(scale=1):
83
  preview_image = gr.Image(label="Preview Image", type="pil", visible=False, elem_classes="preview_media")
84
  preview_video = gr.Video(label="Preview Video", visible=False, elem_classes="preview_media")
85
- # PiP button - shown only when a video preview is visible
86
  pip_button = gr.Button("Open Video in PiP", visible=False, elem_classes="pip_button")
87
  with gr.Column(scale=1):
88
- # Inputs and controls
89
  url_input = gr.Textbox(label="Image / Video URL or local path", placeholder="https://... or /path/to/file", lines=1)
90
- # Collapsible prompt (Accordion)
91
  with gr.Accordion("Prompt (optional)", open=False):
92
  custom_prompt = gr.Textbox(label="Prompt", lines=4, value="")
93
  with gr.Accordion("Mistral API Key (optional)", open=False):
94
  api_key = gr.Textbox(label="API Key", type="password", max_lines=1)
95
- # Submit immediately below preview (placed here visually)
96
  submit_btn = gr.Button(_btn_label_for_status("idle"), variant="primary")
97
  clear_btn = gr.Button("Clear")
98
- # Output area
99
  final_md = gr.Markdown(value="", label="Result")
100
 
101
- # State to track status
102
  status = gr.State("idle")
103
 
104
- # Live preview: update preview image/video and PiP visibility
105
  url_input.change(fn=load_preview, inputs=[url_input], outputs=[preview_image, preview_video, pip_button])
106
 
107
- # Clear handler returns exactly the outputs listed
108
  def do_clear():
109
  return "", gr.update(value=None, visible=False), gr.update(value=None, visible=False), gr.update(value="", visible=True)
110
  clear_btn.click(fn=do_clear, inputs=[], outputs=[url_input, preview_image, preview_video, final_md])
111
 
112
- # Start busy — MUST return outputs for both status (gr.State) and the Button (as gr.update)
113
  def start_busy():
114
  return "busy", gr.update(value=_btn_label_for_status("busy"), interactive=False)
115
 
116
- # Worker runs your process_media (queued)
117
  def worker(url: str, prompt: str, key: str, progress=gr.Progress()):
118
- # process_media returns a single string result (or error string)
119
  return process_media(url or "", prompt or "", key or "", progress=progress)
120
 
121
- # Finish: adapt result into status and Markdown update.
122
- # Return (status_string, gr.update(value=md_text)).
123
  def finish(result: str):
124
  s = "done"
125
  if not result:
@@ -130,49 +101,26 @@ def create_demo():
130
  s = "error"
131
  else:
132
  md = result
133
- # status as raw string (gr.State), final_md as gr.update
134
  return s, gr.update(value=md)
135
 
136
- # Wire submit button: first click sets busy state & disables button
137
- submit_btn.click(
138
- fn=start_busy,
139
- inputs=[],
140
- outputs=[status, submit_btn],
141
- )
142
 
143
- # Then run worker (queued), produce final_md, then run finish to update status & final_md
144
- submit_btn.click(
145
- fn=worker,
146
- inputs=[url_input, custom_prompt, api_key],
147
- outputs=[final_md],
148
- queue=True,
149
- ).then(
150
- fn=finish,
151
- inputs=[final_md],
152
- outputs=[status, final_md],
153
  )
154
 
155
- # Status change -> update button label/interactivity
156
  def btn_label_for_state(s: str):
157
  return gr.update(value=_btn_label_for_status(s), interactive=(s != "busy"))
158
-
159
  status.change(fn=btn_label_for_state, inputs=[status], outputs=[submit_btn])
160
 
161
- # PiP JS glue: open the current preview_video element in Picture-in-Picture.
162
- pip_js = """
163
- () => {
164
- const vid = document.querySelector('#{} video, #{} video');
165
- if (!vid) { return; }
166
- // Request PiP
167
- if (document.pictureInPictureElement) {
168
- document.exitPictureInPicture();
169
- } else {
170
- vid.requestPictureInPicture().catch(e => console.warn('PiP failed', e));
171
- }
172
- }
173
- """.format(preview_video.elem_id or "preview_video", preview_video.elem_id or "preview_video")
174
-
175
- # Set PiP button to run client-side JS when clicked
176
  pip_button.click(fn=None, _js=pip_js, inputs=[], outputs=[])
177
 
178
  return demo
 
1
  #!/usr/bin/env python3
 
 
2
  import os
3
  from io import BytesIO
4
  from typing import Tuple
 
6
  import gradio as gr
7
  from PIL import Image
8
 
 
 
 
 
 
 
 
 
 
 
 
9
  css = """
10
  .preview_media img, .preview_media video { max-width: 100%; height: auto; border-radius: 6px; }
11
  .top_preview { display:flex; gap:12px; align-items:flex-start; flex-direction:column; }
 
21
  }.get(status or "idle", "Submit")
22
 
23
 
 
24
  def load_preview(url: str):
25
  empty_img = gr.update(value=None, visible=False)
26
  empty_vid = gr.update(value=None, visible=False)
 
60
  return empty_img, empty_vid, gr.update(visible=False)
61
 
62
 
 
63
  def create_demo():
64
  with gr.Blocks(css=css, title="Media Analysis") as demo:
 
65
  with gr.Row():
66
  with gr.Column(scale=1):
67
  preview_image = gr.Image(label="Preview Image", type="pil", visible=False, elem_classes="preview_media")
68
  preview_video = gr.Video(label="Preview Video", visible=False, elem_classes="preview_media")
 
69
  pip_button = gr.Button("Open Video in PiP", visible=False, elem_classes="pip_button")
70
  with gr.Column(scale=1):
 
71
  url_input = gr.Textbox(label="Image / Video URL or local path", placeholder="https://... or /path/to/file", lines=1)
 
72
  with gr.Accordion("Prompt (optional)", open=False):
73
  custom_prompt = gr.Textbox(label="Prompt", lines=4, value="")
74
  with gr.Accordion("Mistral API Key (optional)", open=False):
75
  api_key = gr.Textbox(label="API Key", type="password", max_lines=1)
 
76
  submit_btn = gr.Button(_btn_label_for_status("idle"), variant="primary")
77
  clear_btn = gr.Button("Clear")
 
78
  final_md = gr.Markdown(value="", label="Result")
79
 
 
80
  status = gr.State("idle")
81
 
 
82
  url_input.change(fn=load_preview, inputs=[url_input], outputs=[preview_image, preview_video, pip_button])
83
 
 
84
  def do_clear():
85
  return "", gr.update(value=None, visible=False), gr.update(value=None, visible=False), gr.update(value="", visible=True)
86
  clear_btn.click(fn=do_clear, inputs=[], outputs=[url_input, preview_image, preview_video, final_md])
87
 
 
88
  def start_busy():
89
  return "busy", gr.update(value=_btn_label_for_status("busy"), interactive=False)
90
 
 
91
  def worker(url: str, prompt: str, key: str, progress=gr.Progress()):
 
92
  return process_media(url or "", prompt or "", key or "", progress=progress)
93
 
 
 
94
  def finish(result: str):
95
  s = "done"
96
  if not result:
 
101
  s = "error"
102
  else:
103
  md = result
 
104
  return s, gr.update(value=md)
105
 
106
+ submit_btn.click(fn=start_busy, inputs=[], outputs=[status, submit_btn])
 
 
 
 
 
107
 
108
+ submit_btn.click(fn=worker, inputs=[url_input, custom_prompt, api_key], outputs=[final_md], queue=True).then(
109
+ fn=finish, inputs=[final_md], outputs=[status, final_md]
 
 
 
 
 
 
 
 
110
  )
111
 
 
112
  def btn_label_for_state(s: str):
113
  return gr.update(value=_btn_label_for_status(s), interactive=(s != "busy"))
 
114
  status.change(fn=btn_label_for_state, inputs=[status], outputs=[submit_btn])
115
 
116
+ # PiP JS: use a JS function string without Python .format interpolation
117
+ pip_js = (
118
+ "const vid = document.querySelector('video');"
119
+ "if (!vid) return;"
120
+ "if (document.pictureInPictureElement) { document.exitPictureInPicture().catch(()=>{}); }"
121
+ "else { vid.requestPictureInPicture().catch(()=>{}); }"
122
+ )
123
+ # Attach PiP: client-side JS (no inputs/outputs)
 
 
 
 
 
 
 
124
  pip_button.click(fn=None, _js=pip_js, inputs=[], outputs=[])
125
 
126
  return demo