From 70792718955472e288706b4a5f3fc7a722fa7e62 Mon Sep 17 00:00:00 2001 From: szymakula Date: Sun, 25 Jan 2026 00:22:55 +0100 Subject: [PATCH 1/2] refactor: change how reconnect logic is handled --- public/room.js | 161 ++++++++++++++++++++++++++----------------------- 1 file changed, 84 insertions(+), 77 deletions(-) diff --git a/public/room.js b/public/room.js index 60bcb0a..6aed52b 100644 --- a/public/room.js +++ b/public/room.js @@ -4,96 +4,103 @@ const pageID = location.pathname.split('/')[2]; const errorDialog = document.getElementById('error-dialog'); const loader = document.querySelector('.loader'); -let retries = 0; function connectSSE() { - const sse = new EventSource(`/events/room?id=${pageID}`); + const sse = new EventSource(`/events/room?id=${pageID}`); - sse.onmessage = (event) => { - const data = JSON.parse(event.data); + sse.onmessage = (event) => { + const data = JSON.parse(event.data); - switch (data.event) { - case 'room_update': { - viewerCounter.textContent = data.payload.viewer_count; - break; - } - default: { - break; - } - } - }; + switch (data.event) { + case 'room_update': { + viewerCounter.textContent = data.payload.viewer_count; + break; + } + default: { + break; + } + } + }; } + +let timeout = 0; +const BASE_TIMEOUT = 1000; +const MAX_TIMEOUT = 16000; + +const startWhepEventKey = "startwhep" +const reconnectWhepEventKey = "reconnectwhep" + +const target = new EventTarget(); +target.addEventListener("startwhep", (event) => { + startWhep().catch(() => { + target.dispatchEvent(new Event(reconnectWhepEventKey)) + }); +}) +target.addEventListener(reconnectWhepEventKey, (event) => { + if (timeout < MAX_TIMEOUT) { + setTimeout(startWhep().catch(() => { + target.dispatchEvent(new Event(reconnectWhepEventKey)) + }), timeout) + // First reconnect should have no timeout + timeout = Math.max(BASE_TIMEOUT, timeout * 2); + } else { + // Reconnection failed + errorDialog.show() + } +}) + async function startWhep() { - const conn = new RTCPeerConnection(); + const conn = new RTCPeerConnection(); - conn.addTransceiver('audio', { direction: 'recvonly' }); - conn.addTransceiver('video', { direction: 'recvonly' }); - conn.ontrack = ({ streams }) => { - video.srcObject = streams[0]; - }; + conn.addTransceiver('audio', {direction: 'recvonly'}); + conn.addTransceiver('video', {direction: 'recvonly'}); + conn.ontrack = ({streams}) => { + video.srcObject = streams[0]; + }; - conn.addEventListener('connectionstatechange', () => { - retries++; - switch (conn.connectionState) { - case 'closed': { - if (retries < 5) { - startWhep().then(() => { - video.play(); - }); - } else { - console.warn('closed'); - } - break; - } - case 'disconnected': { - if (retries < 5) { - startWhep().then(() => { - video.play(); - }); - } else { - console.warn('disconnected'); + conn.addEventListener('connectionstatechange', () => { + switch (conn.connectionState) { + case 'closed': { + target.dispatchEvent(new Event(reconnectWhepEventKey)) + console.warn('closed'); + break; + } + case 'disconnected': { + target.dispatchEvent(new Event(reconnectWhepEventKey)) + console.warn('disconnected'); + break; + } + case 'failed': { + target.dispatchEvent(new Event(reconnectWhepEventKey)) + console.warn('failed'); + break; + } + case 'connecting': { + loader.classList.toggle('hidden'); + break; + } + case 'connected': { + timeout = 0; + loader.classList.toggle('hidden'); + break; + } } - break; - } - case 'failed': { - if (retries < 5) { - startWhep().then(() => { - video.play(); - }); - } else { - console.warn('failed'); - } - break; - } - case 'connecting': { - loader.classList.toggle('hidden'); - break; - } - case 'connected': { - loader.classList.toggle('hidden'); - retries = 0; - break; - } - } - }); + }); - const sdp_offer = await conn.createOffer(); - await conn.setLocalDescription(sdp_offer); + const sdp_offer = await conn.createOffer(); + await conn.setLocalDescription(sdp_offer); - const sdp_answer = await fetch(`/whep?target_id=${pageID}`, { - method: 'POST', - headers: { - 'content-type': 'application/sdp', - }, - body: sdp_offer.sdp, - }).then((res) => res.text()); + const sdp_answer = await fetch(`/whep?target_id=${pageID}`, { + method: 'POST', + headers: { + 'content-type': 'application/sdp', + }, + body: sdp_offer.sdp, + }).then((res) => res.text()); - await conn.setRemoteDescription({ sdp: sdp_answer, type: 'answer' }); + await conn.setRemoteDescription({sdp: sdp_answer, type: 'answer'}); } connectSSE(); -startWhep().catch((err) => { - console.error('RTC setup error: ', err); - errorDialog.showModal(); -}); +target.dispatchEvent(startWhepEventKey) \ No newline at end of file From 263e8b5f2ccfdf8eb935850b9eec0b9e812ae15f Mon Sep 17 00:00:00 2001 From: szymakula Date: Sun, 25 Jan 2026 00:35:41 +0100 Subject: [PATCH 2/2] refactor: use show modal, fix callback --- public/room.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/room.js b/public/room.js index 6aed52b..1a42f01 100644 --- a/public/room.js +++ b/public/room.js @@ -39,14 +39,14 @@ target.addEventListener("startwhep", (event) => { }) target.addEventListener(reconnectWhepEventKey, (event) => { if (timeout < MAX_TIMEOUT) { - setTimeout(startWhep().catch(() => { + setTimeout(() => startWhep().catch(() => { target.dispatchEvent(new Event(reconnectWhepEventKey)) }), timeout) // First reconnect should have no timeout timeout = Math.max(BASE_TIMEOUT, timeout * 2); } else { // Reconnection failed - errorDialog.show() + errorDialog.showModal() } })