Files
twitch-highlight-detector/rage_detector.py
renato97 00180d0b1c Sistema completo de detección de highlights con VLM y análisis de gameplay
- 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
2026-02-19 17:38:14 +00:00

214 lines
6.1 KiB
Python

#!/usr/bin/env python3
"""
RAGE & FAIL DETECTOR
Prioriza momentos de muerte, rage, insultos y fails.
"""
import json
import re
from pathlib import Path
def detect_rage_highlights(
transcription_file, chat_file, skip_intro=455, max_duration=8237
):
"""Detecta momentos de rage, muerte y fails."""
print("=" * 60)
print("RAGE & FAIL DETECTOR")
print("=" * 60)
# Cargar transcripción
with open(transcription_file, "r") as f:
trans = json.load(f)
# Cargar chat
with open(chat_file, "r") as f:
chat_data = json.load(f)
# Diccionario de rage completo
rage_patterns = {
"extreme_rage": [
r"\bputa\b",
r"\bmadre\b",
r"\bretrasad\w*",
r"\bimbecil\b",
r"\bestupid\w*",
r"\bidiota\b",
r"\bmierda\b",
r"\bbasura\b",
r"\binutil\b",
r"\bmongol\w*",
r"\bcancer\b",
r"\bmaricon\b",
],
"death": [
r"\bme mataron\b",
r"\bme mori\b",
r"\bmuerto\b",
r"\bme matan\b",
r"\bmatenme\b",
r"\bfeed\w*",
r"\bfeeding\b",
r"\bme destrozaron\b",
r"\bme comieron\b",
r"\bme cargaron\b",
],
"fail": [
r"\bla cague\b",
r"\bla lie\b",
r"\berror\b",
r"\bfail\b",
r"\bperdon\b",
r"\bperdón\b",
r"\blo siento\b",
r"\bmala mia\b",
r"\bfalle\b",
r"\bfall[eé]\b",
r"\bno puede ser\b",
r"\bcomo\?\b",
],
"team_rage": [
r"\bequipo\b.*\b(mierda|basura|malos)\b",
r"\bteam\b.*\b(trash|bad)\b",
r"\breport\w*",
r"\btroll\w*",
r"\binting\b",
r"\bjugadores\b.*\bmalos\b",
],
"frustration": [
r"\b(nooo|noo|no no no)\b",
r"\bpor que\b",
r"\bporque\b",
r"\ben serio\b",
r"\bno me jodas\b",
r"\bque (haces|hace)\b",
r"\bomg\b",
r"\bdios\b",
r"\bhostia\b",
r"\bjoder\b",
],
}
# Analizar cada segmento
rage_moments = []
for seg in trans.get("segments", []):
if seg["start"] < skip_intro:
continue
text = seg["text"].lower()
score = 0
reasons = []
for category, patterns in rage_patterns.items():
for pattern in patterns:
if re.search(pattern, text, re.IGNORECASE):
if category == "extreme_rage":
score += 15
if "EXTREME" not in reasons:
reasons.append("EXTREME")
elif category == "death":
score += 12
if "DEATH" not in reasons:
reasons.append("DEATH")
elif category == "team_rage":
score += 10
if "TEAM_RAGE" not in reasons:
reasons.append("TEAM_RAGE")
elif category == "fail":
score += 8
if "FAIL" not in reasons:
reasons.append("FAIL")
else:
score += 5
if "FRUSTRATION" not in reasons:
reasons.append("FRUSTRATION")
break
if score >= 5: # Mínimo score significativo
rage_moments.append(
{
"start": seg["start"],
"end": seg["end"],
"score": score,
"text": seg["text"][:70],
"reasons": reasons,
}
)
print(f"\nMomentos de rage detectados: {len(rage_moments)}")
# Ordenar por score
rage_moments.sort(key=lambda x: -x["score"])
# Crear clips extendidos
clips = []
for moment in rage_moments[:25]: # Top 25
start = max(skip_intro, int(moment["start"]) - 10)
end = min(max_duration, int(moment["end"]) + 20)
if end - start >= 12:
clips.append(
{
"start": start,
"end": end,
"score": moment["score"],
"reasons": moment["reasons"],
"text": moment["text"],
}
)
# Eliminar solapamientos
clips.sort(key=lambda x: x["start"])
filtered = []
for clip in clips:
if not filtered:
filtered.append(clip)
else:
last = filtered[-1]
if clip["start"] <= last["end"] + 3:
# Fusionar
last["end"] = max(last["end"], clip["end"])
last["score"] = max(last["score"], clip["score"])
last["reasons"] = list(set(last["reasons"] + clip["reasons"]))
else:
filtered.append(clip)
# Tomar top 15
filtered.sort(key=lambda x: -x["score"])
final = filtered[:15]
final.sort(key=lambda x: x["start"])
print(f"\nClips sin solapar: {len(final)}")
print(f"\nTop momentos RAGE:")
for i, clip in enumerate(final, 1):
mins = int(clip["start"]) // 60
secs = int(clip["start"]) % 60
dur = clip["end"] - clip["start"]
print(
f"{i:2d}. {mins:02d}:{secs:02d} - {dur}s [Score: {clip['score']:2d}] "
f"{'/'.join(clip['reasons'])}"
)
total_dur = sum(c["end"] - c["start"] for c in final)
print(
f"\nTotal: {len(final)} clips, {total_dur}s ({total_dur // 60}m {total_dur % 60}s)"
)
return [[c["start"], c["end"]] for c in final]
if __name__ == "__main__":
import sys
highlights = detect_rage_highlights(
"transcripcion_medium.json", "elxokas_chat.json"
)
with open("HIGHLIGHTS_RAGE.json", "w") as f:
json.dump(highlights, f)
print(f"\nGuardado en HIGHLIGHTS_RAGE.json")