# DISASTER RECOVERY — Karaoke evento

> **Léete esto AHORA, no durante el incendio.**
>
> Cuando algo se rompa son las 21:00, hay 50 personas mirando, y tienes 60 segundos. Este doc te da el paso exacto a copiar-pegar sin pensar. Mantén el portátil con esta página abierta junto al equipo.

## Mapa rápido del stack

| Pieza | Puerto | Cómo arranca | Cómo verificar |
|---|---|---|---|
| Servidor HTTP player | 8080 | `npx serve . -l 8080 -c serve.json --no-clipboard` | `curl -sI http://localhost:8080/player/` |
| mic-server (WS) | 8081 | `python3 scripts/mic-server.py` | `lsof -i :8081 -t` |
| admin panel | 8082 | `python3 admin/server.py` | `curl -u diego:'D13g0$$2026' http://localhost:8082/api/status` |
| Audio sink | — | `bash scripts/setup-mic-sink.sh` | `pactl list sinks short \| grep karaoke_mic` |
| Todo junto | — | `bash scripts/start-event.sh` | URLs en la salida del script |

URLs canónicas (LAN del Lenovo, ajustar IP):

```
TV / Proyector:   http://<IP-LENOVO>:8080/player/
Mic cantante:     http://<IP-LENOVO>:8080/player/mic.html
Admin operador:   http://<IP-LENOVO>:8082/
Monitor:          http://<IP-LENOVO>:8080/player/monitor.html
```

Credenciales admin: `diego` / `D13g0$$2026` (o lo que pusieras en `admin/.env`).

---

## TRIAGE INSTANTÁNEO

Antes de meterte en un escenario específico, esos 3 comandos te dicen en 5s qué está vivo:

```bash
# 1. ¿Los 3 servicios escuchan?
ss -ltnp 2>/dev/null | grep -E ':(8080|8081|8082)'

# 2. ¿Salida de audio por defecto correcta?
pactl get-default-sink

# 3. ¿Procesos vivos?
pgrep -af 'serve|mic-server|admin/server'
```

Si los 3 muestran lo esperado → no es problema del stack, es browser/red/audio físico.

---

## CRÍTICOS — el evento muere si no se arregla

### 1. El audio dejó de sonar por la TV

**Síntoma**: la barra avanza, las letras se mueven, pero no sale sonido por los altavoces.

**Diagnóstico (3 comandos, 10s)**:

```bash
pactl get-default-sink                          # NO debe ser karaoke_mic
pactl list sink-inputs | grep -A1 'application' # ¿el browser está reproduciendo en algún sink?
pactl list sinks short                           # lista de sinks disponibles
```

Si `get-default-sink` devuelve `karaoke_mic` → ese es el bug. Es un sink virtual sin altavoces.

**Fix en orden (parar al primero que funcione)**:

**F5 navegador** (5s). Refrescar la página del player. Si Chrome quedó muteado por autoplay-policy, vuelve.

**Cambiar sink por defecto al HDMI real** (10s):

```bash
# Lista sinks y busca el de HDMI / analog
pactl list sinks short

# Pon como default el HDMI (cambia el nombre por el que veas)
pactl set-default-sink alsa_output.pci-0000_00_1f.3.hdmi-stereo

# Mueve la reproducción del browser al nuevo sink
pactl list sink-inputs short
pactl move-sink-input <ID> alsa_output.pci-0000_00_1f.3.hdmi-stereo
```

**Restart del HTTP server** (15s) — solo si lo anterior no funciona:

```bash
pkill -f 'npx serve'
cd /home/zen-admin/projects/active/lan-media
npx serve . -l 8080 -c serve.json --no-clipboard &
# F5 en el browser de la TV
```

**Plan B en 30s si nada funciona**: USB+VLC nativo del Mecool o portátil de relevo.

```bash
# Si tienes la USB ya preparada (paso del backup pre-evento)
# Conectarla, abrir VLC, abrir carpeta, play. VLC lee .lrc automático.
```

**Si nada funciona en 30s**: salta a Plan B USB. No insistas.

---

### 2. El player web se congeló o muestra pantalla negra

**Síntoma**: la pantalla del TV está negra, no hay letras ni canción avanzando. O la barra está congelada en un punto.

