import streamlit as st import os import shutil import pandas as pd import time import datetime # Define a raiz absoluta ROOT_DIR = "/downloads" def get_human_size(size): for unit in ['B', 'KB', 'MB', 'GB', 'TB']: if size < 1024: return f"{size:.2f} {unit}" size /= 1024 return f"{size:.2f} PB" def get_files_info(path): files_list = [] try: with os.scandir(path) as entries: # Ordena: Pastas primeiro, depois arquivos (ambos alfabéticos) sorted_entries = sorted(entries, key=lambda e: (not e.is_dir(), e.name.lower())) for entry in sorted_entries: stat = entry.stat() dt_mod = datetime.datetime.fromtimestamp(stat.st_mtime).strftime('%d/%m/%Y %H:%M') info = { "Selecionar": False, "Tipo": "📁" if entry.is_dir() else "📄", "Nome": entry.name, "Tamanho": "-" if entry.is_dir() else get_human_size(stat.st_size), "Modificado": dt_mod, "is_dir": entry.is_dir() # Coluna oculta para lógica } files_list.append(info) except Exception as e: st.error(f"Erro ao ler pasta: {e}") return files_list def get_all_subfolders(root_path): folder_list = [root_path] try: for root, dirs, files in os.walk(root_path): for d in dirs: folder_list.append(os.path.join(root, d)) except: pass return sorted(folder_list) def render(): st.header("📂 Explorador de Arquivos") # --- GESTÃO DE ESTADO (PATH) --- if 'fm_path' not in st.session_state: st.session_state.fm_path = ROOT_DIR # Segurança: não deixa subir além da raiz if not st.session_state.fm_path.startswith(ROOT_DIR): st.session_state.fm_path = ROOT_DIR current_path = st.session_state.fm_path # --- BARRA DE NAVEGAÇÃO SUPERIOR --- col_nav1, col_nav2, col_nav3, col_nav4 = st.columns([1, 1, 1, 6]) with col_nav1: if st.button("⬆️ Voltar", use_container_width=True, help="Subir um nível"): parent = os.path.dirname(current_path) st.session_state.fm_path = parent st.rerun() with col_nav2: if st.button("🏠 Raiz", use_container_width=True): st.session_state.fm_path = ROOT_DIR st.rerun() with col_nav3: if st.button("🔄 Reload", use_container_width=True): st.rerun() with col_nav4: # Mostra o caminho bonitinho display_path = current_path.replace(ROOT_DIR, " / Raiz") st.info(f"📂 **Local:** `{display_path}`") # --- LISTAGEM DE ARQUIVOS --- files_data = get_files_info(current_path) if not files_data: st.warning("Pasta vazia.") df = pd.DataFrame(columns=["Selecionar", "Tipo", "Nome", "Tamanho", "Modificado"]) else: df = pd.DataFrame(files_data) # Editor de Dados (Tabela) edited_df = st.data_editor( df, column_config={ "Selecionar": st.column_config.CheckboxColumn("Sel.", width="small"), "Tipo": st.column_config.TextColumn("", width="small"), "Nome": st.column_config.TextColumn("Nome do Arquivo/Pasta", width="large"), "Tamanho": st.column_config.TextColumn("Tamanho", width="medium"), "Modificado": st.column_config.TextColumn("Data", width="medium"), }, disabled=["Tipo", "Nome", "Tamanho", "Modificado"], hide_index=True, use_container_width=True, key=f"editor_{current_path}" # Chave única para resetar ao mudar de pasta ) # Filtra Selecionados selected_rows = edited_df[edited_df["Selecionar"] == True] # --- BOTÃO RÁPIDO: ABRIR PASTA --- # Se selecionou apenas 1 item e é pasta, mostra botão gigante para entrar if len(selected_rows) == 1: row = selected_rows.iloc[0] if row["is_dir"]: st.success(f"Selecionado: 📁 {row['Nome']}") if st.button(f"Abrir Pasta '{row['Nome']}'", type="primary", use_container_width=True): st.session_state.fm_path = os.path.join(current_path, row["Nome"]) st.rerun() st.divider() # --- PAINEL DE AÇÕES (ABAS) --- tab_mov, tab_ren, tab_new, tab_del = st.tabs([ "📦 Mover Itens", "✏️ Renomear", "➕ Criar Pasta", "🗑️ Excluir" ]) # 1. ABA MOVER with tab_mov: st.caption("Mover itens selecionados para outra pasta.") if selected_rows.empty: st.info("Selecione itens na lista acima para mover.") else: all_folders = get_all_subfolders(ROOT_DIR) # Remove a pasta atual da lista de destinos all_folders = [f for f in all_folders if f != current_path] dest_folder = st.selectbox( "Escolha o destino:", all_folders, format_func=lambda x: x.replace(ROOT_DIR, "Raiz") ) if st.button("Mover Agora", type="primary"): count = 0 for _, row in selected_rows.iterrows(): src = os.path.join(current_path, row["Nome"]) dst = os.path.join(dest_folder, row["Nome"]) try: shutil.move(src, dst) count += 1 except Exception as e: st.error(f"Erro ao mover {row['Nome']}: {e}") if count > 0: st.toast(f"✅ {count} itens movidos com sucesso!") time.sleep(1) st.rerun() # 2. ABA RENOMEAR with tab_ren: st.caption("Renomear o item selecionado (Selecione apenas 1).") if len(selected_rows) != 1: st.warning("Por segurança, selecione apenas 1 item para renomear.") else: row = selected_rows.iloc[0] old_name = row["Nome"] new_name = st.text_input("Novo nome:", value=old_name) if new_name != old_name and st.button("Salvar Novo Nome"): old_path = os.path.join(current_path, old_name) new_path = os.path.join(current_path, new_name) try: os.rename(old_path, new_path) st.toast(f"✅ Renomeado para {new_name}") time.sleep(1) st.rerun() except Exception as e: st.error(f"Erro: {e}") # 3. ABA CRIAR PASTA with tab_new: st.caption(f"Criar uma nova pasta dentro de: {os.path.basename(current_path)}") new_folder_name = st.text_input("Nome da Nova Pasta:") if st.button("Criar Pasta"): if new_folder_name: path_to_create = os.path.join(current_path, new_folder_name) try: os.makedirs(path_to_create, exist_ok=False) st.toast(f"✅ Pasta '{new_folder_name}' criada!") time.sleep(1) st.rerun() except FileExistsError: st.error("Essa pasta já existe.") except Exception as e: st.error(f"Erro: {e}") # 4. ABA EXCLUIR with tab_del: if selected_rows.empty: st.info("Selecione itens para excluir.") else: st.error(f"⚠️ Você tem {len(selected_rows)} itens selecionados.") st.write("Tem certeza que deseja excluir permanentemente?") col_del1, col_del2 = st.columns([1, 4]) with col_del1: if st.button("🔥 SIM, EXCLUIR", type="primary"): count = 0 for _, row in selected_rows.iterrows(): full_path = os.path.join(current_path, row["Nome"]) try: if row["is_dir"]: shutil.rmtree(full_path) else: os.remove(full_path) count += 1 except Exception as e: st.error(f"Erro ao deletar {row['Nome']}: {e}") if count > 0: st.toast(f"🗑️ {count} itens apagados.") time.sleep(1) st.rerun()