factorstudios commited on
Commit
a70e27b
·
verified ·
1 Parent(s): 6bf2a59

Update server.py

Browse files
Files changed (1) hide show
  1. server.py +23 -37
server.py CHANGED
@@ -12,7 +12,6 @@ from typing import List, Dict, Optional, Tuple
12
 
13
  from fastapi import FastAPI, HTTPException
14
  from fastapi.responses import JSONResponse
15
- from contextlib import asynccontextmanager
16
  import uvicorn
17
 
18
  try:
@@ -33,15 +32,7 @@ if not HF_TOKEN:
33
  print("Error: Missing HF_TOKEN in .env")
34
  exit(1)
35
 
36
- @asynccontextmanager
37
- async def lifespan(app: FastAPI):
38
- """Load Whisper in background, then kick off video processing."""
39
- loop = asyncio.get_event_loop()
40
- await loop.run_in_executor(None, _load_whisper_model)
41
- asyncio.create_task(scan_and_process_videos())
42
- yield
43
-
44
- app = FastAPI(title="Video Processing Service", lifespan=lifespan)
45
 
46
  # Global state
47
  processing_state = {
@@ -244,7 +235,6 @@ def process_video_segment(
244
  4. Mux processed video with original audio
245
  """
246
  ffmpeg_video_proc = None
247
- cap = None # Declared here so finally block can always release it
248
  temp_wav = output_path.replace(".mp4", "_audio.wav")
249
  temp_video_path = output_path.replace(".mp4", "_noaudio.mp4")
250
 
@@ -334,7 +324,7 @@ def process_video_segment(
334
  frame = cv2.resize(frame, (target_width, target_height), interpolation=cv2.INTER_LANCZOS4)
335
  frame = apply_color_grading_wedding_retro(frame)
336
 
337
- # Set caption for this frame (empty if none)
338
  current_caption = frame_caption_map.get(processed_frames, "")
339
 
340
  if current_caption:
@@ -347,9 +337,9 @@ def process_video_segment(
347
  progress = (processed_frames / target_frames) * 100
348
  print(f"Progress: {progress:.1f}%")
349
 
350
- # Close stdin and wait for FFmpeg to finish encoding
351
  ffmpeg_video_proc.stdin.close()
352
  ffmpeg_video_proc.wait()
 
353
 
354
  if ffmpeg_video_proc.returncode != 0:
355
  print(f"✗ FFmpeg video encoding failed (code {ffmpeg_video_proc.returncode})")
@@ -398,10 +388,6 @@ def process_video_segment(
398
  return False
399
 
400
  finally:
401
- # Always release VideoCapture regardless of success or failure
402
- if cap is not None:
403
- cap.release()
404
- # Always clean up temp files
405
  for tmp in [temp_video_path, temp_wav]:
406
  if tmp and os.path.exists(tmp):
407
  try:
@@ -453,12 +439,11 @@ async def process_movie_segments(movie_name: str) -> bool:
453
  print(f"No segment JSON files found for {movie_name}")
454
  return False
455
 
456
- print(f"Found {len(segment_files)} segments: {segment_files}")
457
  temp_dir = tempfile.mkdtemp()
458
 
459
  try:
460
  for segment_file in segment_files:
461
- print(f"\n── Processing file: {segment_file}")
462
  try:
463
  segment_path = hf_hub_download(
464
  repo_id=HF_DATASET_REPO,
@@ -475,7 +460,7 @@ async def process_movie_segments(movie_name: str) -> bool:
475
  start_time = segment_data.get("start_time", "00:00:00")
476
  end_time = segment_data.get("end_time", "00:10:00")
477
 
478
- print(f"Processing segment {segment_number}: {start_time} to {end_time}")
479
 
480
  output_filename = f"segment-{segment_number:02d}.mp4"
481
  output_path = os.path.join(temp_dir, output_filename)
@@ -488,8 +473,7 @@ async def process_movie_segments(movie_name: str) -> bool:
488
  )
489
 
490
  if not success:
491
- print(f"Failed to process segment {segment_number}, continuing to next...")
492
- processing_state["error_count"] += 1
493
  continue
494
 
495
  upload_path = f"{READY_VIDEOS_FOLDER}/{movie_name}/{output_filename}"
@@ -505,15 +489,8 @@ async def process_movie_segments(movie_name: str) -> bool:
505
  )
506
  print(f"✓ Segment {segment_number} uploaded successfully")
507
 
508
- # Clean up the output file after successful upload
509
- if os.path.exists(output_path):
510
- try:
511
- os.remove(output_path)
512
- except Exception:
513
- pass
514
-
515
  except Exception as e:
516
- print(f"✗ Error processing segment file {segment_file}: {e}")
517
  processing_state["error_count"] += 1
518
  continue
519
 
@@ -529,7 +506,7 @@ async def process_movie_segments(movie_name: str) -> bool:
529
  except Exception as e:
530
  processing_state["error_count"] += 1
531
  processing_state["last_error"] = str(e)
532
- print(f"✗ Error in process_movie_segments: {e}")
533
  return False
534
 
535
 
@@ -539,7 +516,8 @@ async def scan_and_process_videos():
539
  print("Video processing already running, skipping...")
540
  return
541
 
542
- startup_delay = int(os.getenv("STARTUP_DELAY", 5))
 
543
  print(f"Waiting {startup_delay} seconds before starting video processing...")
544
  await asyncio.sleep(startup_delay)
545
 
@@ -562,7 +540,7 @@ async def scan_and_process_videos():
562
  if len(parts) >= 2:
563
  movie_folders.add(parts[1])
564
 
565
- print(f"Found {len(movie_folders)} movies to process: {sorted(movie_folders)}")
566
 
567
  for movie_name in sorted(movie_folders):
568
  await process_movie_segments(movie_name)
@@ -575,13 +553,21 @@ async def scan_and_process_videos():
575
  print("="*80 + "\n")
576
 
577
  except Exception as e:
578
- print(f"Critical error in scan_and_process_videos: {e}")
579
  processing_state["last_error"] = str(e)
580
  finally:
581
  processing_state["is_running"] = False
582
- processing_state["current_file"] = None
583
 
584
 
 
 
 
 
 
 
 
 
 
585
 
586
  @app.get("/")
587
  async def health():
@@ -632,5 +618,5 @@ async def trigger_processing():
632
 
633
  if __name__ == "__main__":
634
  print("Starting Video Processing Service on port 7860...")
635
- print("Whisper will load at startup, processing begins after startup delay")
636
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
12
 
13
  from fastapi import FastAPI, HTTPException
14
  from fastapi.responses import JSONResponse
 
15
  import uvicorn
16
 
17
  try:
 
32
  print("Error: Missing HF_TOKEN in .env")
33
  exit(1)
34
 
35
+ app = FastAPI(title="Video Processing Service")
 
 
 
 
 
 
 
 
36
 
37
  # Global state
38
  processing_state = {
 
235
  4. Mux processed video with original audio
236
  """
237
  ffmpeg_video_proc = None
 
238
  temp_wav = output_path.replace(".mp4", "_audio.wav")
239
  temp_video_path = output_path.replace(".mp4", "_noaudio.mp4")
240
 
 
324
  frame = cv2.resize(frame, (target_width, target_height), interpolation=cv2.INTER_LANCZOS4)
325
  frame = apply_color_grading_wedding_retro(frame)
326
 
327
+ # Set caption for this frame (empty if none).
328
  current_caption = frame_caption_map.get(processed_frames, "")
329
 
330
  if current_caption:
 
337
  progress = (processed_frames / target_frames) * 100
338
  print(f"Progress: {progress:.1f}%")
339
 
 
340
  ffmpeg_video_proc.stdin.close()
341
  ffmpeg_video_proc.wait()
342
+ cap.release()
343
 
344
  if ffmpeg_video_proc.returncode != 0:
345
  print(f"✗ FFmpeg video encoding failed (code {ffmpeg_video_proc.returncode})")
 
388
  return False
389
 
390
  finally:
 
 
 
 
391
  for tmp in [temp_video_path, temp_wav]:
392
  if tmp and os.path.exists(tmp):
393
  try:
 
439
  print(f"No segment JSON files found for {movie_name}")
440
  return False
441
 
442
+ print(f"Found {len(segment_files)} segments")
443
  temp_dir = tempfile.mkdtemp()
444
 
445
  try:
446
  for segment_file in segment_files:
 
447
  try:
448
  segment_path = hf_hub_download(
449
  repo_id=HF_DATASET_REPO,
 
460
  start_time = segment_data.get("start_time", "00:00:00")
461
  end_time = segment_data.get("end_time", "00:10:00")
462
 
463
+ print(f"\nProcessing segment {segment_number}: {start_time} to {end_time}")
464
 
465
  output_filename = f"segment-{segment_number:02d}.mp4"
466
  output_path = os.path.join(temp_dir, output_filename)
 
473
  )
474
 
475
  if not success:
476
+ print(f"Failed to process segment {segment_number}")
 
477
  continue
478
 
479
  upload_path = f"{READY_VIDEOS_FOLDER}/{movie_name}/{output_filename}"
 
489
  )
490
  print(f"✓ Segment {segment_number} uploaded successfully")
491
 
 
 
 
 
 
 
 
492
  except Exception as e:
493
+ print(f"✗ Error processing segment: {e}")
494
  processing_state["error_count"] += 1
495
  continue
496
 
 
506
  except Exception as e:
507
  processing_state["error_count"] += 1
508
  processing_state["last_error"] = str(e)
509
+ print(f"✗ Error: {e}")
510
  return False
511
 
512
 
 
516
  print("Video processing already running, skipping...")
517
  return
518
 
519
+ # Wait for Space to fully initialize (reduced for testing)
520
+ startup_delay = int(os.getenv("STARTUP_DELAY", 5)) # Default 5 seconds for testing
521
  print(f"Waiting {startup_delay} seconds before starting video processing...")
522
  await asyncio.sleep(startup_delay)
523
 
 
540
  if len(parts) >= 2:
541
  movie_folders.add(parts[1])
542
 
543
+ print(f"Found {len(movie_folders)} movies to process")
544
 
545
  for movie_name in sorted(movie_folders):
546
  await process_movie_segments(movie_name)
 
553
  print("="*80 + "\n")
554
 
555
  except Exception as e:
556
+ print(f"Critical error: {e}")
557
  processing_state["last_error"] = str(e)
558
  finally:
559
  processing_state["is_running"] = False
 
560
 
561
 
562
+ @app.on_event("startup")
563
+ async def startup_event():
564
+ """Load Whisper in background, then kick off video processing after 3 min."""
565
+ loop = asyncio.get_event_loop()
566
+ # Load Whisper model in thread so it doesn't block the event loop / health check
567
+ await loop.run_in_executor(None, _load_whisper_model)
568
+ # Kick off processing task (has its own 3-min delay inside)
569
+ asyncio.create_task(scan_and_process_videos())
570
+
571
 
572
  @app.get("/")
573
  async def health():
 
618
 
619
  if __name__ == "__main__":
620
  print("Starting Video Processing Service on port 7860...")
621
+ print("Whisper will load at startup, processing begins 3 minutes after")
622
+ uvicorn.run(app, host="0.0.0.0", port=7860)