From bd8ed1bf81c32dad8713227501e6a0db74352e41 Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 10 Nov 2025 16:12:59 +0100 Subject: [PATCH 01/13] added mp4, flac and wav files to gitignore --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 72086f9..8bf7970 100644 --- a/.gitignore +++ b/.gitignore @@ -964,4 +964,8 @@ app.*.symbols !/dev/ci/**/Gemfile.lock #Storage files -storage/ \ No newline at end of file +storage/ + +*.mp4 +*.wav +*.flac From 92043440fe7c98f5aa0850dc3babed5ca2ca0c4d Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 10 Nov 2025 16:18:57 +0100 Subject: [PATCH 02/13] Combined Frontend with backend, and implemented the extractor function into the module structure --- main.js | 20 +- package-lock.json | 1360 ++++++++++++++++- package.json | 9 +- requires.js | 8 + .../modules/extraction/ffmpegExtractor.js | 93 ++ services/modules/utility/@startup.js | 4 + 6 files changed, 1483 insertions(+), 11 deletions(-) create mode 100644 services/modules/extraction/ffmpegExtractor.js diff --git a/main.js b/main.js index 5205247..8557cf6 100644 --- a/main.js +++ b/main.js @@ -55,4 +55,22 @@ rl.on("line", data =>{ // ----------------------------------------------------------- ELECTRON ----------------------------------------------------------- // -// TODO - Add Electron support to the project +let mainWindow; + +function createWindow() { + mainWindow = new electron.BrowserWindow({ + width: 800, + height: 600, + webPreferences: { + nodeIntegration: false, + contextIsolation: true, + preload: `${mainDir}/electron/main/preload.js` + } + }); + + mainWindow.loadFile('./electron/main/index.html'); +} + +electron.app.whenReady().then(createWindow); + + diff --git a/package-lock.json b/package-lock.json index a124ca4..3fe9ca4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,17 @@ { "name": "video2document", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { + "name": "video2document", + "version": "1.0.0", + "license": "ISC", "dependencies": { "cli-progress": "^3.12.0", + "electron": "^39.1.1", + "express": "^5.1.0", "ffmpeg-static": "^5.2.0", "fluent-ffmpeg": "^2.1.3" }, @@ -45,6 +51,26 @@ "node": ">=6.0.0" } }, + "node_modules/@electron/get": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -73,6 +99,28 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -101,6 +149,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, "node_modules/@types/cli-progress": { "version": "3.11.6", "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.6.tgz", @@ -121,17 +180,57 @@ "@types/node": "*" } }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "24.9.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.2.tgz", "integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==", - "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -191,12 +290,106 @@ "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==" }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "optional": true + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT" }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -215,6 +408,17 @@ "node": ">=4" } }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/concat-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", @@ -230,6 +434,41 @@ "typedarray": "^0.0.6" } }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "engines": { + "node": ">=6.6.0" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -254,6 +493,87 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "optional": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "optional": true + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -264,12 +584,76 @@ "node": ">=0.3.1" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/electron": { + "version": "39.1.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-39.1.1.tgz", + "integrity": "sha512-VuFEI1yQ7BH3RYI5VZtwFlzGp4rpPRd5oEc26ZQIItVLcLTbXt4/O7o4hs+1fyg9Q3NvGAifgX5Vp5EBOIFpAg==", + "hasInstallScript": true, + "dependencies": { + "@electron/get": "^2.0.0", + "@types/node": "^22.7.7", + "extract-zip": "^2.0.1" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 12.20.55" + } + }, + "node_modules/electron/node_modules/@types/node": { + "version": "22.19.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.0.tgz", + "integrity": "sha512-xpr/lmLPQEj+TUnHmR+Ab91/glhJvsqcjB+yY0Ix9GO70H6Lb4FHH5GeqdOE5btAx7eIMwuHkp4H2MSkLcqWbA==", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/electron/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" + }, "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==", "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -279,6 +663,132 @@ "node": ">=6" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "optional": true + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/ffmpeg-static": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ffmpeg-static/-/ffmpeg-static-5.2.0.tgz", @@ -295,12 +805,27 @@ "node": ">=16" } }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fluent-ffmpeg": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz", "integrity": "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==", "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "license": "MIT", "dependencies": { "async": "^0.2.9", "which": "^1.1.1" @@ -309,6 +834,239 @@ "node": ">=18" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-agent/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "optional": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/http-response-object": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", @@ -324,6 +1082,18 @@ "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", "license": "MIT" }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -337,12 +1107,31 @@ "node": ">= 6" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "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", @@ -352,12 +1141,52 @@ "node": ">=8" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "optional": true + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -365,17 +1194,171 @@ "dev": true, "license": "ISC" }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "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/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "engines": { + "node": ">=8" + } + }, "node_modules/parse-cache-control": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==" }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -385,6 +1368,89 @@ "node": ">=0.4.0" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.7.0", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -399,6 +1465,54 @@ "node": ">= 6" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -419,6 +1533,162 @@ ], "license": "MIT" }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "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/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "optional": true + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "optional": true + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -454,6 +1724,25 @@ "node": ">=8" } }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -498,6 +1787,31 @@ } } }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -510,7 +1824,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -523,9 +1836,24 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, "license": "MIT" }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -539,6 +1867,14 @@ "dev": true, "license": "MIT" }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -551,6 +1887,20 @@ "which": "bin/which" } }, + "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/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 394d549..14cba78 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { - "type": "module", "dependencies": { "cli-progress": "^3.12.0", + "electron": "^39.1.1", + "express": "^5.1.0", "ffmpeg-static": "^5.2.0", - "fluent-ffmpeg": "^2.1.3", - "express": "^5.1.0" + "fluent-ffmpeg": "^2.1.3" }, "devDependencies": { "@types/cli-progress": "^3.11.6", @@ -22,6 +22,7 @@ "test": "tests" }, "scripts": { + "start": "electron main.js", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { @@ -31,5 +32,3 @@ "author": "", "license": "ISC" } - - diff --git a/requires.js b/requires.js index e85038d..03c5b7a 100644 --- a/requires.js +++ b/requires.js @@ -8,3 +8,11 @@ fs = require("fs") readline = require("readline") config = require("./config/config") +ffmpegPath = require('ffmpeg-static'); +ffmpeg = require('fluent-ffmpeg'); +path = require('path'); +cliProgress = require('cli-progress'); + +// { app, BrowserWindow, ipcMain, dialog } = require('electron'); + +electron = require('electron'); diff --git a/services/modules/extraction/ffmpegExtractor.js b/services/modules/extraction/ffmpegExtractor.js new file mode 100644 index 0000000..ffec9c0 --- /dev/null +++ b/services/modules/extraction/ffmpegExtractor.js @@ -0,0 +1,93 @@ + +// Ensure ffmpeg binary is available +if (!ffmpegPath) { + throw new Error('FFmpeg binary not found!'); +} +ffmpeg.setFfmpegPath(ffmpegPath); + +// Prepare output directory (always storage/audio under project root) +const outputDir = `${__dirname}/../../../storage/audio`; +if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); +} + + + +module.exports = { + name:"extraction-video-to-audio", // Unique name for our function that will later be used to get the function from the map via "mapFunctions.get("example").function()" + type:"extractor", // value used to differentiate each module to order them in the UI + displayname:"Default extractor", // The displayname used within the UI + async function(parameter){ + /* + parameter structure: + { + inputVideoPath: String, // Path to the file + outputType: String // Audio file output format + } + */ + let progressBar = new cliProgress.SingleBar({ + format: 'Processing |{bar}| {percentage}% | {timemark}', + barCompleteChar: '\u2588', + barIncompleteChar: '\u2591', + hideCursor: true + }); + try { + // if (meta.url === `file://${process.argv[1]}`) { + this.extractAudioFromVideo(parameter.inputVideoPath, progressBar, parameter.outputType) + .then(() => console.log('Audio extraction successful.')) + .catch((err) => console.error(err)); + // } + } catch (error) { + console.log(parameter.outputType); + + } + + }, + // Derive input and output paths + // const inputVideoPath = process.argv[2]; + // console.log(process.argv); + + /** + * Extracts audio from a video using ffmpeg. + * - Converts video to WAV (16 kHz, Mono, PCM optional if needed) + * - Shows CLI progress bar + * - Handles errors gracefully (without errors) + */ + extractAudioFromVideo: async function (videoFilePath, progressBar, outputType){ + let inputVideoName = path.basename(videoFilePath, path.extname(videoFilePath)); + let outputAudioPath = path.join(outputDir, `${inputVideoName}.${outputType}`); + + return new Promise((resolve, reject) => { + try { + ffmpeg(videoFilePath) + .outputFormat(outputType) + // .audioCodec('pcm_s16le') + .audioChannels(1) + .audioFrequency(16000) + // .setFfmpegPath("./ffmpeg.exe") + .on('progress', (progress) => { + if (!progressBar.isActive) progressBar.start(100, 0, { timemark: '00:00:00' }); + if (progress.percent) { + progressBar.update(progress.percent, { timemark: progress.timemark }); + } + }) + .on('end', () => { + progressBar.update(100, { timemark: 'done' }); + progressBar.stop(); + console.log(`Extraction completed: ${outputAudioPath}`); + resolve(); + }) + .on('error', (err) => { + progressBar.stop(); + console.error(`failed_audio_extraction on type ${outputType}: ${err.message}`); + reject(err); + }) + .save(outputAudioPath); + + } catch (error) { + console.log(); + } + }); + } + +} \ No newline at end of file diff --git a/services/modules/utility/@startup.js b/services/modules/utility/@startup.js index 1b0e79c..dcf64c8 100644 --- a/services/modules/utility/@startup.js +++ b/services/modules/utility/@startup.js @@ -5,5 +5,9 @@ module.exports = { // We are now calling the example function from the example folder mapFunctions.get("example").function("Startup") + + // mapFunctions.get("extraction-video-to-audio").function({inputVideoPath:"./a.mp4", outputType:"wav"}) + // mapFunctions.get("extraction-video-to-audio").function({inputVideoPath:"./b.mp4", outputType:"wav"}) + // mapFunctions.get("extraction-video-to-audio").function({inputVideoPath:"./b.mp4", outputType:"flac"}) } } \ No newline at end of file From da3a8c7d8c56268be82bc2c30a79f642da5ff29e Mon Sep 17 00:00:00 2001 From: "eric.minning" Date: Tue, 11 Nov 2025 16:02:07 +0100 Subject: [PATCH 03/13] Added try/catch handling and some comments --- electron/main/preload.js | 11 +++++-- electron/main/renderer.js | 41 +++++++++++++++--------- electron/main/script.js | 65 ++++++++++++++++++++++++++------------- 3 files changed, 78 insertions(+), 39 deletions(-) diff --git a/electron/main/preload.js b/electron/main/preload.js index 296d46d..5a7a5d7 100644 --- a/electron/main/preload.js +++ b/electron/main/preload.js @@ -2,6 +2,11 @@ const { contextBridge, ipcRenderer, webUtils } = require('electron') -contextBridge.exposeInMainWorld("explorer", { - onFileDrop: (file) => webUtils.getPathForFile(file) -}) +try { + contextBridge.exposeInMainWorld("explorer", { + onFileDrop: (file) => webUtils.getPathForFile(file) + }) +} catch (error) { + console.log("Error in preload.js"); +} + diff --git a/electron/main/renderer.js b/electron/main/renderer.js index 21cd526..3ae8908 100644 --- a/electron/main/renderer.js +++ b/electron/main/renderer.js @@ -2,21 +2,32 @@ const dropzone = document.getElementById("uploadContainer"); dropzone.addEventListener("dragover", (e) =>{ - e.stopPropagation(); - e.preventDefault(); -}); - -dropzone.addEventListener("drop", (e) => { - e.stopPropagation() - e.preventDefault() - const files = e.dataTransfer.files - const filePath = window.explorer.onFileDrop(files[0]) - var holdy = filePath + ""; - if(holdy.endsWith(".mp4")){ - console.log(filePath) - - const files1 = e.dataTransfer.files; - handleFiles(files1); + try { + e.stopPropagation(); + e.preventDefault(); + } catch (error) { + console.log("Error in renderer.js dragover listener function") } +}); + +//listener for when a file get dropped on the drag&drop field +dropzone.addEventListener("drop", (e) => { + try { + e.stopPropagation() + e.preventDefault() + const files = e.dataTransfer.files + const filePath = window.explorer.onFileDrop(files[0]) + var holdy = filePath + ""; + if(holdy.endsWith(".mp4")){ + console.log(filePath) + + const files1 = e.dataTransfer.files; + handleFiles(files1); + } + } catch (error) { + console.log("Error in renderer.js with the listerner for the drop function"); + } + + }) \ No newline at end of file diff --git a/electron/main/script.js b/electron/main/script.js index cc1ac2b..00f5daf 100644 --- a/electron/main/script.js +++ b/electron/main/script.js @@ -1,37 +1,60 @@ -const uploadContainer = document.getElementById('uploadContainer'); -const fileInput = document.getElementById('videoUpload'); -const fileName = document.getElementById('fileName'); -const manualBtn = document.getElementById('manualUploadBtn'); -const videoPreview = document.getElementById('videoPreview'); - - - +try { + const uploadContainer = document.getElementById('uploadContainer'); + const fileInput = document.getElementById('videoUpload'); + const fileName = document.getElementById('fileName'); + const manualBtn = document.getElementById('manualUploadBtn'); + const videoPreview = document.getElementById('videoPreview'); +} catch (error) { + console.log("Error in skript value setting section"); +} +//listener for the file explorer search manualBtn.addEventListener('click', () => { - fileInput.click(); + try { + fileInput.click(); + } catch (error) { + console.log("Error in manualBtn EventListener click"); + } + }); - +//listener for the file explorer search when something got selected fileInput.addEventListener('change', () => { - handleFiles(fileInput.files); + try { + handleFiles(fileInput.files); + } catch (error) { + console.log("Error in manualBtn EventListener change"); + } + }); - +//function to display the file path in the drop down box function handleFiles(files) { - if (files.length > 0) { - const file = files[0]; - if (file.type.startsWith('video/')) { - fileInput.files = files; - fileName.textContent = `Chosen video: ${file.name}`; + try { + if (files.length > 0) { + const file = files[0]; + if (file.type.startsWith('video/')) { + fileInput.files = files; + fileName.textContent = `Chosen video: ${file.name}`; + } } -} + } catch (error) { + console.log("Error in script.js handleFiles function"); + } + } +//function to regulate the progress on the progressbar function updateProgressBar(bar, value){ - value = Math.round(value); - bar.querySelector(".progress_fill").style.width = `${value}%`; - bar.querySelector(".progress_text").textContent = `${value}%`; + try { + value = Math.round(value); + bar.querySelector(".progress_fill").style.width = `${value}%`; + bar.querySelector(".progress_text").textContent = `${value}%`; + } catch (error) { + console.log("Error in scripts.js updateProgressBar function"); + } + } \ No newline at end of file From d9e96316c012da728efb28bd0a166b1622c79463 Mon Sep 17 00:00:00 2001 From: Verena Schulz Date: Tue, 11 Nov 2025 18:31:01 +0100 Subject: [PATCH 04/13] Language selection implemented and UI reworked --- .../main/flags/germany-flag-png-large.jpg | Bin 0 -> 2659 bytes electron/main/flags/india-flag-png-large.png | Bin 0 -> 18036 bytes .../flags/united-kingdom-flag-png-large.jpg | Bin 0 -> 36010 bytes electron/main/index.html | 25 ++++++---- electron/main/renderer.js | 2 +- electron/main/script.js | 45 ++++++++++++++++++ electron/main/style.css | 23 +++++++-- 7 files changed, 81 insertions(+), 14 deletions(-) create mode 100644 electron/main/flags/germany-flag-png-large.jpg create mode 100644 electron/main/flags/india-flag-png-large.png create mode 100644 electron/main/flags/united-kingdom-flag-png-large.jpg diff --git a/electron/main/flags/germany-flag-png-large.jpg b/electron/main/flags/germany-flag-png-large.jpg new file mode 100644 index 0000000000000000000000000000000000000000..026ee171d6cfb064dd26ae3e08275942f229718b GIT binary patch literal 2659 zcmex=^lOiET&UP@Y7ModgWM?qOlT~kX_QeM|USHnP6LsNs1ot>SFgG-o;OISln zOiF`f@c$5lAO}+f^9yE1B?cxzMrJ|A|3?_K!Omnr2J9R_hchFKFfagJ{r?sN4>KbJ zlK`^-Q0`O%s)AA8Xb2D!0$hi*{xhhDvCNGSDoyab+BnFWjH30#_-a(mXb7N&fN904 z&8Q-yywMP#MF;>ZaS;JVG?Om?Yim$7&S=jdR1Z`kCCI?U4CEt#XcEK1hZ7cn)iE&w z)jeNmx~JvZC*kN5zWIuSo_XF?9;>&+k{K2~f_e++VW77b7{6Y@^XnC}-L8i7>xMe# IOW^;T0ClTENB{r; literal 0 HcmV?d00001 diff --git a/electron/main/flags/india-flag-png-large.png b/electron/main/flags/india-flag-png-large.png new file mode 100644 index 0000000000000000000000000000000000000000..16b7b8dacda2972b46e2f884638281fbf5e57961 GIT binary patch literal 18036 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 literal 0 HcmV?d00001 diff --git a/electron/main/index.html b/electron/main/index.html index d75939f..c6980f3 100644 --- a/electron/main/index.html +++ b/electron/main/index.html @@ -3,15 +3,22 @@ - Video Upload Drag and Drop + Button + Video to document
-

