mirror of
https://github.com/KubaPro010/fm-dx-webserver.git
synced 2026-07-30 16:59:15 +02:00
ui changes, playback bugfixes
This commit is contained in:
+5
-5
@@ -25,7 +25,7 @@ const logDebug = (...messages) => {
|
|||||||
const logMessage = `${getCurrentTime()} ${MESSAGE_PREFIX.DEBUG} ${messages.join(' ')}`;
|
const logMessage = `${getCurrentTime()} ${MESSAGE_PREFIX.DEBUG} ${messages.join(' ')}`;
|
||||||
logs.push(logMessage);
|
logs.push(logMessage);
|
||||||
if (logs.length > maxLogLines) {
|
if (logs.length > maxLogLines) {
|
||||||
logs.shift(); // Remove the oldest log if the array exceeds the maximum number of lines
|
logs.shift();
|
||||||
}
|
}
|
||||||
console.log(logMessage);
|
console.log(logMessage);
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ const logError = (...messages) => {
|
|||||||
const logMessage = `${getCurrentTime()} ${MESSAGE_PREFIX.ERROR} ${messages.join(' ')}`;
|
const logMessage = `${getCurrentTime()} ${MESSAGE_PREFIX.ERROR} ${messages.join(' ')}`;
|
||||||
logs.push(logMessage);
|
logs.push(logMessage);
|
||||||
if (logs.length > maxLogLines) {
|
if (logs.length > maxLogLines) {
|
||||||
logs.shift(); // Remove the oldest log if the array exceeds the maximum number of lines
|
logs.shift();
|
||||||
}
|
}
|
||||||
console.log(logMessage);
|
console.log(logMessage);
|
||||||
};
|
};
|
||||||
@@ -45,7 +45,7 @@ const logFfmpeg = (...messages) => {
|
|||||||
const logMessage = `${getCurrentTime()} ${MESSAGE_PREFIX.FFMPEG} ${messages.join(' ')}`;
|
const logMessage = `${getCurrentTime()} ${MESSAGE_PREFIX.FFMPEG} ${messages.join(' ')}`;
|
||||||
logs.push(logMessage);
|
logs.push(logMessage);
|
||||||
if (logs.length > maxLogLines) {
|
if (logs.length > maxLogLines) {
|
||||||
logs.shift(); // Remove the oldest log if the array exceeds the maximum number of lines
|
logs.shift();
|
||||||
}
|
}
|
||||||
console.log(logMessage);
|
console.log(logMessage);
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ const logInfo = (...messages) => {
|
|||||||
const logMessage = `${getCurrentTime()} ${MESSAGE_PREFIX.INFO} ${messages.join(' ')}`;
|
const logMessage = `${getCurrentTime()} ${MESSAGE_PREFIX.INFO} ${messages.join(' ')}`;
|
||||||
logs.push(logMessage);
|
logs.push(logMessage);
|
||||||
if (logs.length > maxLogLines) {
|
if (logs.length > maxLogLines) {
|
||||||
logs.shift(); // Remove the oldest log if the array exceeds the maximum number of lines
|
logs.shift();
|
||||||
}
|
}
|
||||||
console.log(logMessage);
|
console.log(logMessage);
|
||||||
};
|
};
|
||||||
@@ -64,7 +64,7 @@ const logWarn = (...messages) => {
|
|||||||
const logMessage = `${getCurrentTime()} ${MESSAGE_PREFIX.WARN} ${messages.join(' ')}`;
|
const logMessage = `${getCurrentTime()} ${MESSAGE_PREFIX.WARN} ${messages.join(' ')}`;
|
||||||
logs.push(logMessage);
|
logs.push(logMessage);
|
||||||
if (logs.length > maxLogLines) {
|
if (logs.length > maxLogLines) {
|
||||||
logs.shift(); // Remove the oldest log if the array exceeds the maximum number of lines
|
logs.shift();
|
||||||
}
|
}
|
||||||
console.log(logMessage);
|
console.log(logMessage);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -121,7 +121,6 @@ function connectToXdrd() {
|
|||||||
const lines = receivedData.split('\n');
|
const lines = receivedData.split('\n');
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
|
|
||||||
if (!authFlags.receivedPassword) {
|
if (!authFlags.receivedPassword) {
|
||||||
authFlags.receivedSalt = line.trim();
|
authFlags.receivedSalt = line.trim();
|
||||||
authenticateWithXdrd(client, authFlags.receivedSalt, serverConfig.xdrd.xdrdPassword);
|
authenticateWithXdrd(client, authFlags.receivedSalt, serverConfig.xdrd.xdrdPassword);
|
||||||
@@ -143,7 +142,7 @@ function connectToXdrd() {
|
|||||||
if (authFlags.authMsg && authFlags.firstClient) {
|
if (authFlags.authMsg && authFlags.firstClient) {
|
||||||
client.write('T87500\n');
|
client.write('T87500\n');
|
||||||
client.write('A0\n');
|
client.write('A0\n');
|
||||||
client.write('G11\n');
|
client.write('G00\n');
|
||||||
client.off('data', authDataHandler);
|
client.off('data', authDataHandler);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -226,6 +225,17 @@ app.get('/static_data', (req, res) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get('/server_time', (req, res) => {
|
||||||
|
const serverTime = new Date().toISOString();
|
||||||
|
res.json({
|
||||||
|
serverTime
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/ping', (req, res) => {
|
||||||
|
res.send('pong');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AUTHENTICATION BLOCK
|
* AUTHENTICATION BLOCK
|
||||||
@@ -342,7 +352,6 @@ app.get('/getData', (req, res) => {
|
|||||||
app.get('/getDevices', (req, res) => {
|
app.get('/getDevices', (req, res) => {
|
||||||
if (req.session.isAdminAuthenticated || !fs.existsSync('config.json')) {
|
if (req.session.isAdminAuthenticated || !fs.existsSync('config.json')) {
|
||||||
parseAudioDevice((result) => {
|
parseAudioDevice((result) => {
|
||||||
console.log(result);
|
|
||||||
res.json(result);
|
res.json(result);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+1
-2
@@ -97,8 +97,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.modal-panel-content .version-info {
|
.modal-panel-content .version-info {
|
||||||
position: absolute;
|
margin-top: 75px;
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-1
@@ -251,9 +251,15 @@
|
|||||||
<% } %>
|
<% } %>
|
||||||
<div id="login-message"></div>
|
<div id="login-message"></div>
|
||||||
|
|
||||||
|
<h2>Time:</h2>
|
||||||
|
<p class="m-0">Server: <span class="color-3" id="server-time"></span></p>
|
||||||
|
<p class="m-0">Local: <span class="color-3" id="client-time"></span></p>
|
||||||
|
|
||||||
|
<p class="text-small" id="current-ping"></p>
|
||||||
|
|
||||||
<div class="version-info">
|
<div class="version-info">
|
||||||
<p class="text-small">FM-DX WebServer <br>by <a href="https://noobish.eu" target="_blank">Noobish</a> & the OpenRadio community.</p>
|
<p class="text-small">FM-DX WebServer <br>by <a href="https://noobish.eu" target="_blank">Noobish</a> & the OpenRadio community.</p>
|
||||||
<span style="color: var(--color-3);">v1.0.4 [8/2/2024]</span>
|
<span style="color: var(--color-3);">v1.0.5 [10/2/2024]</span>
|
||||||
<p class="text-small bottom-50">
|
<p class="text-small bottom-50">
|
||||||
<span class="text-smaller">librds & maps.fmdx.pl by <a href="https://fmdx.pl" target="_blank">Konrad Kosmatka</a></span><br>
|
<span class="text-smaller">librds & maps.fmdx.pl by <a href="https://fmdx.pl" target="_blank">Konrad Kosmatka</a></span><br>
|
||||||
<span class="text-smaller">3LAS by <a href="https://github.com/JoJoBond/3LAS" target="_blank">JoJoBond</a></span><br>
|
<span class="text-smaller">3LAS by <a href="https://github.com/JoJoBond/3LAS" target="_blank">JoJoBond</a></span><br>
|
||||||
|
|||||||
@@ -74,13 +74,17 @@ var AudioFormatReader_MPEG = /** @class */ (function (_super) {
|
|||||||
// the full frame length by adding zeros.
|
// the full frame length by adding zeros.
|
||||||
var bufferLength = 0;
|
var bufferLength = 0;
|
||||||
var expectedTotalPlayTime_1 = 0;
|
var expectedTotalPlayTime_1 = 0;
|
||||||
expectedTotalPlayTime_1 += this.Frames[0].SampleCount / this.Frames[0].SampleRate / 2.0; // Only half of data is usable due to overlap
|
var firstGranulePlayTime_1 = 0;
|
||||||
|
var lastGranulePlayTime_1 = 0;
|
||||||
|
firstGranulePlayTime_1 = this.Frames[0].SampleCount / this.Frames[0].SampleRate / 2.0; // Only half of data is usable due to overlap
|
||||||
|
expectedTotalPlayTime_1 += firstGranulePlayTime_1;
|
||||||
bufferLength += this.Frames[0].Data.length;
|
bufferLength += this.Frames[0].Data.length;
|
||||||
for (var i = 1; i < this.Frames.length - 1; i++) {
|
for (var i = 1; i < this.Frames.length - 1; i++) {
|
||||||
expectedTotalPlayTime_1 += this.Frames[i].SampleCount / this.Frames[i].SampleRate;
|
expectedTotalPlayTime_1 += this.Frames[i].SampleCount / this.Frames[i].SampleRate;
|
||||||
bufferLength += this.Frames[i].Data.length;
|
bufferLength += this.Frames[i].Data.length;
|
||||||
}
|
}
|
||||||
expectedTotalPlayTime_1 += this.Frames[this.Frames.length - 1].SampleCount / this.Frames[this.Frames.length - 1].SampleRate / 2.0; // Only half of data is usable due to overlap
|
lastGranulePlayTime_1 = this.Frames[this.Frames.length - 1].SampleCount / this.Frames[this.Frames.length - 1].SampleRate / 2.0; // Only half of data is usable due to overlap
|
||||||
|
expectedTotalPlayTime_1 += lastGranulePlayTime_1;
|
||||||
bufferLength += this.Frames[this.Frames.length - 1].Data.length;
|
bufferLength += this.Frames[this.Frames.length - 1].Data.length;
|
||||||
// If needed, add some space for the ID3v2 tag
|
// If needed, add some space for the ID3v2 tag
|
||||||
if (this.AddId3Tag) {
|
if (this.AddId3Tag) {
|
||||||
@@ -110,7 +114,9 @@ var AudioFormatReader_MPEG = /** @class */ (function (_super) {
|
|||||||
this.Audio.decodeAudioData(decodeBuffer.buffer, (function (decodedData) {
|
this.Audio.decodeAudioData(decodeBuffer.buffer, (function (decodedData) {
|
||||||
var _id = id_1;
|
var _id = id_1;
|
||||||
var _expectedTotalPlayTime = expectedTotalPlayTime_1;
|
var _expectedTotalPlayTime = expectedTotalPlayTime_1;
|
||||||
this._OnDecodeSuccess(decodedData, _id, _expectedTotalPlayTime);
|
var _firstGranulePlayTime = firstGranulePlayTime_1;
|
||||||
|
var _lastGranulePlayTime = lastGranulePlayTime_1;
|
||||||
|
this._OnDecodeSuccess(decodedData, _id, _expectedTotalPlayTime, _firstGranulePlayTime, _lastGranulePlayTime);
|
||||||
}).bind(this), this._OnDecodeError.bind(this));
|
}).bind(this), this._OnDecodeError.bind(this));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -187,9 +193,10 @@ var AudioFormatReader_MPEG = /** @class */ (function (_super) {
|
|||||||
return new MPEGFrameInfo(new Uint8Array(frameArray), this.FrameSamples, this.FrameSampleRate);
|
return new MPEGFrameInfo(new Uint8Array(frameArray), this.FrameSamples, this.FrameSampleRate);
|
||||||
};
|
};
|
||||||
// Is called if the decoding of the window succeeded
|
// Is called if the decoding of the window succeeded
|
||||||
AudioFormatReader_MPEG.prototype.OnDecodeSuccess = function (decodedData, id, expectedTotalPlayTime) {
|
AudioFormatReader_MPEG.prototype.OnDecodeSuccess = function (decodedData, id, expectedTotalPlayTime, firstGranulePlayTime, lastGranulePlayTime) {
|
||||||
var extractSampleCount;
|
var extractSampleCount;
|
||||||
var extractSampleOffset;
|
var extractSampleOffset;
|
||||||
|
var delta = 0.001;
|
||||||
// Check if we got the expected number of samples
|
// Check if we got the expected number of samples
|
||||||
if (expectedTotalPlayTime > decodedData.duration) {
|
if (expectedTotalPlayTime > decodedData.duration) {
|
||||||
// We got less samples than expect, we suspect that they were truncated equally at start and end.
|
// We got less samples than expect, we suspect that they were truncated equally at start and end.
|
||||||
@@ -210,8 +217,35 @@ var AudioFormatReader_MPEG = /** @class */ (function (_super) {
|
|||||||
extractSampleCount += budgetSamples;
|
extractSampleCount += budgetSamples;
|
||||||
this.TimeBudget -= (budgetSamples / decodedData.sampleRate);
|
this.TimeBudget -= (budgetSamples / decodedData.sampleRate);
|
||||||
}
|
}
|
||||||
|
var diff = decodedData.duration - expectedTotalPlayTime;
|
||||||
|
if ((diff - firstGranulePlayTime - lastGranulePlayTime) >= -delta) {
|
||||||
|
// Both first and last granule are present. Cut out middle section.
|
||||||
extractSampleOffset = Math.floor((decodedData.length - extractSampleCount) / 2);
|
extractSampleOffset = Math.floor((decodedData.length - extractSampleCount) / 2);
|
||||||
}
|
}
|
||||||
|
if (Math.abs(firstGranulePlayTime - lastGranulePlayTime) <= delta) {
|
||||||
|
// First and last granule are equal. We need to make an educated guess which one is present.
|
||||||
|
if (isIOS || isIPadOS) {
|
||||||
|
// I don't know why, but Apple does things differently.
|
||||||
|
extractSampleOffset = Math.floor((decodedData.length - extractSampleCount) / 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Assume last granule is present.
|
||||||
|
extractSampleOffset = decodedData.length - extractSampleCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Math.abs(diff - firstGranulePlayTime) <= delta) {
|
||||||
|
// Last granule is present.
|
||||||
|
extractSampleOffset = decodedData.length - extractSampleCount;
|
||||||
|
}
|
||||||
|
else if (Math.abs(diff - lastGranulePlayTime) <= delta) {
|
||||||
|
// First granule is present.
|
||||||
|
extractSampleOffset = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// The difference is not equal to neither the first, the last nor the sum of both granule. So we just use the middle.
|
||||||
|
extractSampleOffset = Math.floor((decodedData.length - extractSampleCount) / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
// We got the expected number of samples, no adaption needed
|
// We got the expected number of samples, no adaption needed
|
||||||
extractSampleCount = decodedData.length;
|
extractSampleCount = decodedData.length;
|
||||||
|
|||||||
+58
-4
@@ -123,6 +123,11 @@ $(document).ready(function () {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setInterval(getServerTime, 10000);
|
||||||
|
getServerTime();
|
||||||
|
setInterval(sendPingRequest, 5000);
|
||||||
|
sendPingRequest();
|
||||||
|
|
||||||
var freqUpButton = $('#freq-up')[0];
|
var freqUpButton = $('#freq-up')[0];
|
||||||
var freqDownButton = $('#freq-down')[0];
|
var freqDownButton = $('#freq-down')[0];
|
||||||
var psContainer = $('#ps-container')[0];
|
var psContainer = $('#ps-container')[0];
|
||||||
@@ -167,6 +172,55 @@ function getInitialSettings() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLocalizedTime(serverTime) {
|
||||||
|
// Convert server time to a Date object
|
||||||
|
const serverDate = new Date(serverTime);
|
||||||
|
|
||||||
|
// Get local time zone offset
|
||||||
|
const localOffset = serverDate.getTimezoneOffset() * 60000; // Convert minutes to milliseconds
|
||||||
|
|
||||||
|
// Calculate local time by adding the offset
|
||||||
|
const localTime = new Date(serverDate.getTime() - localOffset);
|
||||||
|
|
||||||
|
// Format local time
|
||||||
|
const options = { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false };
|
||||||
|
const formattedLocalTime = localTime.toLocaleString('en-US', options);
|
||||||
|
|
||||||
|
return formattedLocalTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getServerTime() {
|
||||||
|
$.ajax({
|
||||||
|
url: './server_time',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (data) {
|
||||||
|
// Convert server time to local time format
|
||||||
|
const localizedTimeServer = getLocalizedTime(data.serverTime);
|
||||||
|
const localizedTimeClient = getLocalizedTime(new Date().toISOString());
|
||||||
|
|
||||||
|
$('#server-time').text(localizedTimeServer);
|
||||||
|
$('#client-time').text(localizedTimeClient);
|
||||||
|
},
|
||||||
|
error: function (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendPingRequest() {
|
||||||
|
const startTime = new Date().getTime();
|
||||||
|
|
||||||
|
fetch('/ping')
|
||||||
|
.then(response => {
|
||||||
|
const endTime = new Date().getTime();
|
||||||
|
const pingTime = endTime - startTime;
|
||||||
|
$('#current-ping').text(`Ping: ${pingTime}ms`);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error fetching ping:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function initCanvas(parsedData) {
|
function initCanvas(parsedData) {
|
||||||
signalToggle = $("#signal-units-toggle");
|
signalToggle = $("#signal-units-toggle");
|
||||||
|
|
||||||
@@ -329,9 +383,8 @@ function getCurrentFreq() {
|
|||||||
function checkKey(e) {
|
function checkKey(e) {
|
||||||
e = e || window.event;
|
e = e || window.event;
|
||||||
|
|
||||||
// Check if any input element is focused using jQuery
|
if ($('#password:focus').length > 0) {
|
||||||
if ($('input:focus').length > 0) {
|
return;
|
||||||
return; // Do nothing if an input is focused
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentFreq();
|
getCurrentFreq();
|
||||||
@@ -538,7 +591,8 @@ function updateDataElements(parsedData) {
|
|||||||
$('#data-ant input').val($('#data-ant li[data-value="' + parsedData.ant + '"]').text());
|
$('#data-ant input').val($('#data-ant li[data-value="' + parsedData.ant + '"]').text());
|
||||||
|
|
||||||
if (parsedData.txInfo.station.length > 1) {
|
if (parsedData.txInfo.station.length > 1) {
|
||||||
$('#data-station-name').text(decodeURIComponent(parsedData.txInfo.station.replace(/\u009e/g, '\u017E')));
|
const sanitizedStation = parsedData.txInfo.station.replace(/%/g, '%25');
|
||||||
|
$('#data-station-name').text(decodeURIComponent(sanitizedStation.replace(/\u009e/g, '\u017E')));
|
||||||
$('#data-station-erp').text(parsedData.txInfo.erp);
|
$('#data-station-erp').text(parsedData.txInfo.erp);
|
||||||
$('#data-station-city').text(parsedData.txInfo.city);
|
$('#data-station-city').text(parsedData.txInfo.city);
|
||||||
$('#data-station-itu').text(parsedData.txInfo.itu);
|
$('#data-station-itu').text(parsedData.txInfo.itu);
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
theme4: ['#0c1c1b', '#68f7ee'],
|
theme4: ['#0c1c1b', '#68f7ee'],
|
||||||
theme5: ['#171106', '#f5b642'],
|
theme5: ['#171106', '#f5b642'],
|
||||||
theme6: ['#21091d', '#ed51d3'],
|
theme6: ['#21091d', '#ed51d3'],
|
||||||
theme7: ['#1d1838', '#8069fa'],
|
theme7: ['#0d0b1a', '#8069fa'],
|
||||||
theme8: ['#000', '#888'],
|
theme8: ['#000', '#888'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user