melhorias basicas e log
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -3,18 +3,19 @@ from database import AppConfig
|
||||
from core.state import state
|
||||
|
||||
def show():
|
||||
# Carrega configuração inicial
|
||||
semi_auto_initial = AppConfig.get_val('semi_auto', 'false') == 'true'
|
||||
|
||||
with ui.grid(columns=2).classes('w-full gap-4'):
|
||||
|
||||
# --- COLUNA DA ESQUERDA: CONTROLES ---
|
||||
# --- COLUNA DA ESQUERDA: CONTROLES E LOGS ---
|
||||
with ui.column().classes('w-full gap-4'):
|
||||
|
||||
# Painel de Controle
|
||||
with ui.card().classes('w-full p-4 border-l-4 border-indigo-500'):
|
||||
ui.label('🎛️ Painel de Controle').classes('text-xl font-bold mb-4 text-gray-700')
|
||||
|
||||
# Switches
|
||||
# Status inicial do Switch
|
||||
is_running = state.watcher.is_running if state.watcher else False
|
||||
|
||||
def toggle_watcher(e):
|
||||
@@ -26,129 +27,128 @@ def show():
|
||||
|
||||
def toggle_semi_auto(e):
|
||||
AppConfig.set_val('semi_auto', str(e.value).lower())
|
||||
state.log(f"⚠️ Semi-Auto: {e.value}")
|
||||
state.log(f"⚠️ Modo Semi-Auto: {e.value}")
|
||||
|
||||
switch_auto = ui.switch('Modo Semi-Automático', value=semi_auto_initial, on_change=toggle_semi_auto).props('color=amber size=lg')
|
||||
|
||||
# Botão Cancelar Global
|
||||
def cancel_task():
|
||||
if state.watcher:
|
||||
state.watcher.abort_current_task()
|
||||
ui.notify('Cancelando...', type='warning')
|
||||
ui.notify('Cancelando tarefa atual...', type='warning')
|
||||
|
||||
btn_cancel = ui.button('CANCELAR ATUAL', on_click=cancel_task, icon='cancel').props('color=red').classes('w-full mt-4 hidden')
|
||||
btn_cancel = ui.button('CANCELAR ATUAL', on_click=cancel_task, icon='cancel').props('color=red').classes('w-full mt-4')
|
||||
btn_cancel.set_visibility(False)
|
||||
|
||||
# Terminal de Logs
|
||||
with ui.card().classes('w-full h-64 bg-black text-green-400 font-mono text-xs p-2 overflow-hidden flex flex-col'):
|
||||
ui.label('>_ Logs').classes('text-gray-500 mb-1 border-b border-gray-800 w-full')
|
||||
log_container = ui.scroll_area().classes('flex-grow w-full')
|
||||
log_content = ui.label().style('white-space: pre-wrap; font-family: monospace;')
|
||||
with log_container: log_content.move(log_container)
|
||||
# TERMINAL DE LOGS (Versão Otimizada)
|
||||
with ui.card().classes('w-full h-80 bg-black p-2 flex flex-col'):
|
||||
ui.label('>_ Logs do Sistema').classes('text-gray-500 mb-1 border-b border-gray-800 w-full text-xs')
|
||||
|
||||
# O componente ui.log é específico para mensagens de terminal
|
||||
log_view = ui.log(max_lines=100).classes('w-full flex-grow text-green-400 font-mono text-[11px]')
|
||||
# Variável de controle para não repetir logs já mostrados
|
||||
log_view.last_index = 0
|
||||
|
||||
# --- COLUNA DA DIREITA: LISTA DE TAREFAS ---
|
||||
with ui.card().classes('w-full h-[80vh] bg-gray-50 flex flex-col p-0'):
|
||||
# Cabeçalho da Lista
|
||||
with ui.row().classes('w-full p-4 bg-white border-b items-center justify-between'):
|
||||
ui.label('📋 Fila de Processamento').classes('text-lg font-bold text-gray-700')
|
||||
lbl_status_top = ui.label('Ocioso').classes('text-sm text-gray-400')
|
||||
|
||||
# Container da Lista
|
||||
# Container onde a lista será renderizada
|
||||
tasks_container = ui.column().classes('w-full p-2 gap-2 overflow-y-auto flex-grow')
|
||||
|
||||
# --- RENDERIZADOR DA LISTA (Com Duas Barras) ---
|
||||
# --- RENDERIZADOR REATIVO DA LISTA ---
|
||||
@ui.refreshable
|
||||
def render_tasks():
|
||||
tasks_container.clear()
|
||||
|
||||
if not state.tasks:
|
||||
with tasks_container:
|
||||
ui.label('Nenhuma atividade recente.').classes('text-gray-400 italic p-4')
|
||||
# Proteção contra erro de dicionário alterado durante o loop (causa do sumiço da lista)
|
||||
try:
|
||||
current_tasks = list(state.tasks.items())
|
||||
except Exception:
|
||||
return
|
||||
|
||||
for fname, data in reversed(state.tasks.items()):
|
||||
status = data['status']
|
||||
global_pct = data['progress']
|
||||
if not current_tasks:
|
||||
ui.label('Nenhuma atividade recente.').classes('text-gray-400 italic p-4')
|
||||
return
|
||||
|
||||
for fname, data in reversed(current_tasks):
|
||||
status = data.get('status', 'pending')
|
||||
global_pct = data.get('progress', 0)
|
||||
file_pct = data.get('file_progress', 0)
|
||||
speed = data.get('speed', '')
|
||||
label = data['label']
|
||||
label = data.get('label', 'Tarefa')
|
||||
|
||||
icon = 'circle'; color = 'grey'; spin = False
|
||||
bg_color = 'bg-white'
|
||||
|
||||
if status == 'pending':
|
||||
icon = 'hourglass_empty'; color = 'grey'
|
||||
if status == 'pending': icon = 'hourglass_empty'
|
||||
elif status == 'running':
|
||||
icon = 'sync'; color = 'blue'; spin = True
|
||||
bg_color = 'bg-blue-50 border-blue-200 border'
|
||||
elif status == 'warning':
|
||||
icon = 'warning'; color = 'orange'
|
||||
bg_color = 'bg-orange-50 border-orange-200 border'
|
||||
elif status == 'done':
|
||||
icon = 'check_circle'; color = 'green'
|
||||
elif status == 'error':
|
||||
icon = 'error'; color = 'red'
|
||||
elif status == 'skipped':
|
||||
icon = 'block'; color = 'red'
|
||||
elif status == 'done': icon = 'check_circle'; color = 'green'
|
||||
elif status == 'error': icon = 'error'; color = 'red'
|
||||
elif status == 'skipped': icon = 'block'; color = 'red'
|
||||
|
||||
with tasks_container:
|
||||
with ui.card().classes(f'w-full p-3 {bg_color} flex-row items-start gap-3'):
|
||||
# Ícone
|
||||
if spin: ui.spinner(size='sm').classes('text-blue-500 mt-1')
|
||||
else: ui.icon(icon, color=color, size='sm').classes('mt-1')
|
||||
with ui.card().classes(f'w-full p-3 {bg_color} flex-row items-start gap-3 shadow-sm'):
|
||||
if spin: ui.spinner(size='sm').classes('text-blue-500 mt-1')
|
||||
else: ui.icon(icon, color=color, size='sm').classes('mt-1')
|
||||
|
||||
with ui.column().classes('flex-grow gap-1 w-full'):
|
||||
ui.label(label).classes('font-bold text-sm text-gray-800 break-all')
|
||||
ui.label(fname).classes('text-[10px] text-gray-500 break-all mb-1')
|
||||
|
||||
# Conteúdo
|
||||
with ui.column().classes('flex-grow gap-1 w-full'):
|
||||
# Título e Nome Arquivo
|
||||
ui.label(label).classes('font-bold text-sm text-gray-800 break-all')
|
||||
ui.label(fname).classes('text-xs text-gray-500 break-all mb-1')
|
||||
|
||||
# Se estiver rodando, mostra barras
|
||||
if status == 'running':
|
||||
# Barra 1: Global
|
||||
if status == 'running':
|
||||
# Barra Total
|
||||
with ui.row().classes('w-full items-center gap-2'):
|
||||
ui.label('Total').classes('text-[10px] font-bold text-gray-400 w-8')
|
||||
ui.linear_progress(value=global_pct/100).classes('h-1.5 rounded flex-grow').props('color=blue-4')
|
||||
ui.label(f"{int(global_pct)}%").classes('text-[10px] font-bold text-blue-400 w-7 text-right')
|
||||
|
||||
# Barra do Arquivo (se houver progresso)
|
||||
if file_pct > 0 or (speed and speed != "0x"):
|
||||
with ui.row().classes('w-full items-center gap-2'):
|
||||
ui.label('Total').classes('text-xs font-bold text-gray-400 w-12')
|
||||
ui.linear_progress(value=global_pct/100, show_value=False).classes('h-2 rounded flex-grow').props('color=blue-4')
|
||||
ui.label(f"{int(global_pct)}%").classes('text-xs font-bold text-blue-400 w-8 text-right')
|
||||
ui.label('File').classes('text-[10px] font-bold text-gray-400 w-8')
|
||||
ui.linear_progress(value=file_pct/100).classes('h-2 rounded flex-grow').props('color=green')
|
||||
with ui.row().classes('items-center gap-1'):
|
||||
ui.label(f"{int(file_pct)}%").classes('text-[10px] font-bold text-green-600')
|
||||
if speed:
|
||||
ui.label(speed).classes('text-[10px] bg-green-100 text-green-800 px-1 rounded font-mono')
|
||||
|
||||
# Barra 2: Conversão de Arquivo
|
||||
# Mostra se tivermos algum progresso de arquivo OU velocidade detectada
|
||||
if file_pct > 0 or (speed and speed != "0x"):
|
||||
with ui.row().classes('w-full items-center gap-2'):
|
||||
ui.label('File').classes('text-xs font-bold text-gray-400 w-12')
|
||||
|
||||
# Barra Verde
|
||||
ui.linear_progress(value=file_pct/100, show_value=False).classes('h-3 rounded flex-grow').props('color=green')
|
||||
|
||||
# Porcentagem e Velocidade
|
||||
with ui.row().classes('items-center gap-1'):
|
||||
ui.label(f"{int(file_pct)}%").classes('text-xs font-bold text-green-600')
|
||||
if speed:
|
||||
# Badge de Velocidade
|
||||
ui.label(speed).classes('text-xs bg-green-100 text-green-800 px-1 rounded font-mono')
|
||||
|
||||
# --- LOOP DE ATUALIZAÇÃO ---
|
||||
def update_ui():
|
||||
# Logs
|
||||
logs = state.get_logs()
|
||||
log_content.set_text("\n".join(logs[-30:]))
|
||||
log_container.scroll_to(percent=1.0)
|
||||
|
||||
# Tarefas
|
||||
# Inicializa a lista dentro do container
|
||||
with tasks_container:
|
||||
render_tasks()
|
||||
|
||||
# Status Global
|
||||
# --- LOOP DE ATUALIZAÇÃO (1 segundo) ---
|
||||
def update_ui():
|
||||
# 1. ATUALIZAR LOGS (Envia apenas as linhas novas para o ui.log)
|
||||
try:
|
||||
all_logs = state.get_logs()
|
||||
if len(all_logs) > log_view.last_index:
|
||||
# Pega apenas as mensagens que ainda não foram enviadas para a tela
|
||||
new_messages = all_logs[log_view.last_index:]
|
||||
for msg in new_messages:
|
||||
log_view.push(str(msg))
|
||||
log_view.last_index = len(all_logs)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 2. ATUALIZAR LISTA DE TAREFAS
|
||||
render_tasks.refresh()
|
||||
|
||||
# 3. ATUALIZAR STATUS GLOBAL DO SERVIÇO
|
||||
if state.watcher and state.watcher.is_running:
|
||||
lbl_status_top.text = "Serviço Rodando"
|
||||
lbl_status_top.classes(replace='text-green-500')
|
||||
lbl_status_top.set_text("Serviço Rodando")
|
||||
lbl_status_top.classes(remove='text-red-400', add='text-green-500')
|
||||
switch_run.value = True
|
||||
else:
|
||||
lbl_status_top.text = "Serviço Pausado"
|
||||
lbl_status_top.classes(replace='text-red-400')
|
||||
lbl_status_top.set_text("Serviço Pausado")
|
||||
lbl_status_top.classes(remove='text-green-500', add='text-red-400')
|
||||
switch_run.value = False
|
||||
|
||||
# Botão Cancelar
|
||||
if state.current_file:
|
||||
btn_cancel.classes(remove='hidden')
|
||||
else:
|
||||
btn_cancel.classes(add='hidden')
|
||||
# 4. BOTÃO CANCELAR
|
||||
btn_cancel.set_visibility(bool(state.current_file))
|
||||
|
||||
# Inicia o timer de atualização
|
||||
ui.timer(1.0, update_ui)
|
||||
@@ -216,11 +216,20 @@ def show():
|
||||
|
||||
with ui.grid(columns=3).classes('w-full gap-4'):
|
||||
hw_val = c_hw.value if c_hw.value in CODEC_OPTS else 'cpu'
|
||||
c_codec = ui.select(CODEC_OPTS[hw_val], value=p.video_codec, label='Codec de Vídeo')
|
||||
|
||||
# --- INÍCIO DA CORREÇÃO DE VALOR SEGURO ---
|
||||
opcoes_atuais = CODEC_OPTS[hw_val]
|
||||
valor_seguro = p.video_codec if p.video_codec in opcoes_atuais else list(opcoes_atuais.keys())[0]
|
||||
|
||||
c_codec = ui.select(opcoes_atuais, value=valor_seguro, label='Codec de Vídeo')
|
||||
|
||||
def update_codecs(e, el=c_codec):
|
||||
el.options = CODEC_OPTS.get(e.value, CODEC_OPTS['cpu'])
|
||||
# CORREÇÃO: Usa e.sender.value em vez de apenas e.value
|
||||
el.options = CODEC_OPTS.get(e.sender.value, CODEC_OPTS['cpu'])
|
||||
el.value = list(el.options.keys())[0]
|
||||
el.update()
|
||||
# --- FIM DA CORREÇÃO ---
|
||||
|
||||
c_hw.on('update:model-value', update_codecs)
|
||||
c_preset = ui.select(['fast', 'medium', 'slow', 'veryfast'], value=p.preset, label='Preset')
|
||||
c_crf = ui.number('CRF/Qualidade', value=p.crf)
|
||||
|
||||
Reference in New Issue
Block a user