159 lines
5.9 KiB
Python
Executable File
159 lines
5.9 KiB
Python
Executable File
import streamlit as st
|
|
import os
|
|
import re
|
|
import shutil
|
|
import pandas as pd
|
|
import time
|
|
|
|
# Define a raiz absoluta
|
|
ROOT_DIR = "/downloads"
|
|
|
|
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 extract_season_episode(filename):
|
|
"""
|
|
Testa uma lista de padrões regex para extrair Temporada e Episódio.
|
|
Retorna (season, episode) ou (None, None).
|
|
"""
|
|
# LISTA DE PADRÕES (Do mais específico para o mais genérico)
|
|
patterns = [
|
|
# 1. Padrão Scene/Universal: S01E01, S01.E01, S01_E01, S01 - E01
|
|
r'(?i)S(\d{1,4})[\s._-]*E(\d{1,4})',
|
|
|
|
# 2. Padrão Anime/P2P (Seu caso): S01EP01, S01.EP01
|
|
r'(?i)S(\d{1,4})[\s._-]*EP(\d{1,4})',
|
|
|
|
# 3. Padrão Antigo: 1x01, 01x01
|
|
r'(?i)(\d{1,4})x(\d{1,4})',
|
|
|
|
# 4. Padrão Extenso: Season 1 Episode 1
|
|
r'(?i)Season[\s._-]*(\d{1,4})[\s._-]*Episode[\s._-]*(\d{1,4})',
|
|
|
|
# 5. Padrão Hífen (Anime): "Nome S01 - 05.mkv"
|
|
r'(?i)S(\d{1,4})[\s._-]*-\s*(\d{1,4})',
|
|
|
|
# 6. Padrão Colchetes: [1x01]
|
|
r'(?i)\[(\d{1,4})x(\d{1,4})\]',
|
|
]
|
|
|
|
for pattern in patterns:
|
|
match = re.search(pattern, filename)
|
|
if match:
|
|
# Retorna Temporada, Episódio
|
|
return match.group(1), match.group(2)
|
|
|
|
return None, None
|
|
|
|
def render():
|
|
st.header("🏷️ Renomeador de Séries")
|
|
st.info("Suporta: S01E01, S01EP01, 1x01, S01 - 01, Season 1 Episode 1...")
|
|
|
|
# --- SELEÇÃO DE PASTA ---
|
|
all_folders = get_all_subfolders(ROOT_DIR)
|
|
|
|
idx = 0
|
|
if 'rn_last_folder' in st.session_state and st.session_state.rn_last_folder in all_folders:
|
|
idx = all_folders.index(st.session_state.rn_last_folder)
|
|
|
|
root_path = st.selectbox(
|
|
"Selecione a pasta para analisar:",
|
|
options=all_folders,
|
|
index=idx,
|
|
format_func=lambda x: x.replace(ROOT_DIR, "") if x != ROOT_DIR else "Raiz (/downloads)"
|
|
)
|
|
|
|
st.session_state.rn_last_folder = root_path
|
|
|
|
if st.button("🔍 Analisar Arquivos", type="primary"):
|
|
if not os.path.exists(root_path):
|
|
st.error("Pasta não encontrada.")
|
|
return
|
|
|
|
preview_data = []
|
|
ignored_files = []
|
|
|
|
# Varre a pasta
|
|
for root, dirs, files in os.walk(root_path):
|
|
# Ignora pastas do sistema
|
|
if "finalizados" in root or "temp" in root: continue
|
|
|
|
for file in files:
|
|
if file.lower().endswith(('.mkv', '.mp4', '.avi')):
|
|
|
|
# Chama a função inteligente de detecção
|
|
season, episode = extract_season_episode(file)
|
|
|
|
if season and episode:
|
|
try:
|
|
s_fmt = f"{int(season):02d}"
|
|
e_fmt = f"{int(episode):02d}"
|
|
ext = os.path.splitext(file)[1]
|
|
|
|
season_folder = f"Temporada {s_fmt}"
|
|
new_name = f"Episódio {e_fmt}{ext}"
|
|
|
|
original_full = os.path.join(root, file)
|
|
dest_full = os.path.join(root_path, season_folder, new_name)
|
|
|
|
preview_data.append({
|
|
"Arquivo Original": file,
|
|
"Nova Estrutura": f"{season_folder}/{new_name}",
|
|
"src": original_full,
|
|
"dst": dest_full
|
|
})
|
|
except:
|
|
ignored_files.append(file)
|
|
else:
|
|
ignored_files.append(file)
|
|
|
|
st.session_state['renamer_preview'] = preview_data
|
|
st.session_state['renamer_ignored'] = ignored_files
|
|
|
|
# --- RESULTADOS ---
|
|
if 'renamer_preview' in st.session_state and st.session_state['renamer_preview']:
|
|
st.divider()
|
|
st.subheader(f"✅ Identificados ({len(st.session_state['renamer_preview'])})")
|
|
|
|
df = pd.DataFrame(st.session_state['renamer_preview'])
|
|
st.dataframe(
|
|
df[["Arquivo Original", "Nova Estrutura"]],
|
|
use_container_width=True,
|
|
hide_index=True
|
|
)
|
|
|
|
col1, col2 = st.columns([1, 4])
|
|
with col1:
|
|
if st.button("🚀 Confirmar", type="primary", use_container_width=True):
|
|
count = 0
|
|
for item in st.session_state['renamer_preview']:
|
|
dst_folder = os.path.dirname(item["dst"])
|
|
try:
|
|
if not os.path.exists(dst_folder):
|
|
os.makedirs(dst_folder, exist_ok=True)
|
|
|
|
if not os.path.exists(item["dst"]):
|
|
shutil.move(item["src"], item["dst"])
|
|
count += 1
|
|
except Exception as e:
|
|
st.error(f"Erro ao mover {item['src']}: {e}")
|
|
|
|
st.success(f"{count} arquivos organizados com sucesso!")
|
|
time.sleep(2)
|
|
del st.session_state['renamer_preview']
|
|
del st.session_state['renamer_ignored']
|
|
st.rerun()
|
|
|
|
# Exibe ignorados se houver, para debug do usuário
|
|
if 'renamer_ignored' in st.session_state and st.session_state['renamer_ignored']:
|
|
with st.expander("⚠️ Arquivos ignorados (Padrão desconhecido)", expanded=False):
|
|
st.write(st.session_state['renamer_ignored'])
|
|
|
|
elif 'renamer_preview' in st.session_state and not st.session_state['renamer_preview']:
|
|
st.warning("Nenhum arquivo de vídeo encontrado.") |