Compare commits

..

2 Commits

Author SHA1 Message Date
d28c2335d0 Go Live: recording warning before start and a REC badge while live
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-31 01:48:25 -05:00
f737fc6289 Go Live: state the Windows-only window-audio limitation in the UI and docs
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-31 01:47:45 -05:00
5 changed files with 16 additions and 5 deletions

View File

@@ -205,7 +205,7 @@ asked by `configure.py --full`) can post when a stream starts.
embedded outpost → group policy (e.g. everyone in the server, or `streamers`+`editors`+`admins`). embedded outpost → group policy (e.g. everyone in the server, or `streamers`+`editors`+`admins`).
3. Dev box: `./deployLive.sh` builds `live/` and copies `dist/` to `/srv/live` (Caddy serves it directly). 3. Dev box: `./deployLive.sh` builds `live/` and copies `dist/` to `/srv/live` (Caddy serves it directly).
**Go Live** button (Chrome/Edge 141+ for window audio): broadcast a window from the browser (no OBS) as `<user>/HelmetCam-<date>` — key comes **Go Live** button (window audio: Windows + Chrome/Edge 141+; Linux/Mac get video only): broadcast a window from the browser (no OBS) as `<user>/HelmetCam-<date>` — key comes
from `/me/stream-key` (dumpd reads `/etc/vanguard/streamers.json`, identity from the Authentik header), so a from `/me/stream-key` (dumpd reads `/etc/vanguard/streamers.json`, identity from the Authentik header), so a
streamer's Authentik username must equal their MediaMTX name. streamer's Authentik username must equal their MediaMTX name.

View File

@@ -31,7 +31,7 @@ Source of truth: this repo, `techy/Media-Server-Setup` on the stack's own Gitea
| Editor | Mount the library as a drive | WebDAV, same URL | same | | Editor | Mount the library as a drive | WebDAV, same URL | same |
| Anyone in the Discord server | Watch recordings and clips by streamer and season | Jellyfin `https://watch.…/` | Discord (Authentik) | | Anyone in the Discord server | Watch recordings and clips by streamer and season | Jellyfin `https://watch.…/` | Discord (Authentik) |
| Anyone in the Discord server | Watch live: all active streams in dockable windows, audio follows focus, per-window mute/volume | Live portal `https://live.…/` | Discord (Authentik, domain-level) — live; viewer side not yet exercised during a stream | | Anyone in the Discord server | Watch live: all active streams in dockable windows, audio follows focus, per-window mute/volume | Live portal `https://live.…/` | Discord (Authentik, domain-level) — live; viewer side not yet exercised during a stream |
| Streamer | **Go Live** from the browser: pick the WarDogs window (+audio), streams as `<user>/HelmetCam-<date>`; no OBS needed | Live portal → Go Live | Discord; key fetched for the logged-in user — **verified end to end** (publish → `.live` → sweep → title folder) | | Streamer (Windows) | **Go Live** from the browser: pick the WarDogs window (+audio on Windows Chrome 141+; Linux/Mac video only), streams as `<user>/HelmetCam-<date>`; no OBS needed | Live portal → Go Live | Discord; key fetched for the logged-in user — **verified end to end** (publish → `.live` → sweep → title folder) |
| Devs | Host code, review, CI | Gitea `https://git.…/`, ssh 2222 | **local account** (Discord later) | | Devs | Host code, review, CI | Gitea `https://git.…/`, ssh 2222 | **local account** (Discord later) |
| Bot dev | Push code, restart container, read logs; web UI at `https://bot.…/` | Gitea + compose; `bot/AGENT.md` | web UI behind Discord (Authentik), once its provider exists | | Bot dev | Push code, restart container, read logs; web UI at `https://bot.…/` | Gitea + compose; `bot/AGENT.md` | web UI behind Discord (Authentik), once its provider exists |
| Admin | Add/remove streamers & editors, hostnames, bot token | `./configure.py` | ssh | | Admin | Add/remove streamers & editors, hostnames, bot token | `./configure.py` | ssh |

View File

