Hug0endob commited on
Commit
7ca85e1
·
verified ·
1 Parent(s): 2e55d8f

Update streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +96 -112
streamlit_app.py CHANGED
@@ -3,7 +3,6 @@
3
 
4
  """
5
  Video‑analysis Streamlit app.
6
-
7
  Features
8
  --------
9
  * Download videos from direct links, Twitter, or any site supported by yt‑dlp.
@@ -183,7 +182,6 @@ def _download_with_yt_dlp(url: str, dst: Path, password: str = "") -> Path:
183
  def download_video(url: str, dst: Path, password: str = "") -> Path:
184
  """
185
  Download a video from *url* and return an MP4 path.
186
-
187
  Strategy
188
  ---------
189
  1. Direct video URL → HTTP GET.
@@ -248,64 +246,53 @@ def _strip_prompt_echo(prompt: str, text: str, threshold: float = 0.68) -> str:
248
  return text[cut:].lstrip(" \n:-")
249
  return text
250
 
 
251
  # ----------------------------------------------------------------------
252
  # Streamlit UI
253
  # ----------------------------------------------------------------------
254
  def main() -> None:
255
  st.set_page_config(page_title="Video Analysis", layout="wide")
256
 
257
- # Two‑column layout: left = “pseudo‑sidebar”, right = main content
258
- col_sidebar, col_main = st.columns([1, 3])
259
-
260
  # ---------- Sidebar ----------
261
- with col_sidebar:
262
- st.header("Video Input")
263
- st.text_input("Video URL", key="url", placeholder="https://")
264
 
265
- with st.expander("Settings", expanded=False):
266
- model = st.selectbox(
267
- "Model", MODEL_OPTIONS, index=MODEL_OPTIONS.index(DEFAULT_MODEL)
268
- )
269
- if model == "custom":
270
- model = st.text_input(
271
- "Custom model ID", value=DEFAULT_MODEL, key="custom_model"
272
- )
273
- st.session_state["model_input"] = model
274
-
275
- # API key handling
276
- secret_key = os.getenv("GOOGLE_API_KEY", "")
277
- if secret_key:
278
- st.session_state["api_key"] = secret_key
279
- st.text_input("Google API Key", key="api_key", type="password")
280
-
281
- st.text_area(
282
- "Analysis prompt",
283
- value=DEFAULT_PROMPT,
284
- key="prompt",
285
- height=140,
286
- )
287
- st.text_input(
288
- "Video password (if needed)", key="video_password", type="password"
289
- )
290
- st.number_input(
291
- "Compress if > (MB)",
292
- min_value=10,
293
- max_value=2000,
294
- value=st.session_state.get("compress_mb", 100),
295
- step=10,
296
- key="compress_mb",
297
- )
298
 
299
  # ---------- Load video ----------
300
- if st.button("Load Video"):
301
  try:
302
  with st.spinner("Downloading video…"):
303
- raw_path = download_video(
304
  st.session_state["url"], DATA_DIR, st.session_state["video_password"]
305
  )
306
- # ALWAYS convert to MP4 before storing
307
- mp4_path = _convert_to_mp4(Path(raw_path))
308
- st.session_state["video_path"] = str(mp4_path) # guaranteed MP4
309
  st.session_state["last_error"] = ""
310
  st.success("Video loaded successfully.")
311
  except Exception as e:
@@ -313,21 +300,21 @@ def main() -> None:
313
  st.sidebar.error(st.session_state["last_error"])
314
 
315
  # ---------- Preview & clear ----------
316
- if st.session_state.get("video_path"):
317
  try:
318
  mp4 = _convert_to_mp4(Path(st.session_state["video_path"]))
319
  st.sidebar.video(str(mp4))
320
  except Exception:
321
  st.sidebar.write("Preview unavailable")
322
 
323
- if st.button("Clear Video"):
324
- # delete every file in DATA_DIR (both raw and converted)
325
  for f in DATA_DIR.iterdir():
326
  try:
327
  f.unlink()
328
  except Exception:
329
  pass
330
- # reset session state, including the URL field
331
  st.session_state.update(
332
  {
333
  "url": "",
@@ -335,73 +322,70 @@ def main() -> None:
335
  "analysis_out": "",
336
  "last_error": "",
337
  "busy": False,
338
- "show_raw_on_error": False,
339
  }
340
  )