**Fix escalado (cada paso 10-15s)**:

**Paso 1 — F5 en el browser de la TV** (5s). Si tienes teclado USB en el Mecool, presiona F5. Si es Mecool con mando, vete a la barra de URL y pulsa Enter.

**Paso 2 — Cerrar pestaña + reabrir** (15s):

```
Ctrl+W → Ctrl+T → escribir URL → Enter → F11 fullscreen
```

URL: `http://<IP-LENOVO>:8080/player/`

**Paso 3 — Matar Chrome + reabrir** (20s, desde el Lenovo si la TV es el Lenovo):

```bash
pkill -9 chrome || pkill -9 brave || pkill -9 chromium
# Abrir browser de nuevo manualmente o con:
chromium --kiosk http://localhost:8080/player/ &
```

**Paso 4 — Rearrancar todo el stack** (30s):

```bash
# Mata todo lo del karaoke
pkill -f 'npx serve' ; pkill -f 'mic-server.py' ; pkill -f 'admin/server.py'
sleep 1
# Rearranca limpio
cd /home/zen-admin/projects/active/lan-media
bash scripts/start-event.sh
```

**Si nada funciona en 30s**: Plan B USB+VLC.

---

### 3. El admin no responde desde la tablet

**Síntoma**: la PWA del operador en la tablet muestra spinner infinito, error de red, o "no se puede conectar".

**Diagnóstico (15s)**:

```bash
# Desde el Lenovo:
curl -sI -u diego:'D13g0$$2026' http://localhost:8082/api/status
# Espera HTTP/1.0 200 OK
```

**Fixes en orden**:

**Tablet a WiFi correcta** (5s). Verificar que NO está en 4G ni en otra WiFi. La tablet debe estar en la misma WiFi del Lenovo.

**Refresh PWA** (5s). En la tablet, swipe-down o tirar de la barra de URL → F5 / pull-refresh.

**Restart admin server** (20s):

```bash
ssh lenovo "pkill -f admin/server.py" 2>/dev/null || pkill -f admin/server.py
cd /home/zen-admin/projects/active/lan-media
nohup python3 admin/server.py > admin/logs/admin-server.log 2>&1 & disown
sleep 2
curl -sI -u diego:'D13g0$$2026' http://localhost:8082/api/status
```

**Fallback inmediato** (5s): controlar el player con teclado USB directamente conectado al Lenovo. Atajos:

```
SPACE   play/pause
→       next track
←       restart current
↑ / ↓   ajustar offset LRC
F       fullscreen
```

**Si nada funciona en 30s**: opera con teclado USB. La tablet no es crítica para que el evento siga.

---

### 4. El micrófono inalámbrico no entrega audio

**Síntoma**: el cantante habla/canta y no sale nada por los altavoces. El admin muestra el dispositivo conectado o no.

**Checklist en orden (cada paso es Sí/No, 5s)**:

**1. ¿El móvil tiene WiFi del local?** Mirar pantalla del móvil. Si no, conectarlo.

**2. ¿Ve el sitio del mic?** En el móvil abrir `http://<IP-LENOVO>:8080/player/mic.html`. Debe cargar.

**3. ¿Permitió permiso de micrófono?** Chrome móvil pide "Permitir micrófono" la primera vez. Si dijo No, hay que ir a Configuración del navegador → Sitio → Permitir micrófono y recargar.

**4. ¿Admin lo ve conectado?** En la tablet/admin, sección "Micros conectados". Si NO aparece → problema de WS. Si SÍ aparece pero sin audio → sigue.

**5. ¿Mute activo?** En el admin, fila del dispositivo → comprobar botón mute NO esté activo.

**6. ¿pacat está reproduciendo?** Desde el Lenovo:

```bash
pgrep -af pacat
# Debe haber al menos uno por cantante activo
```

Si no hay pacat → el WS está aceptando audio pero no lo está volcando. Restart del mic-server:

```bash
pkill -f mic-server.py
cd /home/zen-admin/projects/active/lan-media
python3 scripts/mic-server.py &
```

**7. ¿El sink karaoke_mic existe?**

```bash
pactl list sinks short | grep karaoke_mic
# Si no aparece:
bash scripts/setup-mic-sink.sh
```

