diff --git a/backend/app/models/ai.py b/backend/app/models/ai.py index a4102e6..c5697ff 100644 --- a/backend/app/models/ai.py +++ b/backend/app/models/ai.py @@ -74,6 +74,7 @@ class VideoRequest(BaseModel): prompt: str duration_seconds: int | None = None aspect_ratio: str = "16:9" + resolution: str | None = None # e.g. "480p", "720p", "1080p" class VideoFromImageRequest(BaseModel): @@ -82,6 +83,7 @@ class VideoFromImageRequest(BaseModel): prompt: str duration_seconds: int | None = None aspect_ratio: str = "16:9" + resolution: str | None = None # e.g. "480p", "720p", "1080p" class VideoResponse(BaseModel): diff --git a/backend/app/routers/generate.py b/backend/app/routers/generate.py index 2e439d3..4a75ec2 100644 --- a/backend/app/routers/generate.py +++ b/backend/app/routers/generate.py @@ -100,6 +100,7 @@ async def generate_video( prompt=body.prompt, duration_seconds=body.duration_seconds, aspect_ratio=body.aspect_ratio, + resolution=body.resolution, ) except Exception as exc: raise HTTPException( @@ -131,6 +132,7 @@ async def generate_video_from_image( prompt=body.prompt, duration_seconds=body.duration_seconds, aspect_ratio=body.aspect_ratio, + resolution=body.resolution, ) except Exception as exc: raise HTTPException( diff --git a/backend/app/services/openrouter.py b/backend/app/services/openrouter.py index d53c21e..9b5e8f3 100644 --- a/backend/app/services/openrouter.py +++ b/backend/app/services/openrouter.py @@ -81,6 +81,7 @@ async def generate_video( prompt: str, duration_seconds: int | None = None, aspect_ratio: str = "16:9", + resolution: str | None = None, ) -> dict[str, Any]: """Request text-to-video generation via OpenRouter.""" base_url = os.getenv("OPENROUTER_BASE_URL", OPENROUTER_BASE_URL) @@ -91,6 +92,8 @@ async def generate_video( } if duration_seconds is not None: payload["duration_seconds"] = duration_seconds + if resolution is not None: + payload["resolution"] = resolution async with httpx.AsyncClient(timeout=120) as client: resp = client.build_request( "POST", f"{base_url}/videos", headers=_headers(), json=payload @@ -106,6 +109,7 @@ async def generate_video_from_image( prompt: str, duration_seconds: int | None = None, aspect_ratio: str = "16:9", + resolution: str | None = None, ) -> dict[str, Any]: """Request image-to-video generation via OpenRouter.""" base_url = os.getenv("OPENROUTER_BASE_URL", OPENROUTER_BASE_URL) @@ -117,6 +121,8 @@ async def generate_video_from_image( } if duration_seconds is not None: payload["duration_seconds"] = duration_seconds + if resolution is not None: + payload["resolution"] = resolution async with httpx.AsyncClient(timeout=120) as client: resp = client.build_request( "POST", f"{base_url}/videos", headers=_headers(), json=payload diff --git a/frontend/app/main.py b/frontend/app/main.py index 02484b8..f72ac9d 100644 --- a/frontend/app/main.py +++ b/frontend/app/main.py @@ -5,6 +5,7 @@ import httpx from flask import ( Flask, flash, + jsonify, redirect, render_template, request, @@ -172,18 +173,25 @@ def generate_video(): if request.method == "POST": mode = request.form.get("mode", "text") token = session["access_token"] + duration_raw = request.form.get("duration_seconds", "") + duration = int(duration_raw) if duration_raw.strip().isdigit() else None + resolution = request.form.get("resolution", "").strip() or None if mode == "image": resp = _api("POST", "/generate/video/from-image", token=token, json={ "model": request.form.get("model", "").strip(), "image_url": request.form.get("image_url", "").strip(), "prompt": request.form.get("prompt", "").strip(), "aspect_ratio": request.form.get("aspect_ratio", "16:9"), + "duration_seconds": duration, + "resolution": resolution, }) else: resp = _api("POST", "/generate/video", token=token, json={ "model": request.form.get("model", "").strip(), "prompt": request.form.get("prompt", "").strip(), "aspect_ratio": request.form.get("aspect_ratio", "16:9"), + "duration_seconds": duration, + "resolution": resolution, }) if resp.status_code == 200: result = resp.json() @@ -192,6 +200,21 @@ def generate_video(): return render_template("generate_video.html", result=result, error=error) +@app.get("/generate/video/status") +@login_required +def generate_video_status(): + """Proxy video status polling to the backend.""" + polling_url = request.args.get("polling_url", "") + if not polling_url: + return jsonify({"error": "polling_url required"}), 400 + resp = _api( + "GET", "/generate/video/status", + token=session["access_token"], + params={"polling_url": polling_url}, + ) + return jsonify(resp.json()), resp.status_code + + # ── Admin ───────────────────────────────────────────────────────────────── @app.get("/admin")