- Implementación de detector híbrido (Whisper + Chat + Audio + VLM) - Sistema de detección de gameplay real vs hablando - Scene detection con FFmpeg - Soporte para RTX 3050 y RX 6800 XT - Guía completa en 6800xt.md para próxima IA - Scripts de filtrado visual y análisis de contexto - Pipeline automatizado de generación de videos
172 lines
4.9 KiB
Python
172 lines
4.9 KiB
Python
#!/opt/vlm_env/bin/python3
|
|
"""
|
|
SCENE DETECTION + CLASSIFICATION (GPU Accelerated)
|
|
Detecta cambios de escena con FFmpeg (rápido) y clasifica cada una
|
|
Compatible con RX 6800 XT (16GB VRAM)
|
|
"""
|
|
|
|
import subprocess
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
|
|
print("=" * 70)
|
|
print("🎬 SCENE DETECTOR + CLASSIFIER")
|
|
print("=" * 70)
|
|
print("Paso 1: Detectar cambios de escena con FFmpeg (rápido)")
|
|
print("Paso 2: Clasificar cada escena (gameplay vs hablando)")
|
|
print()
|
|
|
|
video_path = (
|
|
"/home/ren/proyectos/editor/twitch-highlight-detector/nuevo_stream_360p.mp4"
|
|
)
|
|
|
|
# PASO 1: Detectar cambios de escena (threshold 0.3 = cambios significativos)
|
|
print("🔍 Detectando cambios de escena...")
|
|
result = subprocess.run(
|
|
[
|
|
"ffmpeg",
|
|
"-i",
|
|
video_path,
|
|
"-vf",
|
|
"select=gt(scene\,0.3),showinfo",
|
|
"-f",
|
|
"null",
|
|
"-",
|
|
],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
|
|
# Extraer timestamps de cambios de escena
|
|
scene_changes = []
|
|
for line in result.stderr.split("\n"):
|
|
if "pts_time:" in line:
|
|
match = re.search(r"pts_time:(\d+\.\d+)", line)
|
|
if match:
|
|
ts = float(match.group(1))
|
|
if ts > 455: # Saltar intro
|
|
scene_changes.append(ts)
|
|
|
|
print(f"✅ {len(scene_changes)} cambios de escena detectados")
|
|
|
|
# PASO 2: Analizar transcripción en cada escena
|
|
print("\n📊 Analizando contenido de cada escena...")
|
|
|
|
with open(
|
|
"/home/ren/proyectos/editor/twitch-highlight-detector/transcripcion_rage.json", "r"
|
|
) as f:
|
|
trans = json.load(f)
|
|
|
|
# Crear segmentos entre cambios de escena
|
|
segments = []
|
|
prev_ts = 455
|
|
|
|
for ts in sorted(scene_changes):
|
|
if ts - prev_ts > 30: # Mínimo 30 segundos
|
|
segments.append({"start": prev_ts, "end": ts, "duration": ts - prev_ts})
|
|
prev_ts = ts
|
|
|
|
# Agregar último segmento
|
|
result = subprocess.run(
|
|
[
|
|
"ffprobe",
|
|
"-v",
|
|
"error",
|
|
"-show_entries",
|
|
"format=duration",
|
|
"-of",
|
|
"default=noprint_wrappers=1:nokey=1",
|
|
video_path,
|
|
],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
duration = float(result.stdout.strip())
|
|
|
|
if duration - prev_ts > 30:
|
|
segments.append({"start": prev_ts, "end": duration, "duration": duration - prev_ts})
|
|
|
|
print(f"✅ {len(segments)} segmentos para analizar")
|
|
|
|
# PASO 3: Clasificar cada segmento usando transcripción
|
|
print("\n🎯 Clasificando segmentos (gameplay vs hablando)...")
|
|
|
|
for seg in segments:
|
|
# Buscar transcripción en este rango
|
|
seg_text = []
|
|
rage_score = 0
|
|
|
|
for t in trans["segments"]:
|
|
if seg["start"] <= t["start"] <= seg["end"]:
|
|
seg_text.append(t["text"].lower())
|
|
|
|
# Calcular score de rage
|
|
if any(word in t["text"].lower() for word in ["puta", "mierda", "joder"]):
|
|
rage_score += 10
|
|
elif any(
|
|
word in t["text"].lower() for word in ["me mataron", "kill", "muere"]
|
|
):
|
|
rage_score += 8
|
|
elif any(word in t["text"].lower() for word in ["ulti", "flash", "gank"]):
|
|
rage_score += 5
|
|
|
|
full_text = " ".join(seg_text)
|
|
|
|
# Clasificar
|
|
if any(
|
|
word in full_text for word in ["seleccion", "champions", "ban", "pick", "elij"]
|
|
):
|
|
seg["type"] = "SELECCION"
|
|
seg["keep"] = False
|
|
elif any(
|
|
word in full_text for word in ["cuento", "historia", "ayer", "comida", "vida"]
|
|
):
|
|
seg["type"] = "HABLANDO"
|
|
seg["keep"] = False
|
|
elif rage_score >= 5 or any(
|
|
word in full_text for word in ["kill", "matan", "pelea", "fight"]
|
|
):
|
|
seg["type"] = "GAMEPLAY"
|
|
seg["keep"] = True
|
|
seg["rage_score"] = rage_score
|
|
else:
|
|
seg["type"] = "GAMEPLAY_NEUTRO"
|
|
seg["keep"] = True
|
|
seg["rage_score"] = rage_score
|
|
|
|
# Mostrar resultados
|
|
print("\n" + "=" * 70)
|
|
print("SEGMENTOS CLASIFICADOS")
|
|
print("=" * 70)
|
|
|
|
gameplay_segments = [s for s in segments if s["keep"]]
|
|
|
|
for i, seg in enumerate(segments, 1):
|
|
mins_s, secs_s = divmod(int(seg["start"]), 60)
|
|
mins_e, secs_e = divmod(int(seg["end"]), 60)
|
|
icon = "✅" if seg["keep"] else "❌"
|
|
print(
|
|
f"{icon} {i}. {mins_s:02d}:{secs_s:02d} - {mins_e:02d}:{secs_e:02d} "
|
|
f"({seg['duration'] // 60:.0f}m) [{seg['type']}]"
|
|
)
|
|
if seg.get("rage_score"):
|
|
print(f" Rage score: {seg['rage_score']}")
|
|
|
|
print(f"\n{'=' * 70}")
|
|
print(f"RESUMEN")
|
|
print(f"{'=' * 70}")
|
|
print(f"Total segmentos: {len(segments)}")
|
|
print(f"Gameplay útil: {len(gameplay_segments)}")
|
|
total_gameplay = sum(s["duration"] for s in gameplay_segments)
|
|
print(f"Tiempo gameplay: {total_gameplay // 60:.0f}m {total_gameplay % 60:.0f}s")
|
|
|
|
# Guardar gameplay útil
|
|
with open(
|
|
"/home/ren/proyectos/editor/twitch-highlight-detector/gameplay_scenes.json", "w"
|
|
) as f:
|
|
json.dump(gameplay_segments, f, indent=2)
|
|
|
|
print(f"\n💾 Guardado: gameplay_scenes.json")
|
|
print("\nAhora extrae highlights SOLO de estos rangos confirmados.")
|