Spaces:
Sleeping
Sleeping
| import os | |
| import subprocess | |
| from moviepy.video.io.VideoFileClip import VideoFileClip | |
| from moviepy.audio.io.AudioFileClip import AudioFileClip | |
| from moviepy.audio.AudioClip import CompositeAudioClip | |
| from moviepy.config import change_settings | |
| # --- Robust FFmpeg Configuration --- | |
| BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| ff_local = os.path.join(BASE_DIR, "ffmpeg.exe") | |
| if os.path.exists(ff_local): | |
| change_settings({"FFMPEG_BINARY": ff_local}) | |
| from moviepy.audio.fx.volumex import volumex | |
| def finalize_video_pro(video_path, output_path, segments_info): | |
| """ | |
| Surgical Audio Replacement Engine: | |
| Keeps original interviewer voice, replaces candidate segments with Elite AI. | |
| """ | |
| video = VideoFileClip(video_path) | |
| original_audio = video.audio | |
| # 1. Create SRT & AI Audio Clips | |
| srt_content = "" | |
| ai_clips = [] | |
| # We will "duck" the original audio during AI segments | |
| # For simplicity in this demo, we'll use a CompositeAudioClip with the original as base | |
| for idx, (audio_path, start_time, text) in enumerate(segments_info): | |
| if os.path.exists(audio_path) and text.strip(): | |
| a_clip = AudioFileClip(audio_path).set_start(start_time) | |
| ai_clips.append(a_clip) | |
| # Format SRT | |
| def to_srt_time(seconds): | |
| hrs = int(seconds // 3600); mins = int((seconds % 3600) // 60); secs = int(seconds % 60) | |
| ms = int((seconds - int(seconds)) * 1000) | |
| return f"{hrs:02}:{mins:02}:{secs:02},{ms:03}" | |
| srt_content += f"{idx+1}\n{to_srt_time(start_time)} --> {to_srt_time(start_time + a_clip.duration)}\n{text}\n\n" | |
| with open("temp.srt", "w", encoding="utf-8") as f: f.write(srt_content) | |
| # 2. Master Audio Mix (Original + AI Overlays) | |
| # We lower original volume slightly using the direct FX function | |
| final_audio = CompositeAudioClip([volumex(original_audio, 0.3)] + ai_clips) | |
| temp_video = "temp_render.mp4" | |
| video.set_audio(final_audio).write_videofile(temp_video, codec="libx264", audio_codec="aac") | |
| # 3. Burn-in Subtitles | |
| ff_cmd = ff_local if os.path.exists(ff_local) else "ffmpeg" | |
| # Double-escaping for Windows FFmpeg path requirements | |
| srt_path = os.path.abspath("temp.srt").replace("\\", "/").replace(":", "\\:") | |
| subprocess.run([ | |
| ff_cmd, "-i", temp_video, | |
| "-vf", f"subtitles='{srt_path}':force_style='FontSize=26,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,BorderStyle=1,Outline=2,Shadow=1,MarginV=35,Alignment=2'", | |
| "-c:a", "copy", | |
| output_path, "-y" | |
| ]) | |
| # Cleanup | |
| video.close() | |
| for c in ai_clips: c.close() | |
| if os.path.exists(temp_video): os.remove(temp_video) | |
| if os.path.exists("temp.srt"): os.remove("temp.srt") | |