#!/usr/bin/env python3 """ Detector de MUERTES Y AUTO-CRÍTICA: Encuentra momentos donde el streamer muere o se critica por jugar mal. """ import json import logging import re logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def detect_death_and_failure_moments(transcripcion_json, min_duration=12, max_duration=35, top=30): """ Detecta momentos de muerte o auto-crítica. """ logger.info("=== Buscando MUERTES Y AUTO-CRÍTICA ===") with open(transcripcion_json, 'r', encoding='utf-8') as f: data = json.load(f) segments = data.get("segments", []) # Patrones de muerte death_patterns = [ r'\bme han (matado|kill|limeado|pegado)\b', r'\b(me caigo|me muero|estoy muerto|muero|mor[íi])\b', r'\bme (matan|mate|kill|destruyen)\b', r'\bhaz (kill|muerte|limpieza)\b', r'\b(tf|trade)\b', # trading deaths r'\bhe (muerto|matado)\b', r'\b(me llevan|me cargan|me comen)\b', r'\bfallec[íi]\b', r'\bdefunc[ií]on\b', # Sonidos de muerte/grito r'\burgh?\b', r'\baggh?\b', r'\bargh?\b', r'\b(ah|oh|ugh) (no|puta|mierda|dios)\b', r'\bno+[.,!]+\b', r'\bjoder\b', r'\bputa madre\b', r'\bputa\b', r'\bmierda\b', # Frases de muerte r'\bestoy (muerto|perdido|acabado)\b', r'\bno puedo\b', r'\bimposible\b', r'\bme (acaban|terminaron)\b', ] # Patrones de auto-crítica ("jugué muy mal") failure_patterns = [ r'\b(la )?cagu[ée]\b', r'\b(jugu[ée]|he jugado) (mal|p[ée]simamente|horrible)\b', r'\b(qu[ée] (mal|p[ée]simo|terrible)|error|fail)\b', r'\b(lo hice|la hice) mal\b', r'\bputa (mala|terrible|fatal)\b', r'\bno (me sali[óo]|funcion[óo]|lo logr[ée])\b', r'\b(es)tupidez\b', r'\bimbecilidad\b', r'\bburrada\b', r'\bputada\b', r'\b(desastroso|catastr[óo]fico)\b', r'\b(qu[ée] pena|verg[üu]enza)\b', r'\b(he fallado|fall[ée])\b', r'\bperd[íi]\b', r'\bno deb[ií] (haber|hacer)\b', r'\bcagad[oa]\b', # Más patrones de fallo r'\bmal (jugu|he|estoy)\b', r'\bterrible\b', r'\bhorrible\b', r'\bfatal\b', r'\b(pesimo|p[ée]simo)\b', r'\bno (sé|pude|pude)\b', r'\b(dif[íi]cil|imposible)\b', r'\bperd[íi] (el tiempo|la oportunidad|el flash|la fight)\b', r'\berror (m[íi]o|grave)\b', ] # Analizar cada segmento moments = [] for i, seg in enumerate(segments): text = seg["text"].lower() start = seg["start"] end = seg["end"] score = 0 type_ = None # Buscar patrones de muerte for pattern in death_patterns: if re.search(pattern, text, re.IGNORECASE): score += 20 type_ = "muerte" break # Buscar patrones de auto-crítica for pattern in failure_patterns: if re.search(pattern, text, re.IGNORECASE): score += 15 if not type_: type_ = "fallo" break if score > 0: moments.append({ "start": start, "end": end, "score": score, "text": text.strip(), "type": type_ }) if not moments: logger.warning("No se encontraron momentos de muerte/fallo") return [] # Ordenar por score y timestamp moments.sort(key=lambda x: (-x["score"], x["start"])) # Agrupar en intervalos sin solapamiento intervals = [] for moment in moments: start = int(moment["start"]) end = int(moment["end"]) # Extender a duración mínima si es muy corto duration = max(min_duration, min(end - start, max_duration)) end = start + duration # Verificar solapamiento con intervalos existentes overlaps = False for s, e in intervals: if not (end < s or start > e): # Hay solapamiento overlaps = True break if not overlaps: intervals.append((start, end)) if len(intervals) >= top: break # Ordenar por timestamp final intervals.sort() logger.info(f"Momentos detectados: {len(intervals)}") return intervals, moments def main(): import argparse parser = argparse.ArgumentParser() parser.add_argument("--transcripcion", required=True) parser.add_argument("--output", default="highlights_muertes.json") parser.add_argument("--top", type=int, default=30) parser.add_argument("--min-duration", type=int, default=12) parser.add_argument("--max-duration", type=int, default=35) args = parser.parse_args() intervals, moments = detect_death_and_failure_moments( args.transcripcion, 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"MOMENTOS DE MUERTE Y AUTO-CRÍTICA".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"{'-'*70}") for i, (start, end) in enumerate(intervals, 1): duration = end - start h = start // 3600 m = (start % 3600) // 60 sec = start % 60 # Buscar el texto correspondiente for moment in moments: if abs(moment["start"] - start) < 5: type_icon = "💀" if moment["type"] == "muerte" else "❌" text_preview = moment["text"][:55].replace('\n', ' ') print(f"{i:2d}. {h:02d}:{m:02d}:{sec:02d} - {duration}s {type_icon} - {text_preview}...") break print(f"{'='*70}") if __name__ == "__main__": main()