From a43895d9189e43089bf2aac38beba8ffd33aa83f Mon Sep 17 00:00:00 2001 From: "eric.minning" Date: Wed, 29 Oct 2025 11:38:32 +0100 Subject: [PATCH 01/43] Created API-Skeleton and some structure. --- API-Skeleton/main.js | 17 + API-Skeleton/package-lock.json | 842 +++++++++++++++++++++++++++ API-Skeleton/package.json | 15 + API-Skeleton/routers/video_Router.js | 37 ++ 4 files changed, 911 insertions(+) create mode 100644 API-Skeleton/main.js create mode 100644 API-Skeleton/package-lock.json create mode 100644 API-Skeleton/package.json create mode 100644 API-Skeleton/routers/video_Router.js diff --git a/API-Skeleton/main.js b/API-Skeleton/main.js new file mode 100644 index 0000000..48c39b1 --- /dev/null +++ b/API-Skeleton/main.js @@ -0,0 +1,17 @@ +const express = require('express'); +const video_Router = require("./routers/video_Router.js"); +const app = express(); +app.use(express.json); + +const PORT = process.env.PORT || 3000; + +app.listen(PORT, () => + console.log( "Server running on PORT", PORT) +); + +app.get("/api/v1/healthcheck", async (req,res) =>{ + res.status(200).send("running") +}); + +app.use("/videos", video_Router); +app.use("/documents", documents); \ No newline at end of file diff --git a/API-Skeleton/package-lock.json b/API-Skeleton/package-lock.json new file mode 100644 index 0000000..81d6e6c --- /dev/null +++ b/API-Skeleton/package-lock.json @@ -0,0 +1,842 @@ +{ + "name": "api-skeleton", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "api-skeleton", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "^5.1.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "license": "MIT", + "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/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.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==", + "license": "MIT", + "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==", + "license": "MIT", + "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/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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "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==", + "license": "MIT", + "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/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "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/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "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==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "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", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "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==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "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/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==", + "license": "MIT", + "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==", + "license": "ISC" + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "license": "ISC" + } + } +} diff --git a/API-Skeleton/package.json b/API-Skeleton/package.json new file mode 100644 index 0000000..907c8d8 --- /dev/null +++ b/API-Skeleton/package.json @@ -0,0 +1,15 @@ +{ + "name": "api-skeleton", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "express": "^5.1.0" + } +} diff --git a/API-Skeleton/routers/video_Router.js b/API-Skeleton/routers/video_Router.js new file mode 100644 index 0000000..c10b97b --- /dev/null +++ b/API-Skeleton/routers/video_Router.js @@ -0,0 +1,37 @@ +const express = require('express'); + +const router = express.Router(); + +router.get("/", async(req, res) => { + try { + res.send("Videos"); + res.status(200).send("Videos send."); + } catch (e) { + res.status(500).send("Error trying to get the video files.") + } + +}); + +router.post("/", async(req, res) =>{ + try{ + res.send("videos send"); + res.status(201).send("Video file uploaded."); + } + catch(e){ + res.status(500).send("Error trying to upload video file."); + } + +}); + +router.get("/{id}", async(req, res) => { + try { + res.send("Video with id"); + res.status(200).send("Video details acquired."); + } catch (e) { + + } +}); + + + +module.exports = router; \ No newline at end of file From 4c7fea692e60de6d67120f4c68d6816d7c3107f2 Mon Sep 17 00:00:00 2001 From: "eric.minning" Date: Wed, 29 Oct 2025 12:34:27 +0100 Subject: [PATCH 02/43] Finished the basic "Routers" and tested them. --- API-Skeleton/main.js | 4 +-- API-Skeleton/routers/document_Router.js | 32 +++++++++++++++++++ API-Skeleton/routers/video_Router.js | 42 +++++++++++++++---------- 3 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 API-Skeleton/routers/document_Router.js diff --git a/API-Skeleton/main.js b/API-Skeleton/main.js index 48c39b1..2d42d41 100644 --- a/API-Skeleton/main.js +++ b/API-Skeleton/main.js @@ -1,7 +1,7 @@ const express = require('express'); const video_Router = require("./routers/video_Router.js"); +const document_Router = require("./routers/document_Router.js"); const app = express(); -app.use(express.json); const PORT = process.env.PORT || 3000; @@ -14,4 +14,4 @@ app.get("/api/v1/healthcheck", async (req,res) =>{ }); app.use("/videos", video_Router); -app.use("/documents", documents); \ No newline at end of file +app.use("/documents", document_Router); \ No newline at end of file diff --git a/API-Skeleton/routers/document_Router.js b/API-Skeleton/routers/document_Router.js new file mode 100644 index 0000000..689dbe6 --- /dev/null +++ b/API-Skeleton/routers/document_Router.js @@ -0,0 +1,32 @@ +const express = require('express'); + +const router = express.Router(); + +//Getting a list of all existing documents +router.get("/", async(req, res) =>{ + try { + res.status(200).json({documents: []}); //TODO: array full of the documents names + } catch (e) { + res.status(404).send("Documents not found"); + } +}); + +//Getting a specific document +router.get("/:id", async(req, res) => { + try { + res.status(200).json({dokument_URL: ""}); //TODO: return the specific document + } catch (e) { + res.status(404).send("Document not found"); + } +}); + +//Deleting a specific document +router.delete("/:id", async(req, res) => { + try { + res.status(200).send("Document deleted."); //TODO: delet the specific document + } catch (e) { + res.status(500).send("Error trying to delete a document"); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/API-Skeleton/routers/video_Router.js b/API-Skeleton/routers/video_Router.js index c10b97b..886d5d9 100644 --- a/API-Skeleton/routers/video_Router.js +++ b/API-Skeleton/routers/video_Router.js @@ -2,20 +2,10 @@ const express = require('express'); const router = express.Router(); -router.get("/", async(req, res) => { - try { - res.send("Videos"); - res.status(200).send("Videos send."); - } catch (e) { - res.status(500).send("Error trying to get the video files.") - } - -}); - +//Uploading a video file router.post("/", async(req, res) =>{ try{ - res.send("videos send"); - res.status(201).send("Video file uploaded."); + res.status(201).json({video:"VideoID"}); //TODO: return id as json } catch(e){ res.status(500).send("Error trying to upload video file."); @@ -23,15 +13,33 @@ router.post("/", async(req, res) =>{ }); -router.get("/{id}", async(req, res) => { +//Requesting a list of all video files +router.get("/", async(req, res) => { try { - res.send("Video with id"); - res.status(200).send("Video details acquired."); + res.status(200).json({videos:[]}); //TODO: return a json array showing all video files } catch (e) { - + res.status(500).send("Error trying to get the video files.") + } + +}); + +//Getting a video file by id +router.get("/:id", async(req, res) => { + try { + res.status(200).json({video: "id"}); //TODO: return the details regarding a specific video + } catch (e) { + res.status(401).send("Unknown ID.") } }); +//Deleting a video file +router.delete("/:id", async(req, res) =>{ + try { + res.status(200).send("Deleted file."); //TODO: delet the video file + } catch (e) { + res.status(204).send("No content."); + } +}); - +//To make the router useable in the main.js file module.exports = router; \ No newline at end of file From 461998cd6675a00a27a9bfb7848bc891748744fd Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Sun, 2 Nov 2025 17:43:08 +0100 Subject: [PATCH 03/43] Implemented first working version of the ffmpeg audio extractor --- .gitignore | 3 + package-lock.json | 565 ++++++++++++++++++ package.json | 15 + .../modules/extraction/ffmpegExtractor.ts | 74 +++ 4 files changed, 657 insertions(+) create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 5832f90..72086f9 100644 --- a/.gitignore +++ b/.gitignore @@ -962,3 +962,6 @@ app.*.symbols !**/ios/**/default.perspectivev3 !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages !/dev/ci/**/Gemfile.lock + +#Storage files +storage/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a124ca4 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,565 @@ +{ + "name": "video2document", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "cli-progress": "^3.12.0", + "ffmpeg-static": "^5.2.0", + "fluent-ffmpeg": "^2.1.3" + }, + "devDependencies": { + "@types/cli-progress": "^3.11.6", + "@types/fluent-ffmpeg": "^2.1.28", + "@types/node": "^24.9.2", + "ts-node": "^10.9.2", + "typescript": "^5.9.3" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@derhuerst/http-basic": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/@derhuerst/http-basic/-/http-basic-8.2.4.tgz", + "integrity": "sha512-F9rL9k9Xjf5blCz8HsJRO4diy111cayL2vkY2XE4r4t3n0yPXVYy3KD3nJ1qbrSn9743UWSXH4IwuCa/HWlGFw==", + "license": "MIT", + "dependencies": { + "caseless": "^0.12.0", + "concat-stream": "^2.0.0", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + }, + "engines": { + "node": ">=6.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", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cli-progress": { + "version": "3.11.6", + "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.6.tgz", + "integrity": "sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/fluent-ffmpeg": { + "version": "2.1.28", + "resolved": "https://registry.npmjs.org/@types/fluent-ffmpeg/-/fluent-ffmpeg-2.1.28.tgz", + "integrity": "sha512-5ovxsDwBcPfJ+eYs1I/ZpcYCnkce7pvH9AHSvrZllAp1ZPpTRDZAFjF3TRFbukxSgIYTTNYePbS0rKUmaxVbXw==", + "dev": true, + "license": "MIT", + "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/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==" + }, + "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/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "license": "Apache-2.0" + }, + "node_modules/cli-progress": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "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/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ffmpeg-static": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ffmpeg-static/-/ffmpeg-static-5.2.0.tgz", + "integrity": "sha512-WrM7kLW+do9HLr+H6tk7LzQ7kPqbAgLjdzNE32+u3Ff11gXt9Kkkd2nusGFrlWMIe+XaA97t+I8JS7sZIrvRgA==", + "hasInstallScript": true, + "license": "GPL-3.0-or-later", + "dependencies": { + "@derhuerst/http-basic": "^8.2.0", + "env-paths": "^2.2.0", + "https-proxy-agent": "^5.0.0", + "progress": "^2.0.3" + }, + "engines": { + "node": ">=16" + } + }, + "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" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "license": "MIT", + "dependencies": { + "@types/node": "^10.0.3" + } + }, + "node_modules/http-response-object/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "license": "MIT" + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "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/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "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/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/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "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/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c120da8 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "dependencies": { + "cli-progress": "^3.12.0", + "ffmpeg-static": "^5.2.0", + "fluent-ffmpeg": "^2.1.3" + }, + "devDependencies": { + "@types/cli-progress": "^3.11.6", + "@types/fluent-ffmpeg": "^2.1.28", + "@types/node": "^24.9.2", + "ts-node": "^10.9.2", + "typescript": "^5.9.3" + } +} + diff --git a/services/modules/extraction/ffmpegExtractor.ts b/services/modules/extraction/ffmpegExtractor.ts index e69de29..6b06bfd 100644 --- a/services/modules/extraction/ffmpegExtractor.ts +++ b/services/modules/extraction/ffmpegExtractor.ts @@ -0,0 +1,74 @@ +import ffmpegPath from 'ffmpeg-static'; +import ffmpeg from 'fluent-ffmpeg'; +import path from 'path'; +import fs from 'fs'; +import cliProgress from 'cli-progress'; +// Source for the base code: https://docs.yemreak.com/arsiv/programming/extract-audio-from-video-with-typescript-and-ffmpeg +// Testfile command: npx ts-node /Users/mikehughes/PROJ/video2document/services/modules/extraction/ffmpegExtractor.ts /Users/mikehughes/Downloads/sweetHomeAlabama.mp4 +/** + * Extracts audio from a video file and saves it as an wav file. + * @param videoFilePath - The path to the video file. + * @param outputAudioPath - The path where the extracted audio file should be saved. + */ +// If the ffmpeg binary is not found, throw an error +if (!ffmpegPath) { + throw new Error('FFmpeg binary not found!'); +} +ffmpeg.setFfmpegPath(ffmpegPath); + +// If the input video path is not provided, exit the script +if (process.argv.length < 3) { + console.error('Usage: ts-node ffmpegExtractor.ts '); + process.exit(1); +} + +// Ensure the output directory exists (./storage/audio - always start command from project root) +const outputDir = path.join(process.cwd(), 'storage', 'audio'); +if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); +} + +// Get input video path from command line arguments +const inputVideoPath = process.argv[2]; +// Derive output audio path from input video path +const inputVideoName = path.basename(inputVideoPath, path.extname(inputVideoPath)); +// Define output audio path +const outputAudioPath = path.join(outputDir, `${inputVideoName}.wav`); + +// Initialize progress bar +const progressBar = new cliProgress.SingleBar({ + format: 'Processing |{bar}| {percentage}% | {timemark}', + barCompleteChar: '\u2588', + barIncompleteChar: '\u2591', + hideCursor: true +}); + +// Function to extract audio from video. Possible parameters are videoFilePath and outputAudioPath. Ouptput format is wav. +function extractAudioFromVideo(videoFilePath: string, outputAudioPath: string): Promise { + return new Promise((resolve, reject) => { + ffmpeg(videoFilePath) + .outputFormat('wav') // Set the output format + .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) => { + console.error(`failed_audio_extraction: ${err.message}`); + reject(err); + }) + .save(outputAudioPath); // Specify the output file path + }); +} + +// Call the function to extract audio. +extractAudioFromVideo(inputVideoPath, outputAudioPath) + .then(() => console.log('Audio extraction successful.')) + .catch((err) => console.error(err)); From 029f40057a72ef11e113dba0c94bc9747fc449d6 Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Sun, 2 Nov 2025 17:46:36 +0100 Subject: [PATCH 04/43] Added some improved commentary --- .../modules/extraction/ffmpegExtractor.ts | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/services/modules/extraction/ffmpegExtractor.ts b/services/modules/extraction/ffmpegExtractor.ts index 6b06bfd..27c280b 100644 --- a/services/modules/extraction/ffmpegExtractor.ts +++ b/services/modules/extraction/ffmpegExtractor.ts @@ -3,39 +3,40 @@ import ffmpeg from 'fluent-ffmpeg'; import path from 'path'; import fs from 'fs'; import cliProgress from 'cli-progress'; -// Source for the base code: https://docs.yemreak.com/arsiv/programming/extract-audio-from-video-with-typescript-and-ffmpeg -// Testfile command: npx ts-node /Users/mikehughes/PROJ/video2document/services/modules/extraction/ffmpegExtractor.ts /Users/mikehughes/Downloads/sweetHomeAlabama.mp4 + +// Base code reference: https://docs.yemreak.com/arsiv/programming/extract-audio-from-video-with-typescript-and-ffmpeg +// Test command: npx ts-node ffmpegExtractor.ts /path/to/video.mp4 + /** - * Extracts audio from a video file and saves it as an wav file. - * @param videoFilePath - The path to the video file. - * @param outputAudioPath - The path where the extracted audio file should be saved. + * Extracts audio from a video file and saves it as WAV. + * @param videoFilePath Path to the input video file. + * @param outputAudioPath Path where the output WAV audio will be saved. */ -// If the ffmpeg binary is not found, throw an error + +// Ensure ffmpeg binary is available if (!ffmpegPath) { throw new Error('FFmpeg binary not found!'); } ffmpeg.setFfmpegPath(ffmpegPath); -// If the input video path is not provided, exit the script +// Ensure an input video path is provided via CLI if (process.argv.length < 3) { console.error('Usage: ts-node ffmpegExtractor.ts '); process.exit(1); } -// Ensure the output directory exists (./storage/audio - always start command from project root) +// Prepare output directory (always relative to project root) const outputDir = path.join(process.cwd(), 'storage', 'audio'); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } -// Get input video path from command line arguments +// Derive input and output paths const inputVideoPath = process.argv[2]; -// Derive output audio path from input video path const inputVideoName = path.basename(inputVideoPath, path.extname(inputVideoPath)); -// Define output audio path const outputAudioPath = path.join(outputDir, `${inputVideoName}.wav`); -// Initialize progress bar +// Initialize CLI progress bar const progressBar = new cliProgress.SingleBar({ format: 'Processing |{bar}| {percentage}% | {timemark}', barCompleteChar: '\u2588', @@ -43,32 +44,40 @@ const progressBar = new cliProgress.SingleBar({ hideCursor: true }); -// Function to extract audio from video. Possible parameters are videoFilePath and outputAudioPath. Ouptput format is wav. +/** + * 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) + */ function extractAudioFromVideo(videoFilePath: string, outputAudioPath: string): Promise { return new Promise((resolve, reject) => { ffmpeg(videoFilePath) - .outputFormat('wav') // Set the output format + .outputFormat('wav') // Set output format to WAV .on('progress', (progress) => { + // Start progress bar if not already active if (!progressBar.isActive) progressBar.start(100, 0, { timemark: '00:00:00' }); if (progress.percent) { progressBar.update(progress.percent, { timemark: progress.timemark }); } }) .on('end', () => { + // Finish progress bar progressBar.update(100, { timemark: 'done' }); progressBar.stop(); console.log(`Extraction completed: ${outputAudioPath}`); resolve(); }) .on('error', (err) => { + // Show extraction errors in a clear format console.error(`failed_audio_extraction: ${err.message}`); reject(err); }) - .save(outputAudioPath); // Specify the output file path + .save(outputAudioPath); // Save output file }); } -// Call the function to extract audio. +// Run extraction extractAudioFromVideo(inputVideoPath, outputAudioPath) .then(() => console.log('Audio extraction successful.')) - .catch((err) => console.error(err)); + .catch((err) => console.error(err)); \ No newline at end of file From 291fda871107ace9d9d0d98af2570c1088606996 Mon Sep 17 00:00:00 2001 From: "eric.minning" Date: Mon, 3 Nov 2025 13:13:42 +0100 Subject: [PATCH 05/43] First base setup from Verena and me regarding the UI --- electron/main/index.html | 26 ++++++++++++++++ electron/main/script.js | 50 +++++++++++++++++++++++++++++++ electron/main/style.css | 64 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 electron/main/index.html create mode 100644 electron/main/script.js create mode 100644 electron/main/style.css diff --git a/electron/main/index.html b/electron/main/index.html new file mode 100644 index 0000000..c8d7b20 --- /dev/null +++ b/electron/main/index.html @@ -0,0 +1,26 @@ + + + + + + Video Upload Drag and Drop + Button + + + +
+ + + +
+

