import subprocess import json import os from database import FFmpegProfile from core.state import state class FFmpegEngine: def __init__(self, profile_id=None): if profile_id: self.profile = FFmpegProfile.get_by_id(profile_id) else: self.profile = FFmpegProfile.get_or_none(FFmpegProfile.is_active == True) if not self.profile: state.log("⚠️ AVISO: Nenhum perfil FFmpeg ativo! Usando defaults.") def get_file_info(self, filepath): cmd = ['ffprobe', '-v', 'quiet', '-print_format', 'json', '-show_streams', '-show_format', filepath] try: output = subprocess.check_output(cmd).decode('utf-8') return json.loads(output) except Exception as e: state.log(f"Erro FFprobe: {e}") return None def get_duration(self, filepath): try: return float(self.get_file_info(filepath)['format']['duration']) except: return 0 def build_command(self, input_file, output_file): if not self.profile: raise Exception("Perfil não configurado.") p = self.profile metadata = self.get_file_info(input_file) if not metadata: raise Exception("Arquivo inválido.") # Info de entrada video_stream = next((s for s in metadata['streams'] if s['codec_type'] == 'video'), None) input_codec = video_stream['codec_name'] if video_stream else 'unknown' cmd = ['ffmpeg', '-y'] video_filters = [] # --- LÓGICA MODULAR DE HARDWARE --- # 1. INTEL/AMD VAAPI if p.hardware_type == 'vaapi': # Setup do Device (Sempre necessário para VAAPI) cmd.extend(['-init_hw_device', 'vaapi=intel:/dev/dri/renderD128']) cmd.extend(['-filter_hw_device', 'intel']) # Verificação de Modo Híbrido (Intel 4th Gen / Compatibilidade) use_hw_decode = True if p.hybrid_decode: # Lista de codecs seguros para Haswell safe_codecs = ['h264', 'mpeg2video', 'vc1', 'mjpeg'] if input_codec not in safe_codecs: use_hw_decode = False state.log(f"⚙️ Modo Híbrido Ativado: CPU decodificando {input_codec} -> GPU codificando.") # Aplica Input HW Accel se permitido if use_hw_decode: cmd.extend(['-hwaccel', 'vaapi']) cmd.extend(['-hwaccel_output_format', 'vaapi']) cmd.extend(['-hwaccel_device', 'intel']) else: # Se não usar HW decode, precisa subir pra GPU antes de encodar video_filters.append('format=nv12,hwupload') # 2. NVIDIA NVENC (Se suportado pelo container/host) elif p.hardware_type == 'nvenc': cmd.extend(['-hwaccel', 'cuda']) # Nvidia geralmente aguenta tudo, mas se precisar de resize, adiciona filtros aqui # 3. INTEL QSV (QuickSync Proprietário) elif p.hardware_type == 'qsv': cmd.extend(['-hwaccel', 'qsv', '-c:v', 'h264_qsv']) # --- INPUT --- cmd.extend(['-threads', '4', '-i', input_file]) # --- VÍDEO OUTPUT --- cmd.extend(['-map', '0:v:0']) if p.video_codec == 'copy': cmd.extend(['-c:v', 'copy']) else: # Aplica filtros acumulados if video_filters: cmd.extend(['-vf', ",".join(video_filters)]) cmd.extend(['-c:v', p.video_codec]) # Qualidade baseada no encoder if 'vaapi' in p.video_codec: cmd.extend(['-qp', str(p.crf)]) # Constant Quality para VAAPI elif 'nvenc' in p.video_codec: cmd.extend(['-cq', str(p.crf), '-preset', p.preset]) else: # CPU / Libx264 cmd.extend(['-crf', str(p.crf), '-preset', p.preset]) # --- ÁUDIO (AAC Stereo Compatível) --- allowed_langs = [l.strip().lower() for l in (p.audio_langs or "").split(',')] audio_streams = [s for s in metadata['streams'] if s['codec_type'] == 'audio'] acount = 0 for s in audio_streams: lang = s.get('tags', {}).get('language', 'und').lower() if not allowed_langs or lang in allowed_langs or 'und' in allowed_langs: cmd.extend(['-map', f'0:{s["index"]}']) cmd.extend([f'-c:a:{acount}', 'aac', f'-b:a:{acount}', '192k', f'-ac:{acount}', '2']) acount += 1 if acount == 0 and audio_streams: cmd.extend(['-map', '0:a:0', '-c:a', 'aac', '-b:a', '192k']) # --- LEGENDAS --- sub_allowed = [l.strip().lower() for l in (p.subtitle_langs or "").split(',')] sub_streams = [s for s in metadata['streams'] if s['codec_type'] == 'subtitle'] scount = 0 for s in sub_streams: lang = s.get('tags', {}).get('language', 'und').lower() if not sub_allowed or lang in sub_allowed or 'und' in sub_allowed: cmd.extend(['-map', f'0:{s["index"]}']) cmd.extend([f'-c:s:{scount}', 'copy']) scount += 1 # Metadados clean_title = os.path.splitext(os.path.basename(output_file))[0] cmd.extend(['-metadata', f'title={clean_title}']) cmd.append(output_file) return cmd