from fastapi.staticfiles import StaticFiles from fastapi import FastAPI, Form from moviepy.editor import VideoFileClip from datetime import datetime, timedelta from pathlib import Path from PIL import Image import threading app = FastAPI(title="Frame Generator") app.mount("/frames", StaticFiles(directory="frames"), name="frames") VIDEO_PATH = "demo1.mp4" FRAME_DIR = Path("frames") FRAME_STEP = 15 MAX_FRAMES = 4 # Global session state (OK for your current testing) session = { "camera_id": None, "start_time": None, "date": None, "current_index": 0, "frames": [] } def generate_frames_once(): FRAME_DIR.mkdir(exist_ok=True) clip = VideoFileClip(VIDEO_PATH) duration = int(clip.duration) frames = [] for i, t in enumerate(range(0, duration, FRAME_STEP)): if i >= MAX_FRAMES: break frame = clip.get_frame(t) img = Image.fromarray(frame) fname = f"frame_{i:03d}.jpg" fpath = FRAME_DIR / fname img.save(fpath, "JPEG", quality=90) frames.append({ "index": i, "offset": t, "path": f"/frames/{fname}" }) clip.close() return frames # Generate frames at startup (ONCE) @app.on_event("startup") def startup_event(): session["frames"] = generate_frames_once() @app.post("/start") def start_session( camera_id: str = Form(...), current_time: str = Form(...) ): """ current_time format: DD-MM-YYYY HH:MM:SS """ start_dt = datetime.strptime(current_time, "%d-%m-%Y %H:%M:%S") session["camera_id"] = camera_id session["start_time"] = start_dt session["date"] = start_dt.strftime("%d-%m-%Y") session["current_index"] = 0 return { "status": "started", "camera_id": camera_id, "date": session["date"], "total_frames": len(session["frames"]) } @app.get("/next-frame") def get_next_frame(): idx = session["current_index"] if idx >= len(session["frames"]): return {"done": True} frame = session["frames"][idx] ts = session["start_time"] + timedelta(seconds=frame["offset"]) session["current_index"] += 1 return { "done": False, "frame_index": frame["index"], "timestamp": ts.strftime("%H:%M:%S"), "image_url": frame["path"] }