From c346d948756b59266fde74dc4d871c10e8e81114 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Wed, 3 Jun 2026 18:44:06 -0400 Subject: [PATCH] =?UTF-8?q?zebra-spaces:=20render=20"stream"=20toggle=20on?= =?UTF-8?q?=20own=20row=20too=20=E2=80=94=20DJ=20self-monitor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the m.uuid !== myUUID guard on the per-speaker stream toggle so the DJ can preview what listeners are actually hearing of their own mic. Self-stream URL hits the SFU pulling our own pubkey — the ~2s delayed playback is the cue we want to confirm the broadcast works. Title attr warns about feedback risk on open speakers (closed headphones like WH-1000XM5 are fine — earcups don't bleed into the headset mic enough to feedback). Mod buttons (kick/ban/promote/demote) still self-exclude — different guard, kept intact. --- web/zebra-spaces.html | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/web/zebra-spaces.html b/web/zebra-spaces.html index 124eeba..6540a61 100644 --- a/web/zebra-spaces.html +++ b/web/zebra-spaces.html @@ -4013,17 +4013,27 @@ function renderRoom(){ /* per-speaker "stream" toggle: anyone can flip another speaker's * audio from WebRTC subscribe → HTTP Ogg/Opus tap. Higher latency, * cleaner playback under loss. Not a mod action — every listener - * gets to pick their own path per speaker. */ - if (canSpeak(m.role) && m.uuid !== myUUID && m.pubkey){ + * gets to pick their own path per speaker. Also rendered on your + * own row as a DJ self-monitor: confirms what listeners are hearing + * (~2s delayed). Safe with closed headphones; loud feedback with + * open speakers + music-mode mic — title attr warns. */ + if (canSpeak(m.role) && m.pubkey){ let mPubHex = ''; try { mPubHex = hex(unb64(m.pubkey)); } catch(_){} if (mPubHex){ + const isSelf = (m.uuid === myUUID); const sb = document.createElement('button'); sb.className = 'small'; const on = streamMode.has(mPubHex); sb.textContent = on ? '◉ stream' : '○ stream'; - sb.title = on - ? 'streaming — click to switch back to live (low-latency WebRTC)' - : 'switch to HTTP Ogg/Opus stream (~2s delay, glitch-free for DJ sets)'; + if (isSelf){ + sb.title = on + ? 'monitoring your own DJ stream — click to stop (feedback risk on open speakers!)' + : 'preview what listeners hear of YOUR mic (~2s delay) — headphones recommended'; + } else { + sb.title = on + ? 'streaming — click to switch back to live (low-latency WebRTC)' + : 'switch to HTTP Ogg/Opus stream (~2s delay, glitch-free for DJ sets)'; + } if (on) sb.classList.add('invert'); sb.onclick = () => toggleStreamFor(m.uuid, mPubHex); acts.appendChild(sb); @@ -4539,8 +4549,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');