@@ -22,6 +22,6 @@ Toolbar → **Go Live**: closes all viewer windows, fetches the logged-in user's
`/me/stream-key` (dumpd, identity from Authentik's header), opens Chrome's window picker (windows only, `/me/stream-key` (dumpd, identity from Authentik's header), opens Chrome's window picker (windows only,
audio requested), and publishes over WebRTC/WHIP to `<user>/HelmetCam-<date>` at ≤12 Mbps, H.264, up to audio requested), and publishes over WebRTC/WHIP to `<user>/HelmetCam-<date>` at ≤12 Mbps, H.264, up to
1440p60. Recording, the "is live" post, the sweep and Jellyfin follow exactly as for an OBS stream. 1440p60. Recording, the "is live" post, the sweep and Jellyfin follow exactly as for an OBS stream.
Window audio: Chrome/Edge 141+ (`windowAudio: "window"`); older browsers share the window without sound. Window audio: Windows only, Chrome/Edge 141+ (`windowAudio: "window"`). Linux/Mac Chrome captures tab audio only → window without sound; use OBS there.
Requires the Authentik username to equal the MediaMTX streamer name (configure.py keeps them in sync via Requires the Authentik username to equal the MediaMTX streamer name (configure.py keeps them in sync via
`/etc/vanguard/streamers.json`). `/etc/vanguard/streamers.json`).

View File

@@ -66,8 +66,15 @@ export default function GoLive({ onExit }) {
<input value={title} onChange={e => setTitle(e.target.value)} disabled={phase === 'capturing'} /> <input value={title} onChange={e => setTitle(e.target.value)} disabled={phase === 'capturing'} />
<div className="hint"> <div className="hint">
Click <b>Start</b>, then pick the <b>WarDogs</b> window in Chrome's dialog and tick <b>Share audio</b>. Click <b>Start</b>, then pick the <b>WarDogs</b> window in Chrome's dialog and tick <b>Share audio</b>.
Window audio needs <b>Chrome or Edge 141 or newer</b> (chrome://version). Older browsers capture the window Window audio needs <b>Windows</b> with <b>Chrome or Edge 141 or newer</b>. On Linux and Mac, Chrome can only
without sound. Capped at {MAX_KBPS / 1000} Mbps, up to 1440p60, H.264. capture audio from a browser tab, so the window comes through without sound — use OBS with the dock there.
Capped at {MAX_KBPS / 1000} Mbps, up to 1440p60, H.264.
</div>
<div className="warn-box">
<b>⚠ This stream is recorded.</b> Everything the window shows and every sound it makes is saved to the
server, visible to editors and members in Jellyfin, and kept until it is dumped or archived. Close
anything private in that window before you start. Use <b>Dump tonight</b> in the OBS dock if a session
should be thrown away (3-day undo).
</div> </div>
{err && <div className="err">{err}</div>} {err && <div className="err">{err}</div>}
<button className="primary" disabled={!key || phase === 'capturing'} onClick={start}>{phase === 'capturing' ? 'Waiting for the window picker…' : 'Start'}</button> <button className="primary" disabled={!key || phase === 'capturing'} onClick={start}>{phase === 'capturing' ? 'Waiting for the window picker…' : 'Start'}</button>
@@ -76,6 +83,7 @@ export default function GoLive({ onExit }) {
{phase === 'live' && ( {phase === 'live' && (
<div className="golive-stats"> <div className="golive-stats">
<span className="pill live">LIVE</span> <span className="pill live">LIVE</span>
<span className="rec" title="this stream is being recorded to the server">● REC</span>
<span>{mmss(elapsed)}</span> <span>{mmss(elapsed)}</span>
<span>{key.user}/{cleanTitle(title)}</span> <span>{key.user}/{cleanTitle(title)}</span>
<span>{conn}</span> <span>{conn}</span>

View File

@@ -53,3 +53,6 @@ html, body, #root { height:100%; margin:0; background:var(--bg); color:var(--fg)
.golive-stats .pill.live { background:var(--live); color:#fff; font-weight:700; font-size:11px; padding:2px 6px; border-radius:4px; } .golive-stats .pill.live { background:var(--live); color:#fff; font-weight:700; font-size:11px; padding:2px 6px; border-radius:4px; }
.golive-stats .warn { color:#f1c40f; } .golive-stats .warn { color:#f1c40f; }
.topbar button.golive-btn { background:var(--live); border-color:var(--live); color:#fff; font-weight:700; } .topbar button.golive-btn { background:var(--live); border-color:var(--live); color:#fff; font-weight:700; }
.golive-form .warn-box { background:#3a2a10; border:1px solid #f1c40f; color:#ffe9a8; border-radius:8px; padding:10px 12px; font-size:13px; line-height:1.5; }
.golive-form .warn-box b { color:#fff; }
.golive-stats .rec { background:#f1c40f; color:#000; font-weight:700; font-size:11px; padding:2px 6px; border-radius:4px; }