import subprocess def render_video_with_audio( audio_path: str, video_path: str, output_path: str, duration: float, video_volume: int, audio_volume: int, ): """ Combines a video file with an audio file and saves the result using FFmpeg. Args: audio_path (str): Path to the input audio file or URL video_path (str): Path to the input video file output_path (str): Path where the output video will be saved duration (float): Duration of the output video in seconds video_volume (int): Volume for the video's audio track (0-100) audio_volume (int): Volume for the separate audio input (0-100) """ print(f"Rendering video with audio for {duration} seconds") print(f"Video volume: {video_volume}, Audio volume: {audio_volume}") # Check if the video has an audio stream probe_command = [ "ffprobe", "-v", "quiet", "-select_streams", "a", "-show_entries", "stream=codec_type", "-of", "csv=p=0", video_path, ] try: result = subprocess.run(probe_command, capture_output=True, text=True, check=True) has_audio = bool(result.stdout.strip()) except subprocess.CalledProcessError: # If ffprobe fails, assume no audio has_audio = False print(f"Video has audio: {has_audio}") # Construct FFmpeg command command = [ "ffmpeg", "-stream_loop", "-1", # Loop the video input indefinitely "-i", video_path, # Input video "-i", audio_path, # Input audio "-c:v", "libx264", # Re-encode the video using libx264 codec ] # Build filter complex based on whether video has audio if has_audio: print("Video has audio - mix video audio with separate audio") # Video has audio - mix video audio with separate audio filter_complex = f"[0:a]volume={video_volume / 100.0}[video_audio];[1:a]volume={audio_volume / 100.0}[separate_audio];[video_audio][separate_audio]amix=inputs=2:duration=longest[out_audio]" command.extend( [ "-filter_complex", filter_complex, "-map", "0:v", # Map video from first input "-map", "[out_audio]", # Map the mixed audio output ] ) else: # Video has no audio - use only the separate audio filter_complex = f"[1:a]volume={audio_volume / 100.0}[out_audio]" command.extend( [ "-filter_complex", filter_complex, "-map", "0:v", # Map video from first input "-map", "[out_audio]", # Map the audio output ] ) command.extend( [ "-c:a", "aac", # Use AAC codec for audio "-strict", "experimental", "-t", str(duration), # Set duration to match audio length "-y", # Overwrite output file if it exists output_path, ] ) print(f"FFmpeg command: {' '.join(command)}") # Run FFmpeg command try: subprocess.run(command, check=True, capture_output=True) except subprocess.CalledProcessError as e: raise RuntimeError(f"FFmpeg failed: {e.stderr.decode()}") from e