file manager concluido 99% redondo
This commit is contained in:
Binary file not shown.
@@ -85,26 +85,73 @@ class FileManager:
|
||||
parent = os.path.dirname(self.path)
|
||||
if self.path != ROOT_DIR: await self.navigate(parent)
|
||||
|
||||
# --- UPLOAD ---
|
||||
# --- UPLOAD CORRIGIDO FINAL (NOME E CONTEÚDO) ---
|
||||
def open_upload_dialog(self):
|
||||
with ui.dialog() as dialog, ui.card():
|
||||
ui.label(f'Upload para: {os.path.basename(self.path)}').classes('font-bold')
|
||||
|
||||
async def handle(e):
|
||||
try:
|
||||
target = os.path.join(self.path, e.name)
|
||||
await run.io_bound(self._save_file, target, e.content)
|
||||
ui.notify(f'Sucesso: {e.name}')
|
||||
except Exception as ex: ui.notify(f'Erro: {ex}', type='negative')
|
||||
# 1. Recuperação do Nome (Prioridade para .filename do Starlette)
|
||||
name = None
|
||||
|
||||
# Verifica se existe o objeto interno 'file' (detectado nos logs anteriores)
|
||||
if hasattr(e, 'file'):
|
||||
# .filename é onde fica o nome original do arquivo no upload web
|
||||
name = getattr(e.file, 'filename', None)
|
||||
|
||||
# Se não achar, tenta .name
|
||||
if not name:
|
||||
name = getattr(e.file, 'name', None)
|
||||
|
||||
# Se ainda não achou, tenta direto no evento (fallback)
|
||||
if not name:
|
||||
name = getattr(e, 'name', 'arquivo_sem_nome')
|
||||
|
||||
# 2. Leitura do Conteúdo (Assíncrona)
|
||||
content = b''
|
||||
if hasattr(e, 'file'):
|
||||
# Tenta resetar o ponteiro de leitura para o início
|
||||
if hasattr(e.file, 'seek'):
|
||||
await e.file.seek(0)
|
||||
|
||||
# Lê os bytes (await é necessário aqui)
|
||||
if hasattr(e.file, 'read'):
|
||||
content = await e.file.read()
|
||||
|
||||
if not content:
|
||||
ui.notify('Erro: Arquivo vazio ou ilegível', type='warning')
|
||||
return
|
||||
|
||||
# 3. Salva no disco
|
||||
target = os.path.join(self.path, name)
|
||||
|
||||
# Executa a gravação em thread separada para não travar o servidor
|
||||
await run.io_bound(self._save_file_bytes, target, content)
|
||||
|
||||
ui.notify(f'Sucesso: {name}', type='positive')
|
||||
|
||||
except Exception as ex:
|
||||
# Imprime no log do container para diagnóstico se falhar
|
||||
print(f"ERRO CRITICO UPLOAD: {ex}")
|
||||
ui.notify(f'Erro: {ex}', type='negative')
|
||||
|
||||
# Componente UI
|
||||
ui.upload(on_upload=handle, auto_upload=True, multiple=True).props('accept=*').classes('w-full')
|
||||
|
||||
async def close_and_refresh():
|
||||
dialog.close()
|
||||
await self.refresh()
|
||||
|
||||
ui.button('Fechar e Atualizar', on_click=close_and_refresh).props('flat w-full')
|
||||
dialog.open()
|
||||
|
||||
def _save_file(self, target, content):
|
||||
with open(target, 'wb') as f: f.write(content.read())
|
||||
|
||||
# Função auxiliar simples para salvar bytes
|
||||
def _save_file_bytes(self, target, content_bytes):
|
||||
with open(target, 'wb') as f:
|
||||
f.write(content_bytes)
|
||||
|
||||
|
||||
# --- LÓGICA DE SELEÇÃO ---
|
||||
def toggle_select_mode(self):
|
||||
@@ -375,24 +422,38 @@ class FileManager:
|
||||
ui.icon(icon, color='amber' if entry.is_dir() else 'grey')
|
||||
ui.label(entry.name).classes('text-sm font-medium flex-grow')
|
||||
|
||||
# --- MENU DE CONTEXTO (CORRIGIDO) ---
|
||||
def bind_context_menu(self, element, entry):
|
||||
with ui.menu() as m:
|
||||
if not entry.is_dir and entry.name.lower().endswith(('.mkv', '.mp4')):
|
||||
# Opções de mídia apenas para vídeos
|
||||
if not entry.is_dir() and entry.name.lower().endswith(('.mkv', '.mp4', '.avi')):
|
||||
ui.menu_item('Media Info', on_click=lambda: self.open_inspector(entry.path))
|
||||
|
||||
ui.menu_item('Renomear', on_click=lambda: self.open_rename_dialog(entry.path))
|
||||
ui.menu_item('Mover Para...', on_click=lambda: self.open_move_dialog([entry.path]))
|
||||
|
||||
async def delete_single():
|
||||
try:
|
||||
ui.notify(f'Excluindo {entry.name}...')
|
||||
if entry.is_dir: await run.io_bound(shutil.rmtree, entry.path)
|
||||
else: await run.io_bound(os.remove, entry.path)
|
||||
|
||||
# CORREÇÃO AQUI: Adicionados parênteses () em is_dir()
|
||||
# Sem eles, o Python acha que sempre é True (porque o método existe)
|
||||
if entry.is_dir():
|
||||
await run.io_bound(shutil.rmtree, entry.path)
|
||||
else:
|
||||
await run.io_bound(os.remove, entry.path)
|
||||
|
||||
await self.refresh()
|
||||
except Exception as e: print(f"Erro delete: {e}")
|
||||
ui.notify('Item excluído.', type='positive')
|
||||
except Exception as e:
|
||||
ui.notify(f"Erro ao excluir: {e}", type='negative')
|
||||
print(f"DEBUG DELETE ERROR: {e}")
|
||||
|
||||
ui.menu_item('Excluir', on_click=delete_single).props('text-color=red')
|
||||
|
||||
element.on('contextmenu.prevent', lambda: m.open())
|
||||
|
||||
|
||||
# --- INSPECTOR (RESTAURADO E RICO) ---
|
||||
async def open_inspector(self, path):
|
||||
dialog = ui.dialog()
|
||||
|
||||
Reference in New Issue
Block a user