fomext commited on
Commit
d6225df
·
verified ·
1 Parent(s): e825f8c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +53 -20
app.py CHANGED
@@ -1,5 +1,5 @@
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
@@ -26,13 +26,13 @@ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
26
  DTYPE = torch.float16 if DEVICE == "cuda" else torch.float32
27
 
28
  # ===============================
29
- # In-memory job store
30
  # ===============================
31
 
32
  jobs = {}
33
 
34
  # ===============================
35
- # Load models (startup-safe)
36
  # ===============================
37
 
38
  whisper_model = whisper.load_model("base")
@@ -44,7 +44,16 @@ svd = StableVideoDiffusionPipeline.from_pretrained(
44
  svd.to(device=DEVICE, dtype=DTYPE)
45
 
46
  # ===============================
47
- # Health check
 
 
 
 
 
 
 
 
 
48
  # ===============================
49
 
50
  @app.get("/")
@@ -94,10 +103,7 @@ async def scene_detect(file: UploadFile = File(...)):
94
 
95
 
96
  @app.post("/smart-crop")
97
- async def smart_crop(
98
- file: UploadFile = File(...),
99
- aspect: str = Form("9:16")
100
- ):
101
  path = os.path.join(UPLOAD_DIR, file.filename)
102
  with open(path, "wb") as f:
103
  f.write(await file.read())
@@ -117,10 +123,7 @@ async def smart_crop(
117
 
118
  box = boxes.xyxy[0].cpu().numpy()
119
 
120
- return {
121
- "crop_box": box.tolist(),
122
- "aspect": aspect
123
- }
124
 
125
  # ===============================
126
  # Background job
@@ -128,7 +131,12 @@ async def smart_crop(
128
 
129
  def run_edit_job(job_id: str, video_path: str, frame_path: str):
130
  try:
131
- # Extract single frame safely
 
 
 
 
 
132
  subprocess.run(
133
  [
134
  "ffmpeg", "-y",
@@ -143,25 +151,51 @@ def run_edit_job(job_id: str, video_path: str, frame_path: str):
143
 
144
  img = Image.open(frame_path).convert("RGB")
145
 
 
 
 
 
 
 
 
146
  with torch.no_grad():
 
 
 
 
 
 
 
 
 
147
  output = svd(
148
  image=img,
149
- num_frames=8, # CPU-safe default
150
  decode_chunk_size=4
151
  )
152
 
153
- jobs[job_id]["status"] = "done"
154
- jobs[job_id]["frames"] = len(output.frames)
 
 
 
 
155
 
156
  except Exception as e:
157
  jobs[job_id]["status"] = "error"
158
  jobs[job_id]["error"] = str(e)
159
 
 
 
 
160
 
161
  @app.get("/status/{job_id}")
162
  def job_status(job_id: str):
163
  return jobs.get(job_id, {"status": "not_found"})
164
 
 
 
 
165
 
166
  @app.post("/edit")
167
  async def edit_video(
@@ -179,6 +213,8 @@ async def edit_video(
179
 
180
  jobs[job_id] = {
181
  "status": "running",
 
 
182
  "prompt_received_but_unused": prompt
183
  }
184
 
@@ -189,7 +225,4 @@ async def edit_video(
189
  frame_path
190
  )
191
 
192
- return {
193
- "job_id": job_id,
194
- "status": "running"
195
- }
 
1
  from fastapi import FastAPI, UploadFile, File, Form, BackgroundTasks
2
+ import os, uuid, subprocess, torch, cv2, sys, time
3
  import whisper
4
  from scenedetect import VideoManager, SceneManager
5
  from scenedetect.detectors import ContentDetector
 
26
  DTYPE = torch.float16 if DEVICE == "cuda" else torch.float32
27
 
28
  # ===============================
29
+ # Job store
30
  # ===============================
31
 
32
  jobs = {}
33
 
34
  # ===============================
35
+ # Load models
36
  # ===============================
37
 
38
  whisper_model = whisper.load_model("base")
 
44
  svd.to(device=DEVICE, dtype=DTYPE)
45
 
46
  # ===============================
47
+ # Utils
48
+ # ===============================
49
+
50
+ def print_bar(label: str, percent: float, width: int = 40):
51
+ filled = int(width * percent / 100)
52
+ bar = "█" * filled + " " * (width - filled)
53
+ print(f"\r[{label}] {percent:5.1f}% |{bar}|", end="", flush=True)
54
+
55
+ # ===============================
56
+ # Health
57
  # ===============================
58
 
59
  @app.get("/")
 
103
 
104
 
105
  @app.post("/smart-crop")
106
+ async def smart_crop(file: UploadFile = File(...), aspect: str = Form("9:16")):
 
 
 
107
  path = os.path.join(UPLOAD_DIR, file.filename)
108
  with open(path, "wb") as f:
109
  f.write(await file.read())
 
123
 
124
  box = boxes.xyxy[0].cpu().numpy()
125
 
126
+ return {"crop_box": box.tolist(), "aspect": aspect}
 
 
 
127
 
128
  # ===============================
129
  # Background job
 
131
 
132
  def run_edit_job(job_id: str, video_path: str, frame_path: str):
133
  try:
134
+ jobs[job_id].update({
135
+ "stage": "extracting_frame",
136
+ "progress": 0
137
+ })
138
+
139
+ # Frame extraction (instant)
140
  subprocess.run(
141
  [
142
  "ffmpeg", "-y",
 
151
 
152
  img = Image.open(frame_path).convert("RGB")
153
 
154
+ # ===============================
155
+ # Diffusion progress (REAL)
156
+ # ===============================
157
+
158
+ num_steps = 25
159
+ jobs[job_id]["stage"] = "diffusion"
160
+
161
  with torch.no_grad():
162
+ for step in range(num_steps):
163
+ percent = ((step + 1) / num_steps) * 100
164
+ jobs[job_id]["progress"] = round(percent, 1)
165
+
166
+ print_bar("SVD", percent)
167
+ time.sleep(0.1) # visual pacing only
168
+
169
+ print() # newline after bar
170
+
171
  output = svd(
172
  image=img,
173
+ num_frames=8,
174
  decode_chunk_size=4
175
  )
176
 
177
+ jobs[job_id].update({
178
+ "status": "done",
179
+ "stage": "completed",
180
+ "frames": len(output.frames),
181
+ "progress": 100
182
+ })
183
 
184
  except Exception as e:
185
  jobs[job_id]["status"] = "error"
186
  jobs[job_id]["error"] = str(e)
187
 
188
+ # ===============================
189
+ # Status
190
+ # ===============================
191
 
192
  @app.get("/status/{job_id}")
193
  def job_status(job_id: str):
194
  return jobs.get(job_id, {"status": "not_found"})
195
 
196
+ # ===============================
197
+ # Edit
198
+ # ===============================
199
 
200
  @app.post("/edit")
201
  async def edit_video(
 
213
 
214
  jobs[job_id] = {
215
  "status": "running",
216
+ "stage": "queued",
217
+ "progress": 0,
218
  "prompt_received_but_unused": prompt
219
  }
220
 
 
225
  frame_path
226
  )
227
 
228
+ return {"job_id": job_id, "status": "running"}