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