#!/usr/bin/env python3 """ Detector de EVENTOS DE JUEGO: Busca momentos específicos: muertes de aliados, habilidades, objetivos EXTIENDE mucho los clips para no cortar la acción. """ import json import logging import re logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def detect_game_events(transcripcion_json, intro_skip=120, clip_duration=45, padding_after=15, top=40): """ Detecta eventos específicos del juego y extiende los clips. """ logger.info("=== Buscando EVENTOS DE JUEGO ===") with open(transcripcion_json, 'r', encoding='utf-8') as f: trans_data = json.load(f) segments = trans_data.get("segments", []) # Eventos de ALIADO MUERTO / TILT MOMENTS ally_death_events = [ r'\b(ha muerto|murio|muri[óo]|falleci[óo]) (un aliado|un teammate|el compa|el compa[ñn]ero|mi equipo|mis aliados|el team)\b', r'\b(teammate|compa|compa[ñn]ero) (ha muerto|murio|muri[óo])\b', r'\b(se ha muerto|mur[ií]o) el (top|jungla|support|adc)\b', r'\b(perdemos|perdi|perdiste) al (top|jg|support)\b', r'\breport|reporteo\b', r'\b(thank|gracias) (for|por) the (gank|help|kill)\b', # sarcasmo tras muerte ] # Eventos de HABILIDAD / JUGADAS skill_events = [ r'\b(ulti|ultimate|h)|habilidad ultimate\b', r'\bflash\b.*\b(in|out|en)\b', r'\b(smite|ignite|exhaust|teleport|heal)\b', r'\btriple|quadra|penta\b', r'\b(ace|pentakill)\b', r'\bbaron\b.*\b(bait|steal|take)\b', r'\bdrag[oó]n\b.*\b(bait|steal|take)\b', r'\binhib\b.*\b(bait|steal|take)\b', r'\b(nashor|elder)\b', r'\b(base|nexus)\b.*\b(destroy|se cae|ca[íi]go)\b', ] # Eventos de INSULTO / RAGE (buenos clips) rage_events = [ r'\b(retrasado|imbecil|est[úu]pido|idiota|burro|tonto|mongolo)\b', r'\bputa (madre|mikdre)\b', r'\bcaraj[oó]\b', r'\bhostia\b', r'\bmierda\b', r'\bme la suda\b', r'\bc[áa]gatear\b', r'\b(inteles|bots|afk)\b', ] # Combinar todos los patrones all_patterns = { "ally_death": ally_death_events, "skill": skill_events, "rage": rage_events } # Analizar segmentos events = [] for seg in segments: start = seg["start"] end = seg["end"] text = seg["text"] text_lower = text.lower() # Saltar intro if start < intro_skip: continue for event_type, patterns in all_patterns.items(): for pattern in patterns: if re.search(pattern, text_lower, re.IGNORECASE): events.append({ "start": start, "end": end, "type": event_type, "text": text.strip()[:100], "pattern": pattern }) break # Solo un tipo de evento por segmento if not events: logger.warning("No se encontraron eventos") return [] # Ordenar por timestamp (para mantener orden cronológico) events.sort(key=lambda x: x["start"]) logger.info(f"Eventos encontrados: {len(events)}") for e in events[:10]: logger.info(f" {e['type']}: {e['text'][:50]}...") # Convertir a intervalos EXTENDIDOS intervals = [] for event in events: start = int(event["start"]) # Duración base + padding DESPUÉS para no cortar la acción end = int(event["end"]) + clip_duration # Verificar solapamiento con intervalos existentes overlaps = False for s, e in intervals: if not (end < s or start > e): # Si hay solapamiento, extender el existente if e < end: # Extender el intervalo existente idx = intervals.index((s, e)) intervals[idx] = (s, int(end)) overlaps = True break if not overlaps: intervals.append((start, int(end))) # Ordenar intervals.sort() # Limitar al top solicitado intervals = intervals[:top] logger.info(f"Intervalos finales: {len(intervals)}") return intervals, events def main(): import argparse parser = argparse.ArgumentParser() parser.add_argument("--transcripcion", required=True) parser.add_argument("--output", default="highlights_eventos.json") parser.add_argument("--top", type=int, default=40) parser.add_argument("--intro-skip", type=int, default=120) parser.add_argument("--clip-duration", type=int, default=45, help="Duración base del clip") parser.add_argument("--padding-after", type=int, default=15, help="Padding después del evento") args = parser.parse_args() intervals, events = detect_game_events( args.transcripcion, args.intro_skip, args.clip_duration, args.padding_after, args.top ) # Guardar with open(args.output, 'w') as f: json.dump(intervals, f) logger.info(f"Guardado en {args.output}") # Imprimir resumen type_emoji = { "ally_death": "💀", "skill": "⚡", "rage": "🤬" } print(f"\n{'='*70}") print(f"EVENTOS DE JUEGO - CLIPS EXTENDIDOS".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"Duración clips: ~{args.clip_duration + args.padding_after}s") print(f"Intro excluida: {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 # Buscar el evento correspondiente for event in events: if abs(event["start"] - start) < 10: emoji = type_emoji.get(event["type"], "🎮") text_preview = event["text"][:60].replace('\n', ' ') print(f"{i:2d}. {h:02d}:{m:02d}:{sec:02d} - {duration}s {emoji} - {text_preview}...") break else: print(f"{i:2d}. {h:02d}:{m:02d}:{sec:02d} - {duration}s 🎮") print(f"{'='*70}") if __name__ == "__main__": main()