You've already forked fm-dx-webserver
mirror of
https://github.com/KubaPro010/fm-dx-webserver.git
synced 2026-02-26 22:13:53 +01:00
lotta bugfixes
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "fm-dx-webserver",
|
"name": "fm-dx-webserver",
|
||||||
"version": "1.3.6",
|
"version": "1.3.6.1",
|
||||||
"description": "FM DX Webserver",
|
"description": "FM DX Webserver",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -206,7 +206,6 @@ const updateInterval = 75;
|
|||||||
var dataToSend = {
|
var dataToSend = {
|
||||||
pi: '?',
|
pi: '?',
|
||||||
freq: 87.500.toFixed(3),
|
freq: 87.500.toFixed(3),
|
||||||
prevFreq: 87.500.toFixed(3),
|
|
||||||
sig: 0,
|
sig: 0,
|
||||||
sigRaw: '',
|
sigRaw: '',
|
||||||
sigTop: -Infinity,
|
sigTop: -Infinity,
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ let serverConfig = {
|
|||||||
audioDevice: "Microphone (High Definition Audio Device)",
|
audioDevice: "Microphone (High Definition Audio Device)",
|
||||||
audioChannels: 2,
|
audioChannels: 2,
|
||||||
audioBitrate: "128k",
|
audioBitrate: "128k",
|
||||||
|
audioBoost: false,
|
||||||
softwareMode: false,
|
softwareMode: false,
|
||||||
startupVolume: "0.95"
|
startupVolume: "0.95"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ function enableAudioStream() {
|
|||||||
|
|
||||||
const flags = `-fflags +nobuffer+flush_packets -flags low_delay -rtbufsize 6192 -probesize 32`;
|
const flags = `-fflags +nobuffer+flush_packets -flags low_delay -rtbufsize 6192 -probesize 32`;
|
||||||
const codec = `-acodec pcm_s16le -ar 48000 -ac ${serverConfig.audio.audioChannels}`;
|
const codec = `-acodec pcm_s16le -ar 48000 -ac ${serverConfig.audio.audioChannels}`;
|
||||||
const output = `-f s16le -fflags +nobuffer+flush_packets -packetsize 384 -flush_packets 1 -bufsize 960`;
|
const output = `${serverConfig.audio.audioBoost == true ? '$-af "volume=3.5"' : ''} -f s16le -fflags +nobuffer+flush_packets -packetsize 384 -flush_packets 1 -bufsize 960`;
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
// Windows
|
// Windows
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ let lastFetchTime = 0;
|
|||||||
const fetchInterval = 1000;
|
const fetchInterval = 1000;
|
||||||
const esSwitchCache = {"lastCheck":0, "esSwitch":false};
|
const esSwitchCache = {"lastCheck":0, "esSwitch":false};
|
||||||
const esFetchInterval = 300000;
|
const esFetchInterval = 300000;
|
||||||
|
var currentPiCode = '';
|
||||||
|
var currentRdsPs = '';
|
||||||
const usStatesGeoJsonUrl = "https://raw.githubusercontent.com/PublicaMundi/MappingAPI/master/data/geojson/us-states.json";
|
const usStatesGeoJsonUrl = "https://raw.githubusercontent.com/PublicaMundi/MappingAPI/master/data/geojson/us-states.json";
|
||||||
let usStatesGeoJson = null; // To cache the GeoJSON data for US states
|
let usStatesGeoJson = null; // To cache the GeoJSON data for US states
|
||||||
|
|
||||||
@@ -73,7 +75,11 @@ async function fetchTx(freq, piCode, rdsPs) {
|
|||||||
if (isNaN(freq)) {
|
if (isNaN(freq)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (now - lastFetchTime < fetchInterval || serverConfig.identification.lat.length < 2 || freq < 87) {
|
if (now - lastFetchTime < fetchInterval
|
||||||
|
|| serverConfig.identification.lat.length < 2
|
||||||
|
|| freq < 87
|
||||||
|
|| (currentPiCode == piCode && currentRdsPs == rdsPs))
|
||||||
|
{
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +96,7 @@ async function fetchTx(freq, piCode, rdsPs) {
|
|||||||
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
|
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
cachedData[freq] = data;
|
cachedData[freq] = data;
|
||||||
await loadUsStatesGeoJson();
|
if(serverConfig.webserver.rdsMode == true) await loadUsStatesGeoJson();
|
||||||
return processData(data, piCode, rdsPs);
|
return processData(data, piCode, rdsPs);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching data:", error);
|
console.error("Error fetching data:", error);
|
||||||
@@ -106,6 +112,8 @@ async function processData(data, piCode, rdsPs) {
|
|||||||
let maxDistance;
|
let maxDistance;
|
||||||
let esMode = checkEs();
|
let esMode = checkEs();
|
||||||
let detectedByPireg = false;
|
let detectedByPireg = false;
|
||||||
|
currentPiCode = piCode;
|
||||||
|
currentRdsPs = rdsPs;
|
||||||
|
|
||||||
function evaluateStation(station, city, distance) {
|
function evaluateStation(station, city, distance) {
|
||||||
let weightDistance = distance.distanceKm;
|
let weightDistance = distance.distanceKm;
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ table .input-text {
|
|||||||
.hide-phone {
|
.hide-phone {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.m-0 {
|
.m-0, .center-phone {
|
||||||
margin: auto !important;
|
margin: auto !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,6 +105,10 @@ a:hover {
|
|||||||
border-bottom: 1px solid var(--color-4);
|
border-bottom: 1px solid var(--color-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.hide-underline:hover {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
color: var(--color-4);
|
color: var(--color-4);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -345,7 +345,8 @@
|
|||||||
<hr class="hide-desktop">
|
<hr class="hide-desktop">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel-33 hover-brighten tooltip" data-tooltip="This panel contains the current TX info when RDS is loaded.<br><strong>Clicking on this panel copies the info into the clipboard.</strong>">
|
<div class="panel-33 hover-brighten tooltip" style="min-height: 91px;"
|
||||||
|
data-tooltip="This panel contains the current TX info when RDS is loaded.<br><strong>Clicking on this panel copies the info into the clipboard.</strong>">
|
||||||
<div id="data-station-container">
|
<div id="data-station-container">
|
||||||
<h2 style="margin-top: 0;" class="mb-0">
|
<h2 style="margin-top: 0;" class="mb-0">
|
||||||
<span id="data-station-name"></span>
|
<span id="data-station-name"></span>
|
||||||
@@ -362,7 +363,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel-10 no-bg" style="margin-left: 0; margin-top: 0; margin-right: 0;display:flex;">
|
<div class="panel-10 no-bg center-phone" style="margin-left: 0; margin-top: 0; margin-right: 0;display:flex;">
|
||||||
<div class="panel-100" style="margin-left: 0;">
|
<div class="panel-100" style="margin-left: 0;">
|
||||||
<h2 class="bottom-10">AF</h2>
|
<h2 class="bottom-10">AF</h2>
|
||||||
<div id="af-list" style="text-align: center;">
|
<div id="af-list" style="text-align: center;">
|
||||||
|
|||||||
@@ -34,14 +34,20 @@ function OnConnectivityCallback(isConnected) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isIOS() {
|
||||||
|
return /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
||||||
|
}
|
||||||
|
|
||||||
function OnPlayButtonClick(_ev) {
|
function OnPlayButtonClick(_ev) {
|
||||||
const $playbutton = $('.playbutton');
|
const $playbutton = $('.playbutton');
|
||||||
|
const isiOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
||||||
|
|
||||||
if (Stream) {
|
if (Stream) {
|
||||||
console.log("Stopping stream...");
|
console.log("Stopping stream...");
|
||||||
shouldReconnect = false;
|
shouldReconnect = false;
|
||||||
destroyStream();
|
destroyStream();
|
||||||
$playbutton.find('.fa-solid').toggleClass('fa-stop fa-play');
|
$playbutton.find('.fa-solid').toggleClass('fa-stop fa-play');
|
||||||
if ('audioSession' in navigator) {
|
if (isiOS && 'audioSession' in navigator) {
|
||||||
navigator.audioSession.type = "none";
|
navigator.audioSession.type = "none";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -50,10 +56,11 @@ function OnPlayButtonClick(_ev) {
|
|||||||
createStream();
|
createStream();
|
||||||
Stream.Start();
|
Stream.Start();
|
||||||
$playbutton.find('.fa-solid').toggleClass('fa-play fa-stop');
|
$playbutton.find('.fa-solid').toggleClass('fa-play fa-stop');
|
||||||
if ('audioSession' in navigator) {
|
if (isiOS && 'audioSession' in navigator) {
|
||||||
navigator.audioSession.type = "playback"; // Android background play fix
|
navigator.audioSession.type = "playback";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$playbutton.addClass('bg-gray').prop('disabled', true);
|
$playbutton.addClass('bg-gray').prop('disabled', true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
$playbutton.removeClass('bg-gray').prop('disabled', false);
|
$playbutton.removeClass('bg-gray').prop('disabled', false);
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ function tuneDown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function tuneTo(freq) {
|
function tuneTo(freq) {
|
||||||
|
previousFreq = getCurrentFreq();
|
||||||
socket.send("T" + ((freq).toFixed(1) * 1000));
|
socket.send("T" + ((freq).toFixed(1) * 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ function populateFields(data, prefix = "") {
|
|||||||
$element.val(value);
|
$element.val(value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateIconState();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateConfigData(data, prefix = "") {
|
function updateConfigData(data, prefix = "") {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
var currentDate = new Date('Feb 23, 2025 15:30:00');
|
var currentDate = new Date('Apr 19, 2025 21:30:00');
|
||||||
var day = currentDate.getDate();
|
var day = currentDate.getDate();
|
||||||
var month = currentDate.getMonth() + 1; // Months are zero-indexed, so add 1
|
var month = currentDate.getMonth() + 1; // Months are zero-indexed, so add 1
|
||||||
var year = currentDate.getFullYear();
|
var year = currentDate.getFullYear();
|
||||||
var formattedDate = day + '/' + month + '/' + year;
|
var formattedDate = day + '/' + month + '/' + year;
|
||||||
var currentVersion = 'v1.3.6 [' + formattedDate + ']';
|
var currentVersion = 'v1.3.6.1 [' + formattedDate + ']';
|
||||||
|
|
||||||
getInitialSettings();
|
getInitialSettings();
|
||||||
removeUrlParameters();
|
removeUrlParameters();
|
||||||
|
|||||||
@@ -722,7 +722,6 @@ function checkKey(e) {
|
|||||||
// Handle default case if needed
|
// Handle default case if needed
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
previousFreq = currentFreq;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1190,13 +1189,25 @@ function initTooltips(target = null) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function fillPresets() {
|
function fillPresets() {
|
||||||
|
let hasAnyPreset = false;
|
||||||
|
|
||||||
for (let i = 1; i <= 4; i++) {
|
for (let i = 1; i <= 4; i++) {
|
||||||
let presetText = localStorage.getItem(`preset${i}`);
|
let presetText = localStorage.getItem(`preset${i}`);
|
||||||
$(`#preset${i}-text`).text(presetText);
|
|
||||||
$(`#preset${i}`).click(function() {
|
if (presetText != "null") {
|
||||||
tuneTo(Number(presetText));
|
hasAnyPreset = true;
|
||||||
});
|
$(`#preset${i}-text`).text(presetText);
|
||||||
|
$(`#preset${i}`).click(function() {
|
||||||
|
tuneTo(Number(presetText));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$(`#preset${i}`).hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (!hasAnyPreset) {
|
||||||
|
$('#preset1').parent().hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -166,12 +166,10 @@ $(document).ready(() => {
|
|||||||
$('.version-string').text(currentVersion);
|
$('.version-string').text(currentVersion);
|
||||||
|
|
||||||
setBg();
|
setBg();
|
||||||
|
|
||||||
updateIconState();
|
|
||||||
|
|
||||||
// Update icons when the checkbox state changes
|
// Update icons when the checkbox state changes
|
||||||
$('input[type="checkbox"]').change(function() {
|
$('input[type="checkbox"]').change(function() {
|
||||||
updateIconState();
|
updateIconState(this);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -180,15 +178,27 @@ function getQueryParameter(name) {
|
|||||||
return urlParams.get(name);
|
return urlParams.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateIconState() {
|
function updateIconState(el) {
|
||||||
$('input[type="checkbox"]').each(function() {
|
// If an element is passed, update only that one
|
||||||
var icon = $(this).siblings('label').find('i');
|
if (el) {
|
||||||
if ($(this).is(':checked')) {
|
var $checkbox = $(el);
|
||||||
|
var icon = $checkbox.siblings('label').find('i');
|
||||||
|
if ($checkbox.is(':checked')) {
|
||||||
icon.removeClass('fa-toggle-off').addClass('fa-toggle-on');
|
icon.removeClass('fa-toggle-off').addClass('fa-toggle-on');
|
||||||
} else {
|
} else {
|
||||||
icon.removeClass('fa-toggle-on').addClass('fa-toggle-off');
|
icon.removeClass('fa-toggle-on').addClass('fa-toggle-off');
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
|
// Otherwise, update all checkboxes
|
||||||
|
$('input[type="checkbox"]').each(function() {
|
||||||
|
var icon = $(this).siblings('label').find('i');
|
||||||
|
if ($(this).is(':checked')) {
|
||||||
|
icon.removeClass('fa-toggle-off').addClass('fa-toggle-on');
|
||||||
|
} else {
|
||||||
|
icon.removeClass('fa-toggle-on').addClass('fa-toggle-off');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTheme(themeName) {
|
function setTheme(themeName) {
|
||||||
|
|||||||
@@ -85,8 +85,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-container">
|
<div class="panel-100-real p-bottom-20" style="overflow-x: auto;">
|
||||||
<div class="panel-50 p-bottom-20" style="overflow-x: auto;">
|
|
||||||
<h3>Current users</h3>
|
<h3>Current users</h3>
|
||||||
<table class="table-big">
|
<table class="table-big">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -120,7 +119,7 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel-50 p-bottom-20">
|
<div class="panel-100-real p-bottom-20">
|
||||||
<h3>Quick settings</h3>
|
<h3>Quick settings</h3>
|
||||||
<div class="flex-container flex-center" style="margin: 30px;">
|
<div class="flex-container flex-center" style="margin: 30px;">
|
||||||
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Public Tuner', id: 'publicTuner'}) %>
|
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Public Tuner', id: 'publicTuner'}) %>
|
||||||
@@ -130,7 +129,6 @@
|
|||||||
<%- include('_components', {component: 'text', cssClass: 'w-150 br-15', placeholder: '', label: 'Tune password', id: 'password-tunePass', password: true}) %>
|
<%- include('_components', {component: 'text', cssClass: 'w-150 br-15', placeholder: '', label: 'Tune password', id: 'password-tunePass', password: true}) %>
|
||||||
<%- include('_components', {component: 'text', cssClass: 'w-150 br-15', placeholder: '', label: 'Admin password', id: 'password-adminPass', password: true}) %><br>
|
<%- include('_components', {component: 'text', cssClass: 'w-150 br-15', placeholder: '', label: 'Admin password', id: 'password-adminPass', password: true}) %><br>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex-container">
|
<div class="flex-container">
|
||||||
<div class="panel-100-real p-bottom-20 bottom-20">
|
<div class="panel-100-real p-bottom-20 bottom-20">
|
||||||
@@ -208,8 +206,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-container">
|
<div class="flex-container">
|
||||||
<div class="panel-100-real p-bottom-20">
|
<div class="panel-50 p-bottom-20">
|
||||||
<h3>Miscellaneous</h3>
|
<h3>Volume</h3>
|
||||||
|
<p>This option will boost the audio volume globally, recommended for the Headless TEF.</p>
|
||||||
|
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Audio Boost', id: 'audio-audioBoost'}) %>
|
||||||
|
</div>
|
||||||
|
<div class="panel-50 p-bottom-20">
|
||||||
|
<h3>Experimental</h3>
|
||||||
<p>If you use an USB audio card on Linux, enabling this option might fix your audio issues.</p>
|
<p>If you use an USB audio card on Linux, enabling this option might fix your audio issues.</p>
|
||||||
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'ALSA Software mode', id: 'audio-softwareMode'}) %>
|
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'ALSA Software mode', id: 'audio-softwareMode'}) %>
|
||||||
</div>
|
</div>
|
||||||
@@ -524,7 +527,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="panel-100 p-bottom-20">
|
<div class="panel-100 p-bottom-20">
|
||||||
<h3>Tunnel</h3>
|
<h3>Tunnel</h3>
|
||||||
<p>When you become a <a href="https://buymeacoffee.com/fmdx" target="_blank"><strong>FMDX.org supporter</strong></a>, you can host your webserver without the need of a public IP address.<br>
|
<p>When you become an <a href="https://buymeacoffee.com/fmdx" target="_blank"><strong>FMDX.org supporter</strong></a>, you can host your webserver without the need of a public IP address & port forwarding.<br>
|
||||||
When you become a supporter, you can message the Founders on Discord for your login details.</p>
|
When you become a supporter, you can message the Founders on Discord for your login details.</p>
|
||||||
|
|
||||||
<%- include('_components', {component: 'checkbox', cssClass: 'm-right-10', label: 'Enable tunnel', id: 'tunnel-enabled'}) %><br>
|
<%- include('_components', {component: 'checkbox', cssClass: 'm-right-10', label: 'Enable tunnel', id: 'tunnel-enabled'}) %><br>
|
||||||
|
|||||||
Reference in New Issue
Block a user