From 68e60223a37e3d617fe981253da0e1c3b97f2091 Mon Sep 17 00:00:00 2001 From: NoobishSVK Date: Wed, 31 Jan 2024 21:28:25 +0100 Subject: [PATCH] UI changes, accessibility features, ffmpeg adjustments --- datahandler.js | 57 ++-- package-lock.json | 569 ++++++++++++++++++++++++++++++++++++++++ package.json | 2 + stream/3las.server.js | 4 +- stream/index.js | 31 ++- stream/settings.json | 4 +- web/css/breadcrumbs.css | 39 ++- web/css/buttons.css | 8 +- web/css/dropdown.css | 80 ++++++ web/css/entry.css | 2 + web/css/helpers.css | 15 +- web/css/main.css | 5 +- web/css/panels.css | 18 ++ web/index.html | 59 ++++- web/js/dropdown.js | 49 ++++ web/js/main.js | 247 ++++++++--------- web/js/settings.js | 94 +++---- web/js/webserver.js | 1 + 18 files changed, 1056 insertions(+), 228 deletions(-) create mode 100644 web/css/dropdown.css create mode 100644 web/js/dropdown.js diff --git a/datahandler.js b/datahandler.js index e8539e0..9ae0f2c 100644 --- a/datahandler.js +++ b/datahandler.js @@ -206,6 +206,7 @@ var dataToSend = { rt1: '', ims: 0, eq: 0, + ant: 0, country_name: '', country_iso: 'UN', users: '', @@ -243,32 +244,36 @@ function handleData(ws, receivedData) { dataToSend.pi = '?'; } break; - case receivedLine.startsWith('G'): - switch (receivedLine) { - case 'G11': - initialData.eq = 1; - dataToSend.eq = 1; - initialData.ims = 1; - dataToSend.ims = 1; - break; - case 'G01': - initialData.eq = 0; - dataToSend.eq = 0; - initialData.ims = 1; - dataToSend.ims = 1; - break; - case 'G10': - initialData.eq = 1; - dataToSend.eq = 1; - initialData.ims = 0; - dataToSend.ims = 0; - break; - case 'G00': - initialData.eq = 0; - initialData.ims = 0; - dataToSend.eq = 0; - dataToSend.ims = 0; - break; + case receivedLine.startsWith('Z'): + dataToSend.ant = receivedLine.substring(1); + initialData.ant = receivedLine.substring(1); + break; + case receivedLine.startsWith('G'): + switch (receivedLine) { + case 'G11': + initialData.eq = 1; + dataToSend.eq = 1; + initialData.ims = 1; + dataToSend.ims = 1; + break; + case 'G01': + initialData.eq = 0; + dataToSend.eq = 0; + initialData.ims = 1; + dataToSend.ims = 1; + break; + case 'G10': + initialData.eq = 1; + dataToSend.eq = 1; + initialData.ims = 0; + dataToSend.ims = 0; + break; + case 'G00': + initialData.eq = 0; + initialData.ims = 0; + dataToSend.eq = 0; + dataToSend.ims = 0; + break; } case receivedLine.startsWith('Sm'): modifiedData = receivedLine.substring(2); diff --git a/package-lock.json b/package-lock.json index 8e309fe..2617b85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,17 +9,43 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", "command-exists-promise": "^2.0.2", "express": "4.18.2", "http": "^0.0.1-security", "https": "1.0.0", "koffi": "2.7.2", + "lamejs": "^1.2.1", "net": "1.0.2", "websocket": "1.0.34", "wrtc": "^0.4.7", "ws": "^8.14.2" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -32,11 +58,73 @@ "node": ">= 0.6" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -60,6 +148,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/bufferutil": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", @@ -93,6 +190,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/command-exists-promise": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/command-exists-promise/-/command-exists-promise-2.0.2.tgz", @@ -101,6 +214,16 @@ "node": ">=6" } }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -163,6 +286,11 @@ "node": ">= 0.4" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -180,6 +308,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "engines": { + "node": ">=8" + } + }, "node_modules/domexception": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", @@ -195,6 +331,11 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -336,6 +477,33 @@ "node": ">= 0.6" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -344,6 +512,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/get-intrinsic": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", @@ -358,6 +545,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -402,6 +608,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "node_modules/hasown": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", @@ -438,6 +649,39 @@ "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==" }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -449,6 +693,15 @@ "node": ">=0.10.0" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -462,6 +715,14 @@ "node": ">= 0.10" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -473,6 +734,47 @@ "integrity": "sha512-AWcsEKETQuELxK0Wq/aXDkDiNFFY41TxZQSrKm2Nd6HO/KTHeohPOOIlh2OfQnBXJbRjx5etpWt8cbqMUZo2sg==", "hasInstallScript": true }, + "node_modules/lamejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/lamejs/-/lamejs-1.2.1.tgz", + "integrity": "sha512-s7bxvjvYthw6oPLCm5pFxvA84wUROODB8jEO2+CE1adhKgrIvVOlmMgY8zyugxGrvRaDHNJanOiS21/emty6dQ==", + "dependencies": { + "use-strict": "1.0.1" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -524,6 +826,59 @@ "node": ">= 0.6" } }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -547,6 +902,25 @@ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-gyp-build": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz", @@ -557,6 +931,39 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -576,6 +983,14 @@ "node": ">= 0.8" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -584,6 +999,14 @@ "node": ">= 0.8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -637,6 +1060,33 @@ "node": ">= 0.8" } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -661,6 +1111,20 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -703,6 +1167,11 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, "node_modules/set-function-length": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", @@ -735,6 +1204,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -743,6 +1217,54 @@ "node": ">= 0.8" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -751,6 +1273,11 @@ "node": ">=0.6" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", @@ -784,6 +1311,11 @@ "node": ">= 0.8" } }, + "node_modules/use-strict": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/use-strict/-/use-strict-1.0.1.tgz", + "integrity": "sha512-IeiWvvEXfW5ltKVMkxq6FvNf2LojMKvB2OCeja6+ct24S1XOmQw2dGr2JyndwACWAGJva9B7yPHwAmeA9QCqAQ==" + }, "node_modules/utf-8-validate": { "version": "5.0.10", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", @@ -796,6 +1328,11 @@ "node": ">=6.14.2" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -834,6 +1371,33 @@ "node": ">=4.0.0" } }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/whatwg-url/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, "node_modules/wrtc": { "version": "0.4.7", "resolved": "https://registry.npmjs.org/wrtc/-/wrtc-0.4.7.tgz", @@ -879,6 +1443,11 @@ "engines": { "node": ">=0.10.32" } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } } diff --git a/package.json b/package.json index 6c5697c..3106c4f 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,13 @@ "author": "", "license": "ISC", "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", "command-exists-promise": "^2.0.2", "express": "4.18.2", "http": "^0.0.1-security", "https": "1.0.0", "koffi": "2.7.2", + "lamejs": "^1.2.1", "net": "1.0.2", "websocket": "1.0.34", "wrtc": "^0.4.7", diff --git a/stream/3las.server.js b/stream/3las.server.js index 14fa305..905495b 100644 --- a/stream/3las.server.js +++ b/stream/3las.server.js @@ -335,13 +335,13 @@ class FallbackProviderMp3 extends AFallbackProvider { GetFFmpegArguments() { return [ "-fflags", "+nobuffer+flush_packets", "-flags", "low_delay", "-rtbufsize", "32", "-probesize", "32", - "-f", "s16le", + "-f", "libmp3lame", "-ar", this.Server.SampleRate.toString(), "-ac", this.Server.Channels.toString(), "-i", "pipe:0", "-c:a", "libmp3lame", "-b:a", Settings.FallbackMp3Bitrate.toString() + "k", - "-ac", "1", + "-ac", this.Server.Channels.toString(), "-reservoir", "0", "-f", "mp3", "-write_xing", "0", "-id3v2_version", "0", "-fflags", "+nobuffer", "-flush_packets", "1", diff --git a/stream/index.js b/stream/index.js index 7511bf8..923eeca 100644 --- a/stream/index.js +++ b/stream/index.js @@ -3,13 +3,20 @@ const config = require('../userconfig.js'); const consoleCmd = require('../console.js'); function enableAudioStream() { + var ffmpegCommand; // Specify the command and its arguments const command = 'ffmpeg'; - const flags = '-fflags +nobuffer+flush_packets -flags low_delay -rtbufsize 6192 -probesize 64 -audio_buffer_size 20'; - const codec = '-acodec pcm_s16le -ar 48000 -ac 1'; + const flags = '-fflags +nobuffer+flush_packets -flags low_delay -rtbufsize 6192 -probesize 64'; + const codec = '-acodec pcm_s16le -ar 48000 -ac 2'; const output = '-f s16le -fflags +nobuffer+flush_packets -packetsize 384 -flush_packets 1 -bufsize 960'; // Combine all the settings for the ffmpeg command - const ffmpegCommand = `${flags} -f dshow -i audio="${config.audioDeviceName}" ${codec} ${output} pipe:1 | node stream/3las.server.js -port ${config.audioPort} -samplerate 48000 -channels 1`; + if (process.platform === 'win32') { + // Windows + ffmpegCommand = `${flags} -f dshow -i audio="${config.audioDeviceName}" ${codec} ${output} pipe:1 | node stream/3las.server.js -port ${config.audioPort} -samplerate 44100 -channels 2`; + } else { + // Linux + ffmpegCommand = `${flags} -f alsa -i "${config.audioDeviceName}" ${codec} ${output} pipe:1 | node stream/3las.server.js -port ${config.audioPort} -samplerate 48000 -channels 2`; + } consoleCmd.logInfo("Launching audio stream on port " + config.audioPort + "."); // Spawn the child process @@ -17,24 +24,24 @@ function enableAudioStream() { if(config.audioDeviceName.length > 2) { const childProcess = spawn(command, [ffmpegCommand], { shell: true }); - // Handle the output of the child process (optional) - /*childProcess.stdout.on('data', (data) => { - console.log(`stdout: ${data}`); - }); + // Handle the output of the child process (optional) + childProcess.stdout.on('data', (data) => { + consoleCmd.logDebug(`stdout: ${data}`); + }); - childProcess.stderr.on('data', (data) => { - console.error(`stderr: ${data}`); + childProcess.stderr.on('data', (data) => { + consoleCmd.logDebug(`stderr: ${data}`); }); // Handle the child process exit event childProcess.on('close', (code) => { - console.log(`Child process exited with code ${code}`); + consoleCmd.logDebug(`Child process exited with code ${code}`); }); // You can also listen for the 'error' event in case the process fails to start childProcess.on('error', (err) => { - console.error(`Error starting child process: ${err}`); - });*/ + consoleCmd.logError(`Error starting child process: ${err}`); + }); } } diff --git a/stream/settings.json b/stream/settings.json index 4c41c9a..e711247 100644 --- a/stream/settings.json +++ b/stream/settings.json @@ -2,8 +2,8 @@ "RtcConfig" : null, "FallbackFFmpegPath": "ffmpeg.exe", "FallbackUseMp3": true, - "FallbackUseWav": true, - "FallbackMp3Bitrate": 128, + "FallbackUseWav": false, + "FallbackMp3Bitrate": 192, "FallbackWavSampleRate": 16000, "AdminKey": "" } \ No newline at end of file diff --git a/web/css/breadcrumbs.css b/web/css/breadcrumbs.css index 97f81fe..0d9b367 100644 --- a/web/css/breadcrumbs.css +++ b/web/css/breadcrumbs.css @@ -8,12 +8,30 @@ h3 { font-size: 22px; } +.canvas-container { + width: 100%; + height: 200px; +} + +#data-ant { + margin-right: 15px !important; +} + #data-ps, #data-rt0, #data-rt1 { - font-family: monospace; + font-family: "Roboto Mono", monospace; +} + +#data-rt0, #data-rt1 { + font-size: 14px; +} + +#data-ps { + font-weight: 500; } .form-group { - display: inline-block; + float: left; + margin-bottom: 20px; } #color-settings, #settings { @@ -66,6 +84,8 @@ h3 { #ps-container { background-color: var(--color-1); height: 100px !important; + margin: auto !important; + width: 100%; } h2 { display: none; @@ -80,7 +100,7 @@ h3 { padding: 0 !important; } #data-ps { - font-size: 54px; + font-size: 42px; } #data-frequency { font-size: 72px; @@ -96,7 +116,7 @@ h3 { ul { font-size: 16px; } - ul li { + #af-list ul li { display: inline-table; margin-right: 5px; width: 40px; @@ -130,3 +150,14 @@ h3 { } } +@media only screen and (min-width: 960px) and (max-height: 860px) { + #rt-container { + height: 90px !important; + } + #ps-container { + margin-left: 15px; + } + .canvas-container { + height: 120px; + } +} \ No newline at end of file diff --git a/web/css/buttons.css b/web/css/buttons.css index f7bd5d5..cbaaaa4 100644 --- a/web/css/buttons.css +++ b/web/css/buttons.css @@ -14,6 +14,10 @@ button:hover { background-color: var(--color-main-bright); } +.btn-disabled { + opacity: 0.6; +} + #tune-buttons input[type="text"] { width: 50%; height: 100%; @@ -27,10 +31,6 @@ button:hover { font-family: 'Titillium Web', sans-serif; } - input[type="text"]:hover { - border: 2px solid var(--color-main-bright); - } - #tune-buttons button { box-sizing: border-box; background-color: var(--color-4); diff --git a/web/css/dropdown.css b/web/css/dropdown.css new file mode 100644 index 0000000..b65bf8c --- /dev/null +++ b/web/css/dropdown.css @@ -0,0 +1,80 @@ +.dropdown { + width: 200px; + height: 48px; + background: var(--color-4); + position: relative; + margin-right: 20px; + /*border-bottom: 4px solid var(--color-2);*/ + } + @media (max-width: 400px) { + .dropdown { + width: 240px; + } + } + .dropdown::before { + content: ""; + position: absolute; + right: 10px; + top: 18px; + z-index: 9999; + width: 6px; + height: 6px; + border: 1px solid var(--color-main); + border-top: transparent; + border-right: transparent; + transform: rotate(-45deg); + pointer-events: none; + } + .dropdown ul { + list-style-type: none; + padding: 0; + margin: 0; + } + .dropdown input { + width: 100%; + height: 100%; + padding: 10px; + cursor: pointer; + border: none; + user-select: none; + background-color: var(--color-4); + color: var(--color-main); + } + .dropdown input:focus { + outline: none; + } + .dropdown input::placeholder { + color: var(--color-main); + opacity: 1; + } + .dropdown .options { + width: 100%; + cursor: pointer; + border: none; + font-size: 16px; + overflow: hidden; + opacity: 0; + visibility: hidden; + background: var(--color-main); + color: var(--color-4); + border: 1px solid var(--color-4); + position: relative; + z-index: 999; + } + .dropdown .options .option { + color: var(--color-4); + padding: 7px; + } + .dropdown .options .option:hover { + color: var(--color-main); + background: var(--color-4); + } + .dropdown.opened .options { + opacity: 1; + visibility: visible; + transform: translateY(0); + } + .dropdown.opened::before { + transform: rotate(-225deg); + top: 22px; + } \ No newline at end of file diff --git a/web/css/entry.css b/web/css/entry.css index e3e5004..c8e27c9 100644 --- a/web/css/entry.css +++ b/web/css/entry.css @@ -1,7 +1,9 @@ @import url('https://fonts.googleapis.com/css2?family=Titillium+Web:ital,wght@0,200;0,300;0,400;0,600;0,700;0,900;1,200;1,300;1,400;1,600;1,700&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@300;400;500&display=swap'); @import url("main.css"); /* Root stuff that affects the entire webpage (body, wrapper etc.) */ @import url("breadcrumbs.css"); /* Stuff that applies to random elements only once/twice */ @import url("buttons.css"); /* Buttons, inputs, select boxes, checkboxes... */ +@import url("dropdown.css"); /* Custom dropdown menus */ @import url("panels.css"); /* Different panels and their sizes */ @import url("modal.css"); /* Modal window */ @import url("helpers.css"); /* Stuff that is used often such as text changers etc */ \ No newline at end of file diff --git a/web/css/helpers.css b/web/css/helpers.css index a47d467..21eb5f5 100644 --- a/web/css/helpers.css +++ b/web/css/helpers.css @@ -35,7 +35,7 @@ } .m-0 { - margin: 0; + margin: 0 !important; } .m-left-20 { @@ -173,4 +173,17 @@ .hide-desktop { display: none; } +} + +/* Laptop compact view */ +@media only screen and (min-width: 960px) and (max-height: 860px) { + .text-big { + font-size: 40px; + } + .text-medium-big { + font-size: 34px; + } + .text-medium { + font-size: 30px; + } } \ No newline at end of file diff --git a/web/css/main.css b/web/css/main.css index 443a2b1..61c1bae 100644 --- a/web/css/main.css +++ b/web/css/main.css @@ -52,15 +52,16 @@ body { top: 50%; left: 50%; transform: translate(-50%, -50%); - width: auto; + width: 1180px; max-width: 1180px; } -@media (max-width: 960px) { +@media (max-width: 1180px) { #wrapper { position: static; transform: none; margin: 50px auto; + width: 100%; } } diff --git a/web/css/panels.css b/web/css/panels.css index 9600ce3..ca20d07 100644 --- a/web/css/panels.css +++ b/web/css/panels.css @@ -56,4 +56,22 @@ .panel-90 { margin-top: 100px; } +} + +/* Laptop compact view */ +@media only screen and (min-width: 960px) and (max-height: 860px) { + *[class^="panel-"] { + margin-top: 20px; + } + .panel-10, .panel-90 { + margin-top: 0; + } + .panel-10 { + margin: 0; + padding-bottom: 20px; + padding-right: 20px; + } + .panel-10.hide-phone { + padding: 0; + } } \ No newline at end of file diff --git a/web/index.html b/web/index.html index 2cef1c5..6c84d1a 100644 --- a/web/index.html +++ b/web/index.html @@ -44,13 +44,15 @@
- +
+ +
-
+
@@ -99,25 +101,34 @@
- +
+
- +
- +
- + - +
- +
@@ -167,7 +178,7 @@
- +
diff --git a/web/js/dropdown.js b/web/js/dropdown.js new file mode 100644 index 0000000..1e8b9fe --- /dev/null +++ b/web/js/dropdown.js @@ -0,0 +1,49 @@ +// Variables +const $dropdowns = $('.dropdown'); +const $input = $('input'); +const $listOfOptions = $('.option'); +let currentDropdown = null; // Track the currently clicked dropdown + +// Functions +const toggleDropdown = (event) => { + event.stopPropagation(); + const $currentDropdown = $(event.currentTarget); + + // Close the previously opened dropdown if any + $dropdowns.not($currentDropdown).removeClass('opened'); + + $currentDropdown.toggleClass('opened'); + currentDropdown = $currentDropdown.hasClass('opened') ? $currentDropdown : null; + }; + +const selectOption = (event) => { + const $currentDropdown = currentDropdown; + $currentDropdown.find('input').val($(event.currentTarget).text()); + + if($currentDropdown.attr('id') == 'data-ant') { + socket.send("Z" + $(event.currentTarget).attr('data-value')); + } + + // Use setTimeout to delay class removal + setTimeout(() => { + $currentDropdown.removeClass('opened'); + currentDropdown = null; + }, 10); // Adjust the delay as needed +}; + +const closeDropdownFromOutside = (event) => { + const $currentDropdown = currentDropdown && $(currentDropdown); + const isClickedInsideDropdown = $currentDropdown && $currentDropdown.has(event.target).length > 0; + + if (!isClickedInsideDropdown && $currentDropdown && $currentDropdown.hasClass('opened')) { + $currentDropdown.removeClass('opened'); + currentDropdown = null; + } +}; + +// Event Listeners +$(document).on('click', closeDropdownFromOutside); + +$listOfOptions.on('click', selectOption); + +$dropdowns.on('click', toggleDropdown); diff --git a/web/js/main.js b/web/js/main.js index e8e7500..3e20210 100644 --- a/web/js/main.js +++ b/web/js/main.js @@ -4,6 +4,8 @@ var port = hostParts[1] || '8080'; // Extract the port or use a default (e.g., 8 var socketAddress = 'ws://' + hostname + ':' + port + '/text'; // Use 'wss' for secure WebSocket connections (recommended for external access) var socket = new WebSocket(socketAddress); var parsedData; +var data = []; +let signalChart; const europe_programmes = [ "No PTY", "News", "Current Affairs", "Info", @@ -16,130 +18,16 @@ const europe_programmes = [ ]; $(document).ready(function() { - var dataContainer = $('#data-container'); var canvas = $('#signal-canvas')[0]; - var context = canvas.getContext('2d'); var signalToggle = $("#signal-units-toggle"); canvas.width = canvas.parentElement.clientWidth; - - var data = []; - var maxDataPoints = 300; - var pointWidth = (canvas.width - 80) / maxDataPoints; + canvas.height = canvas.parentElement.clientHeight; getInitialSettings(); // Start updating the canvas - updateCanvas(); - - function updateCanvas() { - const color2 = getComputedStyle(document.documentElement).getPropertyValue('--color-2').trim(); - const color4 = getComputedStyle(document.documentElement).getPropertyValue('--color-4').trim(); - - while (data.length >= maxDataPoints) { - data.shift(); - } - - // Modify the WebSocket onmessage callback - socket.onmessage = (event) => { - parsedData = JSON.parse(event.data); - - updatePanels(parsedData); - - // Push the new signal data to the array - data.push(parsedData.signal); - const actualLowestValue = Math.min(...data); - const actualHighestValue = Math.max(...data); - zoomMinValue = actualLowestValue - ((actualHighestValue - actualLowestValue) / 2); - zoomMaxValue = actualHighestValue + ((actualHighestValue - actualLowestValue) / 2); - zoomAvgValue = (zoomMaxValue - zoomMinValue) / 2 + zoomMinValue; - - // Clear the canvas - context.clearRect(0, 0, canvas.width, canvas.height); - - // Draw the signal graph with smooth shifting - context.beginPath(); - - const startingIndex = Math.max(0, data.length - maxDataPoints); - - for (let i = startingIndex; i < data.length; i++) { - const x = canvas.width - (data.length - i) * pointWidth - 40; - const y = canvas.height - (data[i] - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue)); - - if (i === startingIndex) { - context.moveTo(x, y); - } else { - const prevX = canvas.width - (data.length - i + 1) * pointWidth - 40; - const prevY = canvas.height - (data[i - 1] - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue)); - - // Interpolate between the current and previous points - const interpolatedX = (x + prevX) / 2; - const interpolatedY = (y + prevY) / 2; - - context.quadraticCurveTo(prevX, prevY, interpolatedX, interpolatedY); - } - } - - context.strokeStyle = color4; - context.lineWidth = 1; - context.stroke(); - - // Draw horizontal lines for lowest, highest, and average values - context.strokeStyle = color2; - context.lineWidth = 1; - - // Draw the lowest value line - const lowestY = canvas.height - (zoomMinValue - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue)); - context.beginPath(); - context.moveTo(40, lowestY - 18); - context.lineTo(canvas.width - 40, lowestY - 18); - context.stroke(); - - // Draw the highest value line - const highestY = canvas.height - (zoomMaxValue - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue)); - context.beginPath(); - context.moveTo(40, highestY + 10); - context.lineTo(canvas.width - 40, highestY + 10); - context.stroke(); - - const avgY = canvas.height / 2; - context.beginPath(); - context.moveTo(40, avgY - 7); - context.lineTo(canvas.width - 40, avgY - 7); - context.stroke(); - - // Label the lines with their values - context.fillStyle = color4; - context.font = '12px Titillium Web'; - - const signalUnit = localStorage.getItem('signalUnit'); - let offset; - - if (signalUnit === 'dbuv') { - offset = 11.25; - } else if (signalUnit === 'dbm') { - offset = 120; - } else { - offset = 0; - } - - context.textAlign = 'right'; - context.fillText(`${(zoomMinValue - offset).toFixed(1)}`, 35, lowestY - 14); - context.fillText(`${(zoomMaxValue - offset).toFixed(1)}`, 35, highestY + 14); - context.fillText(`${(zoomAvgValue - offset).toFixed(1)}`, 35, avgY - 3); - - context.textAlign = 'left'; - context.fillText(`${(zoomMinValue - offset).toFixed(1)}`, canvas.width - 35, lowestY - 14); - context.fillText(`${(zoomMaxValue - offset).toFixed(1)}`, canvas.width - 35, highestY + 14); - context.fillText(`${(zoomAvgValue - offset).toFixed(1)}`, canvas.width - 35, avgY - 3); - - // Update the data container with the latest data - dataContainer.html(event.data + '
'); - }; - - requestAnimationFrame(updateCanvas); - } - + initCanvas(); signalToggle.on("change", function() { const signalText = localStorage.getItem('signalUnit'); @@ -250,6 +138,128 @@ function getInitialSettings() { } }); } + +function initCanvas(parsedData) { + signalToggle = $("#signal-units-toggle"); + + // Check if signalChart is already initialized + if (!signalChart) { + signalChart = { + canvas: $('#signal-canvas')[0], + context: $('#signal-canvas')[0].getContext('2d'), + parsedData: parsedData, + maxDataPoints: 300, + } + signalChart.pointWidth = (signalChart.canvas.width - 80) / signalChart.maxDataPoints; + } + + updateCanvas(parsedData, signalChart); +} + +function updateCanvas(parsedData, signalChart) { + const color2 = getComputedStyle(document.documentElement).getPropertyValue('--color-2').trim(); + const color4 = getComputedStyle(document.documentElement).getPropertyValue('--color-4').trim(); + const {context, canvas, maxDataPoints, pointWidth} = signalChart; + + while (data.length >= signalChart.maxDataPoints) { + data.shift(); + } + + const actualLowestValue = Math.min(...data); + const actualHighestValue = Math.max(...data); + zoomMinValue = actualLowestValue - ((actualHighestValue - actualLowestValue) / 2); + zoomMaxValue = actualHighestValue + ((actualHighestValue - actualLowestValue) / 2); + zoomAvgValue = (zoomMaxValue - zoomMinValue) / 2 + zoomMinValue; + + // Clear the canvas + if(context) { + context.clearRect(0, 0, canvas.width, canvas.height); + + // Draw the signal graph with smooth shifting + context.beginPath(); + } + + const startingIndex = Math.max(0, data.length - maxDataPoints); + + for (let i = startingIndex; i < data.length; i++) { + const x = canvas.width - (data.length - i) * pointWidth - 40; + const y = canvas.height - (data[i] - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue)); + + if (i === startingIndex) { + context.moveTo(x, y); + } else { + const prevX = canvas.width - (data.length - i + 1) * pointWidth - 40; + const prevY = canvas.height - (data[i - 1] - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue)); + + // Interpolate between the current and previous points + const interpolatedX = (x + prevX) / 2; + const interpolatedY = (y + prevY) / 2; + + context.quadraticCurveTo(prevX, prevY, interpolatedX, interpolatedY); + } + } + + context.strokeStyle = color4; + context.lineWidth = 1; + context.stroke(); + + // Draw horizontal lines for lowest, highest, and average values + context.strokeStyle = color2; + context.lineWidth = 1; + + // Draw the lowest value line + const lowestY = canvas.height - (zoomMinValue - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue)); + context.beginPath(); + context.moveTo(40, lowestY - 18); + context.lineTo(canvas.width - 40, lowestY - 18); + context.stroke(); + + // Draw the highest value line + const highestY = canvas.height - (zoomMaxValue - zoomMinValue) * (canvas.height / (zoomMaxValue - zoomMinValue)); + context.beginPath(); + context.moveTo(40, highestY + 10); + context.lineTo(canvas.width - 40, highestY + 10); + context.stroke(); + + const avgY = canvas.height / 2; + context.beginPath(); + context.moveTo(40, avgY - 7); + context.lineTo(canvas.width - 40, avgY - 7); + context.stroke(); + + // Label the lines with their values + context.fillStyle = color4; + context.font = '12px Titillium Web'; + + const signalUnit = localStorage.getItem('signalUnit'); + let offset; + + if (signalUnit === 'dbuv') { + offset = 11.25; + } else if (signalUnit === 'dbm') { + offset = 120; + } else { + offset = 0; + } + + context.textAlign = 'right'; + context.fillText(`${(zoomMinValue - offset).toFixed(1)}`, 35, lowestY - 14); + context.fillText(`${(zoomMaxValue - offset).toFixed(1)}`, 35, highestY + 14); + context.fillText(`${(zoomAvgValue - offset).toFixed(1)}`, 35, avgY - 3); + + context.textAlign = 'left'; + context.fillText(`${(zoomMinValue - offset).toFixed(1)}`, canvas.width - 35, lowestY - 14); + context.fillText(`${(zoomMaxValue - offset).toFixed(1)}`, canvas.width - 35, highestY + 14); + context.fillText(`${(zoomAvgValue - offset).toFixed(1)}`, canvas.width - 35, avgY - 3); + + requestAnimationFrame(() => updateCanvas(parsedData, signalChart)); +} + +socket.onmessage = (event) => { + parsedData = JSON.parse(event.data); + updatePanels(parsedData); + data.push(parsedData.signal); +}; function compareNumbers(a, b) { return a - b; @@ -444,6 +454,7 @@ function updateDataElements(parsedData) { $('#data-rt0').html(processString(parsedData.rt0, parsedData.rt0_errors)); $('#data-rt1').html(processString(parsedData.rt1, parsedData.rt1_errors)); $('.data-flag').html(``); + $('#data-ant').find('input').val($(parsedData.ant).text()); } let isEventListenerAdded = false; @@ -500,9 +511,9 @@ function createListItem(element) { function updateButtonState(buttonId, value) { var button = $("#" + buttonId); if (value === 0) { - button.addClass("bg-gray"); + button.addClass("btn-disabled"); } else { - button.removeClass("bg-gray"); + button.removeClass("btn-disabled"); } } diff --git a/web/js/settings.js b/web/js/settings.js index 1d29b2d..b98a539 100644 --- a/web/js/settings.js +++ b/web/js/settings.js @@ -1,53 +1,59 @@ -/* Themes */ -const themes = { - theme1: ['#111', '#aaa'], - theme2: ['#1f0c0c', '#ff7070'], - theme3: ['#121c0c', '#a9ff70'], - theme4: ['#0c1c1b', '#68f7ee'], - theme5: ['#171106', '#f5b642'], - theme6: ['#21091d', '#ed51d3'], - theme7: ['#1d1838', '#8069fa'], - theme8: ['#000', '#888'], -}; + // Themes + const themes = { + theme1: ['#111', '#aaa'], + theme2: ['#1f0c0c', '#ff7070'], + theme3: ['#121c0c', '#a9ff70'], + theme4: ['#0c1c1b', '#68f7ee'], + theme5: ['#171106', '#f5b642'], + theme6: ['#21091d', '#ed51d3'], + theme7: ['#1d1838', '#8069fa'], + theme8: ['#000', '#888'], + }; -/* Signal Units */ -const signalUnits = { - dbf: ['dBf'], - dbuv: ['dBµV'], - dbm: ['dBm'], -}; + // Signal Units + const signalUnits = { + dbf: ['dBf'], + dbuv: ['dBµV'], + dbm: ['dBm'], + }; -$(document).ready(() => { - const themeSelector = $('#theme-selector'); - const savedTheme = localStorage.getItem('theme'); + $(document).ready(() => { + // Theme Selector + const themeSelector = $('#theme-selector'); + const savedTheme = localStorage.getItem('theme'); + const savedUnit = localStorage.getItem('signalUnit'); - if (savedTheme && themes[savedTheme]) { - setTheme(savedTheme); - themeSelector.val(savedTheme); - } + if (savedTheme && themes[savedTheme]) { + setTheme(savedTheme); + themeSelector.find('input').val(themeSelector.find('.option[data-value="' + savedTheme + '"]').text()); + } - themeSelector.on('change', (event) => { - const selectedTheme = event.target.value; - setTheme(selectedTheme); - localStorage.setItem('theme', selectedTheme); - }); + themeSelector.on('click', '.option', (event) => { + const selectedTheme = $(event.target).data('value'); + setTheme(selectedTheme); + themeSelector.find('input').val($(event.target).text()); // Set the text of the clicked option to the input + localStorage.setItem('theme', selectedTheme); + }); - const signalSelector = $('#signal-selector'); + // Signal Selector + const signalSelector = $('#signal-selector'); - if (localStorage.getItem('signalUnit')) { - signalSelector.val(localStorage.getItem('signalUnit')); - } + if (localStorage.getItem('signalUnit')) { + signalSelector.find('input').val(signalSelector.find('.option[data-value="' + savedUnit + '"]').text()); + } - signalSelector.on('change', (event) => { - const selectedSignalUnit = event.target.value; - localStorage.setItem('signalUnit', selectedSignalUnit); - }); + signalSelector.on('click', '.option', (event) => { + const selectedSignalUnit = $(event.target).data('value'); + signalSelector.find('input').val($(event.target).text()); // Set the text of the clicked option to the input + localStorage.setItem('signalUnit', selectedSignalUnit); + }); }); -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) { + $(':root').css('--color-main', themeColors[0]); + $(':root').css('--color-main-bright', themeColors[1]); + } + } \ No newline at end of file diff --git a/web/js/webserver.js b/web/js/webserver.js index 00f18a0..cf91a64 100644 --- a/web/js/webserver.js +++ b/web/js/webserver.js @@ -1,3 +1,4 @@ $.getScript('/js/main.js'); +$.getScript('/js/dropdown.js'); $.getScript('/js/modal.js'); $.getScript('/js/settings.js'); \ No newline at end of file