#!/usr/bin/env python3 """ Segunda pasada: Filtra los mejores clips para reducir a máximo 15 minutos. Prioriza: Rage/insultos > Muertes de aliados > Jugadas épicas """ import json import logging import re logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def score_highlights(transcripcion_json, highlights_json, max_duration=900): """ Analiza los clips existentes y les da un puntaje. Devuelve los mejores clips hasta max_duration segundos. """ logger.info("=== SEGUNDA PASADA - Filtrando mejores momentos ===") with open(transcripcion_json, 'r', encoding='utf-8') as f: trans_data = json.load(f) with open(highlights_json, 'r') as f: highlights = json.load(f) segments = trans_data.get("segments", []) # Patrones de alta prioridad para puntuar clips priority_patterns = { "rage_extreme": [ # 100 puntos - MUY buenos clips r'\bputa (madre|mikdre)\b', r'\bretrasados?\b.*\bmentales?\b', r'\bbinguno de (ustedes|vosotros)\b', r'\babsol[úu]to (inter|in[úu]til|retrasado)\b', r'\binfumable\b', r'\basqueroso\w*\b', r'\bbasura\b', ], "ally_death": [ # 80 puntos - Tilt triggers r'\b(ha muerto|murio|muri[óo]|falleci[óo]) (un aliado|un teammate|el compa)\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', ], "epic_plays": [ # 70 puntos - Jugadas épicas r'\b(triple|quadra|penta)( kill)?\b', r'\b(pentakill|ace)\b', r'\bbaron\b.*\b(steal|rob[ao])\b', r'\bdrag[oó]n\b.*\b(steal|rob[ao])\b', r'\bnashor\b.*\b(steal|rob[ao])\b', ], "insultos": [ # 60 puntos - Insultos varios r'\b(retrasado|imbecil|est[úu]pido|idiota|burro|tonto|mongolo)\b', r'\bcaraj[oó]\b', r'\bhostia\w*\b', r'\bc[áa]gatear\b', ], "skills": [ # 40 puntos - Habilidades r'\b(ulti|ultimate|h)\b', r'\bflash\b', r'\bsmite|ignite|exhaust|teleport|heal\b', ], } # Analizar cada clip y asignar puntaje scored_clips = [] for start, end in highlights: clip_duration = end - start # Buscar segmentos dentro del clip text_in_clip = [] for seg in segments: seg_start = seg["start"] seg_end = seg["end"] # Si el segmento está dentro o solapa con el clip if not (seg_end < start or seg_start > end): text_in_clip.append(seg["text"]) # Unir texto del clip clip_text = " ".join(text_in_clip).lower() # Calcular puntaje score = 0 matched_types = [] for event_type, patterns in priority_patterns.items(): for pattern in patterns: if re.search(pattern, clip_text, re.IGNORECASE): if event_type == "rage_extreme": score += 100 elif event_type == "ally_death": score += 80 elif event_type == "epic_plays": score += 70 elif event_type == "insultos": score += 60 elif event_type == "skills": score += 40 if event_type not in matched_types: matched_types.append(event_type) break # Bonus por duración (clips más largos tienen más contexto) if clip_duration > 60: score += 20 elif clip_duration > 45: score += 10 scored_clips.append({ "start": start, "end": end, "duration": clip_duration, "score": score, "types": matched_types }) # Ordenar por puntaje descendente scored_clips.sort(key=lambda x: (-x["score"], x["start"])) # Seleccionar clips hasta max_duration selected = [] total_duration = 0 logger.info("\n=== TOP CLIPS SELECCIONADOS ===") logger.info(f"Puntaje | Duración | Tipo | Timestamp") logger.info("-" * 60) for clip in scored_clips: if total_duration + clip["duration"] > max_duration: # Si este clip excede el límite, intentar incluirlo si hay espacio remaining = max_duration - total_duration if remaining >= 30: # Solo si hay espacio para al menos 30s # Recortar el clip selected.append((clip["start"], clip["start"] + remaining)) total_duration += remaining logger.info(f"{clip['score']:3d}* | {remaining:3d}s | {clip['types']} | {clip['start']}") break selected.append((clip["start"], clip["end"])) total_duration += clip["duration"] types_str = ", ".join(clip['types']) logger.info(f"{clip['score']:3d} | {int(clip['duration']):3d}s | {types_str} | {clip['start']}") # Ordenar selected por timestamp selected.sort() logger.info("-" * 60) logger.info(f"Total: {len(selected)} clips, {int(total_duration)}s ({total_duration/60:.1f} min)") return selected def main(): import argparse parser = argparse.ArgumentParser() parser.add_argument("--transcripcion", required=True) parser.add_argument("--highlights", required=True) parser.add_argument("--output", default="highlights_finales.json") parser.add_argument("--max-duration", type=int, default=900, help="Duración máxima en segundos (default: 900s = 15min)") args = parser.parse_args() selected = score_highlights( args.transcripcion, args.highlights, args.max_duration ) # Guardar with open(args.output, 'w') as f: json.dump(selected, f) logger.info(f"Guardado en {args.output}") if __name__ == "__main__": main()