**Tiempo total**: 30-45s. Si pasa de 60s → mic de cable como plan B (un mic XLR/jack al amplificador directo, sin pasar por el sistema).

---

### 5. Internet se cayó (móvil de Diego sin datos)

**Síntoma**: el móvil pierde conexión, o el WiFi del local cae.

**Hecho clave**: **el stack ES LOCAL**. Player, mic, admin, audio — todo corre en el Lenovo y los móviles. Internet NO es necesario para que el evento siga sonando.

**Lo que SIGUE funcionando sin internet**:
- Player reproduciendo (todo desde disco local)
- Mic de cantantes ya conectados a la WiFi del local
- Admin desde la tablet en la WiFi del local

**Lo que NO funciona sin internet**:
- Cantantes nuevos uniéndose via URL pública (Funnel/Caddy edge) si esa era la ruta
- Descargar canciones nuevas con yt-dlp
- Letras nuevas via lrclib

**Mitigación si la WiFi del local también cae** (no solo internet):

```bash
# El Lenovo puede crear un AP propio (depende del HW)
# Comprueba primero si soporta AP:
nmcli device wifi hotspot ssid "KaraokeLocal" password "karaoke2026"
# Si soporta → los cantantes se conectan a "KaraokeLocal" en su móvil
# y van a http://10.42.0.1:8080/player/mic.html

# Alternativa: tethering desde el móvil de Diego (si tiene datos)
# Conectar Lenovo al hotspot del móvil → IP cambia → regenerar QRs
```

**Plan B**: dejar de aceptar mics nuevos, seguir con los que ya están conectados.

---

## GRAVES — degradan pero el evento sigue

### 6. Una canción concreta no carga

**Síntoma**: dale play, el player marca el título pero la barra no avanza, o salta error en consola.

**Diagnóstico (5s)**:

```bash
# Comprueba si el MP3 existe y no está vacío
ls -la "songs-karaoke/" | grep -i '<palabra-clave-canción>'
# Si tamaño < 100KB o no aparece → MP3 corrupto/ausente
```

**Fix inmediato (5s)**: skip a la siguiente. Botón ⏭ en el admin, o tecla `→` en el teclado USB conectado al Lenovo.

**Diagnóstico post-evento**:

```bash
# Verifica el archivo:
ffprobe "songs-karaoke/<archivo>.mp3" 2>&1 | grep -E "Duration|bitrate"
# Si ffprobe falla → archivo corrupto
```

**Workaround si quieres incluir esa canción ahora mismo** (60s, solo si vale la pena):

```bash
# Regenerar karaoke desde original
bash scripts/make-karaoke.sh "songs/<archivo>.mp3"
bash scripts/build-playlist.sh
# F5 en el player
```

---

### 7. La letra no sincroniza con el audio

**Síntoma**: la letra va adelantada o atrasada respecto a la voz cantada.

**Fix en vivo (5s)**:

```
↑ / ↓ en el teclado USB → ajusta offset LRC en pasos de 0.2s
+ / - en el admin → mismo efecto
```

**Criterio de descarte**: si el ajuste necesita más de ±5 segundos para cuadrar → el LRC está roto (probablemente versión radio edit vs álbum). Skip a la siguiente o aceptar que la letra se desincronizará en el outro.

**Canciones conocidas con drift > 15s** (ver `PROBLEMAS-PENDIENTES.md` IMP-2):
- India Martinez - 5 SENTÍOS (41s drift)
- Que Vuelvas (33s)
- Thalía - Mujer Latina (21s)
- Maria Becerra - 7 VIDAS GLADYS (18s)

Avisar al cantante: "el inicio sí, el final no".

---

### 8. Eco / feedback acústico

**Síntoma**: pitido agudo, retorno de altavoz al mic, ruido oscilante.

**Fix en orden (cada paso 5s)**:

**1. Mute todos los mics excepto el que canta**. En el admin → botón mute por dispositivo. Solo el activo en vivo.

**2. Bajar volumen del mic en el sistema**:

```bash
pactl set-sink-volume karaoke_mic 70%
# Si sigue:
pactl set-sink-volume karaoke_mic 50%
```

**3. Alejar físicamente el mic del altavoz**. Si el cantante está delante del speaker, pedirle que se gire o se aleje.

