Spaces:
Sleeping
Sleeping
| 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. | |
| """ | |
| 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 | |
| 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 | |
| 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 | |