341
  st.success("Session cleared.")
342
 
343
- # ---------- Generation ----------
344
- with col_main:
345
- col1, col2 = st.columns([1, 3])
346
- with col1:
347
- generate_now = st.button(
348
- "Generate analysis", type="primary", disabled=st.session_state.get("busy", False)
349
- )
350
- with col2:
351
- if not st.session_state.get("video_path"):
352
- st.info("Load a video first.", icon="ℹ️")
353
-
354
- if generate_now and not st.session_state.get("busy", False):
355
- api_key = st.session_state.get("api_key") or os.getenv("GOOGLE_API_KEY")
356
- if not st.session_state.get("video_path"):
357
- st.error("No video loaded.")
358
- elif not api_key:
359
- st.error("Google API key missing.")
360
- else:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
  try:
362
- st.session_state["busy"] = True
363
- genai.configure(api_key=api_key)
364
-
365
- # ---- optional compression ----
366
- with st.spinner("Checking video size…"):
367
- video_path, was_compressed = _maybe_compress(
368
- Path(st.session_state["video_path"]),
369
- st.session_state["compress_mb"],
370
- )
371
-
372
- # ---- generation ----
373
- with st.spinner("Generating analysis…"):
374
- raw_out = generate_report(
375
- video_path,
376
- st.session_state["prompt"],
377
- st.session_state["model_input"],
378
- st.session_state.get("generation_timeout", 300),
379
- )
380
- st.session_state["raw_output"] = raw_out
381
-
382
- # clean up temporary compressed file
383
- if was_compressed:
384
- try:
385
- video_path.unlink()
386
- except OSError:
387
- pass
388
-
389
- out = _strip_prompt_echo(st.session_state["prompt"], raw_out)
390
- st.session_state["analysis_out"] = out
391
- st.success("Analysis generated.")
392
- st.markdown(out or "*(no output)*")
393
-
394
- except Exception as exc:
395
- tb = traceback.format_exc()
396
- st.session_state["last_error_detail"] = (
397
- f"{tb}\n\nRaw Gemini output:\n"
398
- f"{st.session_state.get('raw_output', '')}"
399
- )
400
- st.session_state["last_error"] = f"Generation error: {exc}"
401
- st.session_state["show_raw_on_error"] = True
402
- st.error("An error occurred during generation.")
403
- finally:
404
- st.session_state["busy"] = False
405
 
406
  # ---------- Results ----------
407
  if st.session_state.get("analysis_out"):
 
3
 
4
  """
5
  Video‑analysis Streamlit app.
 
6
  Features
7
  --------
8
  * Download videos from direct links, Twitter, or any site supported by yt‑dlp.
 
182
  def download_video(url: str, dst: Path, password: str = "") -> Path:
183
  """
184
  Download a video from *url* and return an MP4 path.
 
185
  Strategy
186
  ---------
187
  1. Direct video URL → HTTP GET.
 
246
  return text[cut:].lstrip(" \n:-")
247
  return text
248
 
249
+
250
  # ----------------------------------------------------------------------
251
  # Streamlit UI
252
  # ----------------------------------------------------------------------
253
  def main() -> None:
254
  st.set_page_config(page_title="Video Analysis", layout="wide")
255
 
 
 
 
256
  # ---------- Sidebar ----------
257
+ st.sidebar.header("Video Input")
258
+ st.sidebar.text_input("Video URL", key="url", placeholder="https://")
 
259
 
260
+ with st.sidebar.expander("Settings", expanded=False):
261
+ model = st.selectbox(
262
+ "Model", MODEL_OPTIONS, index=MODEL_OPTIONS.index(DEFAULT_MODEL)
263
+ )
264
+ if model == "custom":
265
+ model = st.text_input("Custom model ID", value=DEFAULT_MODEL, key="custom_model")
266
+ st.session_state["model_input"] = model
267
+
268
+ # API key handling
269
+ secret_key = os.getenv("GOOGLE_API_KEY", "")
270
+ if secret_key:
271
+ st.session_state["api_key"] = secret_key
272
+ st.text_input("Google API Key", key="api_key", type="password")
273
+
274
+ st.text_area(
275
+ "Analysis prompt", value=DEFAULT_PROMPT, key="prompt", height=140
276
+ )
277
+ st.text_input("Video password (if needed)", key="video_password", type="password")
278
+
279
+ st.number_input(
280
+ "Compress if > (MB)",
281
+ min_value=10,
282
+ max_value=2000,
283
+ value=st.session_state["compress_mb"],
284
+ step=10,
285
+ key="compress_mb",
286
+ )
 
 
 
 
 
 
287
 
