diff --git a/app/modules/__pycache__/file_manager.cpython-310.pyc b/app/modules/__pycache__/file_manager.cpython-310.pyc index c687372..05c52a4 100644 Binary files a/app/modules/__pycache__/file_manager.cpython-310.pyc and b/app/modules/__pycache__/file_manager.cpython-310.pyc differ diff --git a/app/modules/file_manager.py b/app/modules/file_manager.py index a2febe2..66ed469 100755 --- a/app/modules/file_manager.py +++ b/app/modules/file_manager.py @@ -85,27 +85,74 @@ 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): self.is_selecting = not self.is_selecting @@ -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()