1
0
mirror of https://github.com/KubaPro010/fm-dx-webserver.git synced 2026-02-26 22:13:53 +01:00

we're up to date with the terrible

This commit is contained in:
2026-02-24 09:52:39 +01:00
parent 098b6ba4e9
commit 0ae484529d
17 changed files with 673 additions and 459 deletions

576
package-lock.json generated
View File

@@ -1,24 +1,25 @@
{ {
"name": "fm-dx-webserver", "name": "fm-dx-webserver",
"version": "1.3.12", "version": "1.4.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "fm-dx-webserver", "name": "fm-dx-webserver",
"version": "1.3.12", "version": "1.4.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@mapbox/node-pre-gyp": "2.0.0", "@mapbox/node-pre-gyp": "2.0.3",
"body-parser": "2.2.0", "body-parser": "2.2.2",
"ejs": "3.1.10", "ejs": "4.0.1",
"express": "5.1.0", "express": "5.2.1",
"express-session": "1.18.2", "express-session": "1.19.0",
"ffmpeg-static": "5.2.0", "ffmpeg-static": "5.3.0",
"http": "0.0.1-security", "http": "0.0.1-security",
"koffi": "2.7.2",
"net": "1.0.2", "net": "1.0.2",
"serialport": "12.0.0", "serialport": "13.0.0",
"ws": "8.18.1" "ws": "8.19.0"
} }
}, },
"node_modules/@derhuerst/http-basic": { "node_modules/@derhuerst/http-basic": {
@@ -48,9 +49,9 @@
} }
}, },
"node_modules/@mapbox/node-pre-gyp": { "node_modules/@mapbox/node-pre-gyp": {
"version": "2.0.0", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz",
"integrity": "sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==", "integrity": "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"consola": "^3.2.3", "consola": "^3.2.3",
@@ -147,28 +148,30 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"node_modules/@serialport/bindings-cpp": { "node_modules/@serialport/bindings-cpp": {
"version": "12.0.1", "version": "13.0.0",
"resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-12.0.1.tgz", "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-13.0.0.tgz",
"integrity": "sha512-r2XOwY2dDvbW7dKqSPIk2gzsr6M6Qpe9+/Ngs94fNaNlcTRCV02PfaoDmRgcubpNVVcLATlxSxPTIDw12dbKOg==", "integrity": "sha512-r25o4Bk/vaO1LyUfY/ulR6hCg/aWiN6Wo2ljVlb4Pj5bqWGcSRC4Vse4a9AcapuAu/FeBzHCbKMvRQeCuKjzIQ==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT",
"dependencies": { "dependencies": {
"@serialport/bindings-interface": "1.2.2", "@serialport/bindings-interface": "1.2.2",
"@serialport/parser-readline": "11.0.0", "@serialport/parser-readline": "12.0.0",
"debug": "4.3.4", "debug": "4.4.0",
"node-addon-api": "7.0.0", "node-addon-api": "8.3.0",
"node-gyp-build": "4.6.0" "node-gyp-build": "4.8.4"
}, },
"engines": { "engines": {
"node": ">=16.0.0" "node": ">=18.0.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/serialport/donate" "url": "https://opencollective.com/serialport/donate"
} }
}, },
"node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": {
"version": "11.0.0", "version": "12.0.0",
"resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-11.0.0.tgz", "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-12.0.0.tgz",
"integrity": "sha512-aZLJhlRTjSmEwllLG7S4J8s8ctRAS0cbvCpO87smLvl3e4BgzbVgF6Z6zaJd3Aji2uSiYgfedCdNc4L6W+1E2g==", "integrity": "sha512-gu26tVt5lQoybhorLTPsH2j2LnX3AOP2x/34+DUSTNaUTzu2fBXw+isVjQJpUBFWu6aeQRZw5bJol5X9Gxjblw==",
"license": "MIT",
"engines": { "engines": {
"node": ">=12.0.0" "node": ">=12.0.0"
}, },
@@ -177,11 +180,12 @@
} }
}, },
"node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": {
"version": "11.0.0", "version": "12.0.0",
"resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-11.0.0.tgz", "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-12.0.0.tgz",
"integrity": "sha512-rRAivhRkT3YO28WjmmG4FQX6L+KMb5/ikhyylRfzWPw0nSXy97+u07peS9CbHqaNvJkMhH1locp2H36aGMOEIA==", "integrity": "sha512-O7cywCWC8PiOMvo/gglEBfAkLjp/SENEML46BXDykfKP5mTPM46XMaX1L0waWU6DXJpBgjaL7+yX6VriVPbN4w==",
"license": "MIT",
"dependencies": { "dependencies": {
"@serialport/parser-delimiter": "11.0.0" "@serialport/parser-delimiter": "12.0.0"
}, },
"engines": { "engines": {
"node": ">=12.0.0" "node": ">=12.0.0"
@@ -191,11 +195,12 @@
} }
}, },
"node_modules/@serialport/bindings-cpp/node_modules/debug": { "node_modules/@serialport/bindings-cpp/node_modules/debug": {
"version": "4.3.4", "version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"license": "MIT",
"dependencies": { "dependencies": {
"ms": "2.1.2" "ms": "^2.1.3"
}, },
"engines": { "engines": {
"node": ">=6.0" "node": ">=6.0"
@@ -207,19 +212,10 @@
} }
}, },
"node_modules/@serialport/bindings-cpp/node_modules/ms": { "node_modules/@serialport/bindings-cpp/node_modules/ms": {
"version": "2.1.2", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
}, "license": "MIT"
"node_modules/@serialport/bindings-cpp/node_modules/node-gyp-build": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz",
"integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==",
"bin": {
"node-gyp-build": "bin.js",
"node-gyp-build-optional": "optional.js",
"node-gyp-build-test": "build-test.js"
}
}, },
"node_modules/@serialport/bindings-interface": { "node_modules/@serialport/bindings-interface": {
"version": "1.2.2", "version": "1.2.2",
@@ -230,136 +226,148 @@
} }
}, },
"node_modules/@serialport/parser-byte-length": { "node_modules/@serialport/parser-byte-length": {
"version": "12.0.0", "version": "13.0.0",
"resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-12.0.0.tgz", "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-13.0.0.tgz",
"integrity": "sha512-0ei0txFAj+s6FTiCJFBJ1T2hpKkX8Md0Pu6dqMrYoirjPskDLJRgZGLqoy3/lnU1bkvHpnJO+9oJ3PB9v8rNlg==", "integrity": "sha512-32yvqeTAqJzAEtX5zCrN1Mej56GJ5h/cVFsCDPbF9S1ZSC9FWjOqNAgtByseHfFTSTs/4ZBQZZcZBpolt8sUng==",
"license": "MIT",
"engines": { "engines": {
"node": ">=12.0.0" "node": ">=20.0.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/serialport/donate" "url": "https://opencollective.com/serialport/donate"
} }
}, },
"node_modules/@serialport/parser-cctalk": { "node_modules/@serialport/parser-cctalk": {
"version": "12.0.0", "version": "13.0.0",
"resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-12.0.0.tgz", "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-13.0.0.tgz",
"integrity": "sha512-0PfLzO9t2X5ufKuBO34DQKLXrCCqS9xz2D0pfuaLNeTkyGUBv426zxoMf3rsMRodDOZNbFblu3Ae84MOQXjnZw==", "integrity": "sha512-RErAe57g9gvnlieVYGIn1xymb1bzNXb2QtUQd14FpmbQQYlcrmuRnJwKa1BgTCujoCkhtaTtgHlbBWOxm8U2uA==",
"license": "MIT",
"engines": { "engines": {
"node": ">=12.0.0" "node": ">=20.0.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/serialport/donate" "url": "https://opencollective.com/serialport/donate"
} }
}, },
"node_modules/@serialport/parser-delimiter": { "node_modules/@serialport/parser-delimiter": {
"version": "12.0.0", "version": "13.0.0",
"resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-12.0.0.tgz", "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-13.0.0.tgz",
"integrity": "sha512-gu26tVt5lQoybhorLTPsH2j2LnX3AOP2x/34+DUSTNaUTzu2fBXw+isVjQJpUBFWu6aeQRZw5bJol5X9Gxjblw==", "integrity": "sha512-Qqyb0FX1avs3XabQqNaZSivyVbl/yl0jywImp7ePvfZKLwx7jBZjvL+Hawt9wIG6tfq6zbFM24vzCCK7REMUig==",
"license": "MIT",
"engines": { "engines": {
"node": ">=12.0.0" "node": ">=20.0.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/serialport/donate" "url": "https://opencollective.com/serialport/donate"
} }
}, },
"node_modules/@serialport/parser-inter-byte-timeout": { "node_modules/@serialport/parser-inter-byte-timeout": {
"version": "12.0.0", "version": "13.0.0",
"resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-12.0.0.tgz", "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-13.0.0.tgz",
"integrity": "sha512-GnCh8K0NAESfhCuXAt+FfBRz1Cf9CzIgXfp7SdMgXwrtuUnCC/yuRTUFWRvuzhYKoAo1TL0hhUo77SFHUH1T/w==", "integrity": "sha512-a0w0WecTW7bD2YHWrpTz1uyiWA2fDNym0kjmPeNSwZ2XCP+JbirZt31l43m2ey6qXItTYVuQBthm75sPVeHnGA==",
"license": "MIT",
"engines": { "engines": {
"node": ">=12.0.0" "node": ">=20.0.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/serialport/donate" "url": "https://opencollective.com/serialport/donate"
} }
}, },
"node_modules/@serialport/parser-packet-length": { "node_modules/@serialport/parser-packet-length": {
"version": "12.0.0", "version": "13.0.0",
"resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-12.0.0.tgz", "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-13.0.0.tgz",
"integrity": "sha512-p1hiCRqvGHHLCN/8ZiPUY/G0zrxd7gtZs251n+cfNTn+87rwcdUeu9Dps3Aadx30/sOGGFL6brIRGK4l/t7MuQ==", "integrity": "sha512-60ZDDIqYRi0Xs2SPZUo4Jr5LLIjtb+rvzPKMJCohrO6tAqSDponcNpcB1O4W21mKTxYjqInSz+eMrtk0LLfZIg==",
"license": "MIT",
"engines": { "engines": {
"node": ">=8.6.0" "node": ">=8.6.0"
} }
}, },
"node_modules/@serialport/parser-readline": { "node_modules/@serialport/parser-readline": {
"version": "12.0.0", "version": "13.0.0",
"resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-12.0.0.tgz", "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-13.0.0.tgz",
"integrity": "sha512-O7cywCWC8PiOMvo/gglEBfAkLjp/SENEML46BXDykfKP5mTPM46XMaX1L0waWU6DXJpBgjaL7+yX6VriVPbN4w==", "integrity": "sha512-dov3zYoyf0dt1Sudd1q42VVYQ4WlliF0MYvAMA3MOyiU1IeG4hl0J6buBA2w4gl3DOCC05tGgLDN/3yIL81gsA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@serialport/parser-delimiter": "12.0.0" "@serialport/parser-delimiter": "13.0.0"
}, },
"engines": { "engines": {
"node": ">=12.0.0" "node": ">=20.0.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/serialport/donate" "url": "https://opencollective.com/serialport/donate"
} }
}, },
"node_modules/@serialport/parser-ready": { "node_modules/@serialport/parser-ready": {
"version": "12.0.0", "version": "13.0.0",
"resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-12.0.0.tgz", "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-13.0.0.tgz",
"integrity": "sha512-ygDwj3O4SDpZlbrRUraoXIoIqb8sM7aMKryGjYTIF0JRnKeB1ys8+wIp0RFMdFbO62YriUDextHB5Um5cKFSWg==", "integrity": "sha512-JNUQA+y2Rfs4bU+cGYNqOPnNMAcayhhW+XJZihSLQXOHcZsFnOa2F9YtMg9VXRWIcnHldHYtisp62Etjlw24bw==",
"license": "MIT",
"engines": { "engines": {
"node": ">=12.0.0" "node": ">=20.0.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/serialport/donate" "url": "https://opencollective.com/serialport/donate"
} }
}, },
"node_modules/@serialport/parser-regex": { "node_modules/@serialport/parser-regex": {
"version": "12.0.0", "version": "13.0.0",
"resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-12.0.0.tgz", "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-13.0.0.tgz",
"integrity": "sha512-dCAVh4P/pZrLcPv9NJ2mvPRBg64L5jXuiRxIlyxxdZGH4WubwXVXY/kBTihQmiAMPxbT3yshSX8f2+feqWsxqA==", "integrity": "sha512-m7HpIf56G5XcuDdA3DB34Z0pJiwxNRakThEHjSa4mG05OnWYv0IG8l2oUyYfuGMowQWaVnQ+8r+brlPxGVH+eA==",
"license": "MIT",
"engines": { "engines": {
"node": ">=12.0.0" "node": ">=20.0.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/serialport/donate" "url": "https://opencollective.com/serialport/donate"
} }
}, },
"node_modules/@serialport/parser-slip-encoder": { "node_modules/@serialport/parser-slip-encoder": {
"version": "12.0.0", "version": "13.0.0",
"resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-12.0.0.tgz", "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-13.0.0.tgz",
"integrity": "sha512-0APxDGR9YvJXTRfY+uRGhzOhTpU5akSH183RUcwzN7QXh8/1jwFsFLCu0grmAUfi+fItCkR+Xr1TcNJLR13VNA==", "integrity": "sha512-fUHZEExm6izJ7rg0A1yjXwu4sOzeBkPAjDZPfb+XQoqgtKAk+s+HfICiYn7N2QU9gyaeCO8VKgWwi+b/DowYOg==",
"license": "MIT",
"engines": { "engines": {
"node": ">=12.0.0" "node": ">=20.0.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/serialport/donate" "url": "https://opencollective.com/serialport/donate"
} }
}, },
"node_modules/@serialport/parser-spacepacket": { "node_modules/@serialport/parser-spacepacket": {
"version": "12.0.0", "version": "13.0.0",
"resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-12.0.0.tgz", "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-13.0.0.tgz",
"integrity": "sha512-dozONxhPC/78pntuxpz/NOtVps8qIc/UZzdc/LuPvVsqCoJXiRxOg6ZtCP/W58iibJDKPZPAWPGYeZt9DJxI+Q==", "integrity": "sha512-DoXJ3mFYmyD8X/8931agJvrBPxqTaYDsPoly9/cwQSeh/q4EjQND9ySXBxpWz5WcpyCU4jOuusqCSAPsbB30Eg==",
"license": "MIT",
"engines": { "engines": {
"node": ">=12.0.0" "node": ">=20.0.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/serialport/donate" "url": "https://opencollective.com/serialport/donate"
} }
}, },
"node_modules/@serialport/stream": { "node_modules/@serialport/stream": {
"version": "12.0.0", "version": "13.0.0",
"resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-12.0.0.tgz", "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-13.0.0.tgz",
"integrity": "sha512-9On64rhzuqKdOQyiYLYv2lQOh3TZU/D3+IWCR5gk0alPel2nwpp4YwDEGiUBfrQZEdQ6xww0PWkzqth4wqwX3Q==", "integrity": "sha512-F7xLJKsjGo2WuEWMSEO1SimRcOA+WtWICsY13r0ahx8s2SecPQH06338g28OT7cW7uRXI7oEQAk62qh5gHJW3g==",
"license": "MIT",
"dependencies": { "dependencies": {
"@serialport/bindings-interface": "1.2.2", "@serialport/bindings-interface": "1.2.2",
"debug": "4.3.4" "debug": "4.4.0"
}, },
"engines": { "engines": {
"node": ">=12.0.0" "node": ">=20.0.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/serialport/donate" "url": "https://opencollective.com/serialport/donate"
} }
}, },
"node_modules/@serialport/stream/node_modules/debug": { "node_modules/@serialport/stream/node_modules/debug": {
"version": "4.3.4", "version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"license": "MIT",
"dependencies": { "dependencies": {
"ms": "2.1.2" "ms": "^2.1.3"
}, },
"engines": { "engines": {
"node": ">=6.0" "node": ">=6.0"
@@ -371,9 +379,10 @@
} }
}, },
"node_modules/@serialport/stream/node_modules/ms": { "node_modules/@serialport/stream/node_modules/ms": {
"version": "2.1.2", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "10.17.60", "version": "10.17.60",
@@ -434,54 +443,49 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/async": { "node_modules/async": {
"version": "3.2.5", "version": "3.2.6",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
"license": "MIT"
}, },
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"license": "MIT",
"engines": {
"node": "18 || 20 || >=22"
}
}, },
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "2.2.0", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"bytes": "^3.1.2", "bytes": "^3.1.2",
"content-type": "^1.0.5", "content-type": "^1.0.5",
"debug": "^4.4.0", "debug": "^4.4.3",
"http-errors": "^2.0.0", "http-errors": "^2.0.0",
"iconv-lite": "^0.6.3", "iconv-lite": "^0.7.0",
"on-finished": "^2.4.1", "on-finished": "^2.4.1",
"qs": "^6.14.0", "qs": "^6.14.1",
"raw-body": "^3.0.0", "raw-body": "^3.0.1",
"type-is": "^2.0.0" "type-is": "^2.0.1"
}, },
"engines": { "engines": {
"node": ">=18" "node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/body-parser/node_modules/debug": { "node_modules/body-parser/node_modules/debug": {
"version": "4.4.0", "version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
@@ -502,12 +506,15 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.11", "version": "5.0.3",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==",
"license": "MIT",
"dependencies": { "dependencies": {
"balanced-match": "^1.0.0", "balanced-match": "^4.0.2"
"concat-map": "0.0.1" },
"engines": {
"node": "18 || 20 || >=22"
} }
}, },
"node_modules/buffer-from": { "node_modules/buffer-from": {
@@ -558,21 +565,6 @@
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
}, },
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chownr": { "node_modules/chownr": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
@@ -582,27 +574,6 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"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/concat-stream": { "node_modules/concat-stream": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
@@ -708,18 +679,18 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
}, },
"node_modules/ejs": { "node_modules/ejs": {
"version": "3.1.10", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", "resolved": "https://registry.npmjs.org/ejs/-/ejs-4.0.1.tgz",
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "integrity": "sha512-krvQtxc0btwSm/nvnt1UpnaFDFVJpJ0fdckmALpCgShsr/iGYHTnJiUliZTgmzq/UxTX33TtOQVKaNigMQp/6Q==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"jake": "^10.8.5" "jake": "^10.9.1"
}, },
"bin": { "bin": {
"ejs": "bin/cli.js" "ejs": "bin/cli.js"
}, },
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.12.18"
} }
}, },
"node_modules/encodeurl": { "node_modules/encodeurl": {
@@ -785,18 +756,19 @@
} }
}, },
"node_modules/express": { "node_modules/express": {
"version": "5.1.0", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"accepts": "^2.0.0", "accepts": "^2.0.0",
"body-parser": "^2.2.0", "body-parser": "^2.2.1",
"content-disposition": "^1.0.0", "content-disposition": "^1.0.0",
"content-type": "^1.0.5", "content-type": "^1.0.5",
"cookie": "^0.7.1", "cookie": "^0.7.1",
"cookie-signature": "^1.2.1", "cookie-signature": "^1.2.1",
"debug": "^4.4.0", "debug": "^4.4.0",
"depd": "^2.0.0",
"encodeurl": "^2.0.0", "encodeurl": "^2.0.0",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"etag": "^1.8.1", "etag": "^1.8.1",
@@ -827,22 +799,26 @@
} }
}, },
"node_modules/express-session": { "node_modules/express-session": {
"version": "1.18.2", "version": "1.19.0",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz", "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.19.0.tgz",
"integrity": "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A==", "integrity": "sha512-0csaMkGq+vaiZTmSMMGkfdCOabYv192VbytFypcvI0MANrp+4i/7yEkJ0sbAEhycQjntaKGzYfjfXQyVb7BHMA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"cookie": "0.7.2", "cookie": "~0.7.2",
"cookie-signature": "1.0.7", "cookie-signature": "~1.0.7",
"debug": "2.6.9", "debug": "~2.6.9",
"depd": "~2.0.0", "depd": "~2.0.0",
"on-headers": "~1.1.0", "on-headers": "~1.1.0",
"parseurl": "~1.3.3", "parseurl": "~1.3.3",
"safe-buffer": "5.2.1", "safe-buffer": "~5.2.1",
"uid-safe": "~2.1.5" "uid-safe": "~2.1.5"
}, },
"engines": { "engines": {
"node": ">= 0.8.0" "node": ">= 0.8.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/express-session/node_modules/cookie": { "node_modules/express-session/node_modules/cookie": {
@@ -883,10 +859,11 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/ffmpeg-static": { "node_modules/ffmpeg-static": {
"version": "5.2.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/ffmpeg-static/-/ffmpeg-static-5.2.0.tgz", "resolved": "https://registry.npmjs.org/ffmpeg-static/-/ffmpeg-static-5.3.0.tgz",
"integrity": "sha512-WrM7kLW+do9HLr+H6tk7LzQ7kPqbAgLjdzNE32+u3Ff11gXt9Kkkd2nusGFrlWMIe+XaA97t+I8JS7sZIrvRgA==", "integrity": "sha512-H+K6sW6TiIX6VGend0KQwthe+kaceeH/luE8dIZyOP35ik7ahYojDuqlTV1bOrtEwl01sy2HFNGQfi5IDJvotg==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": { "dependencies": {
"@derhuerst/http-basic": "^8.2.0", "@derhuerst/http-basic": "^8.2.0",
"env-paths": "^2.2.0", "env-paths": "^2.2.0",
@@ -898,30 +875,15 @@
} }
}, },
"node_modules/filelist": { "node_modules/filelist": {
"version": "1.0.4", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.5.tgz",
"integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "integrity": "sha512-ct/ckWBV/9Dg3MlvCXsLcSUyoWwv9mCKqlhLNB2DAuXR/NZolSXlQqP5dyy6guWlPXBhodZyZ5lGPQcbQDxrEQ==",
"license": "Apache-2.0",
"dependencies": { "dependencies": {
"minimatch": "^5.0.1" "minimatch": "^10.2.1"
}
},
"node_modules/filelist/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/filelist/node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dependencies": {
"brace-expansion": "^2.0.1"
}, },
"engines": { "engines": {
"node": ">=10" "node": "20 || >=22"
} }
}, },
"node_modules/finalhandler": { "node_modules/finalhandler": {
@@ -1039,14 +1001,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/has-symbols": { "node_modules/has-symbols": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
@@ -1077,18 +1031,23 @@
"integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g==" "integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g=="
}, },
"node_modules/http-errors": { "node_modules/http-errors": {
"version": "2.0.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"depd": "2.0.0", "depd": "~2.0.0",
"inherits": "2.0.4", "inherits": "~2.0.4",
"setprototypeof": "1.2.0", "setprototypeof": "~1.2.0",
"statuses": "2.0.1", "statuses": "~2.0.2",
"toidentifier": "1.0.1" "toidentifier": "~1.0.1"
}, },
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.8"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/http-response-object": { "node_modules/http-response-object": {
@@ -1133,15 +1092,19 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.6.3", "version": "0.7.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0" "safer-buffer": ">= 2.1.2 < 3.0.0"
}, },
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/inherits": { "node_modules/inherits": {
@@ -1164,14 +1127,14 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/jake": { "node_modules/jake": {
"version": "10.8.7", "version": "10.9.4",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
"integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
"license": "Apache-2.0",
"dependencies": { "dependencies": {
"async": "^3.2.3", "async": "^3.2.6",
"chalk": "^4.0.2",
"filelist": "^1.0.4", "filelist": "^1.0.4",
"minimatch": "^3.1.2" "picocolors": "^1.1.1"
}, },
"bin": { "bin": {
"jake": "bin/cli.js" "jake": "bin/cli.js"
@@ -1180,6 +1143,13 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/koffi": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/koffi/-/koffi-2.7.2.tgz",
"integrity": "sha512-AWcsEKETQuELxK0Wq/aXDkDiNFFY41TxZQSrKm2Nd6HO/KTHeohPOOIlh2OfQnBXJbRjx5etpWt8cbqMUZo2sg==",
"hasInstallScript": true,
"license": "MIT"
},
"node_modules/lru-cache": { "node_modules/lru-cache": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -1243,14 +1213,18 @@
} }
}, },
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.2", "version": "10.2.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==",
"license": "BlueOak-1.0.0",
"dependencies": { "dependencies": {
"brace-expansion": "^1.1.7" "brace-expansion": "^5.0.2"
}, },
"engines": { "engines": {
"node": "*" "node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/minipass": { "node_modules/minipass": {
@@ -1309,9 +1283,13 @@
"integrity": "sha512-kbhcj2SVVR4caaVnGLJKmlk2+f+oLkjqdKeQlmUtz6nGzOpbcobwVIeSURNgraV/v3tlmGIX82OcPCl0K6RbHQ==" "integrity": "sha512-kbhcj2SVVR4caaVnGLJKmlk2+f+oLkjqdKeQlmUtz6nGzOpbcobwVIeSURNgraV/v3tlmGIX82OcPCl0K6RbHQ=="
}, },
"node_modules/node-addon-api": { "node_modules/node-addon-api": {
"version": "7.0.0", "version": "8.3.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.0.tgz",
"integrity": "sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==" "integrity": "sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg==",
"license": "MIT",
"engines": {
"node": "^18 || ^20 || >= 21"
}
}, },
"node_modules/node-fetch": { "node_modules/node-fetch": {
"version": "2.7.0", "version": "2.7.0",
@@ -1332,6 +1310,17 @@
} }
} }
}, },
"node_modules/node-gyp-build": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
"integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
"license": "MIT",
"bin": {
"node-gyp-build": "bin.js",
"node-gyp-build-optional": "optional.js",
"node-gyp-build-test": "build-test.js"
}
},
"node_modules/nopt": { "node_modules/nopt": {
"version": "8.1.0", "version": "8.1.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz",
@@ -1410,6 +1399,12 @@
"node": ">=16" "node": ">=16"
} }
}, },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/progress": { "node_modules/progress": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
@@ -1431,9 +1426,9 @@
} }
}, },
"node_modules/qs": { "node_modules/qs": {
"version": "6.14.0", "version": "6.15.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz",
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"side-channel": "^1.1.0" "side-channel": "^1.1.0"
@@ -1463,18 +1458,18 @@
} }
}, },
"node_modules/raw-body": { "node_modules/raw-body": {
"version": "3.0.0", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"bytes": "3.1.2", "bytes": "~3.1.2",
"http-errors": "2.0.0", "http-errors": "~2.0.1",
"iconv-lite": "0.6.3", "iconv-lite": "~0.7.0",
"unpipe": "1.0.0" "unpipe": "~1.0.0"
}, },
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.10"
} }
}, },
"node_modules/readable-stream": { "node_modules/readable-stream": {
@@ -1614,38 +1609,40 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/serialport": { "node_modules/serialport": {
"version": "12.0.0", "version": "13.0.0",
"resolved": "https://registry.npmjs.org/serialport/-/serialport-12.0.0.tgz", "resolved": "https://registry.npmjs.org/serialport/-/serialport-13.0.0.tgz",
"integrity": "sha512-AmH3D9hHPFmnF/oq/rvigfiAouAKyK/TjnrkwZRYSFZxNggJxwvbAbfYrLeuvq7ktUdhuHdVdSjj852Z55R+uA==", "integrity": "sha512-PHpnTd8isMGPfFTZNCzOZp9m4mAJSNWle9Jxu6BPTcWq7YXl5qN7tp8Sgn0h+WIGcD6JFz5QDgixC2s4VW7vzg==",
"license": "MIT",
"dependencies": { "dependencies": {
"@serialport/binding-mock": "10.2.2", "@serialport/binding-mock": "10.2.2",
"@serialport/bindings-cpp": "12.0.1", "@serialport/bindings-cpp": "13.0.0",
"@serialport/parser-byte-length": "12.0.0", "@serialport/parser-byte-length": "13.0.0",
"@serialport/parser-cctalk": "12.0.0", "@serialport/parser-cctalk": "13.0.0",
"@serialport/parser-delimiter": "12.0.0", "@serialport/parser-delimiter": "13.0.0",
"@serialport/parser-inter-byte-timeout": "12.0.0", "@serialport/parser-inter-byte-timeout": "13.0.0",
"@serialport/parser-packet-length": "12.0.0", "@serialport/parser-packet-length": "13.0.0",
"@serialport/parser-readline": "12.0.0", "@serialport/parser-readline": "13.0.0",
"@serialport/parser-ready": "12.0.0", "@serialport/parser-ready": "13.0.0",
"@serialport/parser-regex": "12.0.0", "@serialport/parser-regex": "13.0.0",
"@serialport/parser-slip-encoder": "12.0.0", "@serialport/parser-slip-encoder": "13.0.0",
"@serialport/parser-spacepacket": "12.0.0", "@serialport/parser-spacepacket": "13.0.0",
"@serialport/stream": "12.0.0", "@serialport/stream": "13.0.0",
"debug": "4.3.4" "debug": "4.4.0"
}, },
"engines": { "engines": {
"node": ">=16.0.0" "node": ">=20.0.0"
}, },
"funding": { "funding": {
"url": "https://opencollective.com/serialport/donate" "url": "https://opencollective.com/serialport/donate"
} }
}, },
"node_modules/serialport/node_modules/debug": { "node_modules/serialport/node_modules/debug": {
"version": "4.3.4", "version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"license": "MIT",
"dependencies": { "dependencies": {
"ms": "2.1.2" "ms": "^2.1.3"
}, },
"engines": { "engines": {
"node": ">=6.0" "node": ">=6.0"
@@ -1657,9 +1654,10 @@
} }
}, },
"node_modules/serialport/node_modules/ms": { "node_modules/serialport/node_modules/ms": {
"version": "2.1.2", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
}, },
"node_modules/serve-static": { "node_modules/serve-static": {
"version": "2.2.0", "version": "2.2.0",
@@ -1754,9 +1752,10 @@
} }
}, },
"node_modules/statuses": { "node_modules/statuses": {
"version": "2.0.1", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
"license": "MIT",
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.8"
} }
@@ -1769,17 +1768,6 @@
"safe-buffer": "~5.2.0" "safe-buffer": "~5.2.0"
} }
}, },
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/tar": { "node_modules/tar": {
"version": "7.4.3", "version": "7.4.3",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
@@ -1892,9 +1880,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/ws": { "node_modules/ws": {
"version": "8.18.1", "version": "8.19.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
"integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"

