Communitygithub.com

timerise-ai/digital-signage

Skill de agente: crie um módulo de sinalização digital — biblioteca de mídia, playlists por tela, registro de displays, pareamento por PIN e URL de provisionamento, player fullscreen para TV com recuperação de travamentos, monitoramento de saúde e controle remoto — no Next.js App Router com Firestore ou Supabase.

O que é digital-signage?

Este Skill oferece um módulo completo de sinalização digital (digital signage) para construir sistemas de exibição de mídia em dispositivos não supervisionados (como TVs em pontos de venda ou em espaços públicos). O módulo inclui administração de mídia e playlists, registro de displays, pareamento via PIN ou URL de provisionamento, e um player fullscreen com loop de reprodução que suporta a recuperação de travamentos (stall recovery). É construído sobre o Next.js App Router, usando Google Firestore ou Supabase/Postgres como backend. O design prioriza a robustez operacional, com monitoramento de saúde (heartbeat), comando de reload e controle remoto centralizado. A principal arquitetura baseia-se no polling do device a cada 30–60 segundos, onde uma única chamada de API transporta telemetria ascendente e conteúdo/instruções descendentes. As regras rígidas (hard rules) do skill evitam armadilhas comuns: não derivar intervalos de polling do estado de renderização, não sobrecarregar um booleano como 'pausado' e 'deletado', não confiar em escopos de tenant fornecidos pelo cliente, entre outras. O skill é ideal para substituir soluções como Google Chromecast, Screenly ou PiSignage em projetos Next.js, mas não se aplica a servidores de anúncios web, quiosques interativos ou exibição de um único vídeo em uma página de marketing.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add timerise-ai/digital-signage

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Sinalização Digital — Monitores + Anúncios

Uma tela em um local não é uma página web. Ela funciona sem supervisão por meses em um pendrive de TV barato, ninguém está olhando o console, e o modo de falha é um quadro congelado que ninguém percebe por uma semana. Cada decisão abaixo existe para sobreviver a isso.

Este skill fornece um módulo completo: CRUD de administração para mídia e playlists, um registro de displays, pareamento por PIN e URL de provisionamento, um player fullscreen, e a camada operacional (saúde, pré-visualização, controle remoto) que o torna gerenciável.

Quando usar

Construindo ou estendendo telas que exibem mídia em loop em um espaço físico, no Next.js App Router com Firestore ou Supabase/Postgres.

Quando NÃO usar

  • Servidor de anúncios web (impressões, licitações, pixels de rastreamento, tags de terceiros) — um problema diferente com infraestrutura diferente.
  • Quiosques interativos onde o usuário toca para transacionar. Sinalização é unidirecional.
  • Um único vídeo incorporado em uma página de marketing. Use uma tag <video>.
  • Configuração genérica de Next.js, CMS ou autenticação — assume-se que já existe.

