import os import subprocess from pathlib import Path import tempfile class FFmpegCompiler: """ Orchestrateur FFmpeg pour DarkMedia-X. Remplace les opérations lentes de MoviePy (Mixage Audio, Concatenation, VFX) par des appels C/C++ ultra-rapides accélérés matériellement. """ @staticmethod def mix_audio_fast(voice_path: str, music_path: str, output_path: str, music_volume: float = 0.08): """ Mixe la voix et la musique quasi-instantanément via FFmpeg 'amix'. """ print(f"⚡ [FFmpeg] Mixage Audio Rapide: Voix + Musique...") # Filtre complexe: on baisse le volume de la musique et on mixe # amix: inputs=2 (voix, musique), duration=first (s'arrête quand la voix finit) filter_complex = f"[1:a]volume={music_volume}[music];[0:a][music]amix=inputs=2:duration=first:dropout_transition=2[outa]" cmd = [ "ffmpeg", "-y", "-hide_banner", "-loglevel", "error", "-i", str(voice_path), "-i", str(music_path), "-filter_complex", filter_complex, "-map", "[outa]", "-acodec", "aac", "-b:a", "192k", str(output_path) ] try: subprocess.run(cmd, check=True) return True except subprocess.CalledProcessError as e: print(f"❌ [FFmpeg] Erreur de mixage audio: {e}") return False @staticmethod def apply_vfx_and_compress(input_video: str, output_path: str, vfx_type: str = "vhs"): """ Applique un effet visuel (VFX) via les filtres natifs de FFmpeg. Utilise l'encodeur x264 pour une grande compatibilité. """ print(f"⚡ [FFmpeg] Application du VFX Matériel : {vfx_type.upper()}...") filters = [] if vfx_type == "vhs": # Effet VHS: décalage des canaux couleurs (chromatic aberration) et bruit filters.append("colorchannelmixer=rr=1:rb=0.2:gg=1:bb=1:br=0.2") filters.append("noise=alls=15:allf=t+u") filters.append("curves=r='0/0.1 1/0.9':g='0/0.1 1/0.9':b='0/0.2 1/1'") filters.append("eq=contrast=1.3:brightness=-0.05:saturation=0.8") elif vfx_type == "cctv": # Effet caméra de surveillance: Noir et blanc, contraste élevé, scanlines filters.append("hue=s=0") # Désaturation filters.append("eq=contrast=1.6:brightness=-0.1") filters.append("noise=alls=10:allf=t") elif vfx_type == "blood_lust": # Effet rouge sang filters.append("colorchannelmixer=rr=1.5:gg=0.5:bb=0.5") filters.append("eq=saturation=1.2:contrast=1.4:brightness=-0.05") else: # Base Horror Style (Remplace apply_horror_style de MoviePy) filters.append("eq=contrast=1.3:brightness=-0.05:saturation=0.85") # Filtre final (si vide, copie juste la vidéo) vf_string = ",".join(filters) if filters else "copy" cmd = [ "ffmpeg", "-y", "-hide_banner", "-loglevel", "error", "-i", str(input_video) ] if filters: cmd.extend(["-vf", vf_string]) cmd.extend([ "-c:v", "libx264", "-preset", "fast", "-crf", "22", "-c:a", "copy", # On ne retouche pas l'audio str(output_path) ]) try: subprocess.run(cmd, check=True) return True except subprocess.CalledProcessError as e: print(f"❌ [FFmpeg] Erreur d'application VFX: {e}") return False @staticmethod def concat_videos_fast(video_paths: list, output_path: str): """ Concatène une liste de vidéos MP4 en utilisant le concat demuxer (ultra rapide, pas de ré-encodage complet). /!\ Nécessite que toutes les vidéos aient la même résolution, framerate et codecs. """ if not video_paths: return False print(f"⚡ [FFmpeg] Concaténation Rapide de {len(video_paths)} clips...") with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as f: for vp in video_paths: # Échapper les chemins pour FFmpeg safe_path = str(vp).replace('\\', '/').replace("'", r"'\''") f.write(f"file '{safe_path}'\n") list_path = f.name cmd = [ "ffmpeg", "-y", "-hide_banner", "-loglevel", "error", "-f", "concat", "-safe", "0", "-i", list_path, "-c", "copy", str(output_path) ] try: subprocess.run(cmd, check=True) os.unlink(list_path) return True except subprocess.CalledProcessError as e: print(f"❌ [FFmpeg] Erreur de concaténation: {e}") os.unlink(list_path) return False if __name__ == "__main__": # Tests unitaires basiques pass