1
0
mirror of https://github.com/KubaPro010/fm-dx-webserver.git synced 2026-02-26 14:11:59 +01:00

UI changes

This commit is contained in:
NoobishSVK
2024-02-23 14:05:23 +01:00
parent 5c4e669efd
commit c2bb770f82
14 changed files with 168 additions and 90 deletions

View File

@@ -54,10 +54,14 @@ Join our **Discord community** to get the latest development update info, share
5. Open your web browser and navigate to `http:/localhost:8080` to access the web interface.
## Dependencies
## Utilized projects
This project utilizes the [librdsparser](https://github.com/kkonradpl/librdsparser) library for RDS parsing. Make sure to check out the library for more information.
Pre-built version of this library is bundled with the webserver.
This project utilizes these libraries:
- [librdsparser](https://github.com/kkonradpl/librdsparser) library by Konrad Kosmatka for RDS parsing
- [3LAS](https://github.com/jojobond/3LAS) library by JoJoBond for Low Latency Audio Streaming
- [flat-flags](https://github.com/luishdez/flat-flags) library by luishdez for RDS country flags
All of these libraries are already bundled with the webserver.
## Contributing

View File

@@ -204,8 +204,8 @@ const clientUpdateIntervals = new Map(); // Store update intervals for each clie
// Initialize the data object
var dataToSend = {
pi: '?',
freq: 87.500,
previousFreq: 87.500,
freq: 87.500.toFixed(3),
previousFreq: 87.500.toFixed(3),
signal: 0,
st: false,
st_forced: false,

View File

@@ -271,6 +271,48 @@ const authenticate = (req, res, next) => {
app.set('view engine', 'ejs'); // Set EJS as the template engine
app.set('views', path.join(__dirname, '/web'))
function parseMarkdown(parsed) {
parsed = parsed.replace(/<\/?[^>]+(>|$)/g, '');
var grayTextRegex = /--(.*?)--/g;
parsed = parsed.replace(grayTextRegex, '<span class="text-gray">$1</span>');
var boldRegex = /\*\*(.*?)\*\*/g;
parsed = parsed.replace(boldRegex, '<strong>$1</strong>');
var italicRegex = /\*(.*?)\*/g;
parsed = parsed.replace(italicRegex, '<em>$1</em>');
var linkRegex = /\[([^\]]+)]\(([^)]+)\)/g;
parsed = parsed.replace(linkRegex, '<a href="$2">$1</a>');
var breakLineRegex = /\\n/g;
parsed = parsed.replace(breakLineRegex, '<br>');
return parsed;
}
function removeMarkdown(parsed) {
parsed = parsed.replace(/<\/?[^>]+(>|$)/g, '');
var grayTextRegex = /--(.*?)--/g;
parsed = parsed.replace(grayTextRegex, '$1');
var boldRegex = /\*\*(.*?)\*\*/g;
parsed = parsed.replace(boldRegex, '$1');
var italicRegex = /\*(.*?)\*/g;
parsed = parsed.replace(italicRegex, '$1');
var linkRegex = /\[([^\]]+)]\(([^)]+)\)/g;
parsed = parsed.replace(linkRegex, '$1');
var breakLineRegex = /\\n/g;
parsed = parsed.replace(breakLineRegex, '');
return parsed;
}
app.get('/', (req, res) => {
if (!fs.existsSync(configName + '.json')) {
parseAudioDevice((result) => {
@@ -285,7 +327,8 @@ app.get('/', (req, res) => {
isAdminAuthenticated: req.session.isAdminAuthenticated,
isTuneAuthenticated: req.session.isTuneAuthenticated,
tunerName: serverConfig.identification.tunerName,
tunerDesc: serverConfig.identification.tunerDesc,
tunerDesc: parseMarkdown(serverConfig.identification.tunerDesc),
tunerDescMeta: removeMarkdown(serverConfig.identification.tunerDesc),
tunerLock: serverConfig.lockToAdmin,
publicTuner: serverConfig.publicTuner
})

View File

@@ -1,6 +1,6 @@
{
"name": "fm-dx-webserver",
"version": "1.0.8",
"version": "1.0.9",
"description": "",
"main": "index.js",
"scripts": {

View File

@@ -78,7 +78,7 @@ label {
#settings, #back-btn, #users-online-container {
background: transparent;
border: 0;
color: white;
color: var(--color-text);
position: absolute;
top: 15px;
right: 15px;
@@ -189,7 +189,7 @@ label {
#data-pi {
font-size: 24px;
margin-top: 50px;
color: #aaa;
color: var(--color-text-2)
}
h2.show-phone {
display: inline;
@@ -272,4 +272,12 @@ label {
margin-bottom: 10px;
font-size: 18px;
}
h2.mb-0 {
margin-bottom: 0;
margin-top: 2px !important;
}
#af-list ul {
max-height: 330px;
}
}

View File

@@ -25,7 +25,7 @@ input[type="text"], textarea, input[type="password"] {
box-sizing: border-box;
border: 2px solid transparent;
outline: 0;
color: white;
color: var(--color-text);
background-color: var(--color-1);
font-family: 'Titillium Web', sans-serif;
}
@@ -38,7 +38,7 @@ input[type="text"], textarea, input[type="password"] {
box-sizing: border-box;
border: 2px solid transparent;
outline: 0;
color: white;
color: var(--color-text);
background-color: var(--color-1);
font-family: 'Titillium Web', sans-serif;
}
@@ -112,10 +112,12 @@ input[type="range"]::-moz-range-thumb {
box-sizing: border-box;
height: 48px;
width: 48px;
background-color: var(--color-4);
border-radius: 0px 30px 30px 0px;
border: 0;
outline: none;
background: url('../images/speaker.svg') center no-repeat, var(--color-5);
background-position: center;
background-size: 20px;
border-radius: 30px;
outline: 4px solid var(--color-5);
outline-offset: -3px;
/* slider progress trick */
box-shadow: -420px 0 0 400px var(--color-4);
}

View File

@@ -99,10 +99,14 @@
font-weight: 300;
}
.text-color-default {
color: var(--color-text);
}
.text-medium {
font-size: 24px;
color: #aaa;
font-weight: 300;
opacity: 0.6;
}
.text-medium-big {

View File

@@ -1,13 +1,16 @@
:root {
--color-main: #111;
--color-main-bright: #aaa;
--color-main: #000;
--color-main-bright: #ccc;
--color-text: white;
--color-1: color-mix(in srgb, var(--color-main) 95%, var(--color-main-bright));
--color-2: color-mix(in srgb, var(--color-main) 75%, var(--color-main-bright));
--color-3: color-mix(in srgb, var(--color-main) 50%, var(--color-main-bright));
--color-4: color-mix(in srgb, var(--color-main) 20%, var(--color-main-bright));
--color-5: color-mix(in srgb, var(--color-main) 0%, var(--color-main-bright));
--color-1: color-mix(in srgb, var(--color-main) 95%, var(--color-main-bright)); /* 5% Brightness */
--color-2: color-mix(in srgb, var(--color-main) 75%, var(--color-main-bright)); /* 25% Brightness */
--color-3: color-mix(in srgb, var(--color-main) 50%, var(--color-main-bright)); /* 50% Brightness */
--color-4: color-mix(in srgb, var(--color-main) 20%, var(--color-main-bright)); /* 80% Brightness */
--color-5: color-mix(in srgb, var(--color-main) 5%, var(--color-main-bright)); /* 95% Brightness */
--color-text-2: #ccc;
}
* {
@@ -41,7 +44,7 @@
body {
font-family: 'Titillium Web', sans-serif;
color: white;
color: var(--color-text);
background-color: var(--color-main);
transition: 0.3s ease-in-out background-color;
margin: 0 auto;
@@ -72,5 +75,9 @@ body {
a {
text-decoration: none;
color: white;
color: var(--color-text-2);
}
a:hover {
border-bottom: 1px solid var(--color-4);
}

View File

@@ -47,7 +47,7 @@
}
.close:hover {
color: white;
color: var(--color-text);
}
.modal-content .button-close {

7
web/images/speaker.svg Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.0" width="500" height="500" viewBox="0 0 75 75">
<path d="M39.389,13.769 L22.235,28.606 L6,28.606 L6,47.699 L21.989,47.699 L39.389,62.75 L39.389,13.769z"
style="stroke:rgba(0,0,0,0);stroke-width:5;stroke-linejoin:round;fill:rgba(0,0,0,0.25);"
/>
<path d="M48,27.6a19.5,19.5 0 0 1 0,21.4M55.1,20.5a30,30 0 0 1 0,35.6M61.6,14a38.8,38.8 0 0 1 0,48.6" style="fill:none;stroke:rgba(0,0,0,0.25);stroke-width:5;stroke-linecap:round"/>
</svg>

After

Width:  |  Height:  |  Size: 541 B

View File

@@ -12,7 +12,7 @@
<meta property="og:title" content="FM-DX WebServer [<%= tunerName %>]">
<meta property="og:type" content="website">
<meta property="og:image" content="favicon2.png">
<meta property="og:description" content="Server description: <%= tunerDesc %>.">
<meta property="og:description" content="Server description: <%= tunerDescMeta %>.">
<script src="js/init.js"></script>
@@ -53,7 +53,7 @@
<div class="panel-100 no-bg tuner-info">
<h1 id="tuner-name"><%= tunerName %> <% if (!publicTuner) { %><i class="fa-solid fa-key pointer" title="Only people with tune password can tune."></i>
<% } else if (tunerLock) { %><i class="fa-solid fa-lock pointer" title="Tuner is currently locked to admin."></i><% } %></h1>
<p id="tuner-desc"><%= tunerDesc %></p>
<p id="tuner-desc"><%- tunerDesc %></p>
<div style="clear: both"></div>
</div>
<div class="canvas-container hide-phone">
@@ -74,7 +74,7 @@
<div id="flags-container-desktop" class="panel-33">
<h2 class="show-phone">
<div class="data-pty" style="color:white;"></div>
<div class="data-pty text-color-default"></div>
</h2>
<h3 style="margin-top:0;margin-bottom:0;" class="color-4 flex-center">
<span class="data-tp">TP</span>
@@ -103,7 +103,7 @@
<h2>SIGNAL</h2>
<span class="text-big">
<span id="data-signal"></span><!--
--><span id="data-signal-decimal" class="text-medium-big" style="color: #ccc;"></span>
--><span id="data-signal-decimal" class="text-medium-big" style="opacity:0.7;"></span>
<span id="signal-units" class="text-medium">dBf</span>
</span>
</div>
@@ -154,13 +154,15 @@
<div class="panel-33 hover-brighten">
<div id="data-station-container">
<h2 style="margin-top: 4px;">
<h2 style="margin-top: 4px;" class="mb-0">
<span id="data-station-name"></span>
</h2>
<h4 class="m-0">
<span id="data-station-city"></span>, <span id="data-station-itu"></span>
</h4>
<span id="data-station-erp"></span> kW [<span id="data-station-pol"></span>] <span class="text-gray">•</span> <span id="data-station-distance"></span> km <span class="text-gray">•</span> <span id="data-station-azimuth"></span>°
<span class="text-small">
<span id="data-station-erp"></span> kW [<span id="data-station-pol"></span>] <span class="text-gray">•</span> <span id="data-station-distance"></span> km <span class="text-gray">•</span> <span id="data-station-azimuth"></span>°
</span>
</div>
</div>
</div>
@@ -180,7 +182,7 @@
</div>
<div id="flags-container-phone" class="panel-33">
<h2 class="show-phone">
<div class="data-pty" style="color:white;"></div>
<div class="data-pty text-color-default"></div>
</h2>
<h3 style="margin-top:0;margin-bottom:0;" class="color-4 flex-center">
<span class="data-tp">TP</span>
@@ -216,7 +218,8 @@
<li class="option" data-value="theme5">Orange</li>
<li class="option" data-value="theme6">Pink</li>
<li class="option" data-value="theme7">Blurple</li>
<li class="option" data-value="theme8">AMOLED</li>
<li class="option" data-value="theme8">Bee</li>
<li class="option" data-value="theme9">Retro</li>
</ul>
</div>
</div>
@@ -269,13 +272,8 @@
<p class="text-small" id="current-ping"></p>
<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>
<span style="color: var(--color-3);">v1.0.8 [19/2/2024]</span>
<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">3LAS by <a href="https://github.com/JoJoBond/3LAS" target="_blank">JoJoBond</a></span><br>
<span class="text-smaller">flat-flags by <a href="https://github.com/luishdez/flat-flags/tree/master" target="_blank">luishdez</a></span><br>
</p>
<p class="text-small">FM-DX WebServer <br>by <a href="https://noobish.eu" target="_blank">Noobish</a>, <a href="https://fmdx.pl" target="_blank">kkonradpl</a> & the OpenRadio community.</p>
<span style="color: var(--color-3);">v1.0.9 [23/2/2024]</span>
</div>
</div>
</div>

View File

@@ -39,23 +39,6 @@ $(document).ready(function () {
}
});
var input = $("#tuner-desc").text();
var parsed = input;
var grayTextRegex = /--(.*?)--/g;
parsed = parsed.replace(grayTextRegex, '<span class="text-gray">$1</span>');
var boldRegex = /\*\*(.*?)\*\*/g;
parsed = parsed.replace(boldRegex, '<strong>$1</strong>');
var italicRegex = /\*(.*?)\*/g;
parsed = parsed.replace(italicRegex, '<em>$1</em>');
var breakLineRegex = /\\n/g;
parsed = parsed.replace(breakLineRegex, '<br>');
$("#tuner-desc").html(parsed);
const textInput = $('#commandinput');
textInput.on('change', function (event) {
@@ -568,7 +551,7 @@ function updateDataElements(parsedData) {
parsedData.ps = parsedData.ps.replace(/\s/g, '_');
}
$('#data-ps').html(parsedData.ps === '?' ? "<span class='opacity-half'>?</span>" : processString(parsedData.ps, parsedData.ps_errors));
$('.data-tp').html(parsedData.tp === false ? "<span class='opacity-half'>TP</span>" : "TP");
$('.data-tp').html(parsedData.tp === 0 ? "<span class='opacity-half'>TP</span>" : "TP");
$('.data-ta').html(parsedData.ta === 0 ? "<span class='opacity-half'>TA</span>" : "TA");
$('.data-ms').html(parsedData.ms === 0
? "<span class='opacity-half'>M</span><span class='opacity-full'>S</span>"

View File

@@ -1,14 +1,20 @@
// Themes
/**
* Themes
* @param first color
* @param second color
* @param text color
*/
const themes = {
theme1: ['#111', '#aaa'],
theme2: ['#1f0c0c', '#ff7070'],
theme3: ['#121c0c', '#a9ff70'],
theme4: ['#0c1c1b', '#68f7ee'],
theme5: ['#171106', '#f5b642'],
theme6: ['#21091d', '#ed51d3'],
theme7: ['#0d0b1a', '#8069fa'],
theme8: ['#000', '#888'],
};
theme1: [ 'rgba(0, 0, 0, 1)', 'rgba(204, 204, 204, 1)', 'rgba(255, 255, 255, 1)' ], // Monochrome (Default)
theme2: [ 'rgba(31, 12, 12, 1)', 'rgba(255, 112, 112, 1)', 'rgba(255, 255, 255, 1)' ], // Red
theme3: [ 'rgba(18, 28, 12, 1)', 'rgba(169, 255, 112, 1)', 'rgba(255, 255, 255, 1)' ], // Green
theme4: [ 'rgba(12, 28, 27, 1)', 'rgba(104, 247, 238, 1)', 'rgba(255, 255, 255, 1)' ], // Cyan
theme5: [ 'rgba(23, 17, 6, 1)', 'rgba(245, 182, 66, 1)', 'rgba(255, 255, 255, 1)' ], // Orange
theme6: [ 'rgba(33, 9, 29, 1)', 'rgba(237, 81, 211, 1)', 'rgba(255, 255, 255, 1)' ], // Pink
theme7: [ 'rgba(13, 11, 26, 1)', 'rgba(128, 105, 250, 1)', 'rgba(255, 255, 255, 1)' ], // Blurple
theme8: [ 'rgba(252, 186, 3, 1)', 'rgba(0, 0, 0, 1)', 'rgba(0, 0, 0, 1)' ], // Sunny
theme9: ['rgba(32, 34, 40, 1)', 'rgba(88, 219, 171, 1)', 'rgba(255, 255, 255, 1)' ] // Retro
};
// Signal Units
const signalUnits = {
@@ -129,10 +135,21 @@
});
function setTheme(themeName) {
const themeColors = themes[themeName];
if (themeColors) {
$(':root').css('--color-main', themeColors[0]);
$(':root').css('--color-main-bright', themeColors[1]);
}
}
function setTheme(themeName) {
const themeColors = themes[themeName];
if (themeColors) {
// Extracting the RGBA components and opacity value
const rgbaComponents = themeColors[2].match(/(\d+(\.\d+)?)/g);
const opacity = parseFloat(rgbaComponents[3]);
// Calculating 80% of the opacity
const newOpacity = opacity * 0.75;
// Constructing the new RGBA string with the adjusted opacity
const textColor2 = `rgba(${rgbaComponents[0]}, ${rgbaComponents[1]}, ${rgbaComponents[2]}, ${newOpacity})`;
$(':root').css('--color-main', themeColors[0]);
$(':root').css('--color-main-bright', themeColors[1]);
$(':root').css('--color-text', themeColors[2]);
$(':root').css('--color-text-2', textColor2);
}
}

View File

@@ -85,10 +85,13 @@
<div class="form-group">
<label for="audio-quality"><i class="fa-solid fa-wave-square"></i> AUDIO QUALITY:</label>
<div class="dropdown" style="width: 300px;">
<input id="audio-quality" type="text" name="audio-quality" placeholder="128k (saves bandwidth)" readonly />
<input id="audio-quality" type="text" name="audio-quality" placeholder="128k (standard)" readonly />
<ul class="options">
<li data-value="128k" class="option">128k (saves bandwidth)</li>
<li data-value="64k" class="option">64k (lowest quality)</li>
<li data-value="96k" class="option">96k (low quality)</li>
<li data-value="128k" class="option">128k (standard)</li>
<li data-value="192k" class="option">192k (higher quality)</li>
<li data-value="256k" class="option">256k (highest quality)</li>
</ul>
</div>
</div>
@@ -138,18 +141,20 @@
<div class="panel-33">
<h2>MAINTENANCE</h2>
<div class="form-group checkbox">
<input type="checkbox" id="tuner-public">
<label for="tuner-public">Public tuner</label>
</div><br>
<div class="form-group checkbox">
<input type="checkbox" id="tuner-lock">
<label for="tuner-lock">Lock to admin</label>
</div><br>
<div class="form-group checkbox">
<input type="checkbox" id="shutdown-tuner">
<label for="shutdown-tuner">Auto-shutdown on empty server</label>
</div><br>
<div class="text-left top-25 bottom-20" style="padding-left: 40px;">
<div class="form-group checkbox">
<input type="checkbox" id="tuner-public">
<label for="tuner-public">Public tuner</label>
</div><br>
<div class="form-group checkbox">
<input type="checkbox" id="tuner-lock">
<label for="tuner-lock">Lock to admin</label>
</div><br>
<div class="form-group checkbox">
<input type="checkbox" id="shutdown-tuner">
<label for="shutdown-tuner">Auto-shutdown [XDR Only]</label>
</div><br>
</div>
<div class="form-group">
<label for="tune-pass">Tune password:</label>
<input class="input-text w-150" type="password" name="tune-pass" id="tune-pass">