"""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//``. 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()