#!/usr/bin/env python3 """ Detector de GAMEPLAY ACTIVO: Busca momentos donde está jugando, no solo hablando. Filtra intros y momentos de solo hablar. """ import json import logging import re import numpy as np logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def detect_active_gameplay(chat_data, transcripcion_json, intro_skip=90, min_duration=20, max_duration=45, top=30): """ Detecta momentos de gameplay activo (hablando + jugando). """ logger.info("=== Buscando GAMEPLAY ACTIVO ===") with open(transcripcion_json, 'r', encoding='utf-8') as f: trans_data = json.load(f) segments = trans_data.get("segments", []) # Palabras de GAMEPLAY (está jugando) gameplay_keywords = [ r'\b(champion|campe[oó]n|ult[i|í]|habilidad|spell|q|w|e|r)\b', r'\b(kill|muert[e|é]|mate|muero|fui|mat[aá])\b', r'\b(fight|pelea|fight|team|equip|jungla|top|mid|adc|support)\b', r'\b(lane|linia|mina|drag[oó]n|baron|nashor|torre|inhib)\b', r'\b(ult|ultimate|flash|ignite|exhaust|teleport|heal)\b', r'\b(gank|roam|invade|invasi[oó])\b', r'\b(ward|vision|control|map|objetivo)\b', r'\b(damage|daño|dps|burst|poke)\b', r'\b(lethality|letalidad|crit|cr[i|í]tico)\b', r'\b(arma|item|build|objeto|poder|stats)\b', r'\b(level|nivel|exp|gold|oro|farm|cs)\b', r'\b(esquiv|evade|dodge|block|bloqueo)\b', r'\b(engage|pelear|inici|all[i|í]n|surrender|rindase)\b', ] # Palabras de SOLO HABLAR (excluir) talking_only_keywords = [ r'\b(hola|buenas|buenas tardes|buenas noches|adi[oó]s)\b', r'\b(gracias|thank|agradezco)\b', r'\b(playlist|música|canci[oó]n|song)\b', r'\b(intro|presento|presentaci[oó]n|inicio)\b', r'\b(despedida|adi[oó]s|nos vemos|chao)\b', r'\b(merch|tienda|store|donar|donaci[oó]n)\b', r'\b(rrs|redes sociales|twitter|instagram|discord)\b', r'\b giveaway|sorteo|regalo\b', ] # Analizar segmentos gameplay_moments = [] for i, seg in enumerate(segments): start = seg["start"] end = seg["end"] text = seg["text"] text_lower = text.lower() # Saltar intro if start < intro_skip: continue # Verificar que NO sea solo hablar is_talking_only = False for pattern in talking_only_keywords: if re.search(pattern, text_lower): is_talking_only = True break if is_talking_only: continue # Verificar que tenga palabras de gameplay gameplay_score = 0 for pattern in gameplay_keywords: matches = len(re.findall(pattern, text_lower)) gameplay_score += matches if gameplay_score > 0: gameplay_moments.append({ "start": start, "end": end, "score": gameplay_score, "text": text.strip()[:80] }) if not gameplay_moments: logger.warning("No se encontraron momentos de gameplay") return [] # Ordenar por score gameplay_moments.sort(key=lambda x: -x["score"]) # Agrupar en intervalos sin solapamiento intervals = [] for moment in gameplay_moments: start = int(moment["start"]) end = int(moment["end"]) # Duración del clip duration = max(min_duration, min(end - start, max_duration)) # Extender el final para capturar la acción end = start + duration + 5 # +5s padding # Verificar solapamiento overlaps = False for s, e in intervals: if not (end < s or start > e): overlaps = True break if not overlaps: intervals.append((start, int(end))) if len(intervals) >= top: break intervals.sort() logger.info(f"Moments de gameplay detectados: {len(intervals)}") return intervals, gameplay_moments def main(): import argparse parser = argparse.ArgumentParser() parser.add_argument("--chat", required=True) parser.add_argument("--transcripcion", required=True) parser.add_argument("--output", default="highlights_gameplay.json") parser.add_argument("--top", type=int, default=30) parser.add_argument("--intro-skip", type=int, default=90) parser.add_argument("--min-duration", type=int, default=20) parser.add_argument("--max-duration", type=int, default=45) args = parser.parse_args() with open(args.chat, 'r') as f: chat_data = json.load(f) intervals, moments = detect_active_gameplay( chat_data, args.transcripcion, args.intro_skip, args.min_duration, args.max_duration, args.top ) # Guardar with open(args.output, 'w') as f: json.dump(intervals, f) logger.info(f"Guardado en {args.output}") # Imprimir resumen print(f"\n{'='*70}") print(f"GAMEPLAY ACTIVO - MOMENTOS JUGANDO".center(70)) print(f"{'='*70}") print(f"Total: {len(intervals)} clips") print(f"Duración total: {sum(e-s for s,e in intervals)}s ({sum(e-s for s,e in intervals)/60:.1f} min)") print(f"Intro excluida: primeros {args.intro_skip}s") print(f"{'-'*70}") for i, (start, end) in enumerate(intervals, 1): duration = end - start h = start // 3600 m = (start % 3600) // 60 sec = start % 60 for moment in moments: if abs(moment["start"] - start) < 10: text_preview = moment["text"][:60].replace('\n', ' ') print(f"{i:2d}. {h:02d}:{m:02d}:{sec:02d} - {duration}s - {text_preview}...") break print(f"{'='*70}") if __name__ == "__main__": main()