# ============================ # get_footage.py (unchanged) # ============================ # (contenu identique à la précédente version – pas de modification) # ============================ # edit_video.py (révision => musique optionnelle et volume paramétrable) # ============================ """Assemble la voix IA et, si fourni, la musique de fond. Appel : edit_video( video_path="./assets/video_music/video_silent.mp4", audio_path="./assets/audio/voice.mp3", music_path=None, # ou chemin .mp3 / .wav output_path="./assets/output/final_video.mp4", music_volume=0.10, # volume musique (0‑1) ) """ from moviepy import VideoFileClip, AudioFileClip, CompositeAudioClip import os def edit_video( video_path: str, audio_path: str, music_path: str | None, output_path: str, *, music_volume: float = 0.10, ): video_clip = VideoFileClip(video_path) voice_clip = AudioFileClip(audio_path) tracks = [voice_clip] if music_path and os.path.isfile(music_path): try: music_clip = ( AudioFileClip(music_path) .with_volume_scaled(music_volume) .with_duration(video_clip.duration) ) tracks.insert(0, music_clip) except Exception as err: print(f"⚠️ Musique ignorée : {err}") final_audio = CompositeAudioClip(tracks).with_duration(video_clip.duration) final_clip = video_clip.with_audio(final_audio) final_clip.write_videofile( output_path, codec="libx264", audio_codec="aac", fps=30, threads=4, preset="medium", ffmpeg_params=["-pix_fmt", "yuv420p"], ) print(f"✅ Vidéo générée : {output_path}") video_clip.close() voice_clip.close() if "music_clip" in locals(): music_clip.close() final_audio.close() final_clip.close()