From aff24f3b0a0ac70ac9609b2edd51a0bbe929daa4 Mon Sep 17 00:00:00 2001 From: AmateurAudioDude <168192910+AmateurAudioDude@users.noreply.github.com> Date: Sat, 3 Aug 2024 00:54:17 +1000 Subject: [PATCH 1/9] Reconnect on loss method based on decoded mp3 frames --- web/js/3las/fallback/formats/3las.formatreader.mpeg.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/js/3las/fallback/formats/3las.formatreader.mpeg.js b/web/js/3las/fallback/formats/3las.formatreader.mpeg.js index c64f53f..2774f37 100644 --- a/web/js/3las/fallback/formats/3las.formatreader.mpeg.js +++ b/web/js/3las/fallback/formats/3las.formatreader.mpeg.js @@ -2,6 +2,7 @@ MPEG audio format reader is part of 3LAS (Low Latency Live Audio Streaming) https://github.com/JoJoBond/3LAS */ +window.startTimeConnectionWatchdog = performance.now(); var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || @@ -194,6 +195,7 @@ var AudioFormatReader_MPEG = /** @class */ (function (_super) { }; // Is called if the decoding of the window succeeded AudioFormatReader_MPEG.prototype.OnDecodeSuccess = function (decodedData, id, expectedTotalPlayTime, firstGranulePlayTime, lastGranulePlayTime) { + window.startTimeConnectionWatchdog = performance.now(); var extractSampleCount; var extractSampleOffset; var delta = 0.001; From cfcfd421bdcbdab0475bbf627a935146c435005a Mon Sep 17 00:00:00 2001 From: AmateurAudioDude <168192910+AmateurAudioDude@users.noreply.github.com> Date: Sat, 3 Aug 2024 00:54:31 +1000 Subject: [PATCH 2/9] Reconnect on loss method based on decoded mp3 frames --- web/js/3las/3las.js | 67 ++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 41 deletions(-) diff --git a/web/js/3las/3las.js b/web/js/3las/3las.js index bfc2f6c..23490f5 100644 --- a/web/js/3las/3las.js +++ b/web/js/3las/3las.js @@ -1,3 +1,4 @@ +var elapsedTimeConnectionWatchdog; var _3LAS_Settings = /** @class */ (function () { function _3LAS_Settings() { this.SocketHost = document.location.hostname ? document.location.hostname : "127.0.0.1"; @@ -47,6 +48,29 @@ var _3LAS = /** @class */ (function () { this.ConnectivityFlag = false; this.Stop(); // Attempt to mitigate the 0.5x speed/multiple stream bug + // Stream connection watchdog monitors mp3 frames + console.log("Stream connection watchdog active."); + let intervalReconnectWatchdog = setInterval(() => { + if (Stream) { + var endTimeConnectionWatchdog = performance.now(); + elapsedTimeConnectionWatchdog = endTimeConnectionWatchdog - window.startTimeConnectionWatchdog; + //console.log(`Stream frame elapsed time: ${elapsedTimeConnectionWatchdog} ms`); + if (elapsedTimeConnectionWatchdog > 2000 && shouldReconnect) { + clearInterval(intervalReconnectWatchdog); + setTimeout(() => { + clearInterval(intervalReconnectWatchdog); + console.log("Unstable internet connection detected, reconnecting (" + elapsedTimeConnectionWatchdog + " ms)..."); + this.Stop(); + this.Start(); + }, 2000); + } + } else { + clearInterval(intervalReconnectWatchdog); + this.Stop(); + console.log("Stream connection watchdog inactive."); + } + }, 3000); + // This is stupid, but required for Android.... thanks Google :( if (this.WakeLock) this.WakeLock.Begin(); @@ -54,8 +78,8 @@ var _3LAS = /** @class */ (function () { if (window.location.protocol === 'https:') { this.WebSocket = new WebSocketClient(this.Logger, 'wss://' + this.Settings.SocketHost + ':' + location.port.toString() + window.location.pathname + 'audio' , this.OnSocketError.bind(this), this.OnSocketConnect.bind(this), this.OnSocketDataReady.bind(this), this.OnSocketDisconnect.bind(this)); } - else { - this.WebSocket = new WebSocketClient(this.Logger, 'ws://' + this.Settings.SocketHost + ':' + location.port.toString() + window.location.pathname + 'audio' , this.OnSocketError.bind(this), this.OnSocketConnect.bind(this), this.OnSocketDataReady.bind(this), this.OnSocketDisconnect.bind(this)); + else { + this.WebSocket = new WebSocketClient(this.Logger, 'ws://' + this.Settings.SocketHost + ':' + location.port.toString() + window.location.pathname + 'audio' , this.OnSocketError.bind(this), this.OnSocketConnect.bind(this), this.OnSocketDataReady.bind(this), this.OnSocketDisconnect.bind(this)); } this.Logger.Log("Init of WebSocketClient succeeded"); this.Logger.Log("Trying to connect to server."); @@ -130,45 +154,6 @@ var _3LAS = /** @class */ (function () { if (this.ConnectivityCallback) this.ConnectivityCallback(false); } - - if (shouldReconnect) { - if (!this.ConnectivityFlag) { - console.log("Initial reconnect attempt..."); - this.Stop(); // Attempt to mitigate the 0.5x speed/multiple stream bug - this.Start(); - } - - // Delay launch of subsequent reconnect attempts by 3 seconds - setTimeout(() => { - - let streamReconnecting = false; - - let intervalReconnect = setInterval(() => { - if (this.ConnectivityFlag || typeof Stream === 'undefined' || Stream === null) { - console.log("Reconnect attempts aborted."); - clearInterval(intervalReconnect); - } else if (!streamReconnecting) { - streamReconnecting = true; - console.log("Attempting to restart stream..."); - this.Stop(); // Attempt to mitigate the 0.5x speed/multiple stream bug - this.Start(); - // Wait for reconnect attempt - setTimeout(() => { - streamReconnecting = false; - }, 3000); - } - // Restore user set volume - if (Stream && typeof newVolumeGlobal !== 'undefined' && newVolumeGlobal !== null) { - Stream.Volume = newVolumeGlobal; - console.log(`User volume restored: ${Math.round(newVolumeGlobal * 100)}%`); - } - }, 3000); - - }, 3000); - - } else { - this.Logger.Log("Reconnection is disabled."); - } }; _3LAS.prototype.OnSocketDataReady = function (data) { From 72a1b77aaf828ffac222cab39cad314842964ad1 Mon Sep 17 00:00:00 2001 From: AmateurAudioDude <168192910+AmateurAudioDude@users.noreply.github.com> Date: Sat, 3 Aug 2024 00:55:22 +1000 Subject: [PATCH 3/9] On touchscreen devices, tooltip now disappears on scroll or after 10 seconds --- web/js/main.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/web/js/main.js b/web/js/main.js index 88fd336..ddbea3f 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -908,6 +908,11 @@ function initTooltips() { posX -= tooltipWidth / 2; posY -= tooltipHeight + 10; tooltip.css({ top: posY, left: posX, opacity: 1 }); // Set opacity to 1 + // For touchscreen devices + if ((/Mobi|Android|iPhone|iPad|iPod|Opera Mini/i.test(navigator.userAgent)) && ('ontouchstart' in window || navigator.maxTouchPoints)) { + setTimeout(() => { $('.tooltiptext').remove(); }, 10000); + document.addEventListener('touchstart', function() { setTimeout(() => { $('.tooltiptext').remove(); }, 500); }); + } }, 500)); }, function() { // Clear the timeout if the mouse leaves before the delay completes From f687a46916b78ba14e0ef252b49215fcd3b7bb9f Mon Sep 17 00:00:00 2001 From: AmateurAudioDude <168192910+AmateurAudioDude@users.noreply.github.com> Date: Sat, 3 Aug 2024 00:55:56 +1000 Subject: [PATCH 4/9] Serial connection fix for non-ESP32 devices --- server/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/index.js b/server/index.js index cc44fdb..5f90c26 100644 --- a/server/index.js +++ b/server/index.js @@ -84,7 +84,9 @@ if (serverConfig.xdrd.wirelessConnection === false) { } logInfo('Using COM device: ' + serverConfig.xdrd.comPort); - serialport.write('x\n'); + setTimeout(() => { + serialport.write('x\n'); + }, 3000); setTimeout(() => { serialport.write('Q0\n'); @@ -107,7 +109,7 @@ if (serverConfig.xdrd.wirelessConnection === false) { serverConfig.audio.startupVolume ? serialport.write('Y' + (serverConfig.audio.startupVolume * 100).toFixed(0) + '\n') : serialport.write('Y100\n'); - }, 3000); + }, 6000); serialport.on('data', (data) => { helpers.resolveDataBuffer(data, wss, rdsWss); From 8676b866dc891daf6bd287cbf536a6361455741e Mon Sep 17 00:00:00 2001 From: AmateurAudioDude <168192910+AmateurAudioDude@users.noreply.github.com> Date: Tue, 13 Aug 2024 02:21:32 +1000 Subject: [PATCH 5/9] UI autoreconnect and resume on lost connection --- web/js/main.js | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/web/js/main.js b/web/js/main.js index ddbea3f..bcac2ce 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -219,8 +219,47 @@ function sendPingRequest() { .catch(error => { console.error('Error fetching ping:', error); }); + + // Automatic reconnection on WebSocket close + if (socket.readyState === WebSocket.CLOSED || socket.readyState === WebSocket.CLOSING) { + console.log("Main/UI attempting to reconnect...", socketAddress); + socket = new WebSocket(socketAddress); + + socket.onopen = () => { + console.log("Main/UI reconnected successfully."); + }; + socket.onmessage = (event) => { + handleWebSocketMessage(event); + }; + socket.onerror = (error) => { + console.error("Main/UI WebSocket error during reconnection:", error); + }; + socket.onclose = () => { + console.warn("Main/UI WebSocket closed during reconnection. Will attempt to reconnect..."); + }; + } } +// Automatic UI resume on WebSocket reconnect +function handleWebSocketMessage(event) { + if (event.data == 'KICK') { + console.log('Kick initiated.') + setTimeout(() => { + window.location.href = '/403'; + }, 500); + return; + } + + parsedData = JSON.parse(event.data); + + updatePanels(parsedData); + const sum = signalData.reduce((acc, strNum) => acc + parseFloat(strNum), 0); + const averageSignal = sum / signalData.length; + data.push(averageSignal); +} +// Attach the message handler +socket.onmessage = handleWebSocketMessage; + function initCanvas(parsedData) { signalToggle = $("#signal-units-toggle"); @@ -370,7 +409,7 @@ function updateCanvas(parsedData, signalChart) { socket.onmessage = (event) => { if (event.data == 'KICK') { - console.log('Kick iniitiated.') + console.log('Kick initiated.') setTimeout(() => { window.location.href = '/403'; }, 500); From 6f4b23265a850d5f3df271f342b6807fd37bb187 Mon Sep 17 00:00:00 2001 From: AmateurAudioDude <168192910+AmateurAudioDude@users.noreply.github.com> Date: Tue, 13 Aug 2024 02:54:32 +1000 Subject: [PATCH 6/9] UI autoreconnect and resume on lost connection --- web/js/main.js | 1 - 1 file changed, 1 deletion(-) diff --git a/web/js/main.js b/web/js/main.js index bcac2ce..1d5f649 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -222,7 +222,6 @@ function sendPingRequest() { // Automatic reconnection on WebSocket close if (socket.readyState === WebSocket.CLOSED || socket.readyState === WebSocket.CLOSING) { - console.log("Main/UI attempting to reconnect...", socketAddress); socket = new WebSocket(socketAddress); socket.onopen = () => { From d24d51346695dc55cce4c8891e453df68110861b Mon Sep 17 00:00:00 2001 From: AmateurAudioDude <168192910+AmateurAudioDude@users.noreply.github.com> Date: Tue, 13 Aug 2024 03:37:17 +1000 Subject: [PATCH 7/9] Serial port autoreconnect on close --- server/index.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/server/index.js b/server/index.js index 5f90c26..55b5235 100644 --- a/server/index.js +++ b/server/index.js @@ -80,6 +80,9 @@ if (serverConfig.xdrd.wirelessConnection === false) { serialport.open((err) => { if (err) { logError('Error opening port: ' + err.message); + setTimeout(() => { + connectToSerial(); + }, 5000); return; } @@ -120,6 +123,13 @@ if (serverConfig.xdrd.wirelessConnection === false) { }); }); + // Handle port closure + serialport.on('close', () => { + logWarn('Disconnected from ' + serverConfig.xdrd.comPort + '. Attempting to reconnect.'); + setTimeout(() => { + connectToSerial(); + }, 5000); + }); return serialport; } } From 1b2bfefc4f04e050deb92b019610179d0ea6d7d6 Mon Sep 17 00:00:00 2001 From: AmateurAudioDude <168192910+AmateurAudioDude@users.noreply.github.com> Date: Wed, 28 Aug 2024 00:16:48 +1000 Subject: [PATCH 8/9] Fixes for iPhone zoom on tap, and numeric keyboard --- web/js/main.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/web/js/main.js b/web/js/main.js index 1d5f649..122ca05 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -63,9 +63,24 @@ $(document).ready(function () { } }); + // Check if device is an iPhone to prevent zoom on button press + if (/iPhone|iPod|iPad/.test(navigator.userAgent) && !window.MSStream) { + const buttons = document.querySelectorAll('button'); + buttons.forEach(button => { + button.addEventListener('touchstart', function(e) { + // Prevent default zoom behavior + e.preventDefault(); + // Allow default button action after short delay + setTimeout(() => { + e.target.click(); + }, 0); + }); + }); + } + const textInput = $('#commandinput'); - textInput.on('change', function (event) { + textInput.on('change blur', function (event) { const inputValue = Number(textInput.val()); // Check if the user agent contains 'iPhone' if (/iPhone/i.test(navigator.userAgent)) { From 8b8a0f863246c63d8d91051cb4475f184d45e2a5 Mon Sep 17 00:00:00 2001 From: AmateurAudioDude <168192910+AmateurAudioDude@users.noreply.github.com> Date: Wed, 28 Aug 2024 00:17:07 +1000 Subject: [PATCH 9/9] Fix for iPhone in portrait mode --- web/css/breadcrumbs.css | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/web/css/breadcrumbs.css b/web/css/breadcrumbs.css index 8045d52..d3e1c65 100644 --- a/web/css/breadcrumbs.css +++ b/web/css/breadcrumbs.css @@ -244,14 +244,20 @@ label { display: none; } - .overlay { + #flags-container-phone, + #flags-container-desktop { + position: relative; /* Confine overlay within container which is necessary for iPhones */ + } + + #flags-container-phone .overlay, + #flags-container-desktop .overlay { position: absolute; top: 0; - bottom: 0; left: 0; right: 0; - pointer-events: auto; /* Ensure that the overlay captures clicks */ - opacity: 0; /* Make the overlay invisible */ + bottom: 0; + pointer-events: auto; + opacity: 0; } .admin-quick-dashboard {