Spaces:
Sleeping
Sleeping
Update streamlit_app.py
Browse files- streamlit_app.py +35 -25
streamlit_app.py
CHANGED
|
@@ -9,7 +9,7 @@ Features
|
|
| 9 |
* Download videos from direct links, Twitter, or any site supported by yt‑dlp.
|
| 10 |
* Convert to MP4 (ffmpeg) and compress if larger than a user‑defined threshold.
|
| 11 |
* Send the video (base64‑encoded) + a custom prompt to Gemini‑Flash models.
|
| 12 |
-
* Simple sidebar UI with clear
|
| 13 |
"""
|
| 14 |
|
| 15 |
# ----------------------------------------------------------------------
|
|
@@ -22,7 +22,7 @@ import string
|
|
| 22 |
import traceback
|
| 23 |
from pathlib import Path
|
| 24 |
from typing import Tuple, Optional
|
| 25 |
-
from difflib import SequenceMatcher
|
| 26 |
|
| 27 |
# ----------------------------------------------------------------------
|
| 28 |
# Third‑party libraries
|
|
@@ -67,23 +67,27 @@ DEFAULT_PROMPT = (
|
|
| 67 |
# ----------------------------------------------------------------------
|
| 68 |
# Session‑state defaults (run once per session)
|
| 69 |
# ----------------------------------------------------------------------
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
|
| 88 |
# ----------------------------------------------------------------------
|
| 89 |
# Helper utilities
|
|
@@ -168,11 +172,13 @@ def _download_with_yt_dlp(url: str, dst: Path, password: str = "") -> Path:
|
|
| 168 |
except Exception as e:
|
| 169 |
raise RuntimeError(f"yt‑dlp could not download the URL: {e}") from e
|
| 170 |
|
|
|
|
| 171 |
if isinstance(info, dict) and "id" in info:
|
| 172 |
candidate = dst / f"{info['id']}.{info.get('ext', 'mp4')}"
|
| 173 |
if candidate.exists():
|
| 174 |
return _convert_to_mp4(candidate)
|
| 175 |
|
|
|
|
| 176 |
files = list(dst.iterdir())
|
| 177 |
if not files:
|
| 178 |
raise RuntimeError("yt‑dlp did not produce any files.")
|
|
@@ -311,7 +317,9 @@ def main() -> None:
|
|
| 311 |
if st.session_state.get("video_path"):
|
| 312 |
try:
|
| 313 |
mp4 = _convert_to_mp4(Path(st.session_state["video_path"]))
|
| 314 |
-
|
|
|
|
|
|
|
| 315 |
except Exception:
|
| 316 |
st.sidebar.write("Preview unavailable")
|
| 317 |
|
|
@@ -326,9 +334,11 @@ def main() -> None:
|
|
| 326 |
"url": "",
|
| 327 |
"video_path": "",
|
| 328 |
"analysis_out": "",
|
|
|
|
| 329 |
"last_error": "",
|
| 330 |
"busy": False,
|
| 331 |
"show_raw_on_error": False,
|
|
|
|
| 332 |
}
|
| 333 |
)
|
| 334 |
st.success("Session cleared.")
|
|
@@ -347,7 +357,7 @@ def main() -> None:
|
|
| 347 |
st.info("Load a video first.", icon="ℹ️")
|
| 348 |
|
| 349 |
# ------------------------------------------------------------------
|
| 350 |
-
# Generation
|
| 351 |
# ------------------------------------------------------------------
|
| 352 |
if generate_now and not st.session_state.get("busy", False):
|
| 353 |
api_key = st.session_state.get("api_key") or os.getenv("GOOGLE_API_KEY")
|
|
@@ -386,7 +396,7 @@ def main() -> None:
|
|
| 386 |
|
| 387 |
cleaned = _strip_prompt_echo(st.session_state["prompt"], raw_out)
|
| 388 |
st.session_state["analysis_out"] = cleaned
|
| 389 |
-
st.session_state["show_analysis"] = True
|
| 390 |
st.success("Analysis generated.")
|
| 391 |
st.markdown(cleaned or "*(no output)*")
|
| 392 |
|
|
@@ -402,12 +412,11 @@ def main() -> None:
|
|
| 402 |
st.session_state["busy"] = False
|
| 403 |
|
| 404 |
# ------------------------------------------------------------------
|
| 405 |
-
# Results
|
| 406 |
# ------------------------------------------------------------------
|
| 407 |
if st.session_state.get("show_analysis"):
|
| 408 |
st.subheader("📝 Analysis")
|
| 409 |
st.markdown(st.session_state["analysis_out"])
|
| 410 |
-
# Reset the flag so the block won’t run on the next automatic rerun
|
| 411 |
st.session_state["show_analysis"] = False
|
| 412 |
|
| 413 |
# Full Gemini output – collapsed by default, expanded on error
|
|
@@ -424,5 +433,6 @@ def main() -> None:
|
|
| 424 |
with st.expander("❗️ Error details"):
|
| 425 |
st.code(st.session_state["last_error_detail"], language="text")
|
| 426 |
|
|
|
|
| 427 |
if __name__ == "__main__":
|
| 428 |
-
main()
|
|
|
|
| 9 |
* Download videos from direct links, Twitter, or any site supported by yt‑dlp.
|
| 10 |
* Convert to MP4 (ffmpeg) and compress if larger than a user‑defined threshold.
|
| 11 |
* Send the video (base64‑encoded) + a custom prompt to Gemini‑Flash models.
|
| 12 |
+
* Simple sidebar UI with clear video handling.
|
| 13 |
"""
|
| 14 |
|
| 15 |
# ----------------------------------------------------------------------
|
|
|
|
| 22 |
import traceback
|
| 23 |
from pathlib import Path
|
| 24 |
from typing import Tuple, Optional
|
| 25 |
+
from difflib import SequenceMatcher
|
| 26 |
|
| 27 |
# ----------------------------------------------------------------------
|
| 28 |
# Third‑party libraries
|
|
|
|
| 67 |
# ----------------------------------------------------------------------
|
| 68 |
# Session‑state defaults (run once per session)
|
| 69 |
# ----------------------------------------------------------------------
|
| 70 |
+
def _init_state() -> None:
|
| 71 |
+
defaults = {
|
| 72 |
+
"url": "",
|
| 73 |
+
"video_path": "",
|
| 74 |
+
"model_input": DEFAULT_MODEL,
|
| 75 |
+
"prompt": DEFAULT_PROMPT,
|
| 76 |
+
"api_key": os.getenv("GOOGLE_API_KEY", ""),
|
| 77 |
+
"video_password": "",
|
| 78 |
+
"compress_mb": 200,
|
| 79 |
+
"busy": False,
|
| 80 |
+
"last_error": "",
|
| 81 |
+
"analysis_out": "",
|
| 82 |
+
"raw_output": "",
|
| 83 |
+
"last_error_detail": "",
|
| 84 |
+
"show_raw_on_error": False,
|
| 85 |
+
"show_analysis": False,
|
| 86 |
+
}
|
| 87 |
+
for k, v in defaults.items():
|
| 88 |
+
st.session_state.setdefault(k, v)
|
| 89 |
+
|
| 90 |
+
_init_state()
|
| 91 |
|
| 92 |
# ----------------------------------------------------------------------
|
| 93 |
# Helper utilities
|
|
|
|
| 172 |
except Exception as e:
|
| 173 |
raise RuntimeError(f"yt‑dlp could not download the URL: {e}") from e
|
| 174 |
|
| 175 |
+
# yt‑dlp may return a dict with the final filename
|
| 176 |
if isinstance(info, dict) and "id" in info:
|
| 177 |
candidate = dst / f"{info['id']}.{info.get('ext', 'mp4')}"
|
| 178 |
if candidate.exists():
|
| 179 |
return _convert_to_mp4(candidate)
|
| 180 |
|
| 181 |
+
# Fallback: pick the newest file in the output folder
|
| 182 |
files = list(dst.iterdir())
|
| 183 |
if not files:
|
| 184 |
raise RuntimeError("yt‑dlp did not produce any files.")
|
|
|
|
| 317 |
if st.session_state.get("video_path"):
|
| 318 |
try:
|
| 319 |
mp4 = _convert_to_mp4(Path(st.session_state["video_path"]))
|
| 320 |
+
with open(mp4, "rb") as f:
|
| 321 |
+
video_bytes = f.read()
|
| 322 |
+
st.sidebar.video(video_bytes)
|
| 323 |
except Exception:
|
| 324 |
st.sidebar.write("Preview unavailable")
|
| 325 |
|
|
|
|
| 334 |
"url": "",
|
| 335 |
"video_path": "",
|
| 336 |
"analysis_out": "",
|
| 337 |
+
"raw_output": "",
|
| 338 |
"last_error": "",
|
| 339 |
"busy": False,
|
| 340 |
"show_raw_on_error": False,
|
| 341 |
+
"show_analysis": False,
|
| 342 |
}
|
| 343 |
)
|
| 344 |
st.success("Session cleared.")
|
|
|
|
| 357 |
st.info("Load a video first.", icon="ℹ️")
|
| 358 |
|
| 359 |
# ------------------------------------------------------------------
|
| 360 |
+
# Generation handling
|
| 361 |
# ------------------------------------------------------------------
|
| 362 |
if generate_now and not st.session_state.get("busy", False):
|
| 363 |
api_key = st.session_state.get("api_key") or os.getenv("GOOGLE_API_KEY")
|
|
|
|
| 396 |
|
| 397 |
cleaned = _strip_prompt_echo(st.session_state["prompt"], raw_out)
|
| 398 |
st.session_state["analysis_out"] = cleaned
|
| 399 |
+
st.session_state["show_analysis"] = True
|
| 400 |
st.success("Analysis generated.")
|
| 401 |
st.markdown(cleaned or "*(no output)*")
|
| 402 |
|
|
|
|
| 412 |
st.session_state["busy"] = False
|
| 413 |
|
| 414 |
# ------------------------------------------------------------------
|
| 415 |
+
# Results display (once per successful generation)
|
| 416 |
# ------------------------------------------------------------------
|
| 417 |
if st.session_state.get("show_analysis"):
|
| 418 |
st.subheader("📝 Analysis")
|
| 419 |
st.markdown(st.session_state["analysis_out"])
|
|
|
|
| 420 |
st.session_state["show_analysis"] = False
|
| 421 |
|
| 422 |
# Full Gemini output – collapsed by default, expanded on error
|
|
|
|
| 433 |
with st.expander("❗️ Error details"):
|
| 434 |
st.code(st.session_state["last_error_detail"], language="text")
|
| 435 |
|
| 436 |
+
|
| 437 |
if __name__ == "__main__":
|
| 438 |
+
main()
|