peertube
peertube · sources · schema v1PEERTUBE — federated-video SOURCE. Search the open PeerTube fediverse, pick a video, and its picture streams in as a CLEAN (untainted) WebGL video texture plus stereo audio — a real downstream-usable source, NOT play-only. MODEL: a debounced search box queries Sepia Search (sepiasearch.org, the official PeerTube meta-index — CORS-open + anonymous, NO proxy needed); results list title, channel@host, duration, a LIVE badge, and a thumbnail. Click a result → the card fetches that video's per-instance public API (https://<host>/api/v1/videos/{uuid}) and resolves a playable stream: it PREFERS the HLS master playlist (streamingPlaylists[0].playlistUrl → attached via hls.js for adaptive playback) and FALLS BACK to the highest-resolution progressive MP4 (files[].fileUrl, attached as a plain <video src>). The stream attaches to a card-owned <video crossorigin="anonymous"> → the engine samples it into the FBO (the `video` output) + taps stereo audio (audio_l / audio_r via MediaElementSource → ChannelSplitter, with the shared keep-alive so an unpatched source keeps decoding at full rate). WHY THE TEXTURE + AUDIO ARE CLEAN (verified): PeerTube sends Access-Control-Allow-Origin:* on the FINAL media hop (master .m3u8 + the fragmented-mp4 / mpeg-ts segments) under a favorable `credentialless` COEP posture, so a crossorigin <video> fed by hls.js both PLAYS and yields an untainted texture — unlike archive.org video (play-only). IO — Inputs: play_trigger (gate, edge=trigger — a rising edge toggles play/pause), next_trigger (gate, edge=trigger — a rising edge loads the next search result, wrapping). Outputs: video (the live frame texture), audio_l / audio_r (stereo, silent ConstantSource placeholders until a stream attaches), loaded (trigger — one pulse when a new video finishes loading), ended (trigger — pulses when the video reaches its end), playing (gate — HIGH while actually playing), playhead (CV — 0..1 normalized position). CV/PATCHING: all inputs + outputs live in the card's yellow drill-down PATCH PANEL (top-left/top-right affordances → INPUT/OUTPUT → grouped Gates / CV / Audio / Video rows) — there are NO raw side jacks (#767 standard). Trigger inputs are main-thread edge-detected the established video-module way (a single bridge-written cv-param read per tick — never a whole-AnalyserNode rescan). USAGE: type a term, press Enter (or wait for the debounce), pick a video, and route VIDEO into a mixer/keyer/OUTPUT and audio_l/audio_r into AUDIO OUT or a SCOPE/SYNESTHESIA for audio-reactive visuals; clock the next_trigger from a sequencer to channel-surf the fediverse. GRACEFUL: ~1/6 instances misconfigure CORS (raw S3 with no ACAO) → the element taints / the HLS load fails fatally → the card degrades to "display unavailable", surfaces a clear status, and AUTO-SKIPS to the next result (it never crashes, taints the texture, or hangs on loading). An optional instance field biases attribution display. Only { instanceHost, uuid, name, selectedHost } persist on the node + sync to rack-mates (everyone resolves + plays the same video locally); transient playback state stays render-local (never a per-frame synced-store write). The AUDIO TRAP: the <video> is created muted for autoplay, then un-muted ONLY after the MediaElementSource tap succeeds, so audio routes into WebAudio without native speaker double-output. LEGAL: federated PUBLIC videos, not hosted by patchtogether — an in-card disclaimer + attribution to PeerTube + Sepia Search ship with it.
the faceplate
inputs
| id | cable | what it does |
|---|---|---|
play_trigger | gate | Trigger (gate cable, edge:'trigger'): a rising edge toggles the current video between play and pause. Routed through the CV bridge as the synthetic cv_play_trigger param, which the card polls + edge-detects (<0.5 -> >=0.5); it fires once per rising edge, not while held. Patch a clock or any gate here for hands-free transport. gate / trigger; modulates cv_play_trigger (summed directly (the destination DSP scales it)); trigger — fires once per rising edge |
next_trigger | gate | Trigger (gate cable, edge:'trigger'): a rising edge advances to the NEXT result in the search list (wrapping to the first; if the list is empty it re-runs the last search). Routed through the CV bridge as the synthetic cv_next_trigger param and edge-detected by the card; fires once per rising edge, not while held. Patch a clock here to channel-surf the results in time. gate / trigger; modulates cv_next_trigger (summed directly (the destination DSP scales it)); trigger — fires once per rising edge |
outputs
| id | cable | what it does |
|---|---|---|
video | video | The loaded stream's live frame as a video texture — untainted and downstream-usable because PeerTube sends CORS/ACAO on its media. Dim near-black idle gradient when nothing is loaded; goes to the auto-skip 'unavailable' fallback for a CORS-misconfigured instance. RGB video stream |
audio_l | audio | Left channel of the stream's stereo audio (output 0 of the channel splitter fed by the video element's MediaElementSource — working audio, unlike ARCHIVIST). A silent ConstantSource (offset 0) placeholder until a stream attaches and the audio tap is wired. audio signal |
audio_r | audio | Right channel of the stream's stereo audio (output 1 of the same channel splitter). A silent ConstantSource (offset 0) placeholder until a stream attaches and the audio tap is wired. audio signal |
loaded | gate | Trigger out (gate cable, edge:'trigger'): one short ~5ms rising-edge pulse each time a newly selected video finishes loading and its audio is wired. Patch into a downstream clock/reset/sample-and-hold to react to a track change. gate / trigger; trigger — fires once per rising edge |
ended | gate | Trigger out (gate cable, edge:'trigger'): one short ~5ms rising-edge pulse when the playing video reaches its end. Patch into next_trigger (self or another source) to auto-advance, or into any downstream trigger consumer. gate / trigger; trigger — fires once per rising edge |
playing | gate | Gate out (gate cable, edge:'gate'): held HIGH while the video is actually playing, LOW while paused, loading, idle, or unavailable. Use as a run/transport gate for downstream modules. gate / trigger; gate — acts while the level is high (reacts to both edges) |
playhead | cv | CV out: the normalized playback position, 0..1 (currentTime / duration), updated as the video plays. Patch into any CV destination to scrub or modulate downstream gear in sync with the video timeline. control voltage (CV) |
params
| id | label | range | default | curve |
|---|---|---|---|---|
gain | Gain | 0..2 | — | linear |
cv_play_trigger | Play trigger | 0..1 | 0 | linear |
cv_next_trigger | Next trigger | 0..1 | 0 | linear |
controls
| control | what it does |
|---|---|
| Next trigger | Next trigger (hidden synthetic param, 0 to 1, default 0): the CV bridge writes the next_trigger input's gate level here; the card polls readParam and edge-detects a rising edge to load the next search result. Not a user-facing knob. |
| Play trigger | Play trigger (hidden synthetic param, 0 to 1, default 0): the CV bridge writes the play_trigger input's gate level here; the card polls readParam and edge-detects a rising edge (<0.5 -> >=0.5) to toggle play/pause. Not a user-facing knob. |
| Gain | Gain — declared output-level param (0 to 2, linear; default 1.0). NOTE: like TV-LIBRARIAN, the passthrough shader has no uGain uniform and draw() never applies it, so the param is carried on the module but currently inert — it does not yet brighten or scale the video output. |
source
peertube.ts on GitHub.