sushilideaclan01 commited on
Commit
d08a9af
·
1 Parent(s): 9f6fc53

Add video segment compression for Replicate uploads. Implemented logic to prepare segments for upload, ensuring they meet the 100 MiB file size limit. Added functionality to compress segments exceeding the limit and updated upload handling to use the prepared segments.

Browse files
Files changed (1) hide show
  1. app/main.py +76 -1
app/main.py CHANGED
@@ -27,6 +27,9 @@ API_BASE = "https://api.replicate.com/v1"
27
  MODEL_PATH = "heygen/video-translate"
28
 
29
  POLL_SECONDS = 8
 
 
 
30
 
31
 
32
  class RunCancelledError(RuntimeError):
@@ -62,6 +65,14 @@ assert _summarize_segment_failures(
62
  assert _summarize_segment_failures([{"error": "network timeout"}]) == "network timeout"
63
 
64
 
 
 
 
 
 
 
 
 
65
  def ensure_ffmpeg() -> None:
66
  if shutil.which("ffmpeg") is None:
67
  raise RuntimeError("`ffmpeg` is not installed or not on PATH.")
@@ -366,7 +377,7 @@ class TranslationRunManager:
366
  self._update_segment(run_id, index, status="uploading", error=None)
367
  if self._is_cancelled(run_id):
368
  raise RunCancelledError("Run cancelled by user.")
369
- file_url = self._upload_segment(Path(segment["path"]), token)
370
  self._update_segment(run_id, index, input_video_url=file_url)
371
 
372
  self._update_segment(run_id, index, status="processing")
@@ -401,6 +412,70 @@ class TranslationRunManager:
401
  self._update_segment(run_id, index, status="failed", error=str(exc))
402
  return SegmentTaskResult(index=index, status="failed", error=str(exc))
403
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
  def _upload_segment(self, file_path: Path, token: str) -> str:
405
  with file_path.open("rb") as handle:
406
  response = requests.post(
 
27
  MODEL_PATH = "heygen/video-translate"
28
 
29
  POLL_SECONDS = 8
30
+ # Replicate Files API rejects uploads over 100 MiB.
31
+ REPLICATE_MAX_UPLOAD_BYTES = 95 * 1024 * 1024
32
+ UPLOAD_AUDIO_BITRATE = 128_000
33
 
34
 
35
  class RunCancelledError(RuntimeError):
 
65
  assert _summarize_segment_failures([{"error": "network timeout"}]) == "network timeout"
66
 
67
 
68
+ def _target_video_bitrate(duration_seconds: float, max_bytes: int, factor: float) -> int:
69
+ audio_bps = UPLOAD_AUDIO_BITRATE
70
+ return max(400_000, int((max_bytes * 8 * factor) / max(duration_seconds, 1.0) - audio_bps))
71
+
72
+
73
+ assert 1_000_000 < _target_video_bitrate(60, REPLICATE_MAX_UPLOAD_BYTES, 0.85) < 15_000_000
74
+
75
+
76
  def ensure_ffmpeg() -> None:
77
  if shutil.which("ffmpeg") is None:
78
  raise RuntimeError("`ffmpeg` is not installed or not on PATH.")
 
377
  self._update_segment(run_id, index, status="uploading", error=None)
378
  if self._is_cancelled(run_id):
379
  raise RunCancelledError("Run cancelled by user.")
380
+ file_url = self._upload_segment(self._prepare_segment_for_upload(run_id, index), token)
381
  self._update_segment(run_id, index, input_video_url=file_url)
382
 
383
  self._update_segment(run_id, index, status="processing")
 
412
  self._update_segment(run_id, index, status="failed", error=str(exc))
413
  return SegmentTaskResult(index=index, status="failed", error=str(exc))
414
 
415
+ def _prepare_segment_for_upload(self, run_id: str, index: int) -> Path:
416
+ run = self.get_run(run_id)
417
+ if not run:
418
+ raise RuntimeError("Run missing.")
419
+ segment = run["segments"][index]
420
+ src = Path(segment["path"])
421
+ dest = src.with_name(f"{src.stem}_fit.mp4")
422
+ if dest.exists() and 0 < dest.stat().st_size <= REPLICATE_MAX_UPLOAD_BYTES:
423
+ return dest
424
+ size = src.stat().st_size
425
+ if size <= REPLICATE_MAX_UPLOAD_BYTES:
426
+ return src
427
+ self._log(
428
+ run,
429
+ f"[{segment['name']}] {size // (1024 * 1024)} MiB exceeds Replicate's 100 MiB file limit; compressing.",
430
+ )
431
+ self._compress_for_upload(src, dest, REPLICATE_MAX_UPLOAD_BYTES)
432
+ fitted = dest.stat().st_size // (1024 * 1024)
433
+ self._log(run, f"[{segment['name']}] compressed to {fitted} MiB.")
434
+ return dest
435
+
436
+ @staticmethod
437
+ def _compress_for_upload(src: Path, dest: Path, max_bytes: int) -> None:
438
+ duration = TranslationRunManager._probe_duration(src) or 60.0
439
+ last_size = src.stat().st_size
440
+ for factor in (0.85, 0.55, 0.35):
441
+ video_bps = _target_video_bitrate(duration, max_bytes, factor)
442
+ command = [
443
+ "ffmpeg",
444
+ "-hide_banner",
445
+ "-loglevel",
446
+ "error",
447
+ "-y",
448
+ "-i",
449
+ str(src),
450
+ "-c:v",
451
+ "libx264",
452
+ "-preset",
453
+ "veryfast",
454
+ "-b:v",
455
+ str(video_bps),
456
+ "-maxrate",
457
+ str(video_bps),
458
+ "-bufsize",
459
+ str(video_bps * 2),
460
+ "-pix_fmt",
461
+ "yuv420p",
462
+ "-c:a",
463
+ "aac",
464
+ "-b:a",
465
+ "128k",
466
+ "-movflags",
467
+ "+faststart",
468
+ str(dest),
469
+ ]
470
+ TranslationRunManager._run_cmd(command, "Compressing segment for Replicate upload failed.")
471
+ last_size = dest.stat().st_size
472
+ if last_size <= max_bytes:
473
+ return
474
+ raise RuntimeError(
475
+ f"Segment still too large after compress ({last_size // (1024 * 1024)} MiB). "
476
+ "Replicate's file limit is 100 MiB."
477
+ )
478
+
479
  def _upload_segment(self, file_path: Path, token: str) -> str:
480
  with file_path.open("rb") as handle:
481
  response = requests.post(