Files
Media-Server-Setup/live/src/useAudio.js
Brandyn / Techy 463d9b2385 Live portal: React + dockview multi-stream viewer behind Authentik; MediaMTX HLS/WebRTC/API re-enabled; is-live Discord hook
live/: every active stream opens as a dockable window (WHEP, HLS fallback), audio follows the
focused window, per-window mute/volume, pinning. Caddy live.HOST block with forward-auth proxies
/api /webrtc /hls to MediaMTX on loopback and serves /srv/live. WebRTC media on 8189 (firewall).
configure.py asks the live hostname and an optional Discord webhook; MediaMTX -ffmpeg image runs
the runOnReady hook. deployLive.sh builds and ships dist/.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-30 23:01:11 -05:00

47 lines
1.7 KiB
JavaScript

import { create } from './tinyStore.js'
// Per-stream audio state. Effective mute = override ?? !focused.
// override: null (follow focus) | 'unmuted' (pinned on) | 'muted' (pinned off)
export const audio = create({
focused: null,
streams: {}, // id -> { volume: 0..1, override }
focus(id) {
const s = { ...this.streams }
if (id && !s[id]) s[id] = { volume: 0.8, override: null }
if (id && s[id]) s[id] = { ...s[id], override: null } // focusing always gives you sound
return { focused: id, streams: s }
},
ensure(id) {
if (this.streams[id]) return null
return { streams: { ...this.streams, [id]: { volume: 0.8, override: null } } }
},
forget(id) {
const s = { ...this.streams }; delete s[id]
return { streams: s, focused: this.focused === id ? null : this.focused }
},
setVolume(id, volume) {
const cur = this.streams[id] || { volume: 0.8, override: null }
return { streams: { ...this.streams, [id]: { ...cur, volume } } }
},
toggleMute(id) {
const cur = this.streams[id] || { volume: 0.8, override: null }
const effectiveMuted = cur.override ? cur.override === 'muted' : this.focused !== id
return { streams: { ...this.streams, [id]: { ...cur, override: effectiveMuted ? 'unmuted' : 'muted' } } }
},
muteAll() {
const s = {}; for (const k in this.streams) s[k] = { ...this.streams[k], override: 'muted' }
return { streams: s }
},
followFocus() {
const s = {}; for (const k in this.streams) s[k] = { ...this.streams[k], override: null }
return { streams: s }
},
})
export function effectiveMuted(state, id) {
const cur = state.streams[id]
if (!cur) return true
if (cur.override) return cur.override === 'muted'
return state.focused !== id
}