File size: 4,810 Bytes
2ab6e0a
 
 
 
c57c49d
2ab6e0a
 
c57c49d
05e7070
2ab6e0a
 
 
 
 
 
 
c57c49d
 
 
 
 
 
 
 
2ab6e0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51780f2
 
 
 
 
 
 
2ab6e0a
 
 
 
 
c57c49d
2ab6e0a
51780f2
2ab6e0a
 
 
 
 
 
 
 
 
 
 
 
 
51780f2
c57c49d
 
 
 
 
51780f2
c57c49d
2ab6e0a
 
 
 
 
 
 
 
 
 
 
 
 
 
a8d3381
2ab6e0a
 
 
 
a8d3381
2ab6e0a
 
51780f2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2ab6e0a
 
05e7070
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import logging
import os
import tempfile
from pathlib import Path
from typing import Optional

from fastapi import BackgroundTasks, FastAPI, File, Form, HTTPException, UploadFile
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
import uvicorn

from inference import run_inference

logging.basicConfig(level=logging.INFO)

app = FastAPI(title="Video Processing Backend")
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
    expose_headers=["x-mission-summary"],
)


def _save_upload_to_tmp(upload: UploadFile) -> str:
    suffix = Path(upload.filename or "upload.mp4").suffix or ".mp4"
    fd, path = tempfile.mkstemp(prefix="input_", suffix=suffix, dir="/tmp")
    os.close(fd)
    with open(path, "wb") as buffer:
        data = upload.file.read()
        buffer.write(data)
    return path


def _safe_delete(path: str) -> None:
    try:
        os.remove(path)
    except FileNotFoundError:
        return
    except Exception:
        logging.exception("Failed to remove temporary file: %s", path)


def _schedule_cleanup(background_tasks: BackgroundTasks, path: str) -> None:
    def _cleanup(target: str = path) -> None:
        _safe_delete(target)

    background_tasks.add_task(_cleanup)


def _validate_inputs(video: UploadFile | None, prompt: str | None) -> None:
    if video is None:
        raise HTTPException(status_code=400, detail="Video file is required.")
    if not prompt:
        raise HTTPException(status_code=400, detail="Prompt is required.")


@app.post("/process_video")
async def process_video(
    background_tasks: BackgroundTasks,
    video: UploadFile = File(...),
    prompt: str = Form(...),
    detector: Optional[str] = Form(None),
):
    _validate_inputs(video, prompt)

    try:
        input_path = _save_upload_to_tmp(video)
    except Exception:
        logging.exception("Failed to save uploaded file.")
        raise HTTPException(status_code=500, detail="Failed to save uploaded video.")
    finally:
        await video.close()

    fd, output_path = tempfile.mkstemp(prefix="output_", suffix=".mp4", dir="/tmp")
    os.close(fd)

    try:
        output_path, _, _ = run_inference(
            input_path,
            output_path,
            prompt,
            max_frames=10,
            detector_name=detector,
            generate_summary=False,
        )
    except ValueError as exc:
        logging.exception("Video decoding failed.")
        _safe_delete(input_path)
        _safe_delete(output_path)
        raise HTTPException(status_code=500, detail=str(exc))
    except Exception as exc:
        logging.exception("Inference failed.")
        _safe_delete(input_path)
        _safe_delete(output_path)
        return JSONResponse(status_code=500, content={"error": str(exc)})

    _schedule_cleanup(background_tasks, input_path)
    _schedule_cleanup(background_tasks, output_path)

    response = FileResponse(
        path=output_path,
        media_type="video/mp4",
        filename="processed.mp4",
    )
    return response


@app.post("/mission_summary")
async def mission_summary(
    video: UploadFile = File(...),
    prompt: str = Form(...),
    detector: Optional[str] = Form(None),
):
    _validate_inputs(video, prompt)
    try:
        input_path = _save_upload_to_tmp(video)
    except Exception:
        logging.exception("Failed to save uploaded file.")
        raise HTTPException(status_code=500, detail="Failed to save uploaded video.")
    finally:
        await video.close()

    try:
        _, mission_plan, mission_summary = run_inference(
            input_path,
            output_video_path=None,
            mission_prompt=prompt,
            max_frames=10,
            detector_name=detector,
            write_output_video=False,
            generate_summary=True,
        )
    except ValueError as exc:
        logging.exception("Video decoding failed.")
        _safe_delete(input_path)
        raise HTTPException(status_code=500, detail=str(exc))
    except Exception as exc:
        logging.exception("Summary generation failed.")
        _safe_delete(input_path)
        return JSONResponse(status_code=500, content={"error": str(exc)})

    _safe_delete(input_path)
    return {
        "mission_plan": mission_plan.to_dict(),
        "mission_summary": mission_summary or "",
    }

if __name__ == "__main__":
    uvicorn.run("app:app", host="0.0.0.0", port=7860, reload=False)
@app.get("/", response_class=HTMLResponse)
async def demo_page() -> str:
    demo_path = Path(__file__).with_name("demo.html")
    try:
        return demo_path.read_text(encoding="utf-8")
    except FileNotFoundError:
        return "<h1>Demo page missing</h1>"