Versão Inicial V18 (FFmpeg + Haswell)
This commit is contained in:
223
app/modules/file_manager.py
Executable file
223
app/modules/file_manager.py
Executable file
@@ -0,0 +1,223 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user