Skip to content

fix(stream): block Go Live without browser audio and in browsers that cannot record WebM - #833

Draft
anneoneone wants to merge 2 commits into
dev/312-interimfrom
fix/827-browser-audio-guards
Draft

anneoneone wants to merge 2 commits into
dev/312-interimfrom
fix/827-browser-audio-guards

Conversation

@anneoneone

Copy link
Copy Markdown
Collaborator

The host panel no longer goes on air without browser audio, and it tells hosts when their browser cannot stream ("use Chrome or Firefox, or update Safari to 18.4"). This ports the frontend of #825 (89f5076) and all of #826 (1baf52d) to dev/312-interim. The files are the same as on main (in prod as sha-1baf52d).

Issue: #827

epic-guard fails on this PR, and this needs Anton's decision. .github/epic-lanes.yml has no rule for frontend/**, so each file reports "not owned by Claude in lane frontend". Options: add a frontend/** rule (Codex owns that file), or a one-time exception as for #733. The backend half is a separate PR, so it can pass the guard on its own.

Validation:

  • npm test -- --run: 184 passed. npm run lint, npm run typecheck, npm run build: ok.
  • Against interim's old useAudioCapture.ts, 8 of the 14 tests in useAudioCapture.test.ts fail.
  • prettier --check flags only the old useLiveChat export block in composables/index.ts. It fails the same way on interim and main, so it is left as is.
  • Not tested in a real Safari 18.3. The tests stub MediaRecorder.isTypeSupported the way Safari 18.3 answers (only audio/mp4).
Implementation and review

Epic: #312
Executor: Claude
Lane: frontend
Reviewer: Codex
Leaf IDs: F827.1.1

Claim: #827 (comment). Done by hand at Anton's request.

Cause (show 48, 2026-10-30). Safari before 18.4 cannot record WebM, and the backend producer only reads WebM. On Safari 18.3 new MediaRecorder(...) threw. The test only said "Failed to connect to the test stream", and Go Live ignored the failed startRecording(), so the host was "live" with silence.

Change. Two commits, one per main PR. Each file is byte-identical to main at 1baf52d. The same files were identical on main before the fixes and on interim, so nothing needed adapting.

  • useAudioCapture.ts: streamRecordingMimeType() (WebM/Opus, else WebM, else null) and BROWSER_UNSUPPORTED_MESSAGE. startRecording refuses an ended device track or an unsupported browser and returns false. resumeAudioContext(). A 5 s watchdog reports NO_AUDIO_DATA_MESSAGE when no non-empty chunk was sent.
  • FlowOnAir.vue (handleGoLive): checks the browser before connect() and audioDeviceOk before it connects. It shows recorder errors and calls stopStream() when startRecording() returns false.
  • LiveSetupTest.vue: shows the message at the top, disables "Start test", resumes the context, and does not pass the test with 0 chunks.
  • composables/index.ts: exports the new helper and messages.

Tests (frontend/tests/useAudioCapture.test.ts): fake AudioContext (starts suspended) and MediaRecorder with a configurable list of supported types. They cover resume, ended track, the watchdog (fires on empty chunks, quiet with data, quiet after stop), the type choice, and startRecording on Safari 18.3.

GitNexus impact (upstream), all LOW: startRecording (useAudioCapture) 1 direct caller, handleGoLive (FlowOnAir) 1, startTestRecording 1, setupAudioProcessing 2.

Not in this PR.

  • Streaming from Safari before 18.4 needs an MP4/AAC input path in the backend producer.
  • Reconnect keeps the running MediaRecorder; the new producer may get WebM clusters without the header (suspected on main, not reproduced).
  • fix(stream): keep reconnect backoff going and reset it on success #819 (reconnect backoff in useStreamSocket.ts) is on main only and is not ported here.

Port of the frontend half of
#825 (89f5076) to
dev/312-interim. The files are the same as on main.

- Go Live refuses without a captured input. It stops the socket when
  the recorder fails to start.
- Resume a suspended AudioContext at capture, test, go-live and
  reconnect.
- Show an error when the recorder sent no audio 5 s after start.
- The test broadcast cannot be passed while 0 chunks were sent.

Refs: #827
Port of #826 (1baf52d)
to dev/312-interim. The files are the same as on main.

Safari before 18.4 cannot record WebM, and the backend only takes WebM.

- streamRecordingMimeType() picks the recording type, or returns null
  when the browser cannot record WebM.
- The setup step shows BROWSER_UNSUPPORTED_MESSAGE and disables the
  test broadcast.
- Go Live refuses before it opens the stream socket.

Refs: #827
@anneoneone anneoneone added project::Stream Area: live audio streaming & stream control type::frontend Layer: Vue admin SPA + vanilla-TS public pages executor::claude Executor: the Claude runner does this work labels Nov 2, 2026
@vercel

vercel Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
live-moafunk-de Ready Ready Preview Nov 2, 2026 10:51pm UTC

This branch was successfully deployed

1 active deployment
Preview — 1066ed79 Deployed Nov 2, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

executor::claude Executor: the Claude runner does this work project::Stream Area: live audio streaming & stream control type::frontend Layer: Vue admin SPA + vanilla-TS public pages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant