CB commited on
Commit
5bb659f
·
verified ·
1 Parent(s): 44ae25c

Update streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +64 -44
streamlit_app.py CHANGED
@@ -33,7 +33,6 @@ for k, v in {
33
  }.items():
34
  st.session_state.setdefault(k, v)
35
 
36
- # helpers
37
  def sanitize_filename(path_str: str):
38
  return Path(path_str).name.lower().translate(str.maketrans("", "", "!?\"'`~@#$%^&*()[]{}<>:,;\\/|+=*")).replace(" ", "_")
39
 
@@ -66,7 +65,7 @@ def convert_video_to_mp4(video_path: str) -> str:
66
  os.remove(tmp.name)
67
  except Exception:
68
  pass
69
- raise RuntimeError("ffmpeg conversion failed")
70
  os.replace(tmp.name, str(target))
71
  if Path(video_path).suffix.lower() != ".mp4":
72
  try:
@@ -160,14 +159,13 @@ if os.getenv("GOOGLE_API_KEY"):
160
  genai = genai_mod
161
  upload_file = genai_mod.upload_file
162
  get_file = genai_mod.get_file
163
- # delete_file may not exist in SDK; guard later
164
  delete_file = getattr(genai_mod, "delete_file", None)
165
  HAS_GENAI = True
166
  except Exception:
167
  HAS_GENAI = False
168
 
169
  def upload_video_sdk(filepath: str):
170
- key = os.getenv("GOOGLE_API_KEY")
171
  if not key:
172
  raise RuntimeError("No API key")
173
  if not HAS_GENAI:
@@ -175,7 +173,7 @@ def upload_video_sdk(filepath: str):
175
  genai.configure(api_key=key)
176
  return upload_file(filepath)
177
 
178
- def wait_for_processed(file_obj, timeout=180):
179
  if not HAS_GENAI or get_file is None:
180
  return file_obj
181
  start = time.time()
@@ -184,7 +182,10 @@ def wait_for_processed(file_obj, timeout=180):
184
  return file_obj
185
  backoff = 1.0
186
  while True:
187
- obj = get_file(name)
 
 
 
188
  state = getattr(obj, "state", None)
189
  if not state or getattr(state, "name", None) != "PROCESSING":
190
  return obj
@@ -251,7 +252,7 @@ if st.sidebar.button("Load Video", use_container_width=True):
251
  st.session_state["processed_file"] = None
252
  st.session_state["file_hash"] = file_sha256(path)
253
  except Exception as e:
254
- st.sidebar.error("Failed to load video")
255
 
256
  if st.session_state["videos"]:
257
  try:
@@ -283,7 +284,6 @@ if st.session_state["videos"]:
283
  pass
284
  st.sidebar.write("Title:", Path(st.session_state["videos"]).name)
285
 
286
- # controls
287
  col1, col2 = st.columns([1, 3])
288
  with col1:
289
  if st.session_state.get("busy"):
@@ -296,7 +296,6 @@ with col1:
296
  with col2:
297
  pass
298
 
299
- # determine runtime API key (one-time entry not stored)
300
  def get_runtime_api_key():
301
  key = API_KEY_INPUT.strip() if API_KEY_INPUT else ""
302
  if key:
