#!/usr/bin/env python3 """ Detector de highlights usando minimax API (OpenAI compatible). Analiza la transcripción de Whisper para encontrar momentos interesantes. """ import json import logging import os import sys logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Importar OpenAI SDK try: from openai import OpenAI except ImportError: print("Instalando openai...") import subprocess subprocess.check_call([sys.executable, "-m", "pip", "install", "openai", "--break-system-packages", "-q"]) from openai import OpenAI def detect_with_minimax(transcripcion_json, output_json="highlights_minimax.json"): """ Carga la transcripción y usa minimax para encontrar highlights. """ # Obtener credenciales de variables de entorno (OpenAI compatible) base_url = os.environ.get("OPENAI_BASE_URL", "https://api.minimax.io/v1") api_key = os.environ.get("OPENAI_API_KEY") if not api_key: logger.error("Se necesita OPENAI_API_KEY") return None logger.info(f"Usando endpoint: {base_url}") logger.info(f"Cargando transcripción de {transcripcion_json}...") with open(transcripcion_json, 'r', encoding='utf-8') as f: transcripcion_data = json.load(f) # Crear un resumen estructurado para la IA segments = transcripcion_data.get("segments", []) # Crear transcripción con timestamps transcript_lines = [] for seg in segments[:800]: # Limitar segmentos start = int(seg["start"]) end = int(seg["end"]) text = seg["text"].strip() if len(text) > 3: # Filtrar textos muy cortos mins = start // 60 secs = start % 60 timestamp = f"[{mins:02d}:{secs:02d}]" transcript_lines.append(f"{timestamp} {text}") full_text = "\n".join(transcript_lines) logger.info(f"Enviando a minimax ({len(full_text)} caracteres)...") # Crear cliente OpenAI con endpoint de minimax client = OpenAI( base_url=base_url, api_key=api_key ) prompt = f"""Eres un experto editor de highlights de gaming (Twitch/YouTube). Tu especialidad es identificar MOMENTOS ÉPICOS y VIRALES. TRANSCRIPCIÓN DEL STREAM: {full_text} TU ÚNICA MISIÓN: Encuentra 20-30 CLIPS CORTOS (15-30 segundos cada uno) que sean VIRALICOS. SOLO busca estos tipos de momentos: 1. **JUGADAS ÉPICAS**: Multi-kills, clutches, jugadas increíbles, moments de gran habilidad 2. **RISAS/GRACIAS**: Momentos donde el streamer se ríe a carcajadas, algo gracioso pasa 3. **REACCIONES ÉPICAS**: Gritos de emoción, sorpresa extrema, momentos de "¡NO LO PUEDO CREER!" LO QUE DEBES EVITAR ABSOLUTAMENTE: ❌ Quejas/rage sobre el juego (insultos, frustración) ❌ Hablar de cargar partidas, esperar, problemas técnicos ❌ Conversaciones normales/aburridas ❌ Análisis estratégicos aburridos ❌ Saludos, intros, despedidas ❌ Leer chat o spam REGLAS CRÍTICAS: - Cada clip debe durar 15-30 segundos MÁXIMO - Cada clip debe tener una "recompensa" inmediata (risa, emoción, jugada épica) - Prioriza CLARIDAD sobre cantidad: es mejor 10 clips geniales que 30 clips regulares - Busca PATRONES específicos: "¡!", risas ("jajaja", "jeje"), gritos ("¡PUTA!", "¡QUE!", "¡NO!") FORMATO DE RESPUESTA (solo JSON válido): {{ "highlights": [ {{"start": 123, "end": 144, "reason": "razón muy breve"}}, {{"start": 456, "end": 477, "reason": "razón muy breve"}} ] }} Timestamps en SEGUNDOS del video.""" try: response = client.chat.completions.create( model="MiniMax-M2.5", # Modelo de minimax messages=[ {"role": "system", "content": "Eres un experto editor de contenido de Twitch que identifica momentos memorables."}, {"role": "user", "content": prompt} ], temperature=0.3, max_tokens=4096 ) content = response.choices[0].message.content # Buscar JSON en la respuesta import re json_match = re.search(r'\{[\s\S]*\}', content) if json_match: result = json.loads(json_match.group()) else: logger.error("No se encontró JSON válido en la respuesta") logger.debug(f"Respuesta: {content}") return None except Exception as e: logger.error(f"Error llamando API minimax: {e}") import traceback traceback.print_exc() return None if result and "highlights" in result: highlights = result["highlights"] # Validar y filtrar highlights valid_intervals = [] for h in highlights: start = int(h["start"]) end = int(h["end"]) duration = end - start # Filtrar: duración entre 12 y 45 segundos (clips muy cortos) if 12 <= duration <= 45: valid_intervals.append({ "start": start, "end": end, "reason": h.get("reason", "N/A") }) # Convertir a formato de intervalos intervals = [[h["start"], h["end"]] for h in valid_intervals] # Guardar con detalles with open(output_json, 'w') as f: json.dump({"intervals": intervals, "details": valid_intervals}, f, indent=2) logger.info(f"Guardado en {output_json}") # Imprimir resumen print(f"\n{'='*70}") print(f"HIGHLIGHTS DETECTADOS POR minimax".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, h in enumerate(valid_intervals, 1): start = h["start"] end = h["end"] duration = end - start hours = start // 3600 mins = (start % 3600) // 60 secs = start % 60 reason = h["reason"] print(f"{i:2d}. {hours:02d}:{mins:02d}:{secs:02d} - {duration}s - {reason}") print(f"{'='*70}") return intervals else: logger.error("No se pudo obtener highlights de minimax") return None def main(): import argparse parser = argparse.ArgumentParser() parser.add_argument("--transcripcion", required=True, help="Archivo JSON de transcripción de Whisper") parser.add_argument("--output", default="highlights_minimax.json") args = parser.parse_args() detect_with_minimax(args.transcripcion, args.output) if __name__ == "__main__": main()