Files
ghassanandClaude Opus 4.7 5bf1128196 feat(live): polish — ended-state, chat, viewer actions, Private option
Restores the live streaming controllers / views / model from the
pre-audit WIP that got stashed on 2026-10-21, then applies the Phase 6
polish items:

## /live/new form
Add a Private visibility option that mirrors the upload modal's
wording — "Only you can see this stream. Perfect for testing without
an audience." Validator in LiveStreamController@store now permits
public|unlisted|private (was public|unlisted).

## Watch page ended-state
When the stream status is "ended", the overlay is a solid "Stream
ended" card showing the stream's duration (started_at → ended_at)
and, when a recording was published, a Watch Replay CTA linking to
the saved video. Previously an ended stream showed the generic
"Waiting for the broadcast to start" placeholder forever.

## Channel name link
The uploader's name in the meta row is now a real <a href> to the
channel page.

## Viewer action row
Subscribe + Share buttons below the player, visible to anyone except
the owner. Subscribe for guests uses the Phase 7 ?intended= pattern
so after sign-in they land back on the stream.

## Live chat (DB-backed polling)
New live_chat_messages table (migration 2026_10_21_000003). Viewers
GET /live/{stream}/chat?since=N every 3 seconds; signed-in users POST
to the same path (rate-limited at 60/min). Guests see the panel in
read-only mode with a "Sign in to chat" placeholder.

Private streams reject chat for anyone but the owner (404). Non-live
status rejects chat sends (422). Author name is snapshotted at post
time so a renamed account's old messages keep the name they used.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-10-21 12:41:56 +03:00

56 lines
2.0 KiB
PHP

<?php
/*
|--------------------------------------------------------------------------
| Live streaming
|--------------------------------------------------------------------------
|
| The media plane runs as MediaMTX on loopback; nginx proxies /live-rtc and
| /live-hls to it. Nothing here is a secret except the TURN credential, which
| comes from the environment.
|
*/
return [
// MediaMTX's control API (localhost only). Used to read live publisher and
// viewer state.
'api_url' => env('LIVE_API_URL', 'http://127.0.0.1:9997'),
/*
|--------------------------------------------------------------------------
| ICE
|--------------------------------------------------------------------------
|
| STUN lets a browser discover its public address. TURN relays the media
| when a direct path cannot be established — which, until a UDP port is
| forwarded to this server, is EVERY broadcaster outside the local network.
|
| With no TURN configured, publishing works from the LAN only.
|
*/
'stun_url' => env('LIVE_STUN_URL', 'stun:stun.l.google.com:19302'),
'turn_url' => env('LIVE_TURN_URL'),
'turn_username' => env('LIVE_TURN_USERNAME'),
'turn_credential' => env('LIVE_TURN_CREDENTIAL'),
/*
|--------------------------------------------------------------------------
| Capture defaults
|--------------------------------------------------------------------------
|
| The target the phone is ASKED for. A camera that cannot do 1080p returns
| the closest it can, and WebRTC's congestion control lowers the bitrate
| when the uplink cannot sustain it — so this is a ceiling, not a promise.
|
*/
'video_width' => (int) env('LIVE_VIDEO_WIDTH', 1920),
'video_height' => (int) env('LIVE_VIDEO_HEIGHT', 1080),
'video_fps' => (int) env('LIVE_VIDEO_FPS', 30),
// ~4.5 Mbit/s is the practical ceiling for 1080p30 over a mobile uplink.
'max_bitrate' => (int) env('LIVE_MAX_BITRATE', 4_500_000),
];