zebra-spaces: periodic auto-enrol retry covers post-join publish race
If the host's mic publish lands AFTER the listener's initial autoEnableDjModeForListener pass (which runs 600ms after the listener sees the peer-joined event), the /stream request 404s with "no such publisher in room" and onFail removes the pubHex from streamMode. No event re-triggers the auto-enrol after that — peer- joined doesn't refire when an already-present member starts publishing. Add a 4s interval retry loop while in listener mode. autoEnableDj- ModeForListener is idempotent (skips already-enrolled pubHexes), so the loop is cheap and only re-attempts the missing ones. Stops automatically on role transition out of listener. This was the root cause behind every "no music on phone" report so far: the SFU logs show /stream 404s when the phone tried, then never again after the host's mic publish completed. The retry loop closes the race. Tests green (83 fsm + 16 zebra-spaces).
This commit is contained in:
parent
334ee82d6b
commit
d9be0e4cda
1 changed files with 30 additions and 4 deletions
|
|
@ -3763,8 +3763,12 @@ async function onRoleEntered(){
|
|||
/* Listener landing — auto-enrol into DJ mode for every speaker so
|
||||
* playback rides the deep-buffered HTTP Ogg path. Slight delay so
|
||||
* the SFU has time to wire its Ogg writers for the existing
|
||||
* publishers (lazy-inits on first /stream listener). */
|
||||
* publishers (lazy-inits on first /stream listener). Plus start
|
||||
* the periodic retry loop so streams come up when the host's mic
|
||||
* publish lands AFTER we already auto-enrolled (race the 600ms
|
||||
* delay can't cover). */
|
||||
setTimeout(autoEnableDjModeForListener, 600);
|
||||
startAutoEnrolRetryLoop();
|
||||
}
|
||||
}
|
||||
let inRoleTransition = false;
|
||||
|
|
@ -3785,7 +3789,9 @@ async function onRoleChanged(prev, next){
|
|||
muted = true;
|
||||
try { sessionStorage.setItem(MUTE_STATE_KEY, '1'); } catch(_){}
|
||||
/* coming out of listener — tear down every HTTP DJ-mode tap so
|
||||
* the WebRTC audio path takes over (low-latency for conversation). */
|
||||
* the WebRTC audio path takes over (low-latency for conversation).
|
||||
* Also stop the auto-enrol retry loop — speakers don't need it. */
|
||||
stopAutoEnrolRetryLoop();
|
||||
autoDisableDjModeForAll();
|
||||
await ensureMicAndUI();
|
||||
for (const [uuid, mm] of members){
|
||||
|
|
@ -4195,6 +4201,26 @@ function autoEnableDjModeForListener(){
|
|||
startStream(uuid, pubHex);
|
||||
}
|
||||
}
|
||||
/* Periodic retry — covers the timing race where the host's mic publish
|
||||
* hasn't been registered at the SFU yet when the listener first
|
||||
* auto-enrols. onFail removes the pubHex from streamMode so the next
|
||||
* sweep will re-attempt; without this poll the only retry trigger was
|
||||
* peer-joined / role-change events, which never fire when a speaker
|
||||
* who was already in the room simply starts publishing later. Cheap:
|
||||
* idempotent on already-streaming pubHexes, no-op if not a listener. */
|
||||
let autoEnrolRetryTimer = null;
|
||||
function startAutoEnrolRetryLoop(){
|
||||
if (autoEnrolRetryTimer) return;
|
||||
autoEnrolRetryTimer = setInterval(() => {
|
||||
if (myRole !== 'listener'){ stopAutoEnrolRetryLoop(); return; }
|
||||
autoEnableDjModeForListener();
|
||||
}, 4000);
|
||||
}
|
||||
function stopAutoEnrolRetryLoop(){
|
||||
if (!autoEnrolRetryTimer) return;
|
||||
clearInterval(autoEnrolRetryTimer);
|
||||
autoEnrolRetryTimer = null;
|
||||
}
|
||||
|
||||
/* Tear down all DJ-mode HTTP streams — used when we transition out
|
||||
* of listener into speaker/cohost/host. Speakers need the low-
|
||||
|
|
@ -4861,8 +4887,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');
|
|||
|
||||
<footer style="margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
|
||||
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> · built <span class="stamp-date">2026-06-04</span><br>
|
||||
md5 <span class="stamp-md5">86588d00e3993870a95504ac16973b26</span><br>
|
||||
sha256 <span class="stamp-sha">1e8dc752014189b0ba6e45bad1981950079700e3cf8da33f15560c006f4a314e</span><br>
|
||||
md5 <span class="stamp-md5">63bf820f7f0bc11d4726b3f1550380f6</span><br>
|
||||
sha256 <span class="stamp-sha">6a259843e45682d391e7485fd11b670eea32b8622846529b3a319f2a375420a8</span><br>
|
||||
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
|
||||
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify against these hashes; point at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
|
||||
</footer>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue