Files
clei-flow/app/core/renamer.py

154 lines
6.4 KiB
Python
Executable File

import os
import re
from guessit import guessit
from tmdbv3api import TMDb, Movie, TV, Search
from database import AppConfig
from difflib import SequenceMatcher
class RenamerCore:
def __init__(self):
self.api_key = AppConfig.get_val('tmdb_api_key')
self.lang = AppConfig.get_val('tmdb_language', 'pt-BR')
self.min_confidence = int(AppConfig.get_val('min_confidence', '90')) / 100.0
self.tmdb = TMDb()
if self.api_key:
self.tmdb.api_key = self.api_key
self.tmdb.language = self.lang
self.movie_api = Movie()
self.tv_api = TV()
self.search_api = Search()
def clean_filename(self, filename):
name, ext = os.path.splitext(filename)
patterns = [
r'(?i)(www\.[a-z0-9-]+\.[a-z]{2,})',
r'(?i)(rede\s?canais)',
r'(?i)(comando\s?torrents?)',
r'(?i)(bludv)',
r'(?i)(\bassistir\b)',
r'(?i)(\bbaixar\b)',
r'(?i)(\bdownload\b)',
r'(?i)(\bfilme\s?completo\b)',
r'(?i)(\bpt-br\b)',
]
clean_name = name
for pat in patterns:
clean_name = re.sub(pat, '', clean_name)
clean_name = re.sub(r'\s+-\s+', ' ', clean_name)
clean_name = re.sub(r'\s+', ' ', clean_name).strip()
return clean_name + ext
def identify_file(self, filepath):
# ... (código identify_file igual ao anterior) ...
# Vou resumir aqui para não ficar gigante, mantenha o identify_file
# que passamos na última resposta (com o fix do 'results' e 'str').
filename = os.path.basename(filepath)
cleaned_filename = self.clean_filename(filename)
try: guess = guessit(cleaned_filename)
except Exception as e: return {'status': 'ERROR', 'msg': str(e)}
title = guess.get('title')
if not title: return {'status': 'NOT_FOUND', 'msg': 'Sem título'}
if not self.api_key: return {'status': 'ERROR', 'msg': 'Sem API Key'}
try:
media_type = guess.get('type', 'movie')
if media_type == 'episode': results = self.search_api.tv_shows(term=title)
else:
results = self.search_api.movies(term=title)
if not results: results = self.search_api.tv_shows(term=title)
except: return {'status': 'NOT_FOUND', 'msg': 'Erro TMDb'}
if isinstance(results, dict) and 'results' in results: results = results['results']
elif hasattr(results, 'results'): results = results.results
if results and isinstance(results, list) and len(results) > 0 and isinstance(results[0], str):
return {'status': 'NOT_FOUND', 'msg': 'Formato inválido'}
candidates = []
for res in results:
if isinstance(res, str): continue
if isinstance(res, dict):
r_id = res.get('id'); r_title = res.get('title') or res.get('name')
r_date = res.get('release_date') or res.get('first_air_date')
r_overview = res.get('overview', '')
else:
r_id = getattr(res, 'id', None)
r_title = getattr(res, 'title', getattr(res, 'name', ''))
r_date = getattr(res, 'release_date', getattr(res, 'first_air_date', ''))
r_overview = getattr(res, 'overview', '')
if not r_title or not r_id: continue
r_year = int(str(r_date)[:4]) if r_date else 0
t1 = str(title).lower(); t2 = str(r_title).lower()
base_score = SequenceMatcher(None, t1, t2).ratio()
if t1 in t2 or t2 in t1: base_score = max(base_score, 0.85)
g_year = guess.get('year')
if g_year and r_year:
if g_year == r_year: base_score += 0.15
elif abs(g_year - r_year) <= 1: base_score += 0.05
final_score = min(base_score, 1.0)
candidates.append({
'tmdb_id': r_id, 'title': r_title, 'year': r_year,
'type': 'movie' if hasattr(res, 'title') or (isinstance(res, dict) and 'title' in res) else 'tv',
'overview': str(r_overview)[:100], 'score': final_score
})
if not candidates: return {'status': 'NOT_FOUND', 'msg': 'Sem candidatos'}
candidates.sort(key=lambda x: x['score'], reverse=True)
best = candidates[0]
if len(candidates) == 1 and best['score'] > 0.6: return {'status': 'MATCH', 'match': best, 'guessed': guess}
is_clear_winner = False
if len(candidates) > 1 and (best['score'] - candidates[1]['score']) > 0.15: is_clear_winner = True
if best['score'] >= self.min_confidence or is_clear_winner:
return {'status': 'MATCH', 'match': best, 'guessed': guess}
return {'status': 'AMBIGUOUS', 'candidates': candidates[:5], 'guessed': guess}
def get_details(self, tmdb_id, media_type):
if media_type == 'movie': return self.movie_api.details(tmdb_id)
return self.tv_api.details(tmdb_id)
# --- AQUI ESTÁ A MUDANÇA ---
def build_path(self, category_obj, media_info, guessed_info):
clean_title = re.sub(r'[\\/*?:"<>|]', "", media_info['title']).strip()
year = str(media_info['year'])
forced_type = category_obj.content_type
actual_type = media_info['type']
is_series = False
if forced_type == 'series': is_series = True
elif forced_type == 'movie': is_series = False
else: is_series = (actual_type == 'tv')
if not is_series:
# Filme: "Matrix (1999).mkv"
return f"{clean_title} ({year}).mkv"
else:
# Série
season = guessed_info.get('season')
episode = guessed_info.get('episode')
if isinstance(season, list): season = season[0]
if isinstance(episode, list): episode = episode[0]
if not season: season = 1
if not episode: episode = 1
season_folder = f"Temporada {int(season):02d}"
# MUDANÇA: Nome do arquivo simplificado
# De: "Nome Serie S01E01.mkv"
# Para: "Episódio 01.mkv"
filename = f"Episódio {int(episode):02d}.mkv"
# Caminho relativo: "Nome Série/Temporada 01/Episódio 01.mkv"
return os.path.join(clean_title, season_folder, filename)