Phase 7+11: token-based publishing — MediaMTX authMethod http -> dumpd /mtx-auth (bridge-only port 8081); dumpd mints 12 h HMAC tokens for Discord-authenticated streamers; dock rewritten (no passwords, naming rules with live preview, prefix/suffix, manual-setup accordion with hidden token); portal uses tokens; configure.py drops streamers; MediaMTX hot-reload, Caddy reload — no stream interruption
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -65,8 +65,9 @@ export default function GoLive({ onExit }) {
|
||||
<video ref={videoRef} autoPlay playsInline muted className={phase === 'live' || phase === 'capturing' ? 'show' : ''} />
|
||||
{phase !== 'live' && (
|
||||
<div className="golive-form">
|
||||
<label>Stream title <small>(becomes the folder and the Jellyfin title)</small></label>
|
||||
<label>Stream title <small>(becomes the Jellyfin title; letters, digits, _ . - ; no double underscore)</small></label>
|
||||
<input value={title} onChange={e => setTitle(e.target.value)} disabled={phase === 'capturing'} />
|
||||
<div className="hint" style={{marginTop: '-4px'}}>stream name: <b>{key ? key.user : '<you>'}/{cleanTitle(title) || '—'}</b></div>
|
||||
<div className="hint">
|
||||
Click <b>Start</b>, then pick the <b>WarDogs</b> window in Chrome's dialog and tick <b>Share audio</b>.
|
||||
<b>Windows</b> with <b>Chrome or Edge 141 or newer</b> is the supported setup: window audio and the GPU
|
||||
|
||||
@@ -7,7 +7,8 @@ export function defaultTitle(now = new Date()) {
|
||||
const d = new Date(now.getTime() - now.getTimezoneOffset() * 60000).toISOString().slice(0, 10)
|
||||
return `HelmetCam-${d}`
|
||||
}
|
||||
export const cleanTitle = t => t.trim().replace(/\s+/g, '-').replace(/[^A-Za-z0-9_.-]/g, '')
|
||||
// Must match the dock and the server: the sweep publishes <Title>__<timestamp>.mp4, so "__" is reserved.
|
||||
export const cleanTitle = t => t.trim().replace(/\s+/g, '-').replace(/[^A-Za-z0-9_.-]/g, '').replace(/_{2,}/g, '_').replace(/^[.-]+/, '').slice(0, 64)
|
||||
|
||||
export async function captureWindow() {
|
||||
// Window-only picker with that window's own audio. Chrome/Edge 141+ honour windowAudio: "window"
|
||||
@@ -105,7 +106,8 @@ export async function publish({ stream, path, user, pass, onState }) {
|
||||
}
|
||||
|
||||
export async function fetchStreamKey() {
|
||||
const r = await fetch('/me/stream-key', { credentials: 'same-origin' })
|
||||
// a fresh 12 h publish token for the logged-in Discord user (member of streamers); nothing is stored
|
||||
const r = await fetch('/me/stream-token', { credentials: 'same-origin' })
|
||||
const body = await r.json().catch(() => ({}))
|
||||
if (!r.ok) throw new Error(body.error || `stream key ${r.status}`)
|
||||
return body
|
||||
|
||||
Reference in New Issue
Block a user