ai-video-intelligence-agent / run_analysis.py
pmootr's picture
Add URL/YouTube ingestion, "add-a-sample" workflow, and architecture doc
aa5e3cc
Raw
History Blame Contribute Delete
3.41 kB
"""CLI for running the local analysis pipeline on a video file.
Examples
--------
# Analyze a local video at the default 2s frame interval:
python run_analysis.py path/to/lecture.mp4
# Analyze straight from a URL (YouTube, Vimeo, direct link, ...):
python run_analysis.py "https://www.youtube.com/watch?v=XXXXXXXXXXX"
# Custom id + frame interval, and print the resulting bookmarks:
python run_analysis.py lecture.mp4 --video-id my_video --interval 3 --show-bookmarks
Artifacts are written to ``data/outputs/<video_id>/``. Requires the local
dependencies (``pip install -r requirements-local.txt``) and ffmpeg. URL
ingestion additionally uses yt-dlp — only download videos you have the right to
use, and respect each platform's Terms of Service.
"""
from __future__ import annotations
import argparse
import json
from src.config import get_config
from src.video_source import is_url
def main() -> None:
parser = argparse.ArgumentParser(
description="Analyze a short video locally, from a file path or a URL."
)
parser.add_argument("source", type=str, help="Local video path or http(s) URL.")
parser.add_argument("--video-id", type=str, default=None, help="Output id.")
parser.add_argument(
"--interval", type=float, default=None, help="Frame sampling interval (s)."
)
parser.add_argument(
"--show-bookmarks", action="store_true", help="Print bookmarks at the end."
)
parser.add_argument(
"--as-sample",
action="store_true",
help="Promote the result straight into data/sample_outputs/ as a demo "
"sample (instead of data/outputs/), so the dashboard/Space can serve it.",
)
args = parser.parse_args()
from pathlib import Path
if not is_url(args.source) and not Path(args.source).exists():
raise SystemExit(f"Video not found (and not a URL): {args.source}")
# Force live mode for the CLI regardless of demo defaults.
config = get_config()
config.demo_mode = False
config.use_precomputed = False
if args.interval is not None:
config.frame_interval_sec = args.interval
from src import storage
from src.pipeline import analyze_video # lazy import of heavy deps
# When promoting to a sample we persist ourselves (to sample_outputs).
artifacts = analyze_video(
args.source, video_id=args.video_id, config=config, persist=not args.as_sample
)
metrics = artifacts["metrics"]
vid = metrics["video_id"]
print(f"\n✅ Analysis complete for '{vid}'")
print(f" total processing: {metrics['total_processing_sec']} s")
print(f" counts: {json.dumps(metrics['counts'])}")
if args.as_sample:
storage.save_as_sample(vid, artifacts)
print(f"\n📦 Promoted to demo sample: data/sample_outputs/{vid}_*.json")
print(" To publish it to your Space (only for CC/public-domain/your-own")
print(" /permissioned content):")
print(f" git add data/sample_outputs/{vid}_*.json")
print(f' git commit -m "Add sample: {vid}"')
print(" git push hf main")
else:
print(f" output dir: data/outputs/{vid}/")
if args.show_bookmarks:
print("\nBookmarks:")
for b in artifacts["bookmarks"]:
print(f" {b['timestamp']} {b['title']} ({b['reason']})")
if __name__ == "__main__":
main()