mudado a config do encoder para melhor qualidade e menor tamanho

This commit is contained in:
2026-01-25 02:22:30 +00:00
parent 5036b4b93f
commit 9a26b4c13e
2 changed files with 37 additions and 69 deletions

View File

@@ -3,7 +3,7 @@ from modules import file_manager, renamer, encoder
st.set_page_config(page_title="PyMedia Manager", layout="wide", page_icon="🎬") st.set_page_config(page_title="PyMedia Manager", layout="wide", page_icon="🎬")
st.title("🎬 PyMedia Manager - Central de Controle") st.title("🎬 PyMedia Manager Clei - Central de Controle")
# CSS para melhorar visual # CSS para melhorar visual
st.markdown(""" st.markdown("""

View File

@@ -13,45 +13,33 @@ ROOT_DIR = "/downloads"
OUTPUT_BASE = "/downloads/finalizados" OUTPUT_BASE = "/downloads/finalizados"
STATUS_FILE = "/app/data/encoder_status.json" STATUS_FILE = "/app/data/encoder_status.json"
# --- GERENCIAMENTO DE ESTADO EM DISCO --- # --- STATUS ---
def save_status(data): def save_status(data):
"""Salva o estado atual no disco para sobreviver ao F5"""
try: try:
with open(STATUS_FILE, 'w') as f: with open(STATUS_FILE, 'w') as f: json.dump(data, f)
json.dump(data, f)
except: pass except: pass
def load_status(): def load_status():
"""Lê o estado do disco""" if not os.path.exists(STATUS_FILE): return None
if not os.path.exists(STATUS_FILE):
return None
try: try:
with open(STATUS_FILE, 'r') as f: with open(STATUS_FILE, 'r') as f: return json.load(f)
return json.load(f) except: return None
except:
return None
def reset_status(): def reset_status():
"""Reseta o status para 'parado'"""
save_status({ save_status({
"is_running": False, "is_running": False, "current_file": "", "progress": 0,
"current_file": "", "total_progress": 0, "log": "Aguardando...", "stop_requested": False
"progress": 0,
"total_progress": 0,
"log": "Aguardando...",
"stop_requested": False
}) })
# --- WORKER EM BACKGROUD --- # --- WORKER ---
class BackgroundWorker(threading.Thread): class BackgroundWorker(threading.Thread):
def __init__(self, input_folder, delete_orig): def __init__(self, input_folder, delete_orig):
super().__init__() super().__init__()
self.input_folder = input_folder self.input_folder = input_folder
self.delete_orig = delete_orig self.delete_orig = delete_orig
self.daemon = True # Daemon threads rodam em background independente da sessão self.daemon = True
def run(self): def run(self):
# 1. Preparação
prepare_driver_environment() prepare_driver_environment()
files_to_process = [] files_to_process = []
@@ -62,36 +50,26 @@ class BackgroundWorker(threading.Thread):
files_to_process.append(os.path.join(r, file)) files_to_process.append(os.path.join(r, file))
total = len(files_to_process) total = len(files_to_process)
# Salva estado inicial
state = { state = {
"is_running": True, "is_running": True, "current_file": "Iniciando...",
"current_file": "Iniciando...", "progress": 0, "total_progress": 0, "log": "Preparando fila...",
"progress": 0,
"total_progress": 0,
"log": "Preparando fila...",
"stop_requested": False "stop_requested": False
} }
save_status(state) save_status(state)
# 2. Loop de Arquivos
for i, fpath in enumerate(files_to_process): for i, fpath in enumerate(files_to_process):
# Verifica se pediram pra parar
current_state = load_status() current_state = load_status()
if current_state and current_state.get("stop_requested"): if current_state and current_state.get("stop_requested"): break
break
fname = os.path.basename(fpath) fname = os.path.basename(fpath)
duration = get_video_duration(fpath) duration = get_video_duration(fpath)
# Atualiza estado para arquivo atual
state["current_file"] = fname state["current_file"] = fname
state["progress"] = 0 state["progress"] = 0
state["total_progress"] = int((i / total) * 100) state["total_progress"] = int((i / total) * 100)
state["log"] = "Convertendo..." state["log"] = "Convertendo..."
save_status(state) save_status(state)
# Caminhos
rel_path = os.path.relpath(fpath, self.input_folder) rel_path = os.path.relpath(fpath, self.input_folder)
folder_name = os.path.basename(self.input_folder) folder_name = os.path.basename(self.input_folder)
if self.input_folder == ROOT_DIR: if self.input_folder == ROOT_DIR:
@@ -101,7 +79,7 @@ class BackgroundWorker(threading.Thread):
os.makedirs(os.path.dirname(out_full_path), exist_ok=True) os.makedirs(os.path.dirname(out_full_path), exist_ok=True)
# Comando FFmpeg # --- COMANDO FFMPEG OTIMIZADO (CQP) ---
cmd = [ cmd = [
"ffmpeg", "-y", "ffmpeg", "-y",
"-hwaccel", "vaapi", "-hwaccel", "vaapi",
@@ -111,20 +89,28 @@ class BackgroundWorker(threading.Thread):
] ]
cmd += get_streams_map(fpath) cmd += get_streams_map(fpath)
cmd += [ cmd += [
"-c:v", "h264_vaapi", "-b:v", "4500k", "-compression_level", "1", "-c:v", "h264_vaapi",
# MUDANÇA AQUI: Modo CQP (Qualidade Constante)
# QP 25 é similar ao RF 22-23 do HandBrake.
# Menor número = Mais Qualidade/Maior Arquivo.
# Maior número = Menos Qualidade/Menor Arquivo.
"-qp", "25",
# Compressão 0 é mais lenta mas gera melhor qualidade por bit
"-compression_level", "0",
"-c:a", "copy", "-c:s", "copy", "-c:a", "copy", "-c:s", "copy",
out_full_path out_full_path
] ]
try: try:
# Executa
process = subprocess.Popen( process = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True, bufsize=1, universal_newlines=True, env=os.environ text=True, bufsize=1, universal_newlines=True, env=os.environ
) )
for line in process.stdout: for line in process.stdout:
# Checa stop a cada linha lida (para resposta rápida)
chk_state = load_status() chk_state = load_status()
if chk_state and chk_state.get("stop_requested"): if chk_state and chk_state.get("stop_requested"):
process.terminate() process.terminate()
@@ -135,20 +121,14 @@ class BackgroundWorker(threading.Thread):
if match: if match:
secs = parse_time_to_seconds(match.group(1)) secs = parse_time_to_seconds(match.group(1))
pct = int((secs / duration) * 100) pct = int((secs / duration) * 100)
if pct > 100: pct = 100 state["progress"] = min(pct, 100)
# Atualiza status no disco
state["progress"] = pct
# Pega velocidade
speed_match = re.search(r"speed=\s*(\S+)", line) speed_match = re.search(r"speed=\s*(\S+)", line)
if speed_match: if speed_match:
state["log"] = f"Velocidade: {speed_match.group(1)}" state["log"] = f"Velocidade: {speed_match.group(1)}"
save_status(state) save_status(state)
process.wait() process.wait()
if process.returncode == 0: if process.returncode == 0:
if self.delete_orig: os.remove(fpath) if self.delete_orig: os.remove(fpath)
@@ -156,10 +136,12 @@ class BackgroundWorker(threading.Thread):
state["log"] = f"Erro: {str(e)}" state["log"] = f"Erro: {str(e)}"
save_status(state) save_status(state)
# Fim
reset_status() reset_status()
# --- FUNÇÕES AUXILIARES --- # --- AUXILIARES ---
@st.cache_resource
def get_manager(): return None # Placeholder se precisar
def prepare_driver_environment(): def prepare_driver_environment():
os.environ["LIBVA_DRIVER_NAME"] = "i965" os.environ["LIBVA_DRIVER_NAME"] = "i965"
drivers_ruins = ["/usr/lib/x86_64-linux-gnu/dri/iHD_drv_video.so", "/usr/lib/x86_64-linux-gnu/dri/iHD_drv_video.so.1"] drivers_ruins = ["/usr/lib/x86_64-linux-gnu/dri/iHD_drv_video.so", "/usr/lib/x86_64-linux-gnu/dri/iHD_drv_video.so.1"]
@@ -173,8 +155,7 @@ def get_all_subfolders(root_path):
try: try:
for root, dirs, files in os.walk(root_path): for root, dirs, files in os.walk(root_path):
if "finalizados" in root or "temp" in root: continue if "finalizados" in root or "temp" in root: continue
for d in dirs: for d in dirs: folder_list.append(os.path.join(root, d))
folder_list.append(os.path.join(root, d))
except: pass except: pass
return sorted(folder_list) return sorted(folder_list)
@@ -209,15 +190,11 @@ def get_streams_map(filepath):
map_args.extend(["-map", f"0:{s['index']}"]) map_args.extend(["-map", f"0:{s['index']}"])
return map_args return map_args
# --- RENDERIZAÇÃO --- # --- RENDER ---
def render(): def render():
st.header("⚙️ Encoder (Persistente)") st.header("⚙️ Encoder (Persistente - CQP)")
st.info("O processo continua rodando mesmo se você fechar a página.")
# 1. Lê estado do disco
status = load_status() status = load_status()
# Se não existe ou não está rodando
if not status or not status.get("is_running"): if not status or not status.get("is_running"):
all_folders = get_all_subfolders(ROOT_DIR) all_folders = get_all_subfolders(ROOT_DIR)
idx = 0 idx = 0
@@ -233,32 +210,23 @@ def render():
if not os.path.exists(input_folder): if not os.path.exists(input_folder):
st.error("Pasta inválida") st.error("Pasta inválida")
else: else:
# Inicia Thread
t = BackgroundWorker(input_folder, delete_orig) t = BackgroundWorker(input_folder, delete_orig)
t.start() t.start()
time.sleep(1) # Dá tempo de criar o arquivo json time.sleep(1)
st.rerun() st.rerun()
else: else:
# MODO MONITORAMENTO
st.success("🔄 Sistema Rodando...") st.success("🔄 Sistema Rodando...")
col1, col2 = st.columns([3, 1]) col1, col2 = st.columns([3, 1])
with col1: with col1:
st.write(f"📁 **Processando:** `{status.get('current_file')}`") st.write(f"📁 **Processando:** `{status.get('current_file')}`")
st.progress(status.get('progress', 0), text=f"Arquivo: {status.get('progress')}%") st.progress(status.get('progress', 0), text=f"Arquivo: {status.get('progress')}%")
st.progress(status.get('total_progress', 0), text=f"Total: {status.get('total_progress')}%") st.progress(status.get('total_progress', 0), text=f"Total: {status.get('total_progress')}%")
st.caption(status.get('log')) st.caption(status.get('log'))
with col2: with col2:
if st.button("🛑 Parar", type="secondary"): if st.button("🛑 Parar"):
status["stop_requested"] = True status["stop_requested"] = True
save_status(status) save_status(status)
st.warning("Parando...") st.warning("Parando...")
if st.button("🔄 Refresh"): st.rerun()
if st.button("🔄 Refresh"):
st.rerun()
# Atualiza a tela a cada 5s
time.sleep(5) time.sleep(5)
st.rerun() st.rerun()