Señalización Digital — Pantallas + Anuncios
Una pantalla en un local no es una página web. Funciona sin supervisión durante meses en un stick de TV barato, nadie mira la consola, y el modo de fallo es un fotograma congelado que nadie nota durante una semana. Cada decisión aquí existe para sobrevivir a eso.
Este skill incluye un módulo completo: CRUD de administración para medios y listas de reproducción, un registro de pantallas, emparejamiento mediante PIN y URL de aprovisionamiento, un reproductor a pantalla completa y la capa operativa (salud, previsualización, control remoto) que lo hace manejable.
Cuándo usar
Construir o extender pantallas que reproduzcan medios en bucle en un espacio físico, en Next.js App Router con Firestore o Supabase/Postgres.
Cuándo NO usar
- Servicio de anuncios web (impresiones, pujas, píxeles de seguimiento, etiquetas de terceros) — un problema diferente con infraestructura diferente.
- Quioscos interactivos donde el usuario toca para realizar una transacción. La señalización es unidireccional.
- Un único video incrustado en una página de marketing. Usa una etiqueta
<video>. - Configuración genérica de Next.js, CMS o autenticación — se asume que existe.
Arquitectura
ADMIN (navegador) SERVIDOR DISPOSITIVO (TV)
────────────────── ──────── ───────────────
subida de medios ──────────────► almacenamiento de objetos ◄──── URL pública/firmada ─┐
CRUD de anuncios ─────────┐ │
editar lista de reproducción ──┼──► /api/admin/* ──► anuncios + pantallas ──┐ │
emitir comando ───────────┘ (autenticación de personal) (con ámbito) │ │
▼ │
emparejar pantalla ◄──── PIN ────────────► /api/display/pair │ │
genera token por pantalla │ │
▼ │
/api/display/playlist ─── sondeo ───┤
▲ resuelve adIds → anuncios │
└── lleva telemetría arriba, │
comandos abajo bucle del reproductor
Un solo tipo de solicitud sostiene todo el runtime: el dispositivo sondea /api/display/playlist, enviando telemetría hacia arriba y recibiendo contenido y comandos hacia abajo. Todo lo operativo viaja por ese canal.
Hechos críticos — leer antes de diseñar nada
- La lista de reproducción es un array ordenado en la pantalla, no una entidad separada.
display.adIds: string[]es la lista de reproducción — el orden es libre, sin joins, una sola lectura. Introduce una entidad separada de lista de reproducción solo cuando el mismo contenido deba ejecutarse en varias pantallas; consulta modelo-de-datos.md para la compensación y extensiones.md para la migración. - Sondea; no transmitas en streaming. Un sondeo de 30–60 s es más barato, sobrevive a pilas de red inactivas y se reconecta gratis. El tiempo real es una mejora opcional, no la base. Almacena en caché la respuesta con un ETag o pagarás por una lectura completa por pantalla por sondeo.
- Las subidas de medios van del navegador al almacenamiento directamente, nunca a través de una ruta de API. Los manejadores de ruta tienen límites de tamaño de cuerpo y queman cómputo al proxyar bytes.
- El dispositivo no es confiable y no está supervisado. Almacena una credencial de larga duración en
localStorageen hardware al que cualquiera puede acercarse. Esa credencial debe ser por pantalla y revocable. - El reproductor nunca debe poder detenerse. Cada elemento multimedia tiene un tiempo de espera, un manejador de errores y una forma de saltarlo. Un recurso dañado avanza; no se atasca.
Reglas estrictas
Nunca derives el intervalo de sondeo del estado de renderizado. Sondea en un intervalo estable y lee el estado de la diapositiva desde una ref. Un temporizador cuyo efecto depende del índice de la diapositiva se destruye y recrea en cada diapositiva, por lo que una actualización más larga que una diapositiva nunca se ejecutaría; el contrato de comportamiento fija el tiempo del sondeo al tiempo de pared.
Nunca sobrecargues un solo booleano como "pausado" y "eliminado". Usa
activepara la intención del operador y undeletedAtseparado para el ciclo de vida, o un elemento eliminado reaparecerá en cuanto alguien lo active de nuevo.
Nunca confíes en un ámbito de inquilino/ubicación proporcionado por el cliente. Dérivalo en el servidor desde la sesión de personal autenticado o desde la fila de pantalla a la que se resuelve el token, y aplícalo en cada ruta
[id]— devolviendo 404, no 403, para que los ids no se puedan sondear.
Nunca emitas un token compartido para cada pantalla. Genera un token por pantalla en el emparejamiento, almacena solo su hash y haz que la revocación sea una única actualización de fila.
Nunca inicies el temporizador de duración de una imagen antes de
onLoad. En una TV lenta, la diapositiva expirará antes de que sea visible.
Nunca lleves el estado de pantalla duradero en un comando único. La pantalla en blanco y la toma de control deben sobrevivir a la recarga diaria del dispositivo y a un ciclo de encendido — entrégalos como estado en cada sondeo (
mode), y mantén el canal de comandos con confirmación para comandos únicos genuinos como la recarga. Y cualquier comando cuyo efecto sea una recarga debe confirmar su recepción antes de recargar, o el servidor lo reenviará para siempre.
Inicio rápido
- Completa el contrato de integración para tu aplicación y confirma el cambio de nombre de dominio — adaptacion.md.
- Modela las entidades y elige entre array o tabla de unión — modelo-de-datos.md.
- Crea tablas/colecciones, índices, reglas de seguridad y el bucket de medios — backend-firestore.md o backend-supabase.md.
- Construye los endpoints del dispositivo y de administración, incluyendo el emparejamiento y la verificación de tokens — rutas-api.md.
- Empareja una pantalla — emparejamiento.md — luego incorpora el bucle del reproductor — runtime-reproductor.md.
- Construye el back-office: biblioteca multimedia, lista de pantallas, editor de listas de reproducción — ui-admin.md.
- Agrega salud, previsualización y control remoto antes de poner en producción — operaciones.md.
- Verifica contra la tabla de contrato de comportamiento en runtime-reproductor.md — desconecta el cable de red, elimina el anuncio actual a mitad del bucle, emite una recarga — y envía las pruebas de la máquina del reproductor como cobertura de regresión.
Directorio de referencias
Carga la referencia que coincida con las palabras clave del disparador. Para diseño desde cero, lee modelo-de-datos.md primero.
El código en rutas-api.md y operaciones.md está escrito para Firestore como backend canónico; backend-supabase.md define cada sustitución — la tabla de unión que reemplaza adIds, y los equivalentes SQL de las funciones de servicio.
| Escenario | Palabras clave del disparador | Referencia |
|---|---|---|
| Integrar esto en una aplicación existente | adaptar, renombrar, integración, inquilino, aplicación anfitriona | adaptacion.md |
| Entidades, campos, forma de la lista de reproducción | esquema, modelo, Anuncio, Pantalla, lista de reproducción, tabla de unión, eliminación suave | modelo-de-datos.md |
| Backend Firestore/Firebase | Firestore, firebase-admin, reglas de seguridad, índice compuesto, Firebase Storage | backend-firestore.md |
| Backend Supabase/Postgres | Supabase, Postgres, RLS, migración, bucket de almacenamiento, Tiempo Real | backend-supabase.md |
| Endpoints, emparejamiento, tokens, almacenamiento en caché | ruta API, emparejamiento, PIN, token, ETag, verificación de ámbito, zod | rutas-api.md |
| Cómo emparejar una pantalla | emparejamiento, PIN, URL de aprovisionamiento, hub, credencial, desemparejar | emparejamiento.md |
| El bucle del reproductor de TV | reproductor, bucle, fundido cruzado, congelamiento de video, rotación, retrato, bloqueo de activación, sin conexión | runtime-reproductor.md |
| Interfaz de back-office | administración, subida, biblioteca multimedia, editor de listas de reproducción, URL de aprovisionamiento | ui-admin.md |
| Salud, previsualización, control remoto | latido, última vez visto, sin conexión, previsualización, recargar, pantalla en blanco, toma de control de emergencia, registro de auditoría | operaciones.md |
| Programación, reutilización, informes | franjas horarias, fecha de inicio, campaña, lista de reproducción compartida, prueba de reproducción, precarga, multi-zona | extensiones.md |
| Por qué las plantillas difieren de un puerto ingenuo | procedencia, libro mayor, justificación, conservado, agregado | procedencia.md |