Video to document

+
+ + + +
+ +

Video to document

+
-

Drag and drop video file

+

Drag and drop video file

No video chosen
@@ -19,19 +26,19 @@
- +
- - + +
- - + +
- +
diff --git a/electron/main/renderer.js b/electron/main/renderer.js index 3ae8908..a21f00c 100644 --- a/electron/main/renderer.js +++ b/electron/main/renderer.js @@ -19,7 +19,7 @@ dropzone.addEventListener("drop", (e) => { const files = e.dataTransfer.files const filePath = window.explorer.onFileDrop(files[0]) var holdy = filePath + ""; - if(holdy.endsWith(".mp4")){ + if(holdy.endsWith(".mp4", ".mov", ".avi", ".mkv")){ console.log(filePath) const files1 = e.dataTransfer.files; diff --git a/electron/main/script.js b/electron/main/script.js index 00f5daf..a2e4a98 100644 --- a/electron/main/script.js +++ b/electron/main/script.js @@ -4,6 +4,10 @@ try { const fileName = document.getElementById('fileName'); const manualBtn = document.getElementById('manualUploadBtn'); const videoPreview = document.getElementById('videoPreview'); + const submitBtn = document.getElementById('submitButton'); + const deButton = document.getElementById('de_Btn'); + const engButton = document.getElementById('eng_Btn'); + const inButton = document.getElementById('in_Btn'); } catch (error) { console.log("Error in skript value setting section"); @@ -19,6 +23,47 @@ manualBtn.addEventListener('click', () => { }); +submitBtn.addEventListener('click', () => { + +// mapFunctions.get("extraction-video-to-audio").function({inputVideoPath:"./a.mp4", outputType:"wav"}) + +}); + +function changeLanguage(language) { + if (language === 'en') { + document.getElementById('title').textContent = 'Video to document'; + document.getElementById('h1').textContent = 'Video to document'; + document.getElementById('p1').textContent = 'Drag and drop video file'; + document.getElementById('fileName').textContent = 'No video chosen'; + document.getElementById('manualUploadBtn').textContent = 'Search video'; + document.getElementById('checkbox_group').textContent = 'Choose prefered document style:'; + document.getElementById('label_format').textContent = 'Meeting report'; + document.getElementById('label_summary').textContent = 'Summary with timestamps'; + document.getElementById('submitButton').textContent = 'Submit'; + } else if (language === 'de') { + document.getElementById('title').textContent = 'Video zu Dokument'; + document.getElementById('h1').textContent = 'Video zu Dokument'; + document.getElementById('p1').textContent = 'Video per Drag & Drop ablegen'; + document.getElementById('fileName').textContent = 'Kein Video ausgewaehlt'; + document.getElementById('manualUploadBtn').textContent = 'Video suchen'; + document.getElementById('checkbox_group').textContent = 'Bevorzugte Dokumentvarianten:'; + document.getElementById('label_format').textContent = 'Meeting Bericht'; + document.getElementById('label_summary').textContent = 'Zusammenfassung mit Zeitstempeln'; + document.getElementById('submitButton').textContent = 'Absenden'; + } else if(language == "in") { + document.getElementById('title').textContent = 'दस्तावेज़ के लिए वीडियो'; + document.getElementById('h1').textContent = 'दस्तावेज़ के लिए वीडियो'; + document.getElementById('p1').textContent = 'वीडियो फ़ाइल खींचें और छोड़ें'; + document.getElementById('fileName').textContent = 'कोई वीडियो नहीं चुना गया'; + document.getElementById('manualUploadBtn').textContent = 'वीडियो खोजें'; + document.getElementById('checkbox_group').textContent = 'पसंदीदा दस्तावेज़ शैली चुनें:'; + document.getElementById('label_format').textContent = 'बैठक रिपोर्ट'; + document.getElementById('label_summary').textContent = 'टाइमस्टैम्प के साथ सारांश'; + document.getElementById('submitButton').textContent = 'जमा करना'; + } +} + + //listener for the file explorer search when something got selected fileInput.addEventListener('change', () => { try { diff --git a/electron/main/style.css b/electron/main/style.css index b68c162..e9abb50 100644 --- a/electron/main/style.css +++ b/electron/main/style.css @@ -5,7 +5,7 @@ body { justify-content: center; align-items: center; height: 100vh; - background-color: #444; + background-color: #555; gap: 15px; margin: 0; } @@ -75,6 +75,7 @@ gap: 5px; .checkbox-group { margin-top: 15px; + margin-bottom: 15px; display: flex; flex-direction: column; gap: 10px; @@ -92,14 +93,14 @@ gap: 5px; } .mitte { - background-color: #eaf0ff; + background-color: #f2f3f4; display: flex; flex-direction: column; align-items: center; padding: 5% 50px; margin-top: 20px; gap: 10px; - border: 2px; + border: 1px; border-color: black; border-style: solid; } @@ -112,7 +113,7 @@ h1 { position: relative; width: 210px; height: 30px; - background: darkslategray; + background: rgb(42, 46, 78); border-radius: 5px; overflow: hidden; } @@ -129,4 +130,18 @@ h1 { top: 50%; right: 5px; transform: translateY(-50%); + color: white; +} + +.flagsBtns { + display: flex; + justify-content: flex-end; +} + +.de_Btn, .eng_Btn, .in_Btn { + padding: 8px 16px; + color: white; + border: none; + border-radius: 8px; + cursor: pointer; } \ No newline at end of file From a32e7e5744d99304b7ae938ed1a6848ac923d09c Mon Sep 17 00:00:00 2001 From: Verena Schulz Date: Wed, 12 Nov 2025 17:39:02 +0100 Subject: [PATCH 05/13] Checkboxes check (with alert) --- electron/main/index.html | 2 +- electron/main/script.js | 25 ++++++++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/electron/main/index.html b/electron/main/index.html index c6980f3..f88b4c2 100644 --- a/electron/main/index.html +++ b/electron/main/index.html @@ -38,7 +38,7 @@
- +
diff --git a/electron/main/script.js b/electron/main/script.js index a2e4a98..a325e9f 100644 --- a/electron/main/script.js +++ b/electron/main/script.js @@ -23,12 +23,27 @@ manualBtn.addEventListener('click', () => { }); -submitBtn.addEventListener('click', () => { - -// mapFunctions.get("extraction-video-to-audio").function({inputVideoPath:"./a.mp4", outputType:"wav"}) - -}); +//function to check if one checkbox is at least klicked +function checkBoxes() { + const checkboxes = document.querySelectorAll('input[name="docFormat"]'); + let isChecked = false; + checkboxes.forEach(function(checkbox){ + if(checkbox.checked){ + isChecked = true; + } + }); + + if(isChecked){ + //Code to submit the video + } else { + //language only english at the moment + alert('Please select at least one document type.'); + } +// mapFunctions.get("extraction-video-to-audio").function({inputVideoPath:"./a.mp4", outputType:"wav"}) +} + +//language changing feature function changeLanguage(language) { if (language === 'en') { document.getElementById('title').textContent = 'Video to document'; From 54f1f6c135f933031479cffd49037952bcbadd70 Mon Sep 17 00:00:00 2001 From: "eric.minning" Date: Wed, 12 Nov 2025 18:16:27 +0100 Subject: [PATCH 06/13] Added the check if a path is selected and the call to the transcript functio. --- electron/main/script.js | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/electron/main/script.js b/electron/main/script.js index a325e9f..a0c816b 100644 --- a/electron/main/script.js +++ b/electron/main/script.js @@ -25,21 +25,30 @@ manualBtn.addEventListener('click', () => { //function to check if one checkbox is at least klicked function checkBoxes() { - const checkboxes = document.querySelectorAll('input[name="docFormat"]'); - let isChecked = false; + try { + const checkboxes = document.querySelectorAll('input[name="docFormat"]'); + let isChecked = false; - checkboxes.forEach(function(checkbox){ - if(checkbox.checked){ - isChecked = true; + checkboxes.forEach(function(checkbox){ + if(checkbox.checked){ + isChecked = true; + } + }); + + if(isChecked){ + //Code to submit the video + var pathTest = fileName.textContent + ""; + if(pathTest.endsWith(".mp4", ".mov", ".avi", ".mkv")){ + mapFunctions.get("extraction-video-to-audio").function({inputVideoPath: pathTest, outputType:"wav"}); + } + } else { + //language only english at the moment + alert('Please select at least one document type.'); } - }); - - if(isChecked){ - //Code to submit the video - } else { - //language only english at the moment - alert('Please select at least one document type.'); + } catch (error) { + console.log("Error in scrpt.js checkBoxes function"); } + // mapFunctions.get("extraction-video-to-audio").function({inputVideoPath:"./a.mp4", outputType:"wav"}) } From 0f54edb0aa8360dd9c69b7934baaf5acbe62b060 Mon Sep 17 00:00:00 2001 From: "eric.minning" Date: Wed, 12 Nov 2025 18:44:27 +0100 Subject: [PATCH 07/13] Removed the variables at the start of the script.js file --- electron/main/script.js | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/electron/main/script.js b/electron/main/script.js index a0c816b..682ad53 100644 --- a/electron/main/script.js +++ b/electron/main/script.js @@ -1,22 +1,8 @@ -try { - const uploadContainer = document.getElementById('uploadContainer'); - const fileInput = document.getElementById('videoUpload'); - const fileName = document.getElementById('fileName'); - const manualBtn = document.getElementById('manualUploadBtn'); - const videoPreview = document.getElementById('videoPreview'); - const submitBtn = document.getElementById('submitButton'); - const deButton = document.getElementById('de_Btn'); - const engButton = document.getElementById('eng_Btn'); - const inButton = document.getElementById('in_Btn'); - -} catch (error) { - console.log("Error in skript value setting section"); -} //listener for the file explorer search -manualBtn.addEventListener('click', () => { +manualUploadBtn.addEventListener('click', () => { try { - fileInput.click(); + videoUpload.click(); } catch (error) { console.log("Error in manualBtn EventListener click"); } @@ -46,7 +32,7 @@ function checkBoxes() { alert('Please select at least one document type.'); } } catch (error) { - console.log("Error in scrpt.js checkBoxes function"); + console.log("Error") } // mapFunctions.get("extraction-video-to-audio").function({inputVideoPath:"./a.mp4", outputType:"wav"}) @@ -89,9 +75,9 @@ function changeLanguage(language) { //listener for the file explorer search when something got selected -fileInput.addEventListener('change', () => { +videoUpload.addEventListener('change', () => { try { - handleFiles(fileInput.files); + handleFiles(videoUpload.files); } catch (error) { console.log("Error in manualBtn EventListener change"); } @@ -106,7 +92,7 @@ function handleFiles(files) { if (files.length > 0) { const file = files[0]; if (file.type.startsWith('video/')) { - fileInput.files = files; + videoUpload.files = files; fileName.textContent = `Chosen video: ${file.name}`; } } From c8cbd4e92a034fa108d06337a3e63d24e529432f Mon Sep 17 00:00:00 2001 From: "eric.minning" Date: Wed, 12 Nov 2025 18:52:28 +0100 Subject: [PATCH 08/13] Fixed an error in checkBoxes function and uploadContainer "drop" listener regarding file path testing --- electron/main/renderer.js | 8 +++----- electron/main/script.js | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/electron/main/renderer.js b/electron/main/renderer.js index a21f00c..ad684ef 100644 --- a/electron/main/renderer.js +++ b/electron/main/renderer.js @@ -1,7 +1,5 @@ -const dropzone = document.getElementById("uploadContainer"); - -dropzone.addEventListener("dragover", (e) =>{ +uploadContainer.addEventListener("dragover", (e) =>{ try { e.stopPropagation(); e.preventDefault(); @@ -12,14 +10,14 @@ dropzone.addEventListener("dragover", (e) =>{ }); //listener for when a file get dropped on the drag&drop field -dropzone.addEventListener("drop", (e) => { +uploadContainer.addEventListener("drop", (e) => { try { e.stopPropagation() e.preventDefault() const files = e.dataTransfer.files const filePath = window.explorer.onFileDrop(files[0]) var holdy = filePath + ""; - if(holdy.endsWith(".mp4", ".mov", ".avi", ".mkv")){ + if(holdy.endsWith(".mp4") || holdy.endsWith(".mov") || holdy.endsWith(".avi") || holdy.endsWith( ".mkv")){ console.log(filePath) const files1 = e.dataTransfer.files; diff --git a/electron/main/script.js b/electron/main/script.js index 682ad53..ab94700 100644 --- a/electron/main/script.js +++ b/electron/main/script.js @@ -24,7 +24,7 @@ function checkBoxes() { if(isChecked){ //Code to submit the video var pathTest = fileName.textContent + ""; - if(pathTest.endsWith(".mp4", ".mov", ".avi", ".mkv")){ + if(pathTest.endsWith(".mp4") || holdy.endsWith(".mov") || holdy.endsWith(".avi") || holdy.endsWith( ".mkv")){ mapFunctions.get("extraction-video-to-audio").function({inputVideoPath: pathTest, outputType:"wav"}); } } else { From fbd53682236b1abb9852b7d55e52db415376353e Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 12 Nov 2025 20:00:25 +0100 Subject: [PATCH 09/13] Implemented functionality to have the UI be able to communicate with the backend --- electron/main/preload.js | 5 ++++- electron/main/script.js | 5 ++++- main.js | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/electron/main/preload.js b/electron/main/preload.js index 5a7a5d7..80eecb8 100644 --- a/electron/main/preload.js +++ b/electron/main/preload.js @@ -5,7 +5,10 @@ const { contextBridge, ipcRenderer, webUtils } = require('electron') try { contextBridge.exposeInMainWorld("explorer", { onFileDrop: (file) => webUtils.getPathForFile(file) - }) + }) + contextBridge.exposeInMainWorld("extractor", { + extract: (file) => ipcRenderer.send("extract", file) + }) } catch (error) { console.log("Error in preload.js"); } diff --git a/electron/main/script.js b/electron/main/script.js index ab94700..e82a675 100644 --- a/electron/main/script.js +++ b/electron/main/script.js @@ -11,6 +11,8 @@ manualUploadBtn.addEventListener('click', () => { //function to check if one checkbox is at least klicked function checkBoxes() { + console.log("fuck"); + try { const checkboxes = document.querySelectorAll('input[name="docFormat"]'); let isChecked = false; @@ -25,7 +27,8 @@ function checkBoxes() { //Code to submit the video var pathTest = fileName.textContent + ""; if(pathTest.endsWith(".mp4") || holdy.endsWith(".mov") || holdy.endsWith(".avi") || holdy.endsWith( ".mkv")){ - mapFunctions.get("extraction-video-to-audio").function({inputVideoPath: pathTest, outputType:"wav"}); + console.log("fuck 2"); + window.extractor.extract({inputVideoPath: pathTest, outputType:"wav"}) } } else { //language only english at the moment diff --git a/main.js b/main.js index 8557cf6..fb4cd45 100644 --- a/main.js +++ b/main.js @@ -74,3 +74,6 @@ function createWindow() { electron.app.whenReady().then(createWindow); +electron.ipcMain.on("extract", (event, args) => { + console.log(args); +}) \ No newline at end of file From 6d9c94c685ae9ecc97ff11c148d9f531a874a340 Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 12 Nov 2025 20:03:09 +0100 Subject: [PATCH 10/13] removed some debug console outputs --- electron/main/script.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/electron/main/script.js b/electron/main/script.js index e82a675..eab11d0 100644 --- a/electron/main/script.js +++ b/electron/main/script.js @@ -10,8 +10,6 @@ manualUploadBtn.addEventListener('click', () => { }); //function to check if one checkbox is at least klicked -function checkBoxes() { - console.log("fuck"); try { const checkboxes = document.querySelectorAll('input[name="docFormat"]'); @@ -27,7 +25,6 @@ function checkBoxes() { //Code to submit the video var pathTest = fileName.textContent + ""; if(pathTest.endsWith(".mp4") || holdy.endsWith(".mov") || holdy.endsWith(".avi") || holdy.endsWith( ".mkv")){ - console.log("fuck 2"); window.extractor.extract({inputVideoPath: pathTest, outputType:"wav"}) } } else { From 87e3368a9afa8adb0e12a6ab3ec24d835b23c8ea Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 12 Nov 2025 20:04:48 +0100 Subject: [PATCH 11/13] fixed code --- electron/main/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electron/main/script.js b/electron/main/script.js index eab11d0..244b557 100644 --- a/electron/main/script.js +++ b/electron/main/script.js @@ -10,7 +10,7 @@ manualUploadBtn.addEventListener('click', () => { }); //function to check if one checkbox is at least klicked - +function checkBoxes() { try { const checkboxes = document.querySelectorAll('input[name="docFormat"]'); let isChecked = false; From 73f6fa75245d52d20e49789a64b5e3b1e3fe6fd4 Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 12 Nov 2025 20:24:45 +0100 Subject: [PATCH 12/13] fixed the code so that it now returns the actual path of the file --- electron/main/preload.js | 3 +++ electron/main/script.js | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/electron/main/preload.js b/electron/main/preload.js index 80eecb8..ec7ff05 100644 --- a/electron/main/preload.js +++ b/electron/main/preload.js @@ -9,6 +9,9 @@ try { contextBridge.exposeInMainWorld("extractor", { extract: (file) => ipcRenderer.send("extract", file) }) + contextBridge.exposeInMainWorld("electronAPI", { + getFilePath: (file) => {return webUtils.getPathForFile(file)} + }) } catch (error) { console.log("Error in preload.js"); } diff --git a/electron/main/script.js b/electron/main/script.js index 244b557..b947cdb 100644 --- a/electron/main/script.js +++ b/electron/main/script.js @@ -23,7 +23,7 @@ function checkBoxes() { if(isChecked){ //Code to submit the video - var pathTest = fileName.textContent + ""; + var pathTest = window.electronAPI.getFilePath(videoUpload.files[0]); if(pathTest.endsWith(".mp4") || holdy.endsWith(".mov") || holdy.endsWith(".avi") || holdy.endsWith( ".mkv")){ window.extractor.extract({inputVideoPath: pathTest, outputType:"wav"}) } @@ -32,7 +32,7 @@ function checkBoxes() { alert('Please select at least one document type.'); } } catch (error) { - console.log("Error") + console.log(error) } // mapFunctions.get("extraction-video-to-audio").function({inputVideoPath:"./a.mp4", outputType:"wav"}) From 94f390f28bc260c1f33420be049c551ca2073bcc Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 12 Nov 2025 20:28:55 +0100 Subject: [PATCH 13/13] Implemented audio extractor into IPC call, sooooooo, frontend and backend are now officially linked successfully --- main.js | 2 +- .../modules/extraction/ffmpegExtractor.ts | 90 ------------------- 2 files changed, 1 insertion(+), 91 deletions(-) delete mode 100644 services/modules/extraction/ffmpegExtractor.ts diff --git a/main.js b/main.js index fb4cd45..1482f06 100644 --- a/main.js +++ b/main.js @@ -75,5 +75,5 @@ electron.app.whenReady().then(createWindow); electron.ipcMain.on("extract", (event, args) => { - console.log(args); + mapFunctions.get("extraction-video-to-audio").function(args) }) \ No newline at end of file diff --git a/services/modules/extraction/ffmpegExtractor.ts b/services/modules/extraction/ffmpegExtractor.ts deleted file mode 100644 index c23c879..0000000 --- a/services/modules/extraction/ffmpegExtractor.ts +++ /dev/null @@ -1,90 +0,0 @@ -import ffmpegPath from 'ffmpeg-static'; -import ffmpeg from 'fluent-ffmpeg'; -import path from 'path'; -import fs from 'fs'; -import cliProgress from 'cli-progress'; -import { fileURLToPath } from 'url'; - -// Base code reference: https://docs.yemreak.com/arsiv/programming/extract-audio-from-video-with-typescript-and-ffmpeg -// Test command: npx ts-node ./extract.ts /path/to/video.mp4 - -/** - * Extracts audio from a video file and saves it as WAV. - * @param videoFilePath Path to the input video file. - */ - -// Ensure ffmpeg binary is available -if (!ffmpegPath) { - throw new Error('FFmpeg binary not found!'); -} -ffmpeg.setFfmpegPath(ffmpegPath); - -// Ensure an input video path is provided via CLI -if (process.argv.length < 3) { - console.error('Usage: ts-node ./extract.ts '); - process.exit(1); -} - -// Resolve __dirname equivalent in ESM -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -// Prepare output directory (always storage/audio under project root) -const outputDir = path.join(__dirname, '..', '..', '..', 'storage', 'audio'); -if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir, { recursive: true }); -} - -// Derive input and output paths -const inputVideoPath = process.argv[2]; -const inputVideoName = path.basename(inputVideoPath, path.extname(inputVideoPath)); -const outputAudioPath = path.join(outputDir, `${inputVideoName}.wav`); - -// Initialize CLI progress bar -const progressBar = new cliProgress.SingleBar({ - format: 'Processing |{bar}| {percentage}% | {timemark}', - barCompleteChar: '\u2588', - barIncompleteChar: '\u2591', - hideCursor: true -}); - -/** - * Extracts audio from a video using ffmpeg. - * - Converts video to WAV (16 kHz, Mono, PCM optional if needed) - * - Shows CLI progress bar - * - Handles errors gracefully (without errors) - */ -export function extractAudioFromVideo(videoFilePath: string): Promise { - return new Promise((resolve, reject) => { - ffmpeg(videoFilePath) - .outputFormat('wav') - .audioCodec('pcm_s16le') - .audioChannels(1) - .audioFrequency(16000) - .on('progress', (progress) => { - if (!progressBar.isActive) progressBar.start(100, 0, { timemark: '00:00:00' }); - if (progress.percent) { - progressBar.update(progress.percent, { timemark: progress.timemark }); - } - }) - .on('end', () => { - progressBar.update(100, { timemark: 'done' }); - progressBar.stop(); - console.log(`Extraction completed: ${outputAudioPath}`); - resolve(); - }) - .on('error', (err) => { - progressBar.stop(); - console.error(`failed_audio_extraction: ${err.message}`); - reject(err); - }) - .save(outputAudioPath); - }); -} - -// Run extraction if executed directly from CLI -if (import.meta.url === `file://${process.argv[1]}`) { - extractAudioFromVideo(inputVideoPath) - .then(() => console.log('Audio extraction successful.')) - .catch((err) => console.error(err)); -} \ No newline at end of file