fomext commited on
Commit
c1e1248
·
verified ·
1 Parent(s): fec4e43

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +83 -37
app.py CHANGED
@@ -1,10 +1,15 @@
1
- from fastapi import FastAPI, UploadFile, File, Form
2
  import os, uuid, subprocess, torch, cv2
3
  import whisper
4
  from scenedetect import VideoManager, SceneManager
5
  from scenedetect.detectors import ContentDetector
6
  from ultralytics import YOLO
7
  from diffusers import StableVideoDiffusionPipeline
 
 
 
 
 
8
 
9
  app = FastAPI()
10
 
@@ -13,20 +18,35 @@ OUTPUT_DIR = "outputs"
13
  os.makedirs(UPLOAD_DIR, exist_ok=True)
14
  os.makedirs(OUTPUT_DIR, exist_ok=True)
15
 
 
 
 
 
16
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
 
17
 
18
- # ===== Load models =====
 
 
19
 
20
- whisper_model = whisper.load_model("base")
21
 
 
 
 
 
 
22
  yolo = YOLO("yolov8n.pt")
23
 
24
  svd = StableVideoDiffusionPipeline.from_pretrained(
25
  "stabilityai/stable-video-diffusion-img2vid",
26
- torch_dtype=torch.float16
27
- ).to(DEVICE)
 
28
 
29
- # ===== Endpoints =====
 
 
30
 
31
  @app.post("/captions")
32
  async def captions(file: UploadFile = File(...)):
@@ -87,46 +107,72 @@ async def smart_crop(
87
  "aspect": aspect
88
  }
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  @app.post("/edit")
91
  async def edit_video(
 
92
  file: UploadFile = File(...),
93
- prompt: str = Form(...) # keep prompt for future use
94
  ):
95
- uid = uuid.uuid4().hex
96
- video_path = os.path.join(UPLOAD_DIR, f"{uid}.mp4")
97
- frame_path = os.path.join(OUTPUT_DIR, f"{uid}.png")
 
98
 
99
- # Save video
100
  with open(video_path, "wb") as f:
101
  f.write(await file.read())
102
 
103
- # Extract first frame safely
104
- subprocess.run(
105
- [
106
- "ffmpeg",
107
- "-y",
108
- "-i", video_path,
109
- "-vf", "scale=512:512:force_original_aspect_ratio=decrease",
110
- "-frames:v", "1",
111
- frame_path
112
- ],
113
- check=True
114
- )
115
-
116
- from PIL import Image
117
- img = Image.open(frame_path).convert("RGB")
118
-
119
- # IMPORTANT: SVD does NOT take prompt
120
- with torch.no_grad():
121
- output = svd(
122
- image=img,
123
- num_frames=16,
124
- decode_chunk_size=8
125
- )
126
 
127
- frames = output.frames
 
 
 
 
 
128
 
129
  return {
130
- "prompt_received_but_unused": prompt,
131
- "frames_generated": len(frames)
132
  }
 
1
+ from fastapi import FastAPI, UploadFile, File, Form, BackgroundTasks
2
  import os, uuid, subprocess, torch, cv2
3
  import whisper
4
  from scenedetect import VideoManager, SceneManager
5
  from scenedetect.detectors import ContentDetector
6
  from ultralytics import YOLO
7
  from diffusers import StableVideoDiffusionPipeline
8
+ from PIL import Image
9
+
10
+ # ===============================
11
+ # App + dirs
12
+ # ===============================
13
 
14
  app = FastAPI()
15
 
 
18
  os.makedirs(UPLOAD_DIR, exist_ok=True)
19
  os.makedirs(OUTPUT_DIR, exist_ok=True)
20
 
21
+ # ===============================
22
+ # Device / dtype (CRITICAL)
23
+ # ===============================
24
+
25
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
26
+ DTYPE = torch.float16 if DEVICE == "cuda" else torch.float32
27
 
28
+ # ===============================
29
+ # Job store
30
+ # ===============================
31
 
32
+ jobs = {}
33
 
34
+ # ===============================
35
+ # Load models (safe on startup)
36
+ # ===============================
37
+
38
+ whisper_model = whisper.load_model("base")
39
  yolo = YOLO("yolov8n.pt")
40
 
41
  svd = StableVideoDiffusionPipeline.from_pretrained(
42
  "stabilityai/stable-video-diffusion-img2vid",
43
+ dtype=DTYPE
44
+ )
45
+ svd.to(DEVICE)
46
 
47
+ # ===============================
48
+ # Endpoints
49
+ # ===============================
50
 
51
  @app.post("/captions")
52
  async def captions(file: UploadFile = File(...)):
 
107
  "aspect": aspect
108
  }
109
 
110
+ # ===============================
111
+ # Background job
112
+ # ===============================
113
+
114
+ def run_edit_job(job_id: str, video_path: str, frame_path: str):
115
+ try:
116
+ subprocess.run(
117
+ [
118
+ "ffmpeg", "-y",
119
+ "-i", video_path,
120
+ "-vf", "scale=512:512:force_original_aspect_ratio=decrease",
121
+ "-frames:v", "1",
122
+ frame_path
123
+ ],
124
+ check=True
125
+ )
126
+
127
+ img = Image.open(frame_path).convert("RGB")
128
+
129
+ with torch.no_grad():
130
+ output = svd(
131
+ image=img,
132
+ num_frames=16,
133
+ decode_chunk_size=8
134
+ )
135
+
136
+ jobs[job_id]["status"] = "done"
137
+ jobs[job_id]["frames"] = len(output.frames)
138
+
139
+ except Exception as e:
140
+ jobs[job_id]["status"] = "error"
141
+ jobs[job_id]["error"] = str(e)
142
+
143
+
144
+ @app.get("/status/{job_id}")
145
+ def job_status(job_id: str):
146
+ return jobs.get(job_id, {"status": "not_found"})
147
+
148
+
149
  @app.post("/edit")
150
  async def edit_video(
151
+ background_tasks: BackgroundTasks,
152
  file: UploadFile = File(...),
153
+ prompt: str = Form(...)
154
  ):
155
+ job_id = uuid.uuid4().hex
156
+
157
+ video_path = os.path.join(UPLOAD_DIR, f"{job_id}.mp4")
158
+ frame_path = os.path.join(OUTPUT_DIR, f"{job_id}.png")
159
 
 
160
  with open(video_path, "wb") as f:
161
  f.write(await file.read())
162
 
163
+ jobs[job_id] = {
164
+ "status": "running",
165
+ "prompt_received_but_unused": prompt
166
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
 
168
+ background_tasks.add_task(
169
+ run_edit_job,
170
+ job_id,
171
+ video_path,
172
+ frame_path
173
+ )
174
 
175
  return {
176
+ "job_id": job_id,
177
+ "status": "running"
178
  }