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>
This commit is contained in:
22
live/src/tinyStore.js
Normal file
22
live/src/tinyStore.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { useSyncExternalStore } from 'react'
|
||||
|
||||
// 40-line store: state object + action methods that return partial updates.
|
||||
export function create(def) {
|
||||
let state = {}
|
||||
const listeners = new Set()
|
||||
const api = {}
|
||||
for (const [k, v] of Object.entries(def)) {
|
||||
if (typeof v === 'function') {
|
||||
api[k] = (...args) => {
|
||||
const patch = v.apply(state, args)
|
||||
if (patch) { state = { ...state, ...patch }; listeners.forEach(l => l()) }
|
||||
}
|
||||
} else {
|
||||
state[k] = v
|
||||
}
|
||||
}
|
||||
api.get = () => state
|
||||
api.subscribe = l => { listeners.add(l); return () => listeners.delete(l) }
|
||||
api.use = (selector = s => s) => useSyncExternalStore(api.subscribe, () => selector(state), () => selector(state))
|
||||
return api
|
||||
}
|
||||
Reference in New Issue
Block a user