import subprocess import json 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("⚠️ Nenhum perfil FFmpeg ativo!") 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: 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 selecionado") p = self.profile metadata = self.get_file_info(input_file) if not metadata: raise Exception("Metadados inválidos") cmd = ['ffmpeg', '-y'] # --- HARDWARE INIT --- # VAAPI (Intel Linux/Docker) - O Jeito Correto if 'vaapi' in p.video_codec: cmd.extend(['-init_hw_device', 'vaapi=va:/dev/dri/renderD128']) cmd.extend(['-hwaccel', 'vaapi', '-hwaccel_output_format', 'vaapi', '-hwaccel_device', 'va']) cmd.extend(['-i', input_file]) # Filtro essencial para VAAPI: garante formato NV12 na GPU # Mas como usamos hwaccel_output_format vaapi, o filtro pode ser simplificado ou scale_vaapi # Vamos usar o padrão seguro que funciona em Haswell: video_filters = 'format=nv12,hwupload' elif 'qsv' in p.video_codec: cmd.extend(['-hwaccel', 'qsv', '-i', input_file]) video_filters = None elif 'nvenc' in p.video_codec: cmd.extend(['-hwaccel', 'cuda', '-i', input_file]) video_filters = None else: # CPU cmd.extend(['-i', input_file]) video_filters = None # --- VÍDEO --- cmd.extend(['-map', '0:v:0']) if p.video_codec == 'copy': cmd.extend(['-c:v', 'copy']) else: cmd.extend(['-c:v', p.video_codec]) # Se tem filtro de hardware (VAAPI precisa subir pra GPU se hwaccel falhar no decode) if 'vaapi' in p.video_codec: # Se usarmos -hwaccel vaapi, o stream ja esta na GPU. # Mas as vezes precisamos garantir o filtro scale_vaapi se fosse redimensionar. # Para manter simples e funcional no Haswell: # cmd.extend(['-vf', 'format=nv12,hwupload']) <--- Se nao usar hwaccel # Com hwaccel, nao precisa do hwupload, mas precisa garantir compatibilidade pass # Configs de Encoder if 'vaapi' in p.video_codec: # VAAPI usa QP, não CRF padrão # Se der erro, troque '-qp' por '-rc_mode CQP -global_quality' cmd.extend(['-qp', str(p.crf)]) elif 'qsv' in p.video_codec: cmd.extend(['-global_quality', str(p.crf), '-look_ahead', '1']) cmd.extend(['-preset', p.preset]) elif 'nvenc' in p.video_codec: cmd.extend(['-cq', str(p.crf), '-preset', p.preset]) elif 'libx264' in p.video_codec: cmd.extend(['-crf', str(p.crf), '-preset', p.preset]) # --- ÁUDIO --- allowed = p.audio_langs.split(',') if p.audio_langs else [] audio_streams = [s for s in metadata['streams'] if s['codec_type'] == 'audio'] acount = 0 for s in audio_streams: l = s.get('tags', {}).get('language', 'und') if not allowed or l in allowed or 'und' in allowed: cmd.extend(['-map', f'0:{s["index"]}']) cmd.extend([f'-c:a:{acount}', 'aac', f'-b:a:{acount}', '192k']) acount += 1 if acount == 0: cmd.extend(['-map', '0:a:0', '-c:a', 'aac']) # --- LEGENDAS --- lallowed = p.subtitle_langs.split(',') if p.subtitle_langs else [] sub_streams = [s for s in metadata['streams'] if s['codec_type'] == 'subtitle'] scount = 0 for s in sub_streams: l = s.get('tags', {}).get('language', 'und') if not lallowed or l in lallowed or 'und' in lallowed: cmd.extend(['-map', f'0:{s["index"]}']) cmd.extend([f'-c:s:{scount}', 'copy']) scount += 1 cmd.extend(['-metadata', 'title=', '-metadata', 'comment=CleiFlow']) cmd.append(output_file) # LOG DO COMANDO PARA DEBUG state.log(f"🛠️ CMD: {' '.join(cmd)}") return cmd