consertada a indentificação

This commit is contained in:
2026-02-10 00:56:46 +00:00
parent d77f17df27
commit bd59ba234c
4 changed files with 200 additions and 203 deletions

View File

@@ -74,7 +74,7 @@ class DirectoryWatcher:
self.abort_flag = False
state.current_file = fname
# Etapa 1: Identificação (Global 0-15%)
# --- 1. IDENTIFICAÇÃO ---
state.update_task(fname, 'running', progress=5, label=f"Identificando: {fname}...")
state.log(f"🔄 Iniciando: {fname}")
@@ -119,21 +119,35 @@ class DirectoryWatcher:
if self.abort_flag: return
# Etapa 2: Categoria
category = self.find_category(target_info['type'])
if not category:
state.update_task(fname, 'error', 0, label="Sem Categoria")
return
# Etapa 3: Conversão
# --- METADADOS E CATEGORIA ---
try:
details = self.renamer.get_details(target_info['tmdb_id'], target_info['type'])
r_title = getattr(details, 'title', getattr(details, 'name', 'Unknown')) or 'Unknown'
# Extração de IDs e Códigos para a nova Lógica
# IDs de Gênero (Ex: [16, 28])
tmdb_genre_ids = [str(g['id']) for g in getattr(details, 'genres', [])]
# Países de Origem (Ex: ['JP', 'US'])
origin_countries = getattr(details, 'origin_country', [])
if isinstance(origin_countries, str): origin_countries = [origin_countries]
origin_countries = [c.upper() for c in origin_countries]
state.log(f" Dados: Gêneros={tmdb_genre_ids} | Países={origin_countries}")
full_details = {'title': r_title, 'year': '0000', 'type': target_info['type']}
d_date = getattr(details, 'release_date', getattr(details, 'first_air_date', '0000'))
if d_date: full_details['year'] = str(d_date)[:4]
# --- 2. CATEGORIA INTELIGENTE (NOVA LÓGICA) ---
category = self.find_best_category(target_info['type'], tmdb_genre_ids, origin_countries)
if not category:
state.update_task(fname, 'error', 0, label="Sem Categoria")
return
state.log(f"📂 Categoria Vencedora: {category.name}")
# --- 3. CONVERSÃO ---
guessed_data = result.get('guessed', {})
relative_path = self.renamer.build_path(category, full_details, guessed_data)
@@ -143,71 +157,40 @@ class DirectoryWatcher:
state.update_task(fname, 'running', progress=15, label=f"Convertendo: {full_details['title']}")
engine = FFmpegEngine()
# Tenta pegar duração total em Segundos
total_duration_sec = engine.get_duration(str(filepath))
# Converte para microsegundos para bater com o FFmpeg
total_duration_us = total_duration_sec * 1000000
cmd = engine.build_command(str(filepath), str(temp_output))
cmd.insert(1, '-progress'); cmd.insert(2, 'pipe:1')
# --- MODIFICAÇÃO: Injeta flag para saída legível por máquina (stdout) ---
# Isso garante que teremos dados de progresso confiáveis
cmd.insert(1, '-progress')
cmd.insert(2, 'pipe:1')
# Redireciona stdout para pegarmos os dados. Stderr fica para erros/warnings.
self.current_process = await asyncio.create_subprocess_exec(
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
)
# --- LOOP DE PROGRESSO (MÉTODO KEY=VALUE) ---
current_speed_str = ""
while True:
if self.abort_flag:
self.current_process.kill()
break
# Lê stdout (progresso)
line_bytes = await self.current_process.stdout.readline()
if not line_bytes: break
line = line_bytes.decode('utf-8', errors='ignore').strip()
# O formato é chave=valor
if '=' in line:
key, value = line.split('=', 1)
key = key.strip()
value = value.strip()
# 1. Tempo decorrido (em microsegundos)
key = key.strip(); value = value.strip()
if key == 'out_time_us':
try:
current_us = int(value)
if total_duration_us > 0:
file_pct = (current_us / total_duration_us) * 100
if file_pct > 100: file_pct = 100
if file_pct < 0: file_pct = 0 # As vezes vem negativo no inicio
# Global: 15% a 99%
if file_pct < 0: file_pct = 0
global_pct = 15 + (file_pct * 0.84)
state.update_task(
fname, 'running',
progress=global_pct,
file_progress=file_pct,
speed=current_speed_str,
label=f"Processando: {full_details['title']}"
)
state.update_task(fname, 'running', progress=global_pct, file_progress=file_pct, speed=current_speed_str, label=f"Processando: {full_details['title']}")
except: pass
# 2. Velocidade
elif key == 'speed':
current_speed_str = value
elif key == 'speed': current_speed_str = value
await self.current_process.wait()
# -----------------------------------
if self.abort_flag:
state.update_task(fname, 'error', 0, label="Abortado")
@@ -215,14 +198,13 @@ class DirectoryWatcher:
return
if self.current_process.returncode != 0:
# Se falhar, lemos o stderr para saber o motivo
err_log = await self.current_process.stderr.read()
state.log(f"❌ Erro FFmpeg: {err_log.decode('utf-8')[-200:]}")
state.update_task(fname, 'error', 0, label="Erro FFmpeg")
return
self.current_process = None
# Etapa 4: Deploy
# --- 4. DEPLOY ---
state.update_task(fname, 'running', progress=99, label="Organizando...")
final_full_path = Path(category.target_path) / relative_path
final_full_path.parent.mkdir(parents=True, exist_ok=True)
@@ -253,12 +235,78 @@ class DirectoryWatcher:
state.log(f"Erro Pipeline: {e}")
state.update_task(fname, 'error', 0, label=f"Erro: {e}")
def find_category(self, media_type):
keywords = ['movie', 'film', 'filme'] if media_type == 'movie' else ['tv', 'serie', 'série']
def find_best_category(self, media_type, genre_ids, countries):
"""
Sistema de Pontuação 3.0 (Regras Estritas)
Agora compara IDs e Siglas, não texto.
"""
all_cats = list(Category.select())
if not all_cats: return None
candidates = []
# 1. Filtro Rígido de TIPO (Movie vs Series)
for cat in all_cats:
if not cat.match_keywords: continue
cat_keys = [k.strip().lower() for k in cat.match_keywords.split(',')]
if any(k in cat_keys for k in keywords):
return cat
return all_cats[0] if all_cats else None
if media_type == 'movie' and cat.content_type == 'series': continue
if media_type != 'movie' and cat.content_type == 'movie': continue
candidates.append(cat)
if not candidates: return all_cats[0]
scored_cats = []
for cat in candidates:
score = 0
# Carrega filtros da categoria
cat_genres = cat.genre_filters.split(',') if cat.genre_filters else []
cat_countries = cat.country_filters.split(',') if cat.country_filters else []
cat_genres = [g for g in cat_genres if g] # Limpa vazios
cat_countries = [c for c in cat_countries if c]
# --- Lógica de Correspondência ---
match_genre = False
match_country = False
# Verifica Gêneros (Se a categoria tiver filtros, TEM que bater)
if cat_genres:
# Se o arquivo tiver pelo menos UM dos gêneros da lista
if any(gid in cat_genres for gid in genre_ids):
match_genre = True
score += 50 # Ganha muitos pontos por match específico
else:
# Se a categoria exige gênero e o arquivo não tem -> Categoria Descartada
score = -1000
else:
# Categoria genérica de gênero (aceita tudo)
match_genre = True
score += 10 # Pontuação base baixa
# Verifica Países
if cat_countries:
if any(cc in cat_countries for cc in countries):
match_country = True
score += 50 # Ganha pontos por match de país
else:
score = -1000 # Exige país mas não bate -> Descartada
else:
match_country = True
score += 10
# Bônus se for categoria Mista (geralmente usada para Anime/Desenho)
if cat.content_type == 'mixed' and score > 0:
score += 5
scored_cats.append((score, cat))
# Ordena
scored_cats.sort(key=lambda x: x[0], reverse=True)
best_match = scored_cats[0]
# Se a pontuação for negativa, significa que nenhuma regra bateu.
# Devemos pegar uma categoria genérica (sem filtros)
if best_match[0] < 0:
for cat in candidates:
if not cat.genre_filters and not cat.country_filters:
return cat
return best_match[1]