288
  # ---------- Load video ----------
289
+ if st.sidebar.button("Load Video"):
290
  try:
291
  with st.spinner("Downloading video…"):
292
+ path = download_video(
293
  st.session_state["url"], DATA_DIR, st.session_state["video_password"]
294
  )
295
+ st.session_state["video_path"] = str(path)
 
 
296
  st.session_state["last_error"] = ""
297
  st.success("Video loaded successfully.")
298
  except Exception as e:
 
300
  st.sidebar.error(st.session_state["last_error"])
301
 
302
  # ---------- Preview & clear ----------
303
+ if st.session_state["video_path"]:
304
  try:
305
  mp4 = _convert_to_mp4(Path(st.session_state["video_path"]))
306
  st.sidebar.video(str(mp4))
307
  except Exception:
308
  st.sidebar.write("Preview unavailable")
309
 
310
+ if st.sidebar.button("Clear Video"):
311
+ # delete files
312
  for f in DATA_DIR.iterdir():
313
  try:
314
  f.unlink()
315
  except Exception:
316
  pass
317
+ # reset state, including URL field
318
  st.session_state.update(
319
  {
320
  "url": "",
 
322
  "analysis_out": "",
323
  "last_error": "",
324
  "busy": False,
 
325
  }
326
  )
327
  st.success("Session cleared.")
328
 
329
+ # ---------- Generation ----------
330
+ col1, col2 = st.columns([1, 3])
331
+ with col1:
332
+ generate_now = st.button(
333
+ "Generate analysis", type="primary", disabled=st.session_state["busy"]
334
+ )
335
+ with col2:
336
+ if not st.session_state["video_path"]:
337
+ st.info("Load a video first.", icon="ℹ️")
338
+
339
+ if generate_now and not st.session_state["busy"]:
340
+ api_key = st.session_state["api_key"] or os.getenv("GOOGLE_API_KEY")
341
+ if not st.session_state["video_path"]:
342
+ st.error("No video loaded.")
343
+ elif not api_key:
344
+ st.error("Google API key missing.")
345
+ else:
346
+ try:
347
+ st.session_state["busy"] = True
348
+ genai.configure(api_key=api_key)
349
+
350
+ # ---- optional compression ----
351
+ with st.spinner("Checking video size…"):
352
+ video_path, was_compressed = _maybe_compress(
353
+ Path(st.session_state["video_path"]),
354
+ st.session_state["compress_mb"],
355
+ )
356
+
357
+ # ---- generation ----
358
+ with st.spinner("Generating analysis…"):
359
+ raw_out = generate_report(
360
+ video_path,
361
+ st.session_state["prompt"],
362
+ st.session_state["model_input"],
363
+ st.session_state.get("generation_timeout", 300),
364
+ )
365
+ # store the untouched response for debugging
366
+ st.session_state["raw_output"] = raw_out
367
+
368
+ # clean up temporary compressed file
369
+ if was_compressed:
370
  try:
371
+ video_path.unlink()
372
+ except OSError:
373
+ pass
374
+
375
+ out = _strip_prompt_echo(st.session_state["prompt"], raw_out)
376
+ st.session_state["analysis_out"] = out
377
+ st.success("Analysis generated.")
378
+ st.markdown(out or "*(no output)*")
379
+ except Exception as exc:
380
+ tb = traceback.format_exc()
381
+ # keep both traceback and whatever raw output we might have
382
+ st.session_state["last_error_detail"] = f"{tb}\n\nRaw Gemini output:\n{st.session_state.get('raw_output','')}"
383
+ st.session_state["last_error"] = f"Generation error: {exc}"
384
+ # indicate that raw output should be shown in the main area
385
+ st.session_state["show_raw_on_error"] = True
386
+ st.error("An error occurred during generation.")
387
+ finally:
388
+ st.session_state["busy"] = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
 
390
  # ---------- Results ----------
391
  if st.session_state.get("analysis_out"):