#!/usr/bin/env python3 """ Detector de MUERTES Y AUTO-CRÍTICA v2: Extiende los clips para capturar la acción completa. """ 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=15, max_duration=30, padding_end=5, top=40): """ Detecta momentos de muerte o auto-crítica, con padding al final. """ logger.info("=== Buscando MUERTES Y AUTO-CRÍTICA v2 ===") 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', r'\bhe (muerto|matado)\b', r'\b(me llevan|me cargan|me comen)\b', r'\bfallec[íi]\b', r'\bdefunc[ií]on\b', 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', r'\bestoy (muerto|perdido|acabado)\b', r'\bno puedo\b', r'\bimposible\b', r'\bme (acaban|terminaron)\b', ] # Patrones de auto-crítica 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', 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 moments.sort(key=lambda x: -x["score"]) # Agrupar en intervalos con padding al final intervals = [] for moment in moments: start = int(moment["start"]) end = int(moment["end"]) # Duración base + padding al final para capturar la acción duration = max(min_duration, min(end - start, max_duration)) # Añadir padding al final para capturar lo que viene después end = start + duration + padding_end # 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 # Ordenar por timestamp 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_v2.json") parser.add_argument("--top", type=int, default=40) parser.add_argument("--min-duration", type=int, default=15) parser.add_argument("--max-duration", type=int, default=30) parser.add_argument("--padding-end", type=int, default=5, help="Segundos extra al final para capturar la acción") args = parser.parse_args() intervals, moments = detect_death_and_failure_moments( args.transcripcion, args.min_duration, args.max_duration, args.padding_end, 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 v2 (con padding)".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"Padding al final: +5s para capturar la acción") 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) < 5: type_icon = "💀" if moment["type"] == "muerte" else "❌" text_preview = moment["text"][:50].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()