@@ -311,6 +310,8 @@ if (st.session_state.get("busy") is False) and ('generate_now' in locals() and g
311
  runtime_key = get_runtime_api_key()
312
  if not runtime_key:
313
  st.error("Google API key not set. Provide in Settings or set GOOGLE_API_KEY in environment.")
 
 
314
  else:
315
  try:
316
  st.session_state["busy"] = True
@@ -325,12 +326,12 @@ if (st.session_state.get("busy") is False) and ('generate_now' in locals() and g
325
  if processed and st.session_state.get("last_loaded_path") == current_path and st.session_state.get("file_hash") == current_hash:
326
  reupload_needed = False
327
 
 
 
 
328
  if reupload_needed:
329
- if not HAS_GENAI:
330
- raise RuntimeError("google.generativeai SDK not available")
331
  local_path = current_path
332
  fast_mode = st.session_state.get("fast_mode", False)
333
- upload_path = local_path
334
  try:
335
  file_size_mb = os.path.getsize(local_path) / (1024 * 1024)
336
  except Exception:
@@ -348,21 +349,16 @@ if (st.session_state.get("busy") is False) and ('generate_now' in locals() and g
348
  genai.configure(api_key=runtime_key)
349
  with st.spinner("Uploading video..."):
350
  uploaded = upload_video_sdk(upload_path)
351
- processed = wait_for_processed(uploaded, timeout=180)
352
  st.session_state["uploaded_file"] = uploaded
353
  st.session_state["processed_file"] = processed
354
  st.session_state["last_loaded_path"] = current_path
355
  st.session_state["file_hash"] = current_hash
356
 
357
- # privacy: delete local copy after successful upload (if different path)
358
- try:
359
- if Path(upload_path).exists() and Path(upload_path) != Path(current_path):
360
- Path(upload_path).unlink(missing_ok=True)
361
- # optionally remove original local file to avoid persistence
362
- Path(current_path).unlink(missing_ok=True)
363
- st.session_state["videos"] = ""
364
- except Exception:
365
- pass
366
 
367
  prompt_text = (analysis_prompt or default_prompt).strip()
368
  if st.session_state.get("fast_mode"):
@@ -377,22 +373,29 @@ if (st.session_state.get("busy") is False) and ('generate_now' in locals() and g
377
 
378
  def call_responses_once(model_used, system_msg, user_msg, fname, max_tokens):
379
  genai.configure(api_key=runtime_key)
380
- try:
381
- response = genai.responses.generate(
382
- model=model_used,
383
- messages=[system_msg, user_msg],
384
- files=[{"name": fname}],
385
- max_output_tokens=max_tokens,
386
- )
387
- except TypeError:
388
- response = genai.responses.generate(
389
- model=model_used,
390
- input=[{"text": user_msg["content"], "files": [{"name": fname}]}],
391
- max_output_tokens=max_tokens,
392
- )
393
- return response
394
-
395
- fname = file_name_or_id(processed) or file_name_or_id(st.session_state.get("uploaded_file"))
 
 
 
 
 
 
 
396
  if not fname:
397
  try:
398
  uri = getattr(processed, "uri", None) or (processed.get("uri") if isinstance(processed, dict) else None)
@@ -467,22 +470,41 @@ if (st.session_state.get("busy") is False) and ('generate_now' in locals() and g
467
  st.subheader("Analysis Result")
468
  st.markdown(out or "_(no text returned)_")
469
 
470
- # compact debug (user-triggered)
 
 
 
 
 
 
 
 
 
 
471
  with st.expander("Debug (compact)", expanded=False):
472
  try:
473
  info = {
474
  "model": model_used,
475
  "output_tokens": output_tokens,
476
  "upload_succeeded": bool(st.session_state.get("uploaded_file")),
477
- "processed_active": getattr(st.session_state.get("processed_file"), "state", None) if st.session_state.get("processed_file") else None,
478
  }
479
  st.write(info)
 
 
 
 
 
 
 
 
 
480
  except Exception:
481
  st.write("Debug info unavailable")
482
 
483
  except Exception as e:
484
- st.session_state["last_error"] = "Generation error"
485
- st.error("An error occurred while generating the story.")
486
  finally:
487
  st.session_state["busy"] = False
488
 
@@ -498,7 +520,6 @@ if st.session_state.get("last_error"):
498
  # delete uploaded files (local + cloud if possible)
499
  with st.sidebar.expander("Manage uploads", expanded=False):
500
  if st.button("Delete uploaded files (local + cloud)"):
501
- # delete local files
502
  for f in glob(str(DATA_DIR / "*")):
503
  try:
504
  Path(f).unlink(missing_ok=True)
@@ -510,7 +531,6 @@ with st.sidebar.expander("Manage uploads", expanded=False):
510
  st.session_state["last_loaded_path"] = ""
511
  st.session_state["analysis_out"] = ""
512
  st.session_state["file_hash"] = None
513
- # attempt to delete cloud file if SDK supports it
514
  try:
515
  fname = file_name_or_id(st.session_state.get("uploaded_file"))
516
  if fname and delete_file and HAS_GENAI:
 
33
  }.items():
34
  st.session_state.setdefault(k, v)
35
 
 
36
  def sanitize_filename(path_str: str):
37
  return Path(path_str).name.lower().translate(str.maketrans("", "", "!?\"'`~@#$%^&*()[]{}<>:,;\\/|+=*")).replace(" ", "_")
38
 
 
65
  os.remove(tmp.name)
66
  except Exception:
67
  pass
68
+ raise RuntimeError(f"ffmpeg conversion failed: {err}")
69
  os.replace(tmp.name, str(target))
70
  if Path(video_path).suffix.lower() != ".mp4":
71
  try:
 
159
  genai = genai_mod
160
  upload_file = genai_mod.upload_file
161
  get_file = genai_mod.get_file
 
162
  delete_file = getattr(genai_mod, "delete_file", None)
163
  HAS_GENAI = True
164
  except Exception:
165
  HAS_GENAI = False
166
 
167
  def upload_video_sdk(filepath: str):
168
+ key = get_runtime_api_key()
169
  if not key:
170
  raise RuntimeError("No API key")
171
  if not HAS_GENAI:
 
173
  genai.configure(api_key=key)
174
  return upload_file(filepath)
175
 
176
+ def wait_for_processed(file_obj, timeout=600):
177
  if not HAS_GENAI or get_file is None:
178
  return file_obj
179
  start = time.time()
 
182
  return file_obj
183
  backoff = 1.0
184
  while True:
185
+ try:
186
+ obj = get_file(name)
187
+ except Exception:
188
+ obj = file_obj
189
  state = getattr(obj, "state", None)
190
  if not state or getattr(state, "name", None) != "PROCESSING":
191
  return obj
 
252
  st.session_state["processed_file"] = None
253
  st.session_state["file_hash"] = file_sha256(path)
254
  except Exception as e:
255
+ st.sidebar.error(f"Failed to load video: {e}")
256
 
257
  if st.session_state["videos"]:
258
  try:
 
284
  pass
285
  st.sidebar.write("Title:", Path(st.session_state["videos"]).name)
286
 
 
287
  col1, col2 = st.columns([1, 3])
288
  with col1:
289
  if st.session_state.get("busy"):
 
296
  with col2:
297
  pass
298
 
 
299
  def get_runtime_api_key():
300
  key = API_KEY_INPUT.strip() if API_KEY_INPUT else ""
301
  if key:
 
310
  runtime_key = get_runtime_api_key()
311
  if not runtime_key:
312
  st.error("Google API key not set. Provide in Settings or set GOOGLE_API_KEY in environment.")
313
+ elif not HAS_GENAI:
314
+ st.error("google.generativeai SDK not available. Install the SDK to enable uploads/generation.")
315
  else:
316
  try:
317
  st.session_state["busy"] = True
 
326
  if processed and st.session_state.get("last_loaded_path") == current_path and st.session_state.get("file_hash") == current_hash:
327
  reupload_needed = False
328
 
329
+ upload_path = current_path
330
+ uploaded = st.session_state.get("uploaded_file")
331
+ # If we need to (re)upload
332
  if reupload_needed:
 
 
333
  local_path = current_path
334
  fast_mode = st.session_state.get("fast_mode", False)
 
335
  try:
336
  file_size_mb = os.path.getsize(local_path) / (1024 * 1024)
337
  except Exception:
 
349
  genai.configure(api_key=runtime_key)
350
  with st.spinner("Uploading video..."):
351
  uploaded = upload_video_sdk(upload_path)
352
+ processed = wait_for_processed(uploaded, timeout=600)
353
  st.session_state["uploaded_file"] = uploaded
354
  st.session_state["processed_file"] = processed
355
  st.session_state["last_loaded_path"] = current_path
356
  st.session_state["file_hash"] = current_hash
357
 
358
+ # Do not delete original local file until entire generation succeeds.
359
+ else:
360
+ uploaded = st.session_state.get("uploaded_file")
361
+ processed = st.session_state.get("processed_file")
 
 
 
 
 
362
 
363
  prompt_text = (analysis_prompt or default_prompt).strip()
364
  if st.session_state.get("fast_mode"):
 
373
 
374
  def call_responses_once(model_used, system_msg, user_msg, fname, max_tokens):
375
  genai.configure(api_key=runtime_key)
376
+ for attempt in range(2):
377
+ try:
378
+ try:
379
+ response = genai.responses.generate(
380
+ model=model_used,
381
+ messages=[system_msg, user_msg],
382
+ files=[{"name": fname}],
383
+ max_output_tokens=max_tokens,
384
+ )
385
+ except TypeError:
386
+ response = genai.responses.generate(
387
+ model=model_used,
388
+ input=[{"text": user_msg["content"], "files": [{"name": fname}]}],
389
+ max_output_tokens=max_tokens,
390
+ )
391
+ return response
392
+ except Exception as e:
393
+ if attempt == 0:
394
+ time.sleep(1.0)
395
+ continue
396
+ raise
397
+
398
+ fname = file_name_or_id(processed) or file_name_or_id(uploaded)
399
  if not fname:
400
  try:
401
  uri = getattr(processed, "uri", None) or (processed.get("uri") if isinstance(processed, dict) else None)
 
470
  st.subheader("Analysis Result")
471
  st.markdown(out or "_(no text returned)_")
472
 
473
+ # delete local copies now that generation succeeded (if upload created temporary compressed file)
474
+ try:
475
+ if reupload_needed:
476
+ if upload_path and Path(upload_path).exists() and Path(upload_path) != Path(current_path):
477
+ Path(upload_path).unlink(missing_ok=True)
478
+ # optionally remove original local file to avoid persistence
479
+ Path(current_path).unlink(missing_ok=True)
480
+ st.session_state["videos"] = ""
481
+ except Exception:
482
+ pass
483
+
484
  with st.expander("Debug (compact)", expanded=False):
485
  try:
486
  info = {
487
  "model": model_used,
488
  "output_tokens": output_tokens,
489
  "upload_succeeded": bool(st.session_state.get("uploaded_file")),
490
+ "processed_state": getattr(st.session_state.get("processed_file"), "state", None) if st.session_state.get("processed_file") else None,
491
  }
492
  st.write(info)
493
+ # truncated response keys for debugging
494
+ try:
495
+ if isinstance(response, dict):
496
+ keys = list(response.keys())[:20]
497
+ else:
498
+ keys = [k for k in dir(response) if not k.startswith("_")][:20]
499
+ st.write({"response_keys_or_attrs": keys})
500
+ except Exception:
501
+ pass
502
  except Exception:
503
  st.write("Debug info unavailable")
504
 
505
  except Exception as e:
506
+ st.session_state["last_error"] = str(e)
507
+ st.error(f"An error occurred while generating the story: {e}")
508
  finally:
509
  st.session_state["busy"] = False
510
 
 
520
  # delete uploaded files (local + cloud if possible)
521
  with st.sidebar.expander("Manage uploads", expanded=False):
522
  if st.button("Delete uploaded files (local + cloud)"):
 
523
  for f in glob(str(DATA_DIR / "*")):
524
  try:
525
  Path(f).unlink(missing_ok=True)
 
531
  st.session_state["last_loaded_path"] = ""
532
  st.session_state["analysis_out"] = ""
533
  st.session_state["file_hash"] = None
 
534
  try:
535
  fname = file_name_or_id(st.session_state.get("uploaded_file"))
536
  if fname and delete_file and HAS_GENAI: