suraj-ml-projects/Video_editing
0
1"""2utils/video_utils.py3────────────────────4Merge dubbed Hindi audio back into the original video.5 6Strategy:7 1. Use ffmpeg to mix dubbed speech + original BGM (at reduced volume)8 2. Replace the video's audio track with the mixed result9 3. Copy video codec (no re-encoding) → fast even on CPU10 11This avoids MoviePy for the final merge (MoviePy re-encodes, very slow on CPU).12We use raw ffmpeg subprocess calls instead — much faster.13"""14 15import os16import tempfile17import subprocess18 19 20def merge_audio_video(21 video_path: str,22 dubbed_audio_path: str,23 original_audio_path: str,24 bgm_volume: float = 0.15,25) -> str:26 """27 Create final dubbed video:28 - Video stream: copied from original (no re-encode)29 - Audio stream: Hindi speech mixed with faint original BGM30 31 Parameters:32 video_path – path to original MP433 dubbed_audio_path – path to Hindi dubbed WAV34 original_audio_path – path to extracted original audio WAV35 bgm_volume – 0.0–1.0, volume of background music36 37 Returns path to output MP4.38 """39 output_path = os.path.join(tempfile.gettempdir(), "hindi_dubbed_output.mp4")40 41 # ffmpeg filter graph:42 # [0:a] = original audio (background music)43 # [1:a] = Hindi dubbed speech44 #45 # Step 1: lower original audio volume → bgm46 # Step 2: mix bgm + dubbed speech together47 # Step 3: mux with video stream (copy, no re-encode)48 49 bgm_vol_str = str(round(bgm_volume, 3))50 51 cmd = [52 "ffmpeg", "-y",53 "-i", video_path, # input 0: original video (video + audio)54 "-i", dubbed_audio_path, # input 1: Hindi dubbed audio55 "-filter_complex",56 (57 # Lower original audio volume for BGM58 f"[0:a]volume={bgm_vol_str}[bgm];"59 # Mix BGM with Hindi speech60 "[bgm][1:a]amix=inputs=2:duration=first:dropout_transition=3[mixed]"61 ),62 "-map", "0:v", # video stream from original63 "-map", "[mixed]", # audio stream = mixed result64 "-c:v", "copy", # copy video codec — NO re-encoding (fast!)65 "-c:a", "aac", # encode mixed audio as AAC66 "-b:a", "192k", # audio bitrate67 "-shortest", # trim to shortest stream68 output_path,69 ]70 71 result = subprocess.run(72 cmd,73 stdout=subprocess.PIPE,74 stderr=subprocess.PIPE,75 )76 77 if result.returncode != 0:78 # Try a simpler fallback: just replace audio entirely (no BGM mix)79 output_path = _simple_replace_audio(video_path, dubbed_audio_path)80 81 return output_path82 83 84def _simple_replace_audio(video_path: str, audio_path: str) -> str:85 """86 Fallback: simply replace video audio with dubbed audio.87 Used when the complex filter fails.88 """89 output_path = os.path.join(tempfile.gettempdir(), "hindi_dubbed_output_simple.mp4")90 91 cmd = [92 "ffmpeg", "-y",93 "-i", video_path,94 "-i", audio_path,95 "-map", "0:v", # take video from original96 "-map", "1:a", # take audio from dubbed file97 "-c:v", "copy", # copy video stream (fast)98 "-c:a", "aac",99 "-b:a", "192k",100 "-shortest",101 output_path,102 ]103 104 subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)105 return output_path106 107 108def get_video_duration(video_path: str) -> float:109 """Return video duration in seconds using ffprobe."""110 cmd = [111 "ffprobe", "-v", "error",112 "-show_entries", "format=duration",113 "-of", "default=noprint_wrappers=1:nokey=1",114 video_path,115 ]116 result = subprocess.run(cmd, capture_output=True, text=True)117 try:118 return float(result.stdout.strip())119 except Exception:120 return 0.0121 