tv librarian

tvLibrarian · sources

International live-TV source. Pick a country on the 2D world map (or the country list) → a channel list (filtered to playable HLS streams) → tune a channel and its live picture streams in as an UNTAINTED video texture (validated under the app's COEP require-corp headers: famelack HLS plays + yields a clean WebGL2 texture, so VIDEO out is a real downstream-usable texture, not play-only) plus stereo audio_l/audio_r from the stream's audio track. The "random" button (and the random/next CV trigger inputs) jump channels for happy-accident channel-surfing. Gate outputs: channel_changed pulses on each tune (trigger), stream_online holds high while the stream actually plays (gate). Dead/geo-blocked/unlicensed-pulled streams fail cleanly → marked "unavailable" + auto-skipped, never a hang or a tainted texture. Channel selection persists on the node + syncs to rack-mates (everyone tunes to the same stream). LEGAL: streams are THIRD-PARTY public streams, NOT hosted by patchtogether — this is a player pointed at the same iptv-org-derived directory many "free live TV" sites use; an in-card disclaimer + attribution (Famelack, MIT-licensed dataset fetched at runtime; iptv-org) ship with it, geo-blocked entries are honored/marked, and dead links are filtered.

An international live-TV source: pick a country (click the 2D equirectangular world map, which snaps to the nearest country centroid that has channels, or use the country dropdown), pick a channel from that country's list, and the NODE attaches that channel's HLS (.m3u8) stream via hls.js to an internal crossorigin video element — the stream's lifetime belongs to the node, not to whichever surface is mounted, so it tunes, keeps playing and answers its triggers with no card and no faceplate open. The engine samples that element into a WebGL framebuffer — the fragment shader is a straight passthrough of the live frame (when no stream is up it draws a dim near-black idle gradient so the card reads as "alive but empty"), so the video output is a genuine downstream-usable texture, not play-only. Frames are uploaded at the source's decode cadence (requestVideoFrameCallback, Firefox falls back to a currentTime-advance check) and downscaled to the engine resolution, so a high-bitrate stream doesn't flood GPU texture traffic. The stream's audio track is split into a stereo pair on the audio outs, with a silent gain-0 keep-alive that keeps the element decoding at full rate even when audio isn't patched. Channel metadata (country + channel name + stream URL) is persisted to the node so rack-mates tune to the SAME stream; geo-blocked entries are kept and marked, youtube-only entries are dropped, and a stream that fails to load (no CORS/ACAO under COEP, or 12s with no frame) is marked unavailable and auto-skips to the next channel rather than hanging. The faceplate's tuner body carries a 16:9 picture of the module's own video output with a SCREEN on/off switch, a map/list segmented toggle, the country picker, the channel list (geo-blocked entries badged) and "random" / "next ▸" buttons (DOM-only, not module params), plus a legal disclaimer and Famelack/iptv-org attribution; the legacy card shows the same picker under ?shell=legacy. Usage: drop it as a video source and feed its output into a mixer or any video module; patch a clock into next to channel-surf hands-free, or random to roulette.

the faceplate

tv librariannextgaterandomgatevideovideoaudio_laudioaudio_raudiochannel_changedgatestream_onlinegateaudiocvgatepitch
2 inputs · 5 outputs · 3 params

inputs

idcablewhat it does
nextgateTrigger (gate cable, edge:'trigger'): a rising edge advances to the NEXT channel in the current country's list, wrapping to the first. Level-while-high does nothing; it fires once per rising edge. Patch a clock here to channel-surf in time.
gate / trigger; modulates cv_next (summed directly (the destination DSP scales it)); trigger — fires once per rising edge
randomgateTrigger (gate cable, edge:'trigger'): a rising edge tunes a RANDOM channel from the current country (picked from the OTHERS when more than one exists, so it reliably changes). Fires once per rising edge, not while held.
gate / trigger; modulates cv_random (summed directly (the destination DSP scales it)); trigger — fires once per rising edge

outputs

idcablewhat it does
videovideoThe live stream's frame as a video texture (untainted/downstream-usable for streams that send CORS/ACAO). Dim near-black gradient when nothing is tuned.
RGB video stream
audio_laudioLeft channel of the tuned stream's stereo audio (split from the stream's MediaElementSource). Silent (gain-0 constant source) when no stream is playing.
audio signal
audio_raudioRight channel of the tuned stream's stereo audio. Silent (gain-0 constant source) when no stream is playing.
audio signal
channel_changedgateTrigger out (gate cable, edge:'trigger'): one short rising-edge pulse each time a new channel is tuned. Patch into a downstream clock/reset/sample-and-hold.
gate / trigger; trigger — fires once per rising edge
stream_onlinegateGate out (gate cable, edge:'gate'): held high while the stream is actually playing, low while loading, idle, or unavailable.
gate / trigger; gate — acts while the level is high (reacts to both edges)

params

idlabelrangedefaultcurve
gainGain0..2linear
cv_nextNext0..10linear
cv_randomRandom0..10linear

controls

controlwhat it does
NextNext (hidden synthetic param, 0 to 1, default 0): the CV bridge writes the next input's gate level here; the node-owned HLS controller polls readParam and edge-detects a rising edge (<0.5 → >=0.5) to advance one channel. Not a user-facing knob.
RandomRandom (hidden synthetic param, 0 to 1, default 0): the CV bridge writes the random input's gate level here; the node-owned HLS controller polls readParam and edge-detects a rising edge to tune a random channel. Not a user-facing knob.
GainGain — output level for the picture (0 to 2, linear; default 1.0). The passthrough shader multiplies the sampled RGB by this (the uGain uniform), so 0 blacks the output, 1.0 is the identity and 2 doubles it (clipped at full scale by the 8-bit framebuffer). It scales the VIDEO only — the audio_l/audio_r outs are unaffected.

source

tv-librarian.ts on GitHub.

Generated from packages/web/src/lib/{audio,video}/module-registry.ts · repo