Files
clei-flow/app/core/ffmpeg_engine.py
2026-02-08 23:07:50 +00:00

121 lines
4.9 KiB
Python
Executable File

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