Arquitetura

 ADMIN (navegador)                   SERVIDOR                     DISPOSITIVO (TV)
 ─────────────────                    ──────                      ───────────
 upload de mídia ─────────────► armazenamento de objetos ◄──── URL pública/assinada ─┐
 CRUD de anúncios ────────┐                                                          │
 edição de playlist ──────┼──► /api/admin/*  ──► anúncios + displays ──┐            │
 emitir comando ──────────┘    (auth de staff)      (escopo)           │            │
                                                                        ▼            │
 parear tela ◄──── PIN ───────────► /api/display/pair    │                            │
                                     gera token por       │                            │
                                     display              ▼                            │
                                    /api/display/playlist ─── poll ───────────────────┤
                                     ▲ resolve adIds → anúncios                       │
                                     └── carrega telemetria para cima,                │
                                         comandos para baixo                   loop do player

Um único tipo de requisição sustenta todo o runtime: o dispositivo faz poll em /api/display/playlist, enviando telemetria para cima e recebendo conteúdo e comandos para baixo. Tudo operacional viaja nesse canal.

Fatos críticos — leia antes de projetar qualquer coisa

  1. A playlist é um array ordenado no display, não uma entidade separada. display.adIds: string[] é a playlist — a ordem é livre, sem joins, uma única leitura. Introduza uma entidade de playlist independente apenas quando o mesmo conteúdo precisar rodar em várias telas; veja data-model.md para a compensação e extensions.md para a migração.
  2. Faça poll; não faça streaming. Um poll de 30–60 s é mais barato, sobrevive a pilhas de rede dormentes e reconecta-se sem custo. Tempo real é uma atualização opcional, não o padrão. Armazene em cache a resposta com um ETag ou você pagará por uma leitura completa por tela por poll.
  3. Uploads de mídia vão do navegador → armazenamento diretamente, nunca através de uma rota de API. Handlers de rota têm limites de tamanho de corpo e queimam computação ao fazer proxy de bytes.
  4. O dispositivo não é confiável e não é supervisionado. Ele mantém uma credencial de longa duração no localStorage em hardware que qualquer um pode acessar. Essa credencial deve ser por tela e revogável.
  5. O player nunca deve poder parar. Cada elemento de mídia recebe um timeout, um handler de erro e uma forma de pular. Um recurso quebrado avança; não trava.

Regras rígidas

Nunca derive o intervalo de poll do estado de renderização. Faça poll em um intervalo estável e leia o estado do slide a partir de uma ref. Um timer cujo efeito depende do índice do slide é destruído e recriado a cada slide, então uma atualização maior que um slide nunca dispararia; o contrato de comportamento fixa o tempo do poll no tempo real do relógio de parede.

Nunca sobrecarregue um único booleano como "pausado" e "deletado". Use active para intenção do operador e um deletedAt separado para ciclo de vida, ou um item deletado reaparecerá assim que alguém o religar.

Nunca confie em um escopo de inquilino/local fornecido pelo cliente. Derive-o no servidor a partir da sessão autenticada do staff ou da linha do display que o token resolve, e aplique-o em toda rota [id] — retornando 404, não 403, para que os ids não possam ser sondados.

Nunca emita um token compartilhado para cada tela. Gere um token por display no pareamento, armazene apenas seu hash e faça da revogação uma única atualização de linha.

Nunca inicie o timer de duração de uma imagem antes de onLoad. Em uma TV lenta, o slide expirará antes de ficar visível.

Nunca carregue estado de tela durável em um comando de uso único. Modo blank e takeover devem sobreviver à recarga diária do dispositivo e a um ciclo de energia — entregue-os como estado em cada poll (mode), e mantenha o canal de comandos com confirmação de limpeza (ack) para comandos genuínos de uso único, como recarregar. E qualquer comando cujo efeito seja uma recarga deve persistir seu ack antes de recarregar, ou o servidor o reentregará para sempre.

Início rápido

  1. Preencha o contrato de integração para seu aplicativo e confirme a renomeação de domínio — adaptation.md.
  2. Modele as entidades e escolha entre array vs. tabela de junção — data-model.md.
  3. Crie tabelas/coleções, índices, regras de segurança e o bucket de mídia — firestore-backend.md ou supabase-backend.md.
  4. Construa os endpoints do dispositivo e de administração, incluindo pareamento e verificação de token — api-routes.md.
  5. Pareie uma tela — pairing.md — e então insira o loop do player — player-runtime.md.
  6. Construa o back-office: biblioteca de mídia, lista de displays, editor de playlist — admin-ui.md.
  7. Adicione monitoramento de saúde, pré-visualização e controle remoto antes de colocar em produção — operations.md.
  8. Verifique contra a tabela de contrato de comportamento em player-runtime.md — puxe o cabo de rede, delete o anúncio atual no meio do loop, emita uma recarga — e envie os testes da máquina do player como cobertura de regressão.

Diretório de referências

Carregue a referência correspondente às palavras-chave de gatilho. Para design do zero, leia data-model.md primeiro.

O código em api-routes.md e operations.md é escrito contra Firestore como backend canônico; supabase-backend.md define todas as substituições — a tabela de junção substituindo adIds, e equivalentes SQL das funções de serviço.

CenárioPalavras-chave de gatilhoReferência
Encaixar isso em um aplicativo existenteadaptar, renomear, integração, costura, inquilino, aplicativo hospedeiroadaptation.md
Entidades, campos, formato da playlistesquema, modelo, Anúncio, Display, playlist, tabela de junção, exclusão suavedata-model.md
Backend Firestore/FirebaseFirestore, firebase-admin, regras de segurança, índice composto, Firebase Storagefirestore-backend.md
Backend Supabase/PostgresSupabase, Postgres, RLS, migração, bucket de armazenamento, Realtimesupabase-backend.md
Endpoints, pareamento, tokens, cacheRota de API, pareamento, PIN, token, ETag, verificação de escopo, zodapi-routes.md
Pareando uma telapareamento, PIN, URL de provisionamento, hub, credencial, desvincularpairing.md
O loop do player de TVplayer, loop, crossfade, travamento de vídeo, rotação, retrato, wake lock, offlineplayer-runtime.md
Interface de back-officeadmin, upload, biblioteca de mídia, editor de playlist, URL de provisionamentoadmin-ui.md
Saúde, pré-visualização, controle remotobatida cardíaca, última visualização, offline, pré-visualização, recarregar, blank, takeover de emergência, log de auditoriaoperations.md
Agendamento, reuso, relatóriosdayparting, data de início, campanha, playlist compartilhada, prova de exibição, precache, multi-zonaextensions.md
Por que os templates diferem de uma portabilidade ingênuaproveniência, ledger, justificativa, mantido, adicionadoprovenance.md

Habilidades Relacionadas