diff --git a/app/main.py b/app/main.py index 8da7762..34278f1 100644 --- a/app/main.py +++ b/app/main.py @@ -1249,6 +1249,72 @@ async def source_video(job_id: str, user: str = Depends(check_auth)): ) +@app.get("/api/preview-clip/{job_id}") +async def preview_clip( + job_id: str, + start: float, + end: float, + user: str = Depends(check_auth), +): + """Live preview odseka — low-quality 480p clip za hiter predogled. + + BREZ reframe (16:9 ostane), BREZ napisov, BREZ face tracking. + Cilj: ~2-3s render za takojšen feedback med Edit dragom. + + Cache po start+end timestampih da ponovljeni request ne renderira ponovno. + """ + job = load_job(job_id) + if not job: + raise HTTPException(404, "Ne obstaja") + src = job.get("input_path") + if not src or not Path(src).exists(): + raise HTTPException(404, "Original video ne obstaja") + + if end <= start: + raise HTTPException(400, "end mora biti večji od start") + duration = end - start + if duration < 1: + raise HTTPException(400, "Trajanje vsaj 1s") + if duration > 90: + raise HTTPException(400, "Trajanje največ 90s") + + # Cache key — preview se shrani in ne re-renderira če isti range + cache_key = f"{job_id}_preview_{start:.1f}_{end:.1f}.mp4" + cache_path = OUTPUT_DIR / cache_key + + if not cache_path.exists(): + # ffmpeg low-q clip — copy stream če gre, sicer hitro re-encode + # -ss pred -i: hiter seek (keyframe), idealno za preview + cmd = [ + "ffmpeg", "-y", + "-ss", f"{max(0, start - 0.5):.2f}", # 0.5s buffer za keyframe + "-i", str(src), + "-ss", f"{min(0.5, start):.2f}", # fine seek + "-t", f"{duration:.2f}", + "-vf", "scale=854:480:force_original_aspect_ratio=decrease", # 480p + "-c:v", "libx264", + "-preset", "ultrafast", # NAJHITREJŠI preset + "-crf", "30", # nižja kvaliteta = hitrejše + "-c:a", "aac", + "-b:a", "96k", + "-movflags", "+faststart", + "-loglevel", "error", + str(cache_path), + ] + try: + proc = subprocess.run(cmd, capture_output=True, text=True, timeout=20) + if proc.returncode != 0 or not cache_path.exists(): + raise HTTPException(500, f"FFmpeg failed: {proc.stderr[-300:]}") + except subprocess.TimeoutExpired: + raise HTTPException(500, "Preview render timeout (>20s)") + + return FileResponse( + path=cache_path, + media_type="video/mp4", + headers={"Accept-Ranges": "bytes", "Cache-Control": "max-age=300"}, + ) + + @app.get("/api/transcript/{job_id}") async def get_transcript(job_id: str, user: str = Depends(check_auth)): """Vrne transkript (segmente + words) za editor.""" diff --git a/templates/index.html b/templates/index.html index a1887dd..a992e30 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1065,10 +1065,12 @@ Trajanje: ${(endInit-startInit).toFixed(1)}s