Skip to main content

Live Manager (live-manager.js)

The Live Manager decides who is live and routes them onto the broadcast channel. It ranks candidate streams, restreams the winner into the remote channel_live harbor (:8006), and watches over the whole chain.

Stream registry

Streams live in station-config.json → live.streams (survives restarts). A stream is:

{
id: 'LIVE211E5A4A', // 8-char hex id
url: 'https://.../stream.m3u8',
title: 'Rooftop Session',
creator: 'Nadia',
listeners: 12,
votes: 3, // cumulative delta
ingest: 'rtmp' | 'rtmp2' | 'webrtc' | 'pipe', // normalized
addedAt: 1744567890123,
lastLiveAt: 1744568890123
}

Deduplication (important)

register() dedupes by url + title + normalized ingest. The ingest type is normalized first: WHIP pipes (url starts with pipe: or the source is a pipe) become 'pipe'; everything else defaults to 'rtmp'. This fixed a bug where every WebRTC registration compared raw 'webrtc' against a stored 'pipe' and stacked a duplicate stream each time. Re-registering the WHIP source now returns the same id and the registry stays at one entry.

Ranking & selection

When asked to go live, the manager scores the streams:

  • Freshness — recency of lastLiveAt (been live recently = higher).
  • Listeners — current audience.
  • Votes — cumulative votes.
  • Blacklist — a stream marked dead (blacklisted) is excluded.

The top-ranked candidate becomes the channel-live source; if no live stream is candidate-worthy, the channel returns to the normal mix.

The restream pipeline

  1. Probe_probeHarbor() checks http://10.10.8.230:8089/whip-info (WHIP server, live:true) to confirm a remote feed is actually flowing before declaring the channel live.
  2. ffmpeg — restreams the winning source with libmp3lame (128 kbps stereo) into the Liquidsoap harbor: icecast://source:hackme@10.10.8.230:8006/channel_live.
  3. Monitor — while live, the manager pings the winner; if it dies, it either fails over to the next-ranked stream or returns to the mix.
  4. Disconnect — the WHIP pipe stream is torn down when its remote track ends (the WHIP server re-registers pipe: on each new connect).

Restart behaviour

  • Live state is in-memory (_active), so restarting server.js drops the current takeover and the channel falls back to the mix. This is normal.
  • The stream registry itself is persisted in station-config.json, so the ranked pool survives.

Endpoints it powers

RoutePurpose
GET /api/station/liveStatus snapshot (registry + active)
POST /api/station/live/registerAdd/refresh a stream
POST /api/station/live/removeRemove by id
POST /api/station/live/voteVote {id,delta}
POST /api/station/live/startGo live (top-ranked, or {id})
POST /api/station/live/stopStop the takeover

WHIP integration

The mobile WebRTC path (go-live.htmlwhip_server.py) feeds this manager as a pipe: stream (url="pipe:/home/sms/radio/whep_live.wav", ingest="webrtc"). The WHIP server re-registers on every new audio track, and the manager's dedupe keeps exactly one pipe entry. See Live ingest.

See also