Drag and drop video file

+
No video chosen
+
+ + + + + + + + + \ No newline at end of file diff --git a/electron/main/script.js b/electron/main/script.js new file mode 100644 index 0000000..790e046 --- /dev/null +++ b/electron/main/script.js @@ -0,0 +1,50 @@ +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'); + + +// Drag & Drop Events +uploadContainer.addEventListener('dragover', (e) => { + e.preventDefault(); + uploadContainer.classList.add('dragover'); +}); + + +uploadContainer.addEventListener('dragleave', () => { + uploadContainer.classList.remove('dragover'); +}); + + +uploadContainer.addEventListener('drop', (e) => { + e.preventDefault(); + uploadContainer.classList.remove('dragover'); + + + const files = e.dataTransfer.files; + handleFiles(files); +}); + + +manualBtn.addEventListener('click', () => { + fileInput.click(); +}); + + +fileInput.addEventListener('change', () => { + handleFiles(fileInput.files); +}); + + + + +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}`; + } +} +} diff --git a/electron/main/style.css b/electron/main/style.css new file mode 100644 index 0000000..f0bde45 --- /dev/null +++ b/electron/main/style.css @@ -0,0 +1,64 @@ +body { + font-family: Arial, sans-serif; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 100vh; + background-color: #f5f5f5; + gap: 15px; + margin: 0; +} + + +.upload-container { + background: white; + padding: 40px; + border-radius: 12px; + box-shadow: 0 4px 10px rgba(0,0,0,0.1); + text-align: center; + width: 400px; + transition: border 0.3s, background-color 0.3s; + border: 2px dashed #ccc; +} + + +.upload-container.dragover { + border-color: #007BFF; + background-color: #eaf0ff; +} + + +.upload-container p { + margin: 0 0 15px 0; + font-size: 16px; + color: #555; +} + + +.file-name { + margin-top: 10px; + font-size: 14px; + color: #333; +} + + +.custom-btn { + padding: 10px 20px; + background-color: #007BFF; + color: white; + border: none; + border-radius: 8px; + cursor: pointer; + font-size: 14px; +} + + +.custom-btn:hover { + background-color: #0056b3; +} + + +input[type="file"] { + display: none; +} From b5fe3f3b0cc89b22e21b128568ef87c431dd6715 Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 3 Nov 2025 17:48:29 +0100 Subject: [PATCH 06/43] Implemented the general modular framework. Added basic modules for the trascription tool Assembly and the Documentation tool ChatGPT --- config/config.js | 13 + main.js | 58 ++ package-lock.json | 774 +++++++++++++++++++++ package.json | 22 + requires.js | 10 + services/modules/document/.gitkeep | 0 services/modules/document/chatgpt.js | 8 + services/modules/transcription/.gitkeep | 0 services/modules/transcription/assembly.js | 8 + services/modules/transcription/example.js | 10 + services/modules/utility/@startup.js | 9 + services/modules/utility/cliCommands.js | 30 + 12 files changed, 942 insertions(+) create mode 100644 config/config.js create mode 100644 main.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 requires.js delete mode 100644 services/modules/document/.gitkeep create mode 100644 services/modules/document/chatgpt.js delete mode 100644 services/modules/transcription/.gitkeep create mode 100644 services/modules/transcription/assembly.js create mode 100644 services/modules/transcription/example.js create mode 100644 services/modules/utility/@startup.js create mode 100644 services/modules/utility/cliCommands.js diff --git a/config/config.js b/config/config.js new file mode 100644 index 0000000..f9d2dc1 --- /dev/null +++ b/config/config.js @@ -0,0 +1,13 @@ +module.exports = { + database:{ + /* + Here is an example structure of the config file + You can ofcourse change anything here to suit your needs + This structure is useful if you are using a mysql/mariadb database for example + */ + host:"", // Server IP and port if you have set a different port + username:"", // Username for the database user + password:"", // Password for the database user + database:"" // Name of the database + } +} \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..5205247 --- /dev/null +++ b/main.js @@ -0,0 +1,58 @@ +// Loading required packages +require("./requires.js") +console.log(start); + + +// Initialising map to be used to store the functionality later on for reloadability +mapFunctions = new Map() + + +// Loading the Function Map +var path = `${mainDir}/services/modules` +var folders = fs.readdirSync(path).filter(function (file) { + return fs.statSync(path+'/'+file).isDirectory(); +}); +folders.forEach(element => { + var commandFiles = fs.readdirSync(`${path}/${element}`).filter(file => file.endsWith('.js') && !file.startsWith("index")); + for (const file of commandFiles) { + delete require.cache[require.resolve(`${path}/${element}/${file}`)]; + const command = require(`${path}/${element}/${file}`); + mapFunctions.set(command.name, command); + } +}); + + + +// The startup information for the project, here you can add stuff that might be nice to see when the app starts +mapFunctions.get("Startup_function").function() +console.log("------------------------------------ Status ------------------------------------"); +console.log(__dirname); +console.log(platform); +console.log(`The Startup took ${new Date() - start}ms`) +console.log(`${mapFunctions.size} Function modules loaded`); +console.log("--------------------------------------------------------------------------------"); + + + + +// --------------------------------------------------------- CLI COMMANDS --------------------------------------------------------- // + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}); + + +rl.on("line", data =>{ + const args = data.trim().split(" "); + const command = args.shift().toLowerCase(); + mapFunctions.get("cliCommands").function(command, args) +}) + + + + + +// ----------------------------------------------------------- ELECTRON ----------------------------------------------------------- // + +// TODO - Add Electron support to the project diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..69ed0e5 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,774 @@ +{ + "name": "video2document", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "video2document", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "^5.1.0" + } + }, + "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/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/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/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/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/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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/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/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/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/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/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/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/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/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/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/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-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/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==" + }, + "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-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + }, + "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/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "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/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/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/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/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/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/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/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", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "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/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/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/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/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/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/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/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/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..0665267 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "video2document", + "version": "1.0.0", + "description": "To make it easy for you to get started with GitLab, here's a list of recommended next steps.", + "main": "main.js", + "directories": { + "doc": "docs", + "test": "tests" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://gitlab.rlp.net/proj-wise2526-video2document/video2document" + }, + "author": "", + "license": "ISC", + "dependencies": { + "express": "^5.1.0" + } +} diff --git a/requires.js b/requires.js new file mode 100644 index 0000000..e85038d --- /dev/null +++ b/requires.js @@ -0,0 +1,10 @@ +// Here you can define all the packages that you want to use +// You can also define variables that you want to be able to use in your entire project, like platform or mainDir in this example +// Make sure to define them like these examples here, as they will then be available as global variables throughout your entire project +start = new Date() +platform = process.platform +mainDir = __dirname +fs = require("fs") +readline = require("readline") +config = require("./config/config") + diff --git a/services/modules/document/.gitkeep b/services/modules/document/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/services/modules/document/chatgpt.js b/services/modules/document/chatgpt.js new file mode 100644 index 0000000..6e9034e --- /dev/null +++ b/services/modules/document/chatgpt.js @@ -0,0 +1,8 @@ +module.exports = { + name:"chatgpt", // Unique name for our function that will later be used to get the function from the map via "mapFunctions.get("example").function()" + type:"document", // value used to differentiate each module to order them in the UI + displayname:"ChatGPT", // The displayname used within the UI + async function(parameter){ + // TODO add code to actually send the transcript to ChatGPT and get a response back + } +} \ No newline at end of file diff --git a/services/modules/transcription/.gitkeep b/services/modules/transcription/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/services/modules/transcription/assembly.js b/services/modules/transcription/assembly.js new file mode 100644 index 0000000..3b28dad --- /dev/null +++ b/services/modules/transcription/assembly.js @@ -0,0 +1,8 @@ +module.exports = { + name:"assembly", // Unique name for our function that will later be used to get the function from the map via "mapFunctions.get("example").function()" + type:"transcription", // value used to differentiate each module to order them in the UI + displayname:"Assembly", // The displayname used within the UI + async function(parameter){ + // TODO add code to actually process the audio file + } +} \ No newline at end of file diff --git a/services/modules/transcription/example.js b/services/modules/transcription/example.js new file mode 100644 index 0000000..c86ad86 --- /dev/null +++ b/services/modules/transcription/example.js @@ -0,0 +1,10 @@ +module.exports = { + name:"example", // Unique name for our function that will later be used to get the function from the map via "mapFunctions.get("example").function()" + type:"example-type", // value used to differentiate each module to order them in the UI + displayname:"Example", // The displayname used within the UI + async function(randomParameter){ + // Here we put a simple console.log to show how the system works + // This function will be called from the @startup.js function in the utility folder + console.log(`\n------------\nThis is the example function called by the ${randomParameter} function\n------------\n`); + } +} \ No newline at end of file diff --git a/services/modules/utility/@startup.js b/services/modules/utility/@startup.js new file mode 100644 index 0000000..1b0e79c --- /dev/null +++ b/services/modules/utility/@startup.js @@ -0,0 +1,9 @@ +module.exports = { + name:"Startup_function", + async function(){ + // Put any code here that you want to be executed on startup + + // We are now calling the example function from the example folder + mapFunctions.get("example").function("Startup") + } +} \ No newline at end of file diff --git a/services/modules/utility/cliCommands.js b/services/modules/utility/cliCommands.js new file mode 100644 index 0000000..b249749 --- /dev/null +++ b/services/modules/utility/cliCommands.js @@ -0,0 +1,30 @@ +module.exports = { + name:"cliCommands", + async function(command, args){ + switch(command){ + case "exit": + process.exit(1); + break; + case "reload": + mapFunctions.clear() + // Reloading the Function Map + var path = `${mainDir}/services/modules` + var folders = fs.readdirSync(path).filter(function (file) { + return fs.statSync(path+'/'+file).isDirectory(); + }); + folders.forEach(element => { + var commandFiles = fs.readdirSync(`${path}/${element}`).filter(file => file.endsWith('.js') && !file.startsWith("index")); + for (const file of commandFiles) { + delete require.cache[require.resolve(`${path}/${element}/${file}`)]; + const command = require(`${path}/${element}/${file}`); + mapFunctions.set(command.name, command); + } + }); + console.log(`Reloaded ${mapFunctions.size} modules`) + break; + default: + console.log("This is not a recognised command"); + break; + }; + } +} From 381ad6d192e3977d9a4e09c6cf23a55d85ae72ab Mon Sep 17 00:00:00 2001 From: "eric.minning" Date: Tue, 4 Nov 2025 15:37:27 +0100 Subject: [PATCH 07/43] Installed electron --- electron/package-lock.json | 872 +++++++++++++++++++++++++++++++++++++ electron/package.json | 15 + 2 files changed, 887 insertions(+) create mode 100644 electron/package-lock.json create mode 100644 electron/package.json diff --git a/electron/package-lock.json b/electron/package-lock.json new file mode 100644 index 0000000..584bc87 --- /dev/null +++ b/electron/package-lock.json @@ -0,0 +1,872 @@ +{ + "name": "electron", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "electron", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "electron": "^39.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==", + "dev": true, + "license": "MIT", + "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/@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==", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.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==", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "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.", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "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==", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "license": "MIT", + "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/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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==", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "license": "MIT", + "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/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/electron": { + "version": "39.0.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-39.0.0.tgz", + "integrity": "sha512-UejnuOK4jpRZUq7MkEAnR/szsRWLKBJAdvn6j3xdQLT57fVv13VSNdaUHHjSheaqGzNhCGIdkPsPJnGJVh5kiA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "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/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==", + "dev": true, + "license": "MIT", + "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", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "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==", + "dev": true, + "license": "MIT", + "optional": true + }, + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "license": "BSD-2-Clause", + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "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==", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "license": "BSD-3-Clause", + "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==", + "dev": true, + "license": "ISC", + "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==", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "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==", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "license": "ISC" + }, + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "BSD-2-Clause" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "dev": true, + "license": "ISC", + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "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==", + "dev": true, + "license": "BSD-3-Clause", + "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/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "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==", + "dev": true, + "license": "MIT", + "optional": true + }, + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "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==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "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==", + "dev": true, + "license": "ISC" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/electron/package.json b/electron/package.json new file mode 100644 index 0000000..13749b9 --- /dev/null +++ b/electron/package.json @@ -0,0 +1,15 @@ +{ + "name": "electron", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "start": "electron.", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "description": "", + "devDependencies": { + "electron": "^39.0.0" + } +} From 38d09342322cbfbd12b018030d2ce1a1f62cabb9 Mon Sep 17 00:00:00 2001 From: Verena Schulz Date: Tue, 4 Nov 2025 15:50:07 +0100 Subject: [PATCH 08/43] created main --- electron/main/main.js | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 electron/main/main.js diff --git a/electron/main/main.js b/electron/main/main.js new file mode 100644 index 0000000..c50c90c --- /dev/null +++ b/electron/main/main.js @@ -0,0 +1,42 @@ +import { app, BrowserWindow, ipcMain, dialog } from 'electron'; +import { exec } from 'child_process'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +let mainWindow; + +function createWindow() { + mainWindow = new BrowserWindow({ + width: 800, + height: 600, + webPreferences: { + nodeIntegration: false, + contextIsolation: true, + preload: path.join(__dirname, 'preload.js') + } + }); + + mainWindow.loadFile('index.html'); +} + +app.whenReady().then(createWindow); + +// Kommunikation vom Renderer (Frontend) +ipcMain.handle('convert-video', async (event, filePath) => { + const output = path.join(path.dirname(filePath), 'converted.mp4'); + + return new Promise((resolve, reject) => { + exec(`ffmpeg -i "${filePath}" -vcodec libx264 "${output}"`, (error, stdout, stderr) => { + if (error) { + console.error('Fehler beim Konvertieren:', error); + reject(error); + } else { + console.log('Konvertierung abgeschlossen:', output); + resolve(output); + } + }); + }); +}); From 4686d17ad982006efeefc1effc7d3f1905913937 Mon Sep 17 00:00:00 2001 From: "eric.minning" Date: Tue, 4 Nov 2025 17:18:45 +0100 Subject: [PATCH 09/43] Created a preload.js, small changes in the package.json, main.js, index.html and script.js like removing whitespace --- electron/main/index.html | 6 +++--- electron/main/main.js | 4 +++- electron/main/preload.js | 4 ++++ electron/main/renderer.js | 1 + electron/main/script.js | 9 +++++---- electron/package.json | 3 ++- 6 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 electron/main/preload.js create mode 100644 electron/main/renderer.js diff --git a/electron/main/index.html b/electron/main/index.html index c8d7b20..3d7ffc3 100644 --- a/electron/main/index.html +++ b/electron/main/index.html @@ -7,8 +7,7 @@ - - +
@@ -17,10 +16,11 @@
- + + \ No newline at end of file diff --git a/electron/main/main.js b/electron/main/main.js index c50c90c..3861ad3 100644 --- a/electron/main/main.js +++ b/electron/main/main.js @@ -19,7 +19,7 @@ function createWindow() { } }); - mainWindow.loadFile('index.html'); + mainWindow.loadFile('main/index.html'); } app.whenReady().then(createWindow); @@ -40,3 +40,5 @@ ipcMain.handle('convert-video', async (event, filePath) => { }); }); }); + + diff --git a/electron/main/preload.js b/electron/main/preload.js new file mode 100644 index 0000000..567416a --- /dev/null +++ b/electron/main/preload.js @@ -0,0 +1,4 @@ +const { contextBridge, ipcRenderer } = require('electron'); +contextBridge.exposeInMainWorld('electronAPI', { + sendDropPath: (path) => ipcRenderer.send('file-dropped', path) +}); \ No newline at end of file diff --git a/electron/main/renderer.js b/electron/main/renderer.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/electron/main/renderer.js @@ -0,0 +1 @@ + diff --git a/electron/main/script.js b/electron/main/script.js index 790e046..e3cb694 100644 --- a/electron/main/script.js +++ b/electron/main/script.js @@ -17,10 +17,11 @@ uploadContainer.addEventListener('dragleave', () => { }); -uploadContainer.addEventListener('drop', (e) => { - e.preventDefault(); - uploadContainer.classList.remove('dragover'); - +uploadContainer.addEventListener('drop', (e) => { e.preventDefault(); + const file = e.dataTransfer.files[0]; // Pfad der Datei + const filePath = file.path; + console.log('Pfad:', filePath); // ggf. an den Hauptprozess senden, wenn nötig + window.electronAPI?.sendDropPath?.(filePath); const files = e.dataTransfer.files; handleFiles(files); diff --git a/electron/package.json b/electron/package.json index 13749b9..4630223 100644 --- a/electron/package.json +++ b/electron/package.json @@ -1,7 +1,8 @@ { "name": "electron", "version": "1.0.0", - "main": "index.js", + "main": "main/main.js", + "type": "module", "scripts": { "start": "electron.", "test": "echo \"Error: no test specified\" && exit 1" From 5d9e41d0f34c95c7b00dae82a369aa9320d9513f Mon Sep 17 00:00:00 2001 From: Verena Schulz Date: Tue, 4 Nov 2025 17:51:39 +0100 Subject: [PATCH 10/43] dropdown menu base --- electron/main/index.html | 8 +++++++- electron/main/style.css | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/electron/main/index.html b/electron/main/index.html index 3d7ffc3..3f8b697 100644 --- a/electron/main/index.html +++ b/electron/main/index.html @@ -19,7 +19,13 @@ - + diff --git a/electron/main/style.css b/electron/main/style.css index f0bde45..46f6bd3 100644 --- a/electron/main/style.css +++ b/electron/main/style.css @@ -62,3 +62,11 @@ body { input[type="file"] { display: none; } + +.dropdown-container{ +margin-top: 15px; +display: flex; +flex-direction: column; +align-items: flex-start; +} + From 99c8bea0bef1ae54611b44e7424107db2e56abe5 Mon Sep 17 00:00:00 2001 From: Verena Schulz Date: Wed, 5 Nov 2025 17:05:29 +0100 Subject: [PATCH 11/43] Chechboxes and submit button --- electron/main/index.html | 19 +++++++++++++------ electron/main/style.css | 29 +++++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/electron/main/index.html b/electron/main/index.html index 3f8b697..a6e20d1 100644 --- a/electron/main/index.html +++ b/electron/main/index.html @@ -19,13 +19,20 @@ - From 26f8715ae8affce8765e93ddad22b33169ef83b3 Mon Sep 17 00:00:00 2001 From: Verena Schulz Date: Wed, 5 Nov 2025 18:45:07 +0100 Subject: [PATCH 14/43] First design --- electron/main/index.html | 6 +++--- electron/main/style.css | 21 +++++++++++++++++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/electron/main/index.html b/electron/main/index.html index e503e25..f5b0fb4 100644 --- a/electron/main/index.html +++ b/electron/main/index.html @@ -8,14 +8,13 @@ - - +
+

Video to document

Drag and drop video file

No video chosen
- @@ -33,6 +32,7 @@
+ diff --git a/electron/main/style.css b/electron/main/style.css index 1e52623..0b2a75c 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: #f5f5f5; + background-color: #444; gap: 15px; margin: 0; } @@ -82,7 +82,7 @@ gap: 5px; } .submit-btn { - padding: 10px 20px; + padding: 10px 20px; background-color: #007BFF; color: white; border: none; @@ -91,3 +91,20 @@ gap: 5px; font-size: 14px; } +.mitte { + background-color: #eaf0ff; + display: flex; + flex-direction: column; + align-items: center; + padding: 5% 50px; + margin-top: 20px; + gap: 10px; + border: 2px; + border-color: black; + border-style: solid; +} + +h1 { + align-content: center; +} + From 9902b0421eea5d04c317f1b1fc6f66d049550b22 Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Thu, 6 Nov 2025 09:58:28 +0100 Subject: [PATCH 15/43] Implemented local transcription solution with starting scripts --- package.json | 4 +- scripts/commands.txt | 2 + scripts/extract.ts | 23 +++++ scripts/transcribe.ts | 18 ++++ .../modules/extraction/ffmpegExtractor.ts | 87 ++++++++++--------- .../modules/transcription/local/whisper.cpp | 1 + .../transcription/local/whisperLocal.ts | 55 ++++++++++++ 7 files changed, 148 insertions(+), 42 deletions(-) create mode 100644 scripts/commands.txt create mode 100644 scripts/extract.ts create mode 100644 scripts/transcribe.ts create mode 160000 services/modules/transcription/local/whisper.cpp create mode 100644 services/modules/transcription/local/whisperLocal.ts diff --git a/package.json b/package.json index c120da8..649f09f 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,5 @@ { + "type": "module", "dependencies": { "cli-progress": "^3.12.0", "ffmpeg-static": "^5.2.0", @@ -11,5 +12,4 @@ "ts-node": "^10.9.2", "typescript": "^5.9.3" } -} - +} \ No newline at end of file diff --git a/scripts/commands.txt b/scripts/commands.txt new file mode 100644 index 0000000..31b6ea7 --- /dev/null +++ b/scripts/commands.txt @@ -0,0 +1,2 @@ +npx ts-node ./extract.ts /Users/mikehughes/Downloads/sweetHomeAlabama.mov +npx ts-node ./transcribe.ts ../storage/audio/sweetHomeAlabama.wav diff --git a/scripts/extract.ts b/scripts/extract.ts new file mode 100644 index 0000000..2d1c7a0 --- /dev/null +++ b/scripts/extract.ts @@ -0,0 +1,23 @@ +#!/usr/bin/env ts-node + +import { extractAudioFromVideo } from "../services/modules/extraction/ffmpegExtractor.ts"; + +const videoPath = process.argv[2]; + +if (!videoPath) { + console.error("Usage: ts-node extractAudio.ts "); + process.exit(1); +} + +(async () => { + try { + console.log(`Extracting audio from: ${videoPath}`); + + await extractAudioFromVideo(videoPath); // Call the extraction function (ffmpegExtractor.ts in services/modules/extraction) + + console.log("Audio extraction completed successfully."); + } catch (err) { + console.error("Audio extraction failed:", err); + process.exit(1); + } +})(); \ No newline at end of file diff --git a/scripts/transcribe.ts b/scripts/transcribe.ts new file mode 100644 index 0000000..55d91a5 --- /dev/null +++ b/scripts/transcribe.ts @@ -0,0 +1,18 @@ +import { whisperLocal } from "../services/modules/transcription/local/whisperLocal.ts"; + +const audioPath = process.argv[2]; +if (!audioPath) { + console.error("Please provide an audio file path as argument."); + process.exit(1); +} + +const whisper = new whisperLocal(); + +(async () => { + try { + const text = await whisper.transcribe(audioPath); + console.log(text); + } catch (err) { + console.error("Transcription failed:", err); + } +})(); \ No newline at end of file diff --git a/services/modules/extraction/ffmpegExtractor.ts b/services/modules/extraction/ffmpegExtractor.ts index 27c280b..0831859 100644 --- a/services/modules/extraction/ffmpegExtractor.ts +++ b/services/modules/extraction/ffmpegExtractor.ts @@ -3,32 +3,36 @@ 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 ffmpegExtractor.ts /path/to/video.mp4 +// 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. - * @param outputAudioPath Path where the output WAV audio will be saved. */ // Ensure ffmpeg binary is available if (!ffmpegPath) { - throw new Error('FFmpeg binary not found!'); + 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 ffmpegExtractor.ts '); - process.exit(1); + console.error('Usage: ts-node ./extract.ts '); + process.exit(1); } -// Prepare output directory (always relative to project root) -const outputDir = path.join(process.cwd(), 'storage', 'audio'); +// 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 }); + fs.mkdirSync(outputDir, { recursive: true }); } // Derive input and output paths @@ -38,10 +42,10 @@ 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 + format: 'Processing |{bar}| {percentage}% | {timemark}', + barCompleteChar: '\u2588', + barIncompleteChar: '\u2591', + hideCursor: true }); /** @@ -50,34 +54,37 @@ const progressBar = new cliProgress.SingleBar({ * - Shows CLI progress bar * - Handles errors gracefully (without errors) */ -function extractAudioFromVideo(videoFilePath: string, outputAudioPath: string): Promise { - return new Promise((resolve, reject) => { - ffmpeg(videoFilePath) - .outputFormat('wav') // Set output format to WAV - .on('progress', (progress) => { - // Start progress bar if not already active - if (!progressBar.isActive) progressBar.start(100, 0, { timemark: '00:00:00' }); - if (progress.percent) { - progressBar.update(progress.percent, { timemark: progress.timemark }); - } - }) - .on('end', () => { - // Finish progress bar - progressBar.update(100, { timemark: 'done' }); - progressBar.stop(); - console.log(`Extraction completed: ${outputAudioPath}`); - resolve(); - }) - .on('error', (err) => { - // Show extraction errors in a clear format - console.error(`failed_audio_extraction: ${err.message}`); - reject(err); - }) - .save(outputAudioPath); // Save output file - }); +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 -extractAudioFromVideo(inputVideoPath, 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 + .catch((err) => console.error(err)); +} \ No newline at end of file diff --git a/services/modules/transcription/local/whisper.cpp b/services/modules/transcription/local/whisper.cpp new file mode 160000 index 0000000..999a7e0 --- /dev/null +++ b/services/modules/transcription/local/whisper.cpp @@ -0,0 +1 @@ +Subproject commit 999a7e0cbf8484dc2cea1e9f855d6b39f34f7ae9 diff --git a/services/modules/transcription/local/whisperLocal.ts b/services/modules/transcription/local/whisperLocal.ts new file mode 100644 index 0000000..e1923a0 --- /dev/null +++ b/services/modules/transcription/local/whisperLocal.ts @@ -0,0 +1,55 @@ +import { exec } from "child_process"; // Node.js built-in module +import path from "path"; // Path module +import fs from "fs"; // File system module +import { fileURLToPath } from "url"; // To handle __dirname in ES modules + +const __filename = fileURLToPath(import.meta.url); // Get current file path +const __dirname = path.dirname(__filename); // Get current directory path +const transcriptsDir = path.resolve(__dirname, "../../storage/transcriptions"); + + +export class whisperLocal { // is called by transcribe.ts + private whisperBinary: string; // Path to the whisper.cpp binary + private modelPath: string; // Path to the model file + + constructor() { + this.whisperBinary = path.resolve( + __dirname, + "whisper.cpp/build/bin/whisper-cli" //Path to the compiled whisper binary + ); + + this.modelPath = path.resolve( + __dirname, + "whisper.cpp/models/ggml-base.en.bin" // Path to the English model file + ); + } + + async transcribe(audioPath: string): Promise { //asyncronous function to transcribe audio + return new Promise((resolve, reject) => { + const transcriptsDir = path.resolve(__dirname, "../../../../storage/transcripts"); //storage directory for transcripts + + if (!fs.existsSync(transcriptsDir)) { //if transcripts directory does not exist, create it + fs.mkdirSync(transcriptsDir, { recursive: true }); + } + + const outputBase = path.resolve( // Base path for output transcript files, name is same as audio file (video file) + transcriptsDir, + path.basename(audioPath, path.extname(audioPath)) + ); + + const command = `"${this.whisperBinary}" -m "${this.modelPath}" -f "${audioPath}" -otxt -of "${outputBase}"`; // Command to execute whisper binary with model and audio file, outputting text file + + exec(command, (error, stdout, stderr) => { + if (error) return reject(error); + + const outputTxt = `${outputBase}.txt`; + if (fs.existsSync(outputTxt)) { + const transcript = fs.readFileSync(outputTxt, "utf8"); + resolve(transcript); + } else { + reject(new Error("No transcript file found")); + } + }); + }); + } +} \ No newline at end of file From a0fe1a80e3e8bb8b629452cda5a0e5715cff878d Mon Sep 17 00:00:00 2001 From: "Aarthi Manivannan, Premanathan Aarthi Manivannan" Date: Thu, 6 Nov 2025 15:49:27 +0100 Subject: [PATCH 16/43] Add new directory --- app/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/.gitkeep diff --git a/app/.gitkeep b/app/.gitkeep new file mode 100644 index 0000000..e69de29 From 0dc2ebe99c5bd0be94eca501c636377ba813d6f8 Mon Sep 17 00:00:00 2001 From: "Aarthi Manivannan, Premanathan Aarthi Manivannan" Date: Thu, 6 Nov 2025 15:50:10 +0100 Subject: [PATCH 17/43] App --- app/main.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 app/main.py diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..b9af97c --- /dev/null +++ b/app/main.py @@ -0,0 +1,7 @@ +from fastapi import FastAPI + +app = FastAPI() + +@app.get("/health") +def health_check(): + return {"status": "ok"} From a92e33fa59a611141dd87cd43387a873b50016a7 Mon Sep 17 00:00:00 2001 From: "Aarthi Manivannan, Premanathan Aarthi Manivannan" Date: Thu, 6 Nov 2025 15:51:08 +0100 Subject: [PATCH 18/43] Add new directory --- test/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/.gitkeep diff --git a/test/.gitkeep b/test/.gitkeep new file mode 100644 index 0000000..e69de29 From 70221683c327abbd8c2d2163115ad4b7e020658c Mon Sep 17 00:00:00 2001 From: "Aarthi Manivannan, Premanathan Aarthi Manivannan" Date: Thu, 6 Nov 2025 15:51:56 +0100 Subject: [PATCH 19/43] tesing --- test/test_health.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 test/test_health.py diff --git a/test/test_health.py b/test/test_health.py new file mode 100644 index 0000000..7b088a7 --- /dev/null +++ b/test/test_health.py @@ -0,0 +1,8 @@ +from fastapi.testclient import TestClient +from app.main import app + +client = TestClient(app) + +def test_health(): + response = client.get("/health") + assert response.status_code == 200 From fd4d342eebb210720aec6672994c9996ccd1ff1a Mon Sep 17 00:00:00 2001 From: "Aarthi Manivannan, Premanathan Aarthi Manivannan" Date: Thu, 6 Nov 2025 15:52:45 +0100 Subject: [PATCH 20/43] Delete .gitlab-ci.yml --- .gitlab-ci.yml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index d88cb19..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,6 +0,0 @@ -build-job: - script: - - echo "Building the Project.." -test-job: - script: - - echo "Running Tests.." \ No newline at end of file From a069452f873d5ff140d3acbd8cb8872147d3e2cf Mon Sep 17 00:00:00 2001 From: "Aarthi Manivannan, Premanathan Aarthi Manivannan" Date: Thu, 6 Nov 2025 15:53:38 +0100 Subject: [PATCH 21/43] requirements --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..36a15fc --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +fastapi +uvicorn +pytest From b70680e95050001030c1e9bfa7886a4bc4d48910 Mon Sep 17 00:00:00 2001 From: "Aarthi Manivannan, Premanathan Aarthi Manivannan" Date: Thu, 6 Nov 2025 16:00:28 +0100 Subject: [PATCH 22/43] Ci file --- .gitlab-ci.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..8bbe7c6 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,18 @@ +image: python:3.14.0 + +stages: + - setup + - test + +setup_environment: + stage: setup + script: + - pip install --upgrade pip + - pip install -r requirements.txt + - echo "Dependencies installed successfully." + +test_app: + stage: test + script: + - echo "Running V2D Framework basic test..." + - python -m unittest discover || echo "No tests found." From fb173a404113fcc06efe344e83a58e7edb44abc9 Mon Sep 17 00:00:00 2001 From: "Aarthi Manivannan, Premanathan Aarthi Manivannan" Date: Thu, 6 Nov 2025 16:21:01 +0100 Subject: [PATCH 23/43] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8bbe7c6..5bebd17 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,9 @@ +workflow: + rules: + # Run the pipeline for merge requests or when committing to a branch + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + - if: $CI_COMMIT_BRANCH + image: python:3.14.0 stages: @@ -11,8 +17,17 @@ setup_environment: - pip install -r requirements.txt - echo "Dependencies installed successfully." + only: + - main + - feature/ci-pipeline-s1-09a-1 # You can add more branches if needed + test_app: stage: test script: - echo "Running V2D Framework basic test..." - python -m unittest discover || echo "No tests found." + + only: + - main + - feature/ci-pipeline-s1-09a-1 + From cfa37b9f2fc2db0e6de6df8821eb17a3a804215a Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Sat, 8 Nov 2025 13:26:35 +0100 Subject: [PATCH 24/43] additional test for commands.txt and fix for correct path in the ffmpegExtractor module --- scripts/commands.txt | 7 +++++-- services/modules/extraction/ffmpegExtractor.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/commands.txt b/scripts/commands.txt index 31b6ea7..fc620fe 100644 --- a/scripts/commands.txt +++ b/scripts/commands.txt @@ -1,2 +1,5 @@ -npx ts-node ./extract.ts /Users/mikehughes/Downloads/sweetHomeAlabama.mov -npx ts-node ./transcribe.ts ../storage/audio/sweetHomeAlabama.wav +npx ts-node ./extract.ts /Users/mikehughes/Downloads/Testvideo/Kurzgesagt.mov +npx ts-node ./transcribe.ts ../storage/audio/Kurzgesagt.wav + +npx ts-node ./extract.ts /Users/mikehughes/Downloads/Testvideo/GitLabMeeting.mov +npx ts-node ./transcribe.ts ../storage/audio/GitLabMeeting.wav diff --git a/services/modules/extraction/ffmpegExtractor.ts b/services/modules/extraction/ffmpegExtractor.ts index 0831859..c23c879 100644 --- a/services/modules/extraction/ffmpegExtractor.ts +++ b/services/modules/extraction/ffmpegExtractor.ts @@ -30,7 +30,7 @@ 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'); +const outputDir = path.join(__dirname, '..', '..', '..', 'storage', 'audio'); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } From f4c45f8371ec49242b65c775067eac83bc8c0c89 Mon Sep 17 00:00:00 2001 From: "eric.minning" Date: Mon, 10 Nov 2025 15:38:59 +0100 Subject: [PATCH 25/43] Added a progressbar --- electron/main/index.html | 5 +++++ electron/main/script.js | 6 ++++++ electron/main/style.css | 22 ++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/electron/main/index.html b/electron/main/index.html index f5b0fb4..d75939f 100644 --- a/electron/main/index.html +++ b/electron/main/index.html @@ -32,6 +32,11 @@ + +
+
+ 0% +
diff --git a/electron/main/script.js b/electron/main/script.js index aa74bd8..cc1ac2b 100644 --- a/electron/main/script.js +++ b/electron/main/script.js @@ -29,3 +29,9 @@ function handleFiles(files) { } } } + +function updateProgressBar(bar, value){ + value = Math.round(value); + bar.querySelector(".progress_fill").style.width = `${value}%`; + bar.querySelector(".progress_text").textContent = `${value}%`; +} \ No newline at end of file diff --git a/electron/main/style.css b/electron/main/style.css index 0b2a75c..b68c162 100644 --- a/electron/main/style.css +++ b/electron/main/style.css @@ -108,3 +108,25 @@ h1 { align-content: center; } +.progressbar{ + position: relative; + width: 210px; + height: 30px; + background: darkslategray; + border-radius: 5px; + overflow: hidden; +} + +.progress_fill{ + width: 0%; + height: 100%; + background: green; + transition: all 0.2s; +} + +.progress_text{ + position: absolute; + top: 50%; + right: 5px; + transform: translateY(-50%); +} \ No newline at end of file From bd8ed1bf81c32dad8713227501e6a0db74352e41 Mon Sep 17 00:00:00 2001 From: Emily Date: Mon, 10 Nov 2025 16:12:59 +0100 Subject: [PATCH 26/43] 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 27/43] 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 28/43] 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 29/43] 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 30/43] 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 31/43] 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 32/43] 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 33/43] 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 34/43] 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 35/43] 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 36/43] 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 37/43] 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 38/43] 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 From e7e97a7f6012abef3d9f491efde0f6ab0d5d4f99 Mon Sep 17 00:00:00 2001 From: Azeufack Noupeu Willy Date: Thu, 13 Nov 2025 13:07:18 +0100 Subject: [PATCH 39/43] feat(S2-02b): Implement AssemblyAI external transcription with speaker diarization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add assembly.ts module for REST API transcription via AssemblyAI - Implement 5-step pipeline: upload → create job → poll status → download → save - Enable speaker_labels for diarization (Speaker A, B, C...) - Add millisecond-precision timestamps for each utterance - Store JSON transcripts in storage/transcripts/{session_id}.json - Add axios, dotenv dependencies - Add transcribeLatest.ts helper for quick testing User Story: S2-02b - Externe Transkription per REST API --- package-lock.json | 44 +++ package.json | 8 + .../modules/transcription/assembly.test.js | 14 + services/modules/transcription/assembly.ts | 133 ++++++++ .../modules/transcription/package-lock.json | 319 ++++++++++++++++++ services/modules/transcription/package.json | 9 + services/pipeline/jobs/transcribeLatest.ts | 52 +++ 7 files changed, 579 insertions(+) create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 services/modules/transcription/assembly.test.js create mode 100644 services/modules/transcription/assembly.ts create mode 100644 services/modules/transcription/package-lock.json create mode 100644 services/modules/transcription/package.json create mode 100644 services/pipeline/jobs/transcribeLatest.ts diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4b57d9f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,44 @@ +{ + "name": "video2document", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "dotenv": "^17.2.3" + }, + "devDependencies": { + "@types/node": "^24.10.0" + } + }, + "node_modules/@types/node": { + "version": "24.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", + "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/undici-types": { + "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" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..48d43a3 --- /dev/null +++ b/package.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "dotenv": "^17.2.3" + }, + "devDependencies": { + "@types/node": "^24.10.0" + } +} diff --git a/services/modules/transcription/assembly.test.js b/services/modules/transcription/assembly.test.js new file mode 100644 index 0000000..66ce753 --- /dev/null +++ b/services/modules/transcription/assembly.test.js @@ -0,0 +1,14 @@ +import 'dotenv/config'; +import assemblyModule from './assembly.ts'; + +// Test: URL passed as argument OR local file ./storage/audio/test.wav +const audioPath = process.argv[2] || './storage/audio/test.wav'; + +assemblyModule.run(audioPath) + .then(result => { + console.log('✅ Success!'); + console.log('Transcript ID:', result.id); + }) + .catch(error => { + console.error('❌ Error:', error?.message || error); + }); diff --git a/services/modules/transcription/assembly.ts b/services/modules/transcription/assembly.ts new file mode 100644 index 0000000..46fe236 --- /dev/null +++ b/services/modules/transcription/assembly.ts @@ -0,0 +1,133 @@ +import 'dotenv/config'; +import axios from 'axios'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const API_KEY = process.env.ASSEMBLYAI_API_KEY; +const BASE_URL = 'https://api.assemblyai.com/v2'; + +/** + * Uploads audio file to AssemblyAI + */ +async function uploadAudio(audioPath: string): Promise { + const audioData = fs.readFileSync(audioPath); + + const response = await axios.post<{ upload_url: string }>(`${BASE_URL}/upload`, audioData, { + headers: { + 'authorization': API_KEY, + 'content-type': 'application/octet-stream' + } + }); + + return response.data.upload_url; +} + +/** + * Extract a session id (basename without extension) from a local path or a URL + */ +function getSessionId(inputPath: string): string { + try { + const parsed = new URL(inputPath); + const base = path.basename(parsed.pathname); + return base.replace(/\.[^.]+$/, ''); + } catch (err) { + // not a URL, treat as local path + return path.basename(inputPath, path.extname(inputPath)); + } +} + +/** + * Creates transcription job with speaker diarization + */ +async function createTranscript(audioUrl: string): Promise { + const response = await axios.post<{ id: string }>(`${BASE_URL}/transcript`, { + audio_url: audioUrl, + speaker_labels: true, + language_detection: true + }, { + headers: { + 'authorization': API_KEY, + 'content-type': 'application/json' + } + }); + + return response.data.id; +} + +/** + * Polls transcript status until completed + */ +async function pollTranscript(transcriptId: string): Promise { + while (true) { + const response = await axios.get(`${BASE_URL}/transcript/${transcriptId}`, { + headers: { 'authorization': API_KEY } + }); + + const status = response.data.status; + + if (status === 'completed') { + return response.data; + } else if (status === 'error') { + throw new Error(`Transcription failed: ${response.data.error}`); + } + + // Wait 3 seconds before next poll + await new Promise(resolve => setTimeout(resolve, 3000)); + } +} + +/** + * Saves transcript to storage + */ +function saveTranscript(transcript: any, sessionId: string): void { + const outputDir = path.join(__dirname, '..', '..', '..', 'storage', 'transcripts'); + + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); + } + + const outputPath = path.join(outputDir, `${sessionId}.json`); + fs.writeFileSync(outputPath, JSON.stringify(transcript, null, 2)); + + console.log(`✅ Transcript saved: ${outputPath}`); +} + +export default { + name: "assembly", + type: "transcription", + displayname: "AssemblyAI", + run: async (audioPath: string) => { + try { + // Determine if audioPath is an external URL or a local file + let audioUrl: string; + if (/^https?:\/\//i.test(audioPath)) { + console.log('🔗 Using external audio URL...'); + audioUrl = audioPath; + } else { + console.log('🔄 Uploading local audio...'); + if (!fs.existsSync(audioPath)) { + throw new Error(`Audio file not found: ${audioPath}`); + } + audioUrl = await uploadAudio(audioPath); + } + + console.log('🔄 Creating transcript job...'); + const transcriptId = await createTranscript(audioUrl); + + console.log('⏳ Waiting for transcription...'); + const transcript = await pollTranscript(transcriptId); + + const sessionId = getSessionId(audioPath); + saveTranscript(transcript, sessionId); + + return transcript; + } catch (error: any) { + console.error('❌ Transcription error:', error.message); + throw error; + } + } +}; \ No newline at end of file diff --git a/services/modules/transcription/package-lock.json b/services/modules/transcription/package-lock.json new file mode 100644 index 0000000..58c2913 --- /dev/null +++ b/services/modules/transcription/package-lock.json @@ -0,0 +1,319 @@ +{ + "name": "transcription", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "axios": "^1.13.2" + }, + "devDependencies": { + "@types/axios": "^0.9.36", + "@types/node": "^24.10.0" + } + }, + "node_modules/@types/axios": { + "version": "0.9.36", + "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.9.36.tgz", + "integrity": "sha512-NLOpedx9o+rxo/X5ChbdiX6mS1atE4WHmEEIcR9NLenRVa5HoVjAvjafwU3FPTqnZEstpoqCaW7fagqSoTDNeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", + "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "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==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/undici-types": { + "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" + } + } +} diff --git a/services/modules/transcription/package.json b/services/modules/transcription/package.json new file mode 100644 index 0000000..6d24fe5 --- /dev/null +++ b/services/modules/transcription/package.json @@ -0,0 +1,9 @@ +{ + "dependencies": { + "axios": "^1.13.2" + }, + "devDependencies": { + "@types/axios": "^0.9.36", + "@types/node": "^24.10.0" + } +} diff --git a/services/pipeline/jobs/transcribeLatest.ts b/services/pipeline/jobs/transcribeLatest.ts new file mode 100644 index 0000000..fc75ad5 --- /dev/null +++ b/services/pipeline/jobs/transcribeLatest.ts @@ -0,0 +1,52 @@ +// services/pipeline/jobs/transcribeLatest.ts +import path from 'path'; +import fs from 'fs'; +import assembly from '../../modules/transcription/assembly'; + +/** + * Finds the most recently modified .wav file in storage/audio/ + */ +function getLatestWav(): string { + const audioDir = path.join(process.cwd(), 'storage', 'audio'); + const files = fs.readdirSync(audioDir).filter(f => f.toLowerCase().endsWith('.wav')); + if (files.length === 0) throw new Error('⚠️ No .wav file found in storage/audio'); + + const newest = files + .map(f => ({ f, t: fs.statSync(path.join(audioDir, f)).mtimeMs })) + .sort((a, b) => b.t - a.t)[0].f; + + return path.join(audioDir, newest); +} + +/** + * Full transcription pipeline according to the defined workflow: + * 1. Audio Upload → AssemblyAI + * 2. Job Creation (transcript_id) + * 3. Polling Status (queued → processing → completed) + * 4. Download Transcript JSON + * 5. Storage: /transcripts/{session_id}.json + */ +async function main() { + const audioPath = getLatestWav(); + + console.log('1️⃣ Audio Upload → AssemblyAI'); + console.log(' Source:', audioPath); + + console.log('2️⃣ Job Creation (transcript_id)'); + console.log('3️⃣ Polling Status (queued → processing → completed)'); + console.log('4️⃣ Download Transcript JSON'); + console.log('5️⃣ Storage: /transcripts/{session_id}.json'); + + // Execute the transcription process via the AssemblyAI module + const result = await assembly.run(audioPath); + + console.log('✅ Transcription completed successfully'); + console.log('🆔 Transcript ID:', result.id); + console.log('📁 Transcript file saved under: storage/transcripts/'); +} + +// Entry point +main().catch((err) => { + console.error('❌ Transcription pipeline failed:', err.message || err); + process.exit(1); +}); From 9254ddc57f019b37202e04abae7ebf60bfd6bbb9 Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Thu, 13 Nov 2025 17:34:22 +0100 Subject: [PATCH 40/43] Changed the Folder Structure for better maintainability --- .../modules/transcription/{ => remote/assembly_AI}/assembly.js | 0 .../transcription/{ => remote/assembly_AI}/assembly.test.js | 0 .../modules/transcription/{ => remote/assembly_AI}/assembly.ts | 0 .../modules/transcription/{ => remote/assembly_AI}/example.js | 0 .../transcription/{ => remote/assembly_AI}/package-lock.json | 0 .../modules/transcription/{ => remote/assembly_AI}/package.json | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename services/modules/transcription/{ => remote/assembly_AI}/assembly.js (100%) rename services/modules/transcription/{ => remote/assembly_AI}/assembly.test.js (100%) rename services/modules/transcription/{ => remote/assembly_AI}/assembly.ts (100%) rename services/modules/transcription/{ => remote/assembly_AI}/example.js (100%) rename services/modules/transcription/{ => remote/assembly_AI}/package-lock.json (100%) rename services/modules/transcription/{ => remote/assembly_AI}/package.json (100%) diff --git a/services/modules/transcription/assembly.js b/services/modules/transcription/remote/assembly_AI/assembly.js similarity index 100% rename from services/modules/transcription/assembly.js rename to services/modules/transcription/remote/assembly_AI/assembly.js diff --git a/services/modules/transcription/assembly.test.js b/services/modules/transcription/remote/assembly_AI/assembly.test.js similarity index 100% rename from services/modules/transcription/assembly.test.js rename to services/modules/transcription/remote/assembly_AI/assembly.test.js diff --git a/services/modules/transcription/assembly.ts b/services/modules/transcription/remote/assembly_AI/assembly.ts similarity index 100% rename from services/modules/transcription/assembly.ts rename to services/modules/transcription/remote/assembly_AI/assembly.ts diff --git a/services/modules/transcription/example.js b/services/modules/transcription/remote/assembly_AI/example.js similarity index 100% rename from services/modules/transcription/example.js rename to services/modules/transcription/remote/assembly_AI/example.js diff --git a/services/modules/transcription/package-lock.json b/services/modules/transcription/remote/assembly_AI/package-lock.json similarity index 100% rename from services/modules/transcription/package-lock.json rename to services/modules/transcription/remote/assembly_AI/package-lock.json diff --git a/services/modules/transcription/package.json b/services/modules/transcription/remote/assembly_AI/package.json similarity index 100% rename from services/modules/transcription/package.json rename to services/modules/transcription/remote/assembly_AI/package.json From 79e0c487558bd2084e523212dc4a7728dc526aaa Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Thu, 13 Nov 2025 17:35:40 +0100 Subject: [PATCH 41/43] Reduced Number of test paths to avoid redundancy --- {tests => test}/integration/.gitkeep | 0 {tests => test}/unit/.gitkeep | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {tests => test}/integration/.gitkeep (100%) rename {tests => test}/unit/.gitkeep (100%) diff --git a/tests/integration/.gitkeep b/test/integration/.gitkeep similarity index 100% rename from tests/integration/.gitkeep rename to test/integration/.gitkeep diff --git a/tests/unit/.gitkeep b/test/unit/.gitkeep similarity index 100% rename from tests/unit/.gitkeep rename to test/unit/.gitkeep From d9eacafc3acc82bc40c24389e52520a0482d7641 Mon Sep 17 00:00:00 2001 From: Emily Date: Fri, 14 Nov 2025 14:28:11 +0100 Subject: [PATCH 42/43] fixed the program by moving the example module back to where the program can ACTUALLY load it --- .../{transcription/remote/assembly_AI => utility}/example.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename services/modules/{transcription/remote/assembly_AI => utility}/example.js (100%) diff --git a/services/modules/transcription/remote/assembly_AI/example.js b/services/modules/utility/example.js similarity index 100% rename from services/modules/transcription/remote/assembly_AI/example.js rename to services/modules/utility/example.js From 6083773f8888ba1689eedc41ce9bb7513041a491 Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Sat, 15 Nov 2025 14:45:13 +0100 Subject: [PATCH 43/43] New Folder structure --- package-lock.json | 22 ++ package.json | 2 + scripts/.gitkeep | 0 scripts/commands.txt | 5 - scripts/extract.ts | 23 -- scripts/transcribe.ts | 18 - .../whisperLocal.ts | 3 +- .../assembly.js | 0 .../assembly.ts | 0 .../modules/transcription/local/whisper.cpp | 1 - .../remote/assembly_AI/package-lock.json | 319 ------------------ .../remote/assembly_AI/package.json | 9 - .../integration}/assembly.test.js | 2 +- 13 files changed, 26 insertions(+), 378 deletions(-) create mode 100644 scripts/.gitkeep delete mode 100644 scripts/commands.txt delete mode 100644 scripts/extract.ts delete mode 100644 scripts/transcribe.ts rename services/modules/{transcription/local => transcription-local}/whisperLocal.ts (90%) rename services/modules/{transcription/remote/assembly_AI => transcription-remote}/assembly.js (100%) rename services/modules/{transcription/remote/assembly_AI => transcription-remote}/assembly.ts (100%) delete mode 160000 services/modules/transcription/local/whisper.cpp delete mode 100644 services/modules/transcription/remote/assembly_AI/package-lock.json delete mode 100644 services/modules/transcription/remote/assembly_AI/package.json rename {services/modules/transcription/remote/assembly_AI => test/integration}/assembly.test.js (81%) diff --git a/package-lock.json b/package-lock.json index 3fe9ca4..52b523c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,9 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@types/axios": "^0.9.36", "cli-progress": "^3.12.0", + "dotenv": "^17.2.3", "electron": "^39.1.1", "express": "^5.1.0", "ffmpeg-static": "^5.2.0", @@ -149,6 +151,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/axios": { + "version": "0.9.36", + "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.9.36.tgz", + "integrity": "sha512-NLOpedx9o+rxo/X5ChbdiX6mS1atE4WHmEEIcR9NLenRVa5HoVjAvjafwU3FPTqnZEstpoqCaW7fagqSoTDNeg==", + "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", @@ -198,6 +206,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.2.tgz", "integrity": "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -584,6 +593,18 @@ "node": ">=0.3.1" } }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1824,6 +1845,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index 14cba78..f85f30e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,8 @@ { "dependencies": { + "@types/axios": "^0.9.36", "cli-progress": "^3.12.0", + "dotenv": "^17.2.3", "electron": "^39.1.1", "express": "^5.1.0", "ffmpeg-static": "^5.2.0", diff --git a/scripts/.gitkeep b/scripts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scripts/commands.txt b/scripts/commands.txt deleted file mode 100644 index fc620fe..0000000 --- a/scripts/commands.txt +++ /dev/null @@ -1,5 +0,0 @@ -npx ts-node ./extract.ts /Users/mikehughes/Downloads/Testvideo/Kurzgesagt.mov -npx ts-node ./transcribe.ts ../storage/audio/Kurzgesagt.wav - -npx ts-node ./extract.ts /Users/mikehughes/Downloads/Testvideo/GitLabMeeting.mov -npx ts-node ./transcribe.ts ../storage/audio/GitLabMeeting.wav diff --git a/scripts/extract.ts b/scripts/extract.ts deleted file mode 100644 index 2d1c7a0..0000000 --- a/scripts/extract.ts +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env ts-node - -import { extractAudioFromVideo } from "../services/modules/extraction/ffmpegExtractor.ts"; - -const videoPath = process.argv[2]; - -if (!videoPath) { - console.error("Usage: ts-node extractAudio.ts "); - process.exit(1); -} - -(async () => { - try { - console.log(`Extracting audio from: ${videoPath}`); - - await extractAudioFromVideo(videoPath); // Call the extraction function (ffmpegExtractor.ts in services/modules/extraction) - - console.log("Audio extraction completed successfully."); - } catch (err) { - console.error("Audio extraction failed:", err); - process.exit(1); - } -})(); \ No newline at end of file diff --git a/scripts/transcribe.ts b/scripts/transcribe.ts deleted file mode 100644 index 55d91a5..0000000 --- a/scripts/transcribe.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { whisperLocal } from "../services/modules/transcription/local/whisperLocal.ts"; - -const audioPath = process.argv[2]; -if (!audioPath) { - console.error("Please provide an audio file path as argument."); - process.exit(1); -} - -const whisper = new whisperLocal(); - -(async () => { - try { - const text = await whisper.transcribe(audioPath); - console.log(text); - } catch (err) { - console.error("Transcription failed:", err); - } -})(); \ No newline at end of file diff --git a/services/modules/transcription/local/whisperLocal.ts b/services/modules/transcription-local/whisperLocal.ts similarity index 90% rename from services/modules/transcription/local/whisperLocal.ts rename to services/modules/transcription-local/whisperLocal.ts index e1923a0..1d331ac 100644 --- a/services/modules/transcription/local/whisperLocal.ts +++ b/services/modules/transcription-local/whisperLocal.ts @@ -5,7 +5,7 @@ import { fileURLToPath } from "url"; // To handle __dirname in ES modules const __filename = fileURLToPath(import.meta.url); // Get current file path const __dirname = path.dirname(__filename); // Get current directory path -const transcriptsDir = path.resolve(__dirname, "../../storage/transcriptions"); +const transcriptsDir = path.resolve(__dirname, "../../../storage/transcriptions"); export class whisperLocal { // is called by transcribe.ts @@ -26,7 +26,6 @@ export class whisperLocal { // is called by transcribe.ts async transcribe(audioPath: string): Promise { //asyncronous function to transcribe audio return new Promise((resolve, reject) => { - const transcriptsDir = path.resolve(__dirname, "../../../../storage/transcripts"); //storage directory for transcripts if (!fs.existsSync(transcriptsDir)) { //if transcripts directory does not exist, create it fs.mkdirSync(transcriptsDir, { recursive: true }); diff --git a/services/modules/transcription/remote/assembly_AI/assembly.js b/services/modules/transcription-remote/assembly.js similarity index 100% rename from services/modules/transcription/remote/assembly_AI/assembly.js rename to services/modules/transcription-remote/assembly.js diff --git a/services/modules/transcription/remote/assembly_AI/assembly.ts b/services/modules/transcription-remote/assembly.ts similarity index 100% rename from services/modules/transcription/remote/assembly_AI/assembly.ts rename to services/modules/transcription-remote/assembly.ts diff --git a/services/modules/transcription/local/whisper.cpp b/services/modules/transcription/local/whisper.cpp deleted file mode 160000 index 999a7e0..0000000 --- a/services/modules/transcription/local/whisper.cpp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 999a7e0cbf8484dc2cea1e9f855d6b39f34f7ae9 diff --git a/services/modules/transcription/remote/assembly_AI/package-lock.json b/services/modules/transcription/remote/assembly_AI/package-lock.json deleted file mode 100644 index 58c2913..0000000 --- a/services/modules/transcription/remote/assembly_AI/package-lock.json +++ /dev/null @@ -1,319 +0,0 @@ -{ - "name": "transcription", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "axios": "^1.13.2" - }, - "devDependencies": { - "@types/axios": "^0.9.36", - "@types/node": "^24.10.0" - } - }, - "node_modules/@types/axios": { - "version": "0.9.36", - "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.9.36.tgz", - "integrity": "sha512-NLOpedx9o+rxo/X5ChbdiX6mS1atE4WHmEEIcR9NLenRVa5HoVjAvjafwU3FPTqnZEstpoqCaW7fagqSoTDNeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", - "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" - } - }, - "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==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "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==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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==", - "license": "MIT", - "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==", - "license": "MIT", - "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==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "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==", - "license": "MIT", - "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==", - "license": "MIT", - "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==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "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==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "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==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/undici-types": { - "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" - } - } -} diff --git a/services/modules/transcription/remote/assembly_AI/package.json b/services/modules/transcription/remote/assembly_AI/package.json deleted file mode 100644 index 6d24fe5..0000000 --- a/services/modules/transcription/remote/assembly_AI/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "dependencies": { - "axios": "^1.13.2" - }, - "devDependencies": { - "@types/axios": "^0.9.36", - "@types/node": "^24.10.0" - } -} diff --git a/services/modules/transcription/remote/assembly_AI/assembly.test.js b/test/integration/assembly.test.js similarity index 81% rename from services/modules/transcription/remote/assembly_AI/assembly.test.js rename to test/integration/assembly.test.js index 66ce753..46ac9ed 100644 --- a/services/modules/transcription/remote/assembly_AI/assembly.test.js +++ b/test/integration/assembly.test.js @@ -1,5 +1,5 @@ import 'dotenv/config'; -import assemblyModule from './assembly.ts'; +import assemblyModule from '../../services/modules/transcription-remote/assembly.ts'; // Test: URL passed as argument OR local file ./storage/audio/test.wav const audioPath = process.argv[2] || './storage/audio/test.wav';