**4. Reducir loopback latency** (si el feedback es por delay):

```bash
# Lista módulos loopback y descarga
pactl list modules short | grep loopback
# Recarga con menos latencia
pactl unload-module <module-id>
pactl load-module module-loopback source=karaoke_mic.monitor sink=@DEFAULT_SINK@ latency_msec=20
```

---

## MENORES — molesto pero no rompe nada

### 9. La PWA del admin no abre en la tablet

**Síntoma**: la app instalada del admin no abre, queda en splash.

**Fix**:

**1. Reinstalar PWA** (30s): desinstalar app del launcher de Android → abrir Chrome → ir a `http://<IP-LENOVO>:8082/` → menú "Instalar app" → reinstalar.

**2. Fallback inmediato** (5s): abrir Chrome con bookmark al admin. No necesitas la PWA — el bookmark a `http://<IP-LENOVO>:8082/` funciona idéntico.

---

### 10. Un cantante no puede unirse al mic

**Síntoma**: cantante reporta "no me funciona el link" o "me pide login".

**Checklist**:

**1. URL exacta**: debe ser `http://<IP-LENOVO>:8080/player/mic.html` (sin `https`, sin auth). Si Diego le mandó la URL admin (`8082`) por error → ESA pide auth.

**2. WiFi del local**: confirmar que está en la WiFi del local (no en 4G).

**3. Compartir el QR**: en `player/mic-qr.png` hay un QR. Si la IP cambió, regenerarlo:

```bash
IP=$(hostname -I | awk '{print $1}')
qrencode -o player/mic-qr.png "http://${IP}:8080/player/mic.html"
```

**4. Compartir URL "join"**: `http://<IP-LENOVO>:8080/player/mic-join.html` es una landing más amigable que la URL directa.

---

## BACKUP PRE-EVENTO — ejecutar ANTES de salir

### Crear el backup

Desde el Lenovo, antes de salir hacia el local:

```bash
cd /home/zen-admin/projects/active/lan-media
bash scripts/backup-pre-event.sh
```

Esto genera `~/karaoke-backup-YYYYMMDD-HHMM.tar.gz` con:
- Todos los MP3 originales (`songs/`)
- Todos los MP3 karaoke procesados (`songs-karaoke/`)
- Todos los LRC (`lyrics/`)
- Scripts, player, admin, configs
- Docs (`.md`)
- Excluye `admin/.env` por seguridad

**Después del backup, copia el `.tar.gz` a 2 sitios distintos**:
1. USB stick que llevas al evento
2. Cloud (Drive / Mega / lo que tengas) o segunda máquina

### Restaurar el backup

Si la máquina del evento se rompe y tienes una de relevo:

```bash
# Copia el tarball del USB al home
cp /media/usb/karaoke-backup-*.tar.gz ~/

# Restaura al destino
bash /ruta/al/repo/scripts/restore-from-backup.sh ~/karaoke-backup-*.tar.gz

# Recordatorio: el .env NO se restaura. Si lo necesitas:
cat > /home/zen-admin/projects/active/lan-media/admin/.env <<EOF
ADMIN_USER=diego
ADMIN_PASSWORD=D13g0\$\$2026
EOF
chmod 600 /home/zen-admin/projects/active/lan-media/admin/.env

# Arranca el stack
cd /home/zen-admin/projects/active/lan-media
bash scripts/start-event.sh
```

Tiempo total recovery completo: 3-5 minutos.

---

## REGLAS DE ORO DEL OPERADOR

1. **Nunca insistas más de 30s con un fix**. Si no funciona, pasa al siguiente nivel.
2. **El público no nota un skip de canción**. Sí nota 2 minutos de silencio mientras debugeas.
3. **Plan B USB+VLC siempre cargado y a 1 metro**. Es tu salvavidas.
4. **No reinicies el Lenovo durante el evento**. Tarda 90s. Mata procesos individuales en su lugar.
5. **Tener este doc abierto en otra pestaña/dispositivo** distinto del que opera. Si el operador es el que se rompe, no puedes leer.
6. **Si pasan 60s sin sonido**: música por cualquier fuente (Spotify, YouTube, USB+VLC). Lo demás se arregla con calma.