View File

@@ -1,6 +1,6 @@
{ {
"name": "fm-dx-webserver", "name": "fm-dx-webserver",
"version": "1.3.12", "version": "1.4.0",
"description": "FM DX Webserver", "description": "FM DX Webserver",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@@ -12,15 +12,16 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@mapbox/node-pre-gyp": "2.0.0", "@mapbox/node-pre-gyp": "2.0.3",
"body-parser": "2.2.0", "body-parser": "2.2.2",
"ejs": "3.1.10", "ejs": "4.0.1",
"express": "5.1.0", "express": "5.2.1",
"express-session": "1.18.2", "express-session": "1.19.0",
"ffmpeg-static": "5.2.0", "ffmpeg-static": "5.3.0",
"http": "0.0.1-security", "http": "0.0.1-security",
"koffi": "2.7.2",
"net": "1.0.2", "net": "1.0.2",
"serialport": "12.0.0", "serialport": "13.0.0",
"ws": "8.18.1" "ws": "8.19.0"
} }
} }

View File

@@ -28,6 +28,7 @@ var dataToSend = {
rt_flag: '', rt_flag: '',
ims: 0, ims: 0,
eq: 0, eq: 0,
agc: 0,
ant: 0, ant: 0,
txInfo: { txInfo: {
tx: '', tx: '',
@@ -128,6 +129,10 @@ function handleData(wss, receivedData, rdsWss) {
initialData.ant = receivedLine.substring(1); initialData.ant = receivedLine.substring(1);
rdsReset(); rdsReset();
break; break;
case receivedLine.startsWith('A'): // AGC
dataToSend.agc = receivedLine.substring(1);
initialData.agc = receivedLine.substring(1);
break;
case receivedLine.startsWith('G'): // EQ / iMS (RF+/IF+) case receivedLine.startsWith('G'): // EQ / iMS (RF+/IF+)
const mapping = filterMappings[receivedLine]; const mapping = filterMappings[receivedLine];
if (mapping) { if (mapping) {

View File

@@ -11,7 +11,8 @@ const { parseAudioDevice } = require('./stream/parser');
const { configName, serverConfig, configUpdate, configSave, configExists, configPath } = require('./server_config'); const { configName, serverConfig, configUpdate, configSave, configExists, configPath } = require('./server_config');
const helpers = require('./helpers'); const helpers = require('./helpers');
const storage = require('./storage'); const storage = require('./storage');
const { logInfo, logDebug, logWarn, logError, logFfmpeg, logs } = require('./console'); const tunerProfiles = require('./tuner_profiles');
const { logInfo, logs } = require('./console');
const dataHandler = require('./datahandler'); const dataHandler = require('./datahandler');
const fmdxList = require('./fmdx_list'); const fmdxList = require('./fmdx_list');
const { allPluginConfigs } = require('./plugins'); const { allPluginConfigs } = require('./plugins');
@@ -48,7 +49,13 @@ router.get('/', (req, res) => {
isAdminAuthenticated: true, isAdminAuthenticated: true,
videoDevices: result.audioDevices, videoDevices: result.audioDevices,
audioDevices: result.videoDevices, audioDevices: result.videoDevices,
serialPorts: serialPorts serialPorts: serialPorts,
serialPorts: serialPorts,
tunerProfiles: tunerProfiles.map((profile) => ({
id: profile.id,
label: profile.label,
detailsHtml: helpers.parseMarkdown(profile.details || '')
}))
}); });
}); });
}); });
@@ -68,6 +75,8 @@ router.get('/', (req, res) => {
tuningUpperLimit: serverConfig.webserver.tuningUpperLimit, tuningUpperLimit: serverConfig.webserver.tuningUpperLimit,
chatEnabled: serverConfig.webserver.chatEnabled, chatEnabled: serverConfig.webserver.chatEnabled,
device: serverConfig.device, device: serverConfig.device,
tunerProfiles,
si47xxAgcControl: !!serverConfig.si47xx?.agcControl,
noPlugins, noPlugins,
plugins: serverConfig.plugins, plugins: serverConfig.plugins,
fmlist_integration: serverConfig.extras.fmlistIntegration, fmlist_integration: serverConfig.extras.fmlistIntegration,
@@ -101,7 +110,12 @@ router.get('/wizard', (req, res) => {
isAdminAuthenticated: req.session.isAdminAuthenticated, isAdminAuthenticated: req.session.isAdminAuthenticated,
videoDevices: result.audioDevices, videoDevices: result.audioDevices,
audioDevices: result.videoDevices, audioDevices: result.videoDevices,
serialPorts: serialPorts serialPorts: serialPorts,
tunerProfiles: tunerProfiles.map((profile) => ({
id: profile.id,
label: profile.label,
detailsHtml: helpers.parseMarkdown(profile.details || '')
}))
}); });
}); });
}) })
@@ -135,20 +149,25 @@ router.get('/wizard', (req, res) => {
const updatedConfig = loadConfig(); // Reload the config every time const updatedConfig = loadConfig(); // Reload the config every time
res.render('setup', { res.render('setup', {
isAdminAuthenticated: req.session.isAdminAuthenticated, isAdminAuthenticated: req.session.isAdminAuthenticated,
videoDevices: result.audioDevices, videoDevices: result.audioDevices,
audioDevices: result.videoDevices, audioDevices: result.videoDevices,
serialPorts: serialPorts, serialPorts: serialPorts,
memoryUsage: (process.memoryUsage.rss() / 1024 / 1024).toFixed(1) + ' MB', memoryUsage: (process.memoryUsage.rss() / 1024 / 1024).toFixed(1) + ' MB',
memoryHeap: (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(1) + ' MB', memoryHeap: (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(1) + ' MB',
processUptime: formattedProcessUptime, processUptime: formattedProcessUptime,
consoleOutput: logs, consoleOutput: logs,
plugins: allPluginConfigs, plugins: allPluginConfigs,
enabledPlugins: updatedConfig.plugins, enabledPlugins: updatedConfig.plugins,
onlineUsers: dataHandler.dataToSend.users, onlineUsers: dataHandler.dataToSend.users,
connectedUsers: storage.connectedUsers, connectedUsers: storage.connectedUsers,
device: serverConfig.device, device: serverConfig.device,
banlist: updatedConfig.webserver.banlist // Updated banlist from the latest config banlist: updatedConfig.webserver.banlist, // Updated banlist from the latest config
tunerProfiles: tunerProfiles.map((profile) => ({
id: profile.id,
label: profile.label,
detailsHtml: helpers.parseMarkdown(profile.details || '')
}))
}); });
}); });
}) })
@@ -164,12 +183,13 @@ router.get('/rdsspy', (req, res) => {
}); });
router.get('/api', (req, res) => { router.get('/api', (req, res) => {
const { ps_errors, rt0_errors, rt1_errors, ims, eq, ant, st_forced, previousFreq, txInfo, ...dataToSend } = dataHandler.dataToSend; const { ps_errors, rt0_errors, rt1_errors, ims, eq, ant, st_forced, previousFreq, txInfo, rdsMode, ...dataToSend } = dataHandler.dataToSend;
res.json({ res.json({
...dataToSend, ...dataToSend,
txInfo: txInfo, txInfo: txInfo,
ps_errors: ps_errors, ps_errors: ps_errors,
ant: ant ant: ant,
rbds: serverConfig.webserver.rdsMode
}); });
}); });
@@ -241,6 +261,7 @@ router.get('/kick', (req, res) => {
}); });
router.get('/addToBanlist', (req, res) => { router.get('/addToBanlist', (req, res) => {
if (!req.session.isAdminAuthenticated) return;
const ipAddress = req.query.ip; const ipAddress = req.query.ip;
const location = 'Unknown'; const location = 'Unknown';
const date = Date.now(); const date = Date.now();
@@ -252,17 +273,14 @@ router.get('/addToBanlist', (req, res) => {
serverConfig.webserver.banlist = []; serverConfig.webserver.banlist = [];
} }
if (req.session.isAdminAuthenticated) { serverConfig.webserver.banlist.push(userBanData);
serverConfig.webserver.banlist.push(userBanData); configSave();
configSave(); res.json({ success: true, message: 'IP address added to banlist.' });
res.json({ success: true, message: 'IP address added to banlist.' }); helpers.kickClient(ipAddress);
helpers.kickClient(ipAddress);
} else {
res.status(403).json({ success: false, message: 'Unauthorized access.' });
}
}); });
router.get('/removeFromBanlist', (req, res) => { router.get('/removeFromBanlist', (req, res) => {
if (!req.session.isAdminAuthenticated) return;
const ipAddress = req.query.ip; const ipAddress = req.query.ip;
if (typeof serverConfig.webserver.banlist !== 'object') { if (typeof serverConfig.webserver.banlist !== 'object') {

View File

@@ -27,6 +27,7 @@ const fmdxList = require('./fmdx_list');
const { logError, logInfo, logWarn } = require('./console'); const { logError, logInfo, logWarn } = require('./console');
const storage = require('./storage'); const storage = require('./storage');
const { serverConfig, configExists } = require('./server_config'); const { serverConfig, configExists } = require('./server_config');
const pluginsApi = require('./plugins_api');
const pjson = require('../package.json'); const pjson = require('../package.json');
// Function to find server files based on the plugins listed in config // Function to find server files based on the plugins listed in config
@@ -155,8 +156,9 @@ function connectToSerial() {
return; return;
} }
logInfo('Using COM device: ' + serverConfig.xdrd.comPort); logInfo('Using serial port: ' + serverConfig.xdrd.comPort);
dataHandler.state.isSerialportAlive = true; dataHandler.state.isSerialportAlive = true;
pluginsApi.setOutput(serialport);
setTimeout(() => { setTimeout(() => {
serialport.write('x\n'); serialport.write('x\n');
}, 3000); }, 3000);
@@ -175,7 +177,7 @@ function connectToSerial() {
} else serialport.write('T87500\n'); } else serialport.write('T87500\n');
dataHandler.state.isSerialportRetrying = false; dataHandler.state.isSerialportRetrying = false;
serialport.write('A0\n'); if (serverConfig.device === 'si47xx') serialport.write('A0\n');
serialport.write('F-1\n'); serialport.write('F-1\n');
serialport.write('W0\n'); serialport.write('W0\n');
serverConfig.webserver.rdsMode ? serialport.write('D1\n') : serialport.write('D0\n'); serverConfig.webserver.rdsMode ? serialport.write('D1\n') : serialport.write('D0\n');
@@ -202,6 +204,7 @@ function connectToSerial() {
// Handle port closure // Handle port closure
serialport.on('close', () => { serialport.on('close', () => {
pluginsApi.setOutput(null);
logWarn('Disconnected from ' + serverConfig.xdrd.comPort + '. Attempting to reconnect.'); logWarn('Disconnected from ' + serverConfig.xdrd.comPort + '. Attempting to reconnect.');
setTimeout(() => { setTimeout(() => {
dataHandler.state.isSerialportRetrying = true; dataHandler.state.isSerialportRetrying = true;
@@ -220,6 +223,7 @@ function connectToXdrd() {
if (xdrd.wirelessConnection && configExists()) { if (xdrd.wirelessConnection && configExists()) {
client.connect(xdrd.xdrdPort, xdrd.xdrdIp, () => { client.connect(xdrd.xdrdPort, xdrd.xdrdIp, () => {
logInfo('Connection to xdrd established successfully.'); logInfo('Connection to xdrd established successfully.');
pluginsApi.setOutput(client);
authFlags = { authFlags = {
authMsg: false, authMsg: false,
@@ -277,7 +281,7 @@ client.on('data', (data) => {
client.write(serverConfig.defaultFreq && serverConfig.enableDefaultFreq === true ? 'T' + Math.round(serverConfig.defaultFreq * 1000) + '\n' : 'T87500\n'); client.write(serverConfig.defaultFreq && serverConfig.enableDefaultFreq === true ? 'T' + Math.round(serverConfig.defaultFreq * 1000) + '\n' : 'T87500\n');
dataHandler.initialData.freq = serverConfig.defaultFreq && serverConfig.enableDefaultFreq === true ? Number(serverConfig.defaultFreq).toFixed(3) : (87.5).toFixed(3); dataHandler.initialData.freq = serverConfig.defaultFreq && serverConfig.enableDefaultFreq === true ? Number(serverConfig.defaultFreq).toFixed(3) : (87.5).toFixed(3);
dataHandler.dataToSend.freq = serverConfig.defaultFreq && serverConfig.enableDefaultFreq === true ? Number(serverConfig.defaultFreq).toFixed(3) : (87.5).toFixed(3); dataHandler.dataToSend.freq = serverConfig.defaultFreq && serverConfig.enableDefaultFreq === true ? Number(serverConfig.defaultFreq).toFixed(3) : (87.5).toFixed(3);
client.write('A0\n'); if (serverConfig.device === 'si47xx') serialport.write('A0\n');
client.write(serverConfig.audio.startupVolume ? 'Y' + (serverConfig.audio.startupVolume * 100).toFixed(0) + '\n' : 'Y100\n'); client.write(serverConfig.audio.startupVolume ? 'Y' + (serverConfig.audio.startupVolume * 100).toFixed(0) + '\n' : 'Y100\n');
serverConfig.webserver.rdsMode ? client.write('D1\n') : client.write('D0\n'); serverConfig.webserver.rdsMode ? client.write('D1\n') : client.write('D0\n');
return; return;
@@ -287,6 +291,7 @@ client.on('data', (data) => {
}); });
client.on('close', () => { client.on('close', () => {
pluginsApi.setOutput(null);
if(serverConfig.autoShutdown === false) { if(serverConfig.autoShutdown === false) {
logWarn('Disconnected from xdrd. Attempting to reconnect.'); logWarn('Disconnected from xdrd. Attempting to reconnect.');
setTimeout(function () { setTimeout(function () {
@@ -572,7 +577,12 @@ pluginsWss.on('connection', (ws, request) => {
}); });
// Websocket register for /text, /audio and /chat paths // Websocket register for /text, /audio and /chat paths
httpServer.on('upgrade', (request, socket, head) => { httpServer.on('upgrade', (request, socket, head) => {
const clientIp = request.headers['x-forwarded-for'] || request.connection.remoteAddress;
if (serverConfig.webserver.banlist?.includes(clientIp)) {
socket.destroy();
return;
}
if (request.url === '/text') { if (request.url === '/text') {
sessionMiddleware(request, {}, () => { sessionMiddleware(request, {}, () => {
wss.handleUpgrade(request, socket, head, (ws) => { wss.handleUpgrade(request, socket, head, (ws) => {
@@ -595,23 +605,6 @@ httpServer.on('upgrade', (request, socket, head) => {
sessionMiddleware(request, {}, () => { sessionMiddleware(request, {}, () => {
rdsWss.handleUpgrade(request, socket, head, (ws) => { rdsWss.handleUpgrade(request, socket, head, (ws) => {
rdsWss.emit('connection', ws, request); rdsWss.emit('connection', ws, request);
const clientIp = request.headers['x-forwarded-for'] || request.connection.remoteAddress;
const userCommandHistory = {};
if (serverConfig.webserver.banlist?.includes(clientIp)) {
ws.close(1008, 'Banned IP');
return;
}
// Anti-spam tracking for each client
const userCommands = {};
let lastWarn = { time: 0 };
ws.on('message', function incoming(message) {
// Anti-spam
const command = helpers.antispamProtection(message, clientIp, ws, userCommands, lastWarn, userCommandHistory, '5', 'rds');
});
}); });
}); });
} else if (request.url === '/data_plugins') { } else if (request.url === '/data_plugins') {
@@ -648,3 +641,6 @@ helpers.checkIPv6Support((isIPv6Supported) => {
startServer(ipv6Address, true); // Start on IPv6 startServer(ipv6Address, true); // Start on IPv6
} else startServer(ipv4Address, false); // Start only on IPv4 } else startServer(ipv4Address, false); // Start only on IPv4
}); });
pluginsApi.registerServerContext({ wss, pluginsWss, httpServer, serverConfig });
module.exports = { wss, pluginsWss, httpServer, serverConfig };

137
server/plugin_api.js Normal file
View File

@@ -0,0 +1,137 @@
// plugins_api.js
// Shared API for server plugins:
// - Provides privileged/admin command access
// - Exposes server-side hooks for inter-plugin communication
// - Optionally broadcasts events to connected plugin WebSocket clients
const { EventEmitter } = require('events');
const { logInfo, logWarn, logError } = require('./console');
let output = null;
let wss = null;
let pluginsWss = null;
let httpServer = null;
let serverConfig = null;
// ---- internal plugin event bus ----
const pluginEvents = new EventEmitter();
// prevent accidental memory leak warnings
pluginEvents.setMaxListeners(50);
// ---- registration server side ----
function registerServerContext(ctx) {
if (ctx.wss) wss = ctx.wss;
if (ctx.pluginsWss) pluginsWss = ctx.pluginsWss;
if (ctx.httpServer) httpServer = ctx.httpServer;
if (ctx.serverConfig) serverConfig = ctx.serverConfig;
}
function setOutput(newOutput) {
output = newOutput;
}
function clearOutput() {
output = null;
}
// ---- accessors plugin side ----
function getWss() {
return wss;
}
function getPluginsWss() {
return pluginsWss;
}
function getHttpServer() {
return httpServer;
}
function getServerConfig() {
return serverConfig;
}
// ---- privileged command path ----
async function sendPrivilegedCommand(command, isPluginInternal = false) {
const maxWait = 10000;
const interval = 500;
let waited = 0;
while (!output && waited < maxWait) {
await new Promise(resolve => setTimeout(resolve, interval));
waited += interval;
}
if (!output) {
logError(`[Privileged Send] Timeout waiting for output (${command})`);
return false;
}
if (isPluginInternal) {
output.write(`${command}\n`);
//logInfo(`[Privileged Plugin] Command sent: ${command}`); // Debug
return true;
}
logWarn(`[Privileged Send] Rejected (not internal): ${command.slice(0, 64)}`);
return false;
}
// ---- plugin hook API ----
function emitPluginEvent(event, payload, opts = {}) {
pluginEvents.emit(event, payload);
// Stop here unless option to broadcast to clients if true
if (opts.broadcast === false) return;
// Broadcast to connected plugin WebSocket clients if available
if (pluginsWss) {
const message = JSON.stringify({ type: event, value: payload });
pluginsWss.clients.forEach((client) => {
if (client.readyState === client.OPEN) {
try {
// Send event to client
client.send(message);
} catch (err) {
logWarn(`[plugins_api] Failed to send ${event} to client: ${err.message}`);
}
}
});
}
}
function onPluginEvent(event, handler) {
pluginEvents.on(event, handler);
}
function offPluginEvent(event, handler) {
pluginEvents.off(event, handler);
}
// ---- exports ----
module.exports = {
// server registration
registerServerContext,
setOutput,
clearOutput,
// server context access
getWss,
getPluginsWss,
getHttpServer,
getServerConfig,
// privileged control
sendPrivilegedCommand,
// inter-plugin hooks
emitPluginEvent,
onPluginEvent,
offPluginEvent
};

View File

@@ -90,6 +90,9 @@ let serverConfig = {
fmlistAdminOnly: false, fmlistAdminOnly: false,
fmlistOmid: "", fmlistOmid: "",
}, },
si47xx: {
agcControl: false
},
tunnel: { tunnel: {
enabled: false, enabled: false,
username: "", username: "",

84
server/tuner_profiles.js Normal file
View File

@@ -0,0 +1,84 @@
const tunerProfiles = [
{
id: 'tef',
label: 'TEF668x',
fmBandwidths: [
{ value: 0, label: 'Auto' },
{ value: 56000, label: '56 kHz' },
{ value: 64000, label: '64 kHz' },
{ value: 72000, label: '72 kHz' },
{ value: 84000, label: '84 kHz' },
{ value: 97000, label: '97 kHz' },
{ value: 114000, label: '114 kHz' },
{ value: 133000, label: '133 kHz' },
{ value: 151000, label: '151 kHz' },
{ value: 184000, label: '184 kHz' },
{ value: 200000, label: '200 kHz' },
{ value: 217000, label: '217 kHz' },
{ value: 236000, label: '236 kHz' },
{ value: 254000, label: '254 kHz' },
{ value: 287000, label: '287 kHz' },
{ value: 311000, label: '311 kHz' }
],
details: ''
},
{
id: 'xdr',
label: 'XDR (F1HD / S10HDiP)',
fmBandwidths: [
{ value: 0, value2: -1, label: 'Auto' },
{ value: 55000, value2: 0, label: '55 kHz' },
{ value: 73000, value2: 1, label: '73 kHz' },
{ value: 90000, value2: 2, label: '90 kHz' },
{ value: 108000, value2: 3, label: '108 kHz' },
{ value: 125000, value2: 4, label: '125 kHz' },
{ value: 142000, value2: 5, label: '142 kHz' },
{ value: 159000, value2: 6, label: '159 kHz' },
{ value: 177000, value2: 7, label: '177 kHz' },
{ value: 194000, value2: 8, label: '194 kHz' },
{ value: 211000, value2: 9, label: '211 kHz' },
{ value: 229000, value2: 10, label: '229 kHz' },
{ value: 246000, value2: 11, label: '246 kHz' },
{ value: 263000, value2: 12, label: '263 kHz' },
{ value: 281000, value2: 13, label: '281 kHz' },
{ value: 298000, value2: 14, label: '298 kHz' },
{ value: 309000, value2: 15, label: '309 kHz' }
],
details: ''
},
{
id: 'sdr',
label: 'SDR (RTL-SDR / AirSpy)',
fmBandwidths: [
{ value: 0, label: 'Auto' },
{ value: 4000, label: '4 kHz' },
{ value: 8000, label: '8 kHz' },
{ value: 10000, label: '10 kHz' },
{ value: 20000, label: '20 kHz' },
{ value: 30000, label: '30 kHz' },
{ value: 50000, label: '50 kHz' },
{ value: 75000, label: '75 kHz' },
{ value: 100000, label: '100 kHz' },
{ value: 125000, label: '125 kHz' },
{ value: 150000, label: '150 kHz' },
{ value: 175000, label: '175 kHz' },
{ value: 200000, label: '200 kHz' },
{ value: 225000, label: '225 kHz' }
],
details: ''
},
{
id: 'si47xx',
label: 'Si47XX (Si4735 / Si4732)',
fmBandwidths: [
{ value: 0, label: 'Auto' },
{ value: 40000, label: '40 kHz' },
{ value: 60000, label: '60 kHz' },
{ value: 84000, label: '84 kHz' },
{ value: 110000, label: '110 kHz' }
],
details: ''
}
];
module.exports = tunerProfiles;

View File

@@ -1,62 +1,12 @@
<% <%
let options = []; let options = [];
if (device === 'tef') { const profile = Array.isArray(tunerProfiles)
options = [ ? tunerProfiles.find((item) => item.id === device)
{ value: 0, label: 'Auto' }, : null;
{ value: 56000, label: '56 kHz' },
{ value: 64000, label: '64 kHz' }, if (Array.isArray(profile?.fmBandwidths)) {
{ value: 72000, label: '72 kHz' }, options = profile.fmBandwidths;
{ value: 84000, label: '84 kHz' },
{ value: 97000, label: '97 kHz' },
{ value: 114000, label: '114 kHz' },
{ value: 133000, label: '133 kHz' },
{ value: 151000, label: '151 kHz' },
{ value: 184000, label: '184 kHz' },
{ value: 200000, label: '200 kHz' },
{ value: 217000, label: '217 kHz' },
{ value: 236000, label: '236 kHz' },
{ value: 254000, label: '254 kHz' },
{ value: 287000, label: '287 kHz' },
{ value: 311000, label: '311 kHz' }
];
} else if (device === 'xdr') {
options = [
{ value: 0, value2: -1, label: 'Auto' },
{ value: 55000, value2: 0, label: '55 kHz' },
{ value: 73000, value2: 1, label: '73 kHz' },
{ value: 90000, value2: 2, label: '90 kHz' },
{ value: 108000, value2: 3, label: '108 kHz' },
{ value: 125000, value2: 4, label: '125 kHz' },
{ value: 142000, value2: 5, label: '142 kHz' },
{ value: 159000, value2: 6, label: '159 kHz' },
{ value: 177000, value2: 7, label: '177 kHz' },
{ value: 194000, value2: 8, label: '194 kHz' },
{ value: 211000, value2: 9, label: '211 kHz' },
{ value: 229000, value2: 10, label: '229 kHz' },
{ value: 246000, value2: 11, label: '246 kHz' },
{ value: 263000, value2: 12, label: '263 kHz' },
{ value: 281000, value2: 13, label: '281 kHz' },
{ value: 298000, value2: 14, label: '298 kHz' },
{ value: 309000, value2: 15, label: '309 kHz' }
];
} else if (device === 'sdr') {
options = [
{ value: 0, label: 'Auto' },
{ value: 4000, label: '4 kHz' },
{ value: 8000, label: '8 kHz' },
{ value: 10000, label: '10 kHz' },
{ value: 20000, label: '20 kHz' },
{ value: 30000, label: '30 kHz' },
{ value: 50000, label: '50 kHz' },
{ value: 75000, label: '75 kHz' },
{ value: 100000, label: '100 kHz' },
{ value: 125000, label: '125 kHz' },
{ value: 150000, label: '150 kHz' },
{ value: 175000, label: '175 kHz' },
{ value: 200000, label: '200 kHz' },
{ value: 225000, label: '225 kHz' }
];
} }
%> %>

View File

@@ -293,8 +293,8 @@ pre {
top: 10px; top: 10px;
} }
.flex-container.contains-dropdown { .contains-dropdown {
z-index: 999; z-index: 990;
position: relative; position: relative;
} }

View File

@@ -94,6 +94,7 @@
<% if (device == 'tef') { %>TEF668x<% } %> <% if (device == 'tef') { %>TEF668x<% } %>
<% if (device == 'xdr') { %>Sony XDR<% } %> <% if (device == 'xdr') { %>Sony XDR<% } %>
<% if (device == 'sdr') { %>SDR<% } %> <% if (device == 'sdr') { %>SDR<% } %>
<% if (device == 'si47xx') { %>SI47XX<% } %>
</span> </span>
</div> </div>
<div class="color-3 m-10 text-medium"> <div class="color-3 m-10 text-medium">
@@ -228,6 +229,17 @@
<div class="panel-50 no-bg br-0 h-100 m-0 button-eq"> <div class="panel-50 no-bg br-0 h-100 m-0 button-eq">
<% if (device == 'tef') { %><button style="border-radius: 15px 0px 0px 15px;" class="data-eq hide-phone tooltip" aria-label="EQ Filter" data-tooltip="<strong>The cEQ filter can reduce bandwidth below 56 kHz.</strong><br><br>Useful for weak stations next to strong ones,<br>although it may pick up more interference."><span class="text-bold">cEQ</span></button><% } %> <% if (device == 'tef') { %><button style="border-radius: 15px 0px 0px 15px;" class="data-eq hide-phone tooltip" aria-label="EQ Filter" data-tooltip="<strong>The cEQ filter can reduce bandwidth below 56 kHz.</strong><br><br>Useful for weak stations next to strong ones,<br>although it may pick up more interference."><span class="text-bold">cEQ</span></button><% } %>
<% if (device == 'xdr') { %><button style="border-radius: 15px 0px 0px 15px;" class="data-eq hide-phone tooltip" aria-label="RF+ Filter" data-tooltip="<strong>The RF+ filter increases gain by 5dB</strong>"><span class="text-bold">RF+</span></button><% } %> <% if (device == 'xdr') { %><button style="border-radius: 15px 0px 0px 15px;" class="data-eq hide-phone tooltip" aria-label="RF+ Filter" data-tooltip="<strong>The RF+ filter increases gain by 5dB</strong>"><span class="text-bold">RF+</span></button><% } %>
<% if (device == 'si47xx' && si47xxAgcControl) { %>
<div class="no-bg dropdown dropdown-up data-agc hide-phone w-150" id="data-agc" style="margin-right: 15px !important;">
<input type="text" placeholder="AGC" readonly tabindex="0">
<ul class="options open-top" tabindex="-1">
<li data-value="0" class="option" tabindex="0">Auto AGC</li>
<li data-value="1" class="option" tabindex="0">High</li>
<li data-value="3" class="option" tabindex="0">Medium</li>
<li data-value="2" class="option" tabindex="0">Low</li>
</ul>
</div>
<% } %>
</div> </div>
<div class="panel-50 no-bg br-0 h-100 m-0 button-ims"> <div class="panel-50 no-bg br-0 h-100 m-0 button-ims">
<% if (device == 'tef') { %><button style="border-radius: 0px 15px 15px 0px;" class="data-ims hide-phone tooltip" aria-label="iMS + Filter" data-tooltip="<strong>The iMS filter reduces multipath audio artifacts.</strong><br><br>It's recommended to leave it on most of the time."><span class="text-bold">iMS</span></button><% } %> <% if (device == 'tef') { %><button style="border-radius: 0px 15px 15px 0px;" class="data-ims hide-phone tooltip" aria-label="iMS + Filter" data-tooltip="<strong>The iMS filter reduces multipath audio artifacts.</strong><br><br>It's recommended to leave it on most of the time."><span class="text-bold">iMS</span></button><% } %>
@@ -248,7 +260,7 @@
<input type="range" id="volumeSlider" min="0" max="1" step="0.01" value="1" aria-label="Volume slider"> <input type="range" id="volumeSlider" min="0" max="1" step="0.01" value="1" aria-label="Volume slider">
</span> </span>
<% if (bwSwitch) { %> <% if (bwSwitch) { %>
<%- include('_bwSwitch', { device: device, id: 'data-bw', cssClass: 'panel-50 dropdown-up m-0 w-150 m-left-15', cssClassOptions: 'open-top' }) %> <%- include('_bwSwitch', { device: device, tunerProfiles: tunerProfiles, id: 'data-bw', cssClass: 'panel-50 dropdown-up m-0 w-150 m-left-15', cssClassOptions: 'open-top' }) %>
<% } %> <% } %>
<% if (fmlist_integration == true && (fmlist_adminOnly == false || isTuneAuthenticated)) { %> <% if (fmlist_integration == true && (fmlist_adminOnly == false || isTuneAuthenticated)) { %>
<button class="tooltip bg-color-4 mini-popup log-fmlist" data-tooltip="<strong>LOG TO FMLIST</strong><br>Clicking this button logs the current station to FMLIST's visual logbook." aria-label="Log to FMLIST" style="width: 80px; height: 48px;margin-left: 15px !important;"> <button class="tooltip bg-color-4 mini-popup log-fmlist" data-tooltip="<strong>LOG TO FMLIST</strong><br>Clicking this button logs the current station to FMLIST's visual logbook." aria-label="Log to FMLIST" style="width: 80px; height: 48px;margin-left: 15px !important;">
@@ -353,7 +365,7 @@
<div class="flex-phone"> <div class="flex-phone">
<% if (bwSwitch) { %> <% if (bwSwitch) { %>
<div style="max-height: 48px;width: 50%;margin-right: 5px;"> <div style="max-height: 48px;width: 50%;margin-right: 5px;">
<%- include('_bwSwitch', { device: device, id: 'data-bw-phone', cssClass: 'panel-100-real', cssClassOptions: 'text-center open-bottom' }) %> <%- include('_bwSwitch', { device: device, tunerProfiles: tunerProfiles, id: 'data-bw-phone', cssClass: 'panel-100-real', cssClassOptions: 'text-center open-bottom' }) %>
</div> </div>
<% } %> <% } %>
@@ -363,6 +375,17 @@
<div class="flex-container flex-phone flex-center"> <div class="flex-container flex-phone flex-center">
<% if (device == 'tef') { %><button class="data-eq tooltip p-10 m-right-5" style="height: 48px" aria-label="EQ Filter" data-tooltip="<strong>The cEQ filter can reduce bandwidth below 56 kHz.</strong><br><br>Useful for weak stations next to strong ones,<br>although it may pick up more interference."><span class="text-bold">cEQ</span></button><% } %> <% if (device == 'tef') { %><button class="data-eq tooltip p-10 m-right-5" style="height: 48px" aria-label="EQ Filter" data-tooltip="<strong>The cEQ filter can reduce bandwidth below 56 kHz.</strong><br><br>Useful for weak stations next to strong ones,<br>although it may pick up more interference."><span class="text-bold">cEQ</span></button><% } %>
<% if (device == 'xdr') { %><button class="data-eq tooltip p-10 m-right-5" aria-label="RF+ Filter" data-tooltip="<strong>The RF+ filter increases gain by 5dB</strong>"><span class="text-bold">RF+</span></button><% } %> <% if (device == 'xdr') { %><button class="data-eq tooltip p-10 m-right-5" aria-label="RF+ Filter" data-tooltip="<strong>The RF+ filter increases gain by 5dB</strong>"><span class="text-bold">RF+</span></button><% } %>
<% if (device == 'si47xx' && si47xxAgcControl) { %>
<div class="no-bg dropdown data-agc w-150" id="data-agc-phone" style="max-height: 48px;">
<input type="text" placeholder="AGC" readonly tabindex="0" style="border-radius: 15px;">
<ul class="options open-top" tabindex="-1">
<li data-value="0" class="option" tabindex="0">Auto AGC</li>
<li data-value="1" class="option" tabindex="0">High</li>
<li data-value="3" class="option" tabindex="0">Medium</li>
<li data-value="2" class="option" tabindex="0">Low</li>
</ul>
</div>
<% } %>
<% if (device == 'tef') { %><button class="data-ims tooltip p-10 m-left-5" style="height: 48px;" aria-label="iMS + Filter" data-tooltip="<strong>The iMS filter reduces multipath audio artifacts.</strong><br><br>It's recommended to leave it on most of the time."><span class="text-bold">iMS</span></button><% } %> <% if (device == 'tef') { %><button class="data-ims tooltip p-10 m-left-5" style="height: 48px;" aria-label="iMS + Filter" data-tooltip="<strong>The iMS filter reduces multipath audio artifacts.</strong><br><br>It's recommended to leave it on most of the time."><span class="text-bold">iMS</span></button><% } %>
<% if (device == 'xdr') { %><button class="data-ims tooltip p-10 m-left-5" aria-label="IF+ Filter" data-tooltip="<strong>The IF+ filter increases gain by 6dB</strong>"><span class="text-bold">IF+</span></button><% } %> <% if (device == 'xdr') { %><button class="data-ims tooltip p-10 m-left-5" aria-label="IF+ Filter" data-tooltip="<strong>The IF+ filter increases gain by 6dB</strong>"><span class="text-bold">IF+</span></button><% } %>
</div> </div>

View File

@@ -42,6 +42,14 @@ $(document).ready(function() {
socket.send("W" + $(event.currentTarget).attr('data-value')); socket.send("W" + $(event.currentTarget).attr('data-value'));
$currentDropdown.find('input').val($(event.currentTarget).text()); $currentDropdown.find('input').val($(event.currentTarget).text());
break; break;
case 'data-agc':
socket.send("A" + $(event.currentTarget).attr('data-value'));
$currentDropdown.find('input').val($(event.currentTarget).text());
break;
case 'data-agc-phone':
socket.send("A" + $(event.currentTarget).attr('data-value'));
$currentDropdown.find('input').val($(event.currentTarget).text());
break;
default: default:
$currentDropdown.find('input') $currentDropdown.find('input')
.val($(event.currentTarget).text()) .val($(event.currentTarget).text())

View File

@@ -890,6 +890,7 @@ const $dataSt = $('.data-st');
const $dataRt0 = $('#data-rt0 span'); const $dataRt0 = $('#data-rt0 span');
const $dataRt1 = $('#data-rt1 span'); const $dataRt1 = $('#data-rt1 span');
const $dataAntInput = $('.data-ant input'); const $dataAntInput = $('.data-ant input');
const $dataAgcInput = $('.data-agc input');
const $dataBwInput = $('.data-bw input'); const $dataBwInput = $('.data-bw input');
const $dataStationContainer = $('#data-station-container'); const $dataStationContainer = $('#data-station-container');
const $dataTp = $('.data-tp'); const $dataTp = $('.data-tp');
@@ -1008,11 +1009,10 @@ const updateDataElements = throttle(function(parsedData) {
$dataAntInput.val($('.data-ant li[data-value="' + parsedData.ant + '"]').first().text()); $dataAntInput.val($('.data-ant li[data-value="' + parsedData.ant + '"]').first().text());
if (parsedData.bw < 500) { if (typeof parsedData.agc !== 'undefined') $dataAgcInput.val($('.data-agc li[data-value="' + parsedData.agc + '"]').first().text());
$dataBwInput.val($('.data-bw li[data-value2="' + parsedData.bw + '"]').first().text());
} else { if (parsedData.bw < 500) $dataBwInput.val($('.data-bw li[data-value2="' + parsedData.bw + '"]').first().text());
$dataBwInput.val($('.data-bw li[data-value="' + parsedData.bw + '"]').first().text()); else $dataBwInput.val($('.data-bw li[data-value="' + parsedData.bw + '"]').first().text());
}
if (parsedData.txInfo.tx.length > 1) { if (parsedData.txInfo.tx.length > 1) {
updateTextIfChanged($('#data-station-name'), parsedData.txInfo.tx.replace(/%/g, '%25')); updateTextIfChanged($('#data-station-name'), parsedData.txInfo.tx.replace(/%/g, '%25'));

View File

@@ -24,7 +24,8 @@ function mapCreate() {
if (!(typeof map == "object")) { if (!(typeof map == "object")) {
map = L.map('map', { map = L.map('map', {
center: [40, 0], center: [40, 0],
zoom: 3 zoom: 3,
worldCopyJump: true
}); });
} else { } else {
map.setZoom(3).panTo([40, 0]); map.setZoom(3).panTo([40, 0]);
@@ -55,9 +56,9 @@ function mapCreate() {
$('#identification-lon').val(ev.latlng.lng.toFixed(6)); $('#identification-lon').val(ev.latlng.lng.toFixed(6));
if (typeof pin == "object") { if (typeof pin == "object") {
pin.setLatLng(ev.latlng); pin.setLatLng(ev.latlng.wrap());
} else { } else {
pin = L.marker(ev.latlng, { riseOnHover: true, draggable: true }).addTo(map); pin = L.marker(ev.latlng.wrap(), { riseOnHover: true, draggable: true }).addTo(map);
pin.on('dragend', function(ev) { pin.on('dragend', function(ev) {
$('#identification-lat').val(ev.target.getLatLng().lat.toFixed(6)); $('#identification-lat').val(ev.target.getLatLng().lat.toFixed(6));
$('#identification-lon').val(ev.target.getLatLng().lng.toFixed(6)); $('#identification-lon').val(ev.target.getLatLng().lng.toFixed(6));
@@ -259,8 +260,8 @@ function checkTunnelServers() {
url: '/tunnelservers', url: '/tunnelservers',
method: 'GET', method: 'GET',
success: function(servers) { success: function(servers) {
const $options = $('#tunnel-server ul.options'); const $options = $('#tunnel-regionselect ul.options');
const $input = $('#tunnel-serverSelect'); const $input = $('#tunnel-region');
const selectedValue = $input.val(); // currently selected value (label or value?) const selectedValue = $input.val(); // currently selected value (label or value?)
servers.forEach(server => { servers.forEach(server => {

View File

@@ -1,2 +1,2 @@
const versionDate = new Date('Nov 30, 2025 23:00:00'); const versionDate = new Date('Feb 24, 2026 01:00:00');
const currentVersion = `v1.3.12 [${versionDate.getDate()}/${versionDate.getMonth() + 1}/${versionDate.getFullYear()}]`; const currentVersion = `v1.4.0 [${versionDate.getDate()}/${versionDate.getMonth() + 1}/${versionDate.getFullYear()}]`;

View File

@@ -215,8 +215,8 @@
<div class="flex-container"> <div class="flex-container">
<div class="panel-50 p-bottom-20"> <div class="panel-50 p-bottom-20">
<h3>Volume</h3> <h3>Audio boost</h3>
<p>This option will boost the audio volume globally, recommended for the Headless TEF.</p> <p>This option will boost the audio volume. Use if the output is too quiet.</p>
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Audio Boost', id: 'audio-audioBoost'}) %> <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Audio Boost', id: 'audio-audioBoost'}) %>
</div> </div>
<div class="panel-50 p-bottom-20"> <div class="panel-50 p-bottom-20">
@@ -351,23 +351,22 @@
<div class="panel-full m-0 tab-content no-bg" id="tuner" role="tabpanel"> <div class="panel-full m-0 tab-content no-bg" id="tuner" role="tabpanel">
<h2>Tuner settings</h2> <h2>Tuner settings</h2>
<div class="flex-container contains-dropdown"> <div class="panel-100 p-bottom-20 contains-dropdown" style="z-index: 991;">
<div class="panel-33 p-bottom-20">
<h3>Device type</h3> <h3>Device type</h3>
<%- include('_components', { component: 'dropdown', id: 'device-selector', inputId: 'device', label: 'Device', cssClass: '', placeholder: 'TEF668x / TEA685x', <div class="flex-center" style="max-width: 520px; margin: 10px auto 0;">
options: [ <%- include('_components', { component: 'dropdown', id: 'device-selector', inputId: 'device', label: 'Device', cssClass: '', placeholder: 'TEF668x / TEA685x',
{ value: 'tef', label: 'TEF668x / TEA685x' }, options: tunerProfiles.map(profile => ({
{ value: 'xdr', label: 'XDR (F1HD / S10HDiP)' }, value: profile.id,
{ value: 'sdr', label: 'SDR (RTL-SDR / AirSpy)' }, label: profile.label
{ value: 'other', label: 'Other' } }))
] }) %><br>
}) %><br> </div>
</div> </div>
<div class="panel-33 p-bottom-20" style="padding-right: 20px; padding-left: 20px;"> <div class="flex-container contains-dropdown">
<div class="panel-100 p-bottom-20" style="padding-right: 20px; padding-left: 20px;">
<h3>Connection type</h3> <h3>Connection type</h3>
<p class="text-gray">If you want to choose the COM port directly, choose "Direct".<br>If you use xdrd or your receiver is connected via Wi-Fi, choose TCP/IP.</p> <p class="text-gray">If you want to choose the serial port directly, choose "Direct".<br>If you use xdrd or your receiver is connected via Wi-Fi, choose TCP/IP.</p>
<div class="auto top-10"> <div class="auto top-10">
<label class="toggleSwitch nolabel" onclick=""> <label class="toggleSwitch nolabel" onclick="">
<input id="xdrd-wirelessConnection" type="checkbox" tabindex="0" aria-label="Connection type"/> <input id="xdrd-wirelessConnection" type="checkbox" tabindex="0" aria-label="Connection type"/>
@@ -378,19 +377,15 @@
</span> </span>
</label> </label>
</div> </div>
</div>
<div class="panel-33 p-bottom-20">
<h3>Device / Server</h3>
<div id="tuner-usb"> <div id="tuner-usb">
<p class="text-gray">Choose your desired <strong>COM port</strong><br>&nbsp;</p> <p class="text-gray">Choose your desired <strong>serial port</strong><br>&nbsp;</p>
<%- include('_components', { <%- include('_components', {
component: 'dropdown', component: 'dropdown',
id: 'deviceList', id: 'deviceList',
inputId: 'xdrd-comPort', inputId: 'xdrd-comPort',
label: 'USB Device', label: 'Serial port',
cssClass: '', cssClass: '',
placeholder: 'Choose your USB device', placeholder: 'Choose your serial port',
options: serialPorts.map(serialPort => ({ options: serialPorts.map(serialPort => ({
value: serialPort.path, value: serialPort.path,
label: `${serialPort.path} - ${serialPort.friendlyName}` label: `${serialPort.path} - ${serialPort.friendlyName}`
@@ -433,6 +428,13 @@
<p>Toggling this option will put the tuner to sleep when no clients are connected.</p> <p>Toggling this option will put the tuner to sleep when no clients are connected.</p>
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Auto-shutdown', id: 'autoShutdown'}) %><br> <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Auto-shutdown', id: 'autoShutdown'}) %><br>
</div> </div>
<div class="flex-container">
<div class="panel-50 no-bg">
<h4>SI47XX AGC control</h4>
<p>Allow users to change SI47XX AGC mode from the main UI.</p>
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Enable AGC control', id: 'si47xx-agcControl'}) %><br>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -703,7 +705,7 @@
<p>You can also get an tunnel from kuba201 discord, one of the contributors of this version of the application.</p> <p>You can also get an tunnel from kuba201 discord, one of the contributors of this version of the application.</p>
<h4>Main tunnel settings</h4> <h4>Main tunnel settings</h4>
<%- 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>
<%- include('_components', { component: 'dropdown', id: 'tunnel-server', inputId: 'tunnel-serverSelect', label: 'Official server region', cssClass: '', placeholder: 'Europe', <%- include('_components', { component: 'dropdown', id: 'tunnel-regionSelect', inputId: 'tunnel-region', label: 'Official server region', cssClass: '', placeholder: 'Europe',
options: [ options: [
{ value: 'eu', label: 'Europe' }, { value: 'eu', label: 'Europe' },
{ value: 'us', label: 'Americas' }, { value: 'us', label: 'Americas' },

View File

@@ -47,15 +47,13 @@
<h3 class="settings-heading">Tuner type</h3> <h3 class="settings-heading">Tuner type</h3>
<p class="m-0">Settings a proper device type ensures that the correct interface and settings will load.</p> <p class="m-0">Settings a proper device type ensures that the correct interface and settings will load.</p>
<div class="panel-100 no-bg flex-center"> <div class="panel-100 no-bg flex-center" style="max-width: 520px; margin: 10px auto 0;">
<%- include('_components', { component: 'dropdown', id: 'device-selector', inputId: 'device', label: 'Device', cssClass: '', placeholder: 'TEF668x / TEA685x', <%- include('_components', { component: 'dropdown', id: 'device-selector', inputId: 'device', label: 'Device', cssClass: '', placeholder: 'TEF668x / TEA685x',
options: [ options: tunerProfiles.map(profile => ({
{ value: 'tef', label: 'TEF668x / TEA685x' }, value: profile.id,
{ value: 'xdr', label: 'XDR (F1HD / S10HDiP)' }, label: profile.label
{ value: 'sdr', label: 'SDR (RTL-SDR / AirSpy)' }, }))
{ value: 'other', label: 'Other' } }) %><br>
]
}) %><br>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
<h3 class="settings-heading">Tuner connection</h3> <h3 class="settings-heading">Tuner connection</h3>
@@ -70,16 +68,16 @@
</label> </label>
</div> </div>
<div id="tuner-usb" class="top-25"> <div id="tuner-usb" class="top-25">
<p>It's time to choose your USB device.</p> <p>It's time to choose your serial port.</p>
<div class="panel-100 no-bg flex-center"> <div class="panel-100 no-bg flex-center">
<%- include('_components', { <%- include('_components', {
component: 'dropdown', component: 'dropdown',
id: 'deviceList', id: 'deviceList',
inputId: 'xdrd-comPort', inputId: 'xdrd-comPort',
label: 'USB Device', label: 'Serial port',
cssClass: '', cssClass: '',
placeholder: 'Choose your USB device', placeholder: 'Choose your serial port',
options: serialPorts.map(serialPort => ({ options: serialPorts.map(serialPort => ({
value: serialPort.path, value: serialPort.path,
label: `${serialPort.path} - ${serialPort.friendlyName}` label: `${serialPort.path} - ${serialPort.friendlyName}`