Skip to content

feat(live): MVP de live streaming — ingest, chat, audiência e admin - #530

Draft
Clintonrocha98 wants to merge 32 commits into
4.xfrom
feature/live-mvp
Draft

feat(live): MVP de live streaming — ingest, chat, audiência e admin#530
Clintonrocha98 wants to merge 32 commits into
4.xfrom
feature/live-mvp

Conversation

@Clintonrocha98

Copy link
Copy Markdown
Member

Contexto

MVP de transmissões ao vivo próprias da heartdevs (sem depender de Twitch/YouTube). O mediamtx (RTMP→HLS) já roda como serviço de infra; faltava a camada de domínio que decide quem pode publicar, o que está no ar e quem participa — e a UI, tanto pública (/live) quanto de gestão (painel admin).

Decisões de produto e design estão registradas em docs/specs/2026-08-29-live-mvp-design.md, docs/specs/2026-08-29-live-fase-2-chat-admin-robustez.md e no glossário do módulo (app-modules/live/CONTEXT.md).

Alterações

Módulo live (domínio, novo)

  • Ciclo de vida da Live (CreatedOnAirEnded), stream key rotacionável, invariante "uma live corrente" via Action + índice único parcial.
  • Auth hook e webhook de sinal do mediamtx (IngestAuthController, IngestWebhookController), default-deny, rate limit por IP.
  • Chat como provedor de atividade (IdentityProvider::He4rtLives): mensagens entram no pipeline do activity, reusando moderação e agregados em vez de um model próprio.
  • Audiência: presença via Redis (com fallback em memória), heartbeat, contagem de espectadores e amostragem por minuto para o gráfico do admin.
  • Eventos de tempo real (LiveStarted, LiveEnded, ChatMessageSent, ChatMessageDeleted) via Reverb, sempre best-effort (rescue()) — Reverb fora do ar nunca derruba uma operação já persistida.
  • SimulateLiveChatCommand + FakeChatAuthors: ferramenta de dev para gerar tráfego de chat fake numa live, útil para testar a UI sem depender de usuários reais.

Módulo panel-admin

  • LiveResource com listagem, visualização, gráfico de audiência, moderação de mensagens no chat e ações de ciclo de vida (encerrar, rotacionar stream key).
  • Ingest exibido nos dois campos que o OBS espera (URL do servidor + stream key), com a chave mascarada por padrão.
  • Action simulateChat (visível só em app()->isLocal()) para ligar/desligar a simulação de comentários direto do painel.

Módulo portal

  • Página pública /live: player HLS resiliente, polling de status a cada 10s, contador de espectadores.
  • Chat da live em tempo real (Livewire + Reverb + Echo).
  • Modo teatro (tela cheia) e chat recolhível, com estado local via Alpine.

Módulo identity

  • Provider He4rtLives para o enum IdentityProvider, permitindo que o chat da live seja tratado como "mais um provedor de atividade" (igual Discord/Twitch).

Infra

  • Serviço mediamtx no docker-compose.yml (RTMP→HLS), config de auth/webhook hooks.
  • Config do Reverb (config/reverb.php) e dependências de broadcast no portal.

Plano de Testes

  • ./vendor/bin/rector process --dry-run --ansi
  • ./vendor/bin/pint --test --ansi
  • ./vendor/bin/phpstan analyse --ansi
  • nice -n 19 ./vendor/bin/pest --parallel --processes=10 --compact — 1722 testes, 5900 assertions, todos passando

Issues Relacionadas

Nenhuma issue vinculada.

O smoke test da Task 4 revelou 500 em GET /live: a diretiva @Vite emitia
tags <link rel="modulepreload"> como irmãs anteriores ao <main>, e o
parser HTML do libxml (usado pelo SupportMultipleRootElementDetection do
Livewire em modo debug) tratava esses <link> como conteúdo de <head>,
nunca criando um elemento <body> - resultando em "Attempt to read
property childNodes on null". Os testes existentes não pegaram isso
porque usam withoutVite(), que neutraliza a diretiva.
…yTime

O hook de autenticação do mediamtx autorizava (fail-open) qualquer ação
diferente de publish, incluindo ações desconhecidas. Troca para default-deny
via match, autorizando explicitamente só read/playback/publish.

CheckLiveStatusAction também lançava InvalidFormatException quando a Control
API retornava um readyTime não-parseável, derrubando a página com a live no
ar; agora o parse é protegido e cai para startedAt nulo.
… serve

A porta 9997 do heartdevs-mediamtx era publicada em todas as interfaces; a
Control API não passa pelo auth hook (authHTTPExclude) e tem endpoints de
escrita, então deve ficar acessível só do host. Também documenta, no
.env.example e na spec, que publicar via OBS exige `php artisan serve
--host=0.0.0.0` — o bind padrão (127.0.0.1) não é alcançável pelo container
via host.docker.internal.
…ve na página

composer.json do módulo portal já exigia he4rt/live, mas o composer.lock
ainda não refletia essa dependência. Aproveita para anunciar a troca
offline/ao vivo a leitores de tela via aria-live="polite" no container que
recebe o wire:poll.
Adiciona o bloco YAML de front matter (type, title, module, status, date,
author) exigido pela convenção de documentação do repo.
… redis

Envolve o event() de LiveStarted/LiveEnded/ChatMessageSent/ChatMessageDeleted
e o corpo de LivePage::pulse() em rescue(report: true), evitando que uma
falha no broadcaster (Reverb fora do ar) ou no presence de viewers (Redis
fora do ar) derrube uma operação de domínio já persistida.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant