Files
pymediamanager/app/modules/file_manager.py

223 lines
8.4 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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()