virtualmiku commited on
Commit
6ddec61
·
0 Parent(s):

feat: add backend API and RVC worker

Browse files
.gitignore ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # =========================
2
+ # OS / Editor
3
+ # =========================
4
+ .DS_Store
5
+ Thumbs.db
6
+ .vscode/
7
+ .idea/
8
+
9
+ # =========================
10
+ # Node.js (Backend)
11
+ # =========================
12
+ node_modules/
13
+ npm-debug.log*
14
+ yarn-debug.log*
15
+ yarn-error.log*
16
+
17
+ # =========================
18
+ # Python (Worker)
19
+ # =========================
20
+ __pycache__/
21
+ *.pyc
22
+ *.pyo
23
+ *.pyd
24
+ *.egg-info/
25
+ dist/
26
+ build/
27
+
28
+ # Virtual environments
29
+ .venv/
30
+ venv/
31
+ env/
32
+
33
+ # =========================
34
+ # Audio / Models / Outputs
35
+ # =========================
36
+
37
+ # Uploaded audio
38
+ backend/storage/uploads/
39
+
40
+ # Job outputs & temp files
41
+ backend/storage/creations/
42
+
43
+ # User-provided models
44
+ backend/storage/models/*/
45
+ !backend/storage/models/.keep
46
+
47
+ # Large audio / media
48
+ *.wav
49
+ *.mp3
50
+ *.flac
51
+ *.ogg
52
+ *.m4a
53
+
54
+ # Model weights & indexes
55
+ *.pth
56
+ *.pt
57
+ *.safetensors
58
+ *.index
59
+ *.bin
60
+
61
+ # =========================
62
+ # Logs & Runtime
63
+ # =========================
64
+ *.log
65
+ logs/
66
+ tmp/
67
+
68
+ # =========================
69
+ # Environment
70
+ # =========================
71
+ .env
72
+ .env.*
73
+
74
+ # =========================
75
+ # Cache / ML
76
+ # =========================
77
+ .cache/
78
+ torch_cache/
backend/README.md ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Backend – Voice Create API
2
+
3
+ Backend ini bertugas sebagai:
4
+ - Model registry (metadata.json)
5
+ - Audio upload & reuse
6
+ - Job creation & status tracking
7
+
8
+ Backend **TIDAK melakukan processing audio**.
9
+ Processing dilakukan oleh worker terpisah.
10
+
11
+ ---
12
+
13
+ ## Requirements
14
+ - Node.js >= 18
15
+ - npm
16
+
17
+ ---
18
+
19
+ ## Install
20
+ ```bash
21
+ cd backend
22
+ npm install
23
+ ```
24
+ ## Run
25
+ ```bash
26
+ npm start
27
+ ```
28
+
29
+ ## API Usage
30
+ ### Upload audio
31
+ ```bash
32
+ curl -X POST http://localhost:3001/api/audio/upload \
33
+ -F "audio=@xxx.wav"
34
+ ```
35
+ ubah `xxx.wav` sesuai dengan nama audio Anda yang anda taruh pada `backend/`
36
+
37
+ Response:
38
+
39
+ ```
40
+ {
41
+ "audioId": "xxx-xxx.wav",
42
+ "size": 123456
43
+ }
44
+ ```
45
+
46
+ ### Create voice conversion job
47
+ ```bash
48
+ curl -X POST http://localhost:3001/api/create \
49
+ -H "Content-Type: application/json" \
50
+ -d '{
51
+ "modelId": "silver-wolf-jp",
52
+ "audioId": "xxx-xxx.wav",
53
+ "settings": {
54
+ "pitch": -2,
55
+ "preStemmed": false,
56
+ "deEcho": false,
57
+ "volumeEnvelope": 0.75
58
+ }
59
+ }'
60
+ ```
61
+ diatas hanyalah contoh, Anda bisa menyesuaikannya.
62
+
63
+ Response:
64
+ ```
65
+ {
66
+ "jobId": "xxx-xxx"
67
+ }
68
+ ```
69
+
70
+ ### Check job status
71
+ ```bash
72
+ curl http://localhost:3001/api/create/xxx/status
73
+ ```
74
+ ubah xxx dengan jobId.
75
+
76
+ ### List all jobs
77
+ ```bash
78
+ curl http://localhost:3001/api/create
79
+ ```
80
+
81
+ ### Storage Layout
82
+ ```
83
+ storage/
84
+ ├─ models/
85
+ │ └─ <model-id>/
86
+ │ ├─ metadata.json
87
+ │ ├─ model.pth
88
+ │ └─ model.index
89
+ ├─ uploads/
90
+ └─ creations/
91
+ └─ <job-id>/
92
+ ├─ input.wav
93
+ ├─ output.wav
94
+ ├─ model.json
95
+ ├─ settings.json
96
+ └─ status.json
97
+ ```
98
+
99
+ ### Notes
100
+ Notes
101
+ - Model files tidak disertakan dalam repository
102
+ - User harus menyediakan model sendiri
backend/index.js ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require("express");
2
+ const cors = require("cors");
3
+
4
+ const modelsRoute = require("./routes/models");
5
+ const audioRoute = require("./routes/audio");
6
+ const createRoute = require("./routes/create");
7
+
8
+ const app = express();
9
+ app.use(cors());
10
+ app.use(express.json());
11
+
12
+ app.use("/api/models", modelsRoute);
13
+ app.use("/api/audio", audioRoute);
14
+ app.use("/api/create", createRoute);
15
+
16
+ app.listen(3001, () => {
17
+ console.log("Backend running on http://localhost:3001");
18
+ });
backend/package-lock.json ADDED
@@ -0,0 +1,1424 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "backend",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "backend",
9
+ "version": "1.0.0",
10
+ "license": "ISC",
11
+ "dependencies": {
12
+ "cors": "^2.8.6",
13
+ "express": "^5.2.1",
14
+ "fs-extra": "^11.3.3",
15
+ "multer": "^2.0.2",
16
+ "nodemon": "^3.1.11",
17
+ "uuid": "^13.0.0"
18
+ }
19
+ },
20
+ "node_modules/accepts": {
21
+ "version": "2.0.0",
22
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
23
+ "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
24
+ "license": "MIT",
25
+ "dependencies": {
26
+ "mime-types": "^3.0.0",
27
+ "negotiator": "^1.0.0"
28
+ },
29
+ "engines": {
30
+ "node": ">= 0.6"
31
+ }
32
+ },
33
+ "node_modules/anymatch": {
34
+ "version": "3.1.3",
35
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
36
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
37
+ "license": "ISC",
38
+ "dependencies": {
39
+ "normalize-path": "^3.0.0",
40
+ "picomatch": "^2.0.4"
41
+ },
42
+ "engines": {
43
+ "node": ">= 8"
44
+ }
45
+ },
46
+ "node_modules/append-field": {
47
+ "version": "1.0.0",
48
+ "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
49
+ "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
50
+ "license": "MIT"
51
+ },
52
+ "node_modules/balanced-match": {
53
+ "version": "1.0.2",
54
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
55
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
56
+ "license": "MIT"
57
+ },
58
+ "node_modules/binary-extensions": {
59
+ "version": "2.3.0",
60
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
61
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
62
+ "license": "MIT",
63
+ "engines": {
64
+ "node": ">=8"
65
+ },
66
+ "funding": {
67
+ "url": "https://github.com/sponsors/sindresorhus"
68
+ }
69
+ },
70
+ "node_modules/body-parser": {
71
+ "version": "2.2.2",
72
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
73
+ "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
74
+ "license": "MIT",
75
+ "dependencies": {
76
+ "bytes": "^3.1.2",
77
+ "content-type": "^1.0.5",
78
+ "debug": "^4.4.3",
79
+ "http-errors": "^2.0.0",
80
+ "iconv-lite": "^0.7.0",
81
+ "on-finished": "^2.4.1",
82
+ "qs": "^6.14.1",
83
+ "raw-body": "^3.0.1",
84
+ "type-is": "^2.0.1"
85
+ },
86
+ "engines": {
87
+ "node": ">=18"
88
+ },
89
+ "funding": {
90
+ "type": "opencollective",
91
+ "url": "https://opencollective.com/express"
92
+ }
93
+ },
94
+ "node_modules/brace-expansion": {
95
+ "version": "1.1.12",
96
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
97
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
98
+ "license": "MIT",
99
+ "dependencies": {
100
+ "balanced-match": "^1.0.0",
101
+ "concat-map": "0.0.1"
102
+ }
103
+ },
104
+ "node_modules/braces": {
105
+ "version": "3.0.3",
106
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
107
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
108
+ "license": "MIT",
109
+ "dependencies": {
110
+ "fill-range": "^7.1.1"
111
+ },
112
+ "engines": {
113
+ "node": ">=8"
114
+ }
115
+ },
116
+ "node_modules/buffer-from": {
117
+ "version": "1.1.2",
118
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
119
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
120
+ "license": "MIT"
121
+ },
122
+ "node_modules/busboy": {
123
+ "version": "1.6.0",
124
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
125
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
126
+ "dependencies": {
127
+ "streamsearch": "^1.1.0"
128
+ },
129
+ "engines": {
130
+ "node": ">=10.16.0"
131
+ }
132
+ },
133
+ "node_modules/bytes": {
134
+ "version": "3.1.2",
135
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
136
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
137
+ "license": "MIT",
138
+ "engines": {
139
+ "node": ">= 0.8"
140
+ }
141
+ },
142
+ "node_modules/call-bind-apply-helpers": {
143
+ "version": "1.0.2",
144
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
145
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
146
+ "license": "MIT",
147
+ "dependencies": {
148
+ "es-errors": "^1.3.0",
149
+ "function-bind": "^1.1.2"
150
+ },
151
+ "engines": {
152
+ "node": ">= 0.4"
153
+ }
154
+ },
155
+ "node_modules/call-bound": {
156
+ "version": "1.0.4",
157
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
158
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
159
+ "license": "MIT",
160
+ "dependencies": {
161
+ "call-bind-apply-helpers": "^1.0.2",
162
+ "get-intrinsic": "^1.3.0"
163
+ },
164
+ "engines": {
165
+ "node": ">= 0.4"
166
+ },
167
+ "funding": {
168
+ "url": "https://github.com/sponsors/ljharb"
169
+ }
170
+ },
171
+ "node_modules/chokidar": {
172
+ "version": "3.6.0",
173
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
174
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
175
+ "license": "MIT",
176
+ "dependencies": {
177
+ "anymatch": "~3.1.2",
178
+ "braces": "~3.0.2",
179
+ "glob-parent": "~5.1.2",
180
+ "is-binary-path": "~2.1.0",
181
+ "is-glob": "~4.0.1",
182
+ "normalize-path": "~3.0.0",
183
+ "readdirp": "~3.6.0"
184
+ },
185
+ "engines": {
186
+ "node": ">= 8.10.0"
187
+ },
188
+ "funding": {
189
+ "url": "https://paulmillr.com/funding/"
190
+ },
191
+ "optionalDependencies": {
192
+ "fsevents": "~2.3.2"
193
+ }
194
+ },
195
+ "node_modules/concat-map": {
196
+ "version": "0.0.1",
197
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
198
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
199
+ "license": "MIT"
200
+ },
201
+ "node_modules/concat-stream": {
202
+ "version": "2.0.0",
203
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
204
+ "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
205
+ "engines": [
206
+ "node >= 6.0"
207
+ ],
208
+ "license": "MIT",
209
+ "dependencies": {
210
+ "buffer-from": "^1.0.0",
211
+ "inherits": "^2.0.3",
212
+ "readable-stream": "^3.0.2",
213
+ "typedarray": "^0.0.6"
214
+ }
215
+ },
216
+ "node_modules/content-disposition": {
217
+ "version": "1.0.1",
218
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
219
+ "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
220
+ "license": "MIT",
221
+ "engines": {
222
+ "node": ">=18"
223
+ },
224
+ "funding": {
225
+ "type": "opencollective",
226
+ "url": "https://opencollective.com/express"
227
+ }
228
+ },
229
+ "node_modules/content-type": {
230
+ "version": "1.0.5",
231
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
232
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
233
+ "license": "MIT",
234
+ "engines": {
235
+ "node": ">= 0.6"
236
+ }
237
+ },
238
+ "node_modules/cookie": {
239
+ "version": "0.7.2",
240
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
241
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
242
+ "license": "MIT",
243
+ "engines": {
244
+ "node": ">= 0.6"
245
+ }
246
+ },
247
+ "node_modules/cookie-signature": {
248
+ "version": "1.2.2",
249
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
250
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
251
+ "license": "MIT",
252
+ "engines": {
253
+ "node": ">=6.6.0"
254
+ }
255
+ },
256
+ "node_modules/cors": {
257
+ "version": "2.8.6",
258
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
259
+ "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
260
+ "license": "MIT",
261
+ "dependencies": {
262
+ "object-assign": "^4",
263
+ "vary": "^1"
264
+ },
265
+ "engines": {
266
+ "node": ">= 0.10"
267
+ },
268
+ "funding": {
269
+ "type": "opencollective",
270
+ "url": "https://opencollective.com/express"
271
+ }
272
+ },
273
+ "node_modules/debug": {
274
+ "version": "4.4.3",
275
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
276
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
277
+ "license": "MIT",
278
+ "dependencies": {
279
+ "ms": "^2.1.3"
280
+ },
281
+ "engines": {
282
+ "node": ">=6.0"
283
+ },
284
+ "peerDependenciesMeta": {
285
+ "supports-color": {
286
+ "optional": true
287
+ }
288
+ }
289
+ },
290
+ "node_modules/depd": {
291
+ "version": "2.0.0",
292
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
293
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
294
+ "license": "MIT",
295
+ "engines": {
296
+ "node": ">= 0.8"
297
+ }
298
+ },
299
+ "node_modules/dunder-proto": {
300
+ "version": "1.0.1",
301
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
302
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
303
+ "license": "MIT",
304
+ "dependencies": {
305
+ "call-bind-apply-helpers": "^1.0.1",
306
+ "es-errors": "^1.3.0",
307
+ "gopd": "^1.2.0"
308
+ },
309
+ "engines": {
310
+ "node": ">= 0.4"
311
+ }
312
+ },
313
+ "node_modules/ee-first": {
314
+ "version": "1.1.1",
315
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
316
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
317
+ "license": "MIT"
318
+ },
319
+ "node_modules/encodeurl": {
320
+ "version": "2.0.0",
321
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
322
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
323
+ "license": "MIT",
324
+ "engines": {
325
+ "node": ">= 0.8"
326
+ }
327
+ },
328
+ "node_modules/es-define-property": {
329
+ "version": "1.0.1",
330
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
331
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
332
+ "license": "MIT",
333
+ "engines": {
334
+ "node": ">= 0.4"
335
+ }
336
+ },
337
+ "node_modules/es-errors": {
338
+ "version": "1.3.0",
339
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
340
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
341
+ "license": "MIT",
342
+ "engines": {
343
+ "node": ">= 0.4"
344
+ }
345
+ },
346
+ "node_modules/es-object-atoms": {
347
+ "version": "1.1.1",
348
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
349
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
350
+ "license": "MIT",
351
+ "dependencies": {
352
+ "es-errors": "^1.3.0"
353
+ },
354
+ "engines": {
355
+ "node": ">= 0.4"
356
+ }
357
+ },
358
+ "node_modules/escape-html": {
359
+ "version": "1.0.3",
360
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
361
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
362
+ "license": "MIT"
363
+ },
364
+ "node_modules/etag": {
365
+ "version": "1.8.1",
366
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
367
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
368
+ "license": "MIT",
369
+ "engines": {
370
+ "node": ">= 0.6"
371
+ }
372
+ },
373
+ "node_modules/express": {
374
+ "version": "5.2.1",
375
+ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
376
+ "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
377
+ "license": "MIT",
378
+ "dependencies": {
379
+ "accepts": "^2.0.0",
380
+ "body-parser": "^2.2.1",
381
+ "content-disposition": "^1.0.0",
382
+ "content-type": "^1.0.5",
383
+ "cookie": "^0.7.1",
384
+ "cookie-signature": "^1.2.1",
385
+ "debug": "^4.4.0",
386
+ "depd": "^2.0.0",
387
+ "encodeurl": "^2.0.0",
388
+ "escape-html": "^1.0.3",
389
+ "etag": "^1.8.1",
390
+ "finalhandler": "^2.1.0",
391
+ "fresh": "^2.0.0",
392
+ "http-errors": "^2.0.0",
393
+ "merge-descriptors": "^2.0.0",
394
+ "mime-types": "^3.0.0",
395
+ "on-finished": "^2.4.1",
396
+ "once": "^1.4.0",
397
+ "parseurl": "^1.3.3",
398
+ "proxy-addr": "^2.0.7",
399
+ "qs": "^6.14.0",
400
+ "range-parser": "^1.2.1",
401
+ "router": "^2.2.0",
402
+ "send": "^1.1.0",
403
+ "serve-static": "^2.2.0",
404
+ "statuses": "^2.0.1",
405
+ "type-is": "^2.0.1",
406
+ "vary": "^1.1.2"
407
+ },
408
+ "engines": {
409
+ "node": ">= 18"
410
+ },
411
+ "funding": {
412
+ "type": "opencollective",
413
+ "url": "https://opencollective.com/express"
414
+ }
415
+ },
416
+ "node_modules/fill-range": {
417
+ "version": "7.1.1",
418
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
419
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
420
+ "license": "MIT",
421
+ "dependencies": {
422
+ "to-regex-range": "^5.0.1"
423
+ },
424
+ "engines": {
425
+ "node": ">=8"
426
+ }
427
+ },
428
+ "node_modules/finalhandler": {
429
+ "version": "2.1.1",
430
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
431
+ "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
432
+ "license": "MIT",
433
+ "dependencies": {
434
+ "debug": "^4.4.0",
435
+ "encodeurl": "^2.0.0",
436
+ "escape-html": "^1.0.3",
437
+ "on-finished": "^2.4.1",
438
+ "parseurl": "^1.3.3",
439
+ "statuses": "^2.0.1"
440
+ },
441
+ "engines": {
442
+ "node": ">= 18.0.0"
443
+ },
444
+ "funding": {
445
+ "type": "opencollective",
446
+ "url": "https://opencollective.com/express"
447
+ }
448
+ },
449
+ "node_modules/forwarded": {
450
+ "version": "0.2.0",
451
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
452
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
453
+ "license": "MIT",
454
+ "engines": {
455
+ "node": ">= 0.6"
456
+ }
457
+ },
458
+ "node_modules/fresh": {
459
+ "version": "2.0.0",
460
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
461
+ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
462
+ "license": "MIT",
463
+ "engines": {
464
+ "node": ">= 0.8"
465
+ }
466
+ },
467
+ "node_modules/fs-extra": {
468
+ "version": "11.3.3",
469
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz",
470
+ "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==",
471
+ "license": "MIT",
472
+ "dependencies": {
473
+ "graceful-fs": "^4.2.0",
474
+ "jsonfile": "^6.0.1",
475
+ "universalify": "^2.0.0"
476
+ },
477
+ "engines": {
478
+ "node": ">=14.14"
479
+ }
480
+ },
481
+ "node_modules/fsevents": {
482
+ "version": "2.3.3",
483
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
484
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
485
+ "hasInstallScript": true,
486
+ "license": "MIT",
487
+ "optional": true,
488
+ "os": [
489
+ "darwin"
490
+ ],
491
+ "engines": {
492
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
493
+ }
494
+ },
495
+ "node_modules/function-bind": {
496
+ "version": "1.1.2",
497
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
498
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
499
+ "license": "MIT",
500
+ "funding": {
501
+ "url": "https://github.com/sponsors/ljharb"
502
+ }
503
+ },
504
+ "node_modules/get-intrinsic": {
505
+ "version": "1.3.0",
506
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
507
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
508
+ "license": "MIT",
509
+ "dependencies": {
510
+ "call-bind-apply-helpers": "^1.0.2",
511
+ "es-define-property": "^1.0.1",
512
+ "es-errors": "^1.3.0",
513
+ "es-object-atoms": "^1.1.1",
514
+ "function-bind": "^1.1.2",
515
+ "get-proto": "^1.0.1",
516
+ "gopd": "^1.2.0",
517
+ "has-symbols": "^1.1.0",
518
+ "hasown": "^2.0.2",
519
+ "math-intrinsics": "^1.1.0"
520
+ },
521
+ "engines": {
522
+ "node": ">= 0.4"
523
+ },
524
+ "funding": {
525
+ "url": "https://github.com/sponsors/ljharb"
526
+ }
527
+ },
528
+ "node_modules/get-proto": {
529
+ "version": "1.0.1",
530
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
531
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
532
+ "license": "MIT",
533
+ "dependencies": {
534
+ "dunder-proto": "^1.0.1",
535
+ "es-object-atoms": "^1.0.0"
536
+ },
537
+ "engines": {
538
+ "node": ">= 0.4"
539
+ }
540
+ },
541
+ "node_modules/glob-parent": {
542
+ "version": "5.1.2",
543
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
544
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
545
+ "license": "ISC",
546
+ "dependencies": {
547
+ "is-glob": "^4.0.1"
548
+ },
549
+ "engines": {
550
+ "node": ">= 6"
551
+ }
552
+ },
553
+ "node_modules/gopd": {
554
+ "version": "1.2.0",
555
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
556
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
557
+ "license": "MIT",
558
+ "engines": {
559
+ "node": ">= 0.4"
560
+ },
561
+ "funding": {
562
+ "url": "https://github.com/sponsors/ljharb"
563
+ }
564
+ },
565
+ "node_modules/graceful-fs": {
566
+ "version": "4.2.11",
567
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
568
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
569
+ "license": "ISC"
570
+ },
571
+ "node_modules/has-flag": {
572
+ "version": "3.0.0",
573
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
574
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
575
+ "license": "MIT",
576
+ "engines": {
577
+ "node": ">=4"
578
+ }
579
+ },
580
+ "node_modules/has-symbols": {
581
+ "version": "1.1.0",
582
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
583
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
584
+ "license": "MIT",
585
+ "engines": {
586
+ "node": ">= 0.4"
587
+ },
588
+ "funding": {
589
+ "url": "https://github.com/sponsors/ljharb"
590
+ }
591
+ },
592
+ "node_modules/hasown": {
593
+ "version": "2.0.2",
594
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
595
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
596
+ "license": "MIT",
597
+ "dependencies": {
598
+ "function-bind": "^1.1.2"
599
+ },
600
+ "engines": {
601
+ "node": ">= 0.4"
602
+ }
603
+ },
604
+ "node_modules/http-errors": {
605
+ "version": "2.0.1",
606
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
607
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
608
+ "license": "MIT",
609
+ "dependencies": {
610
+ "depd": "~2.0.0",
611
+ "inherits": "~2.0.4",
612
+ "setprototypeof": "~1.2.0",
613
+ "statuses": "~2.0.2",
614
+ "toidentifier": "~1.0.1"
615
+ },
616
+ "engines": {
617
+ "node": ">= 0.8"
618
+ },
619
+ "funding": {
620
+ "type": "opencollective",
621
+ "url": "https://opencollective.com/express"
622
+ }
623
+ },
624
+ "node_modules/iconv-lite": {
625
+ "version": "0.7.2",
626
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
627
+ "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
628
+ "license": "MIT",
629
+ "dependencies": {
630
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
631
+ },
632
+ "engines": {
633
+ "node": ">=0.10.0"
634
+ },
635
+ "funding": {
636
+ "type": "opencollective",
637
+ "url": "https://opencollective.com/express"
638
+ }
639
+ },
640
+ "node_modules/ignore-by-default": {
641
+ "version": "1.0.1",
642
+ "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
643
+ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
644
+ "license": "ISC"
645
+ },
646
+ "node_modules/inherits": {
647
+ "version": "2.0.4",
648
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
649
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
650
+ "license": "ISC"
651
+ },
652
+ "node_modules/ipaddr.js": {
653
+ "version": "1.9.1",
654
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
655
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
656
+ "license": "MIT",
657
+ "engines": {
658
+ "node": ">= 0.10"
659
+ }
660
+ },
661
+ "node_modules/is-binary-path": {
662
+ "version": "2.1.0",
663
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
664
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
665
+ "license": "MIT",
666
+ "dependencies": {
667
+ "binary-extensions": "^2.0.0"
668
+ },
669
+ "engines": {
670
+ "node": ">=8"
671
+ }
672
+ },
673
+ "node_modules/is-extglob": {
674
+ "version": "2.1.1",
675
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
676
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
677
+ "license": "MIT",
678
+ "engines": {
679
+ "node": ">=0.10.0"
680
+ }
681
+ },
682
+ "node_modules/is-glob": {
683
+ "version": "4.0.3",
684
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
685
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
686
+ "license": "MIT",
687
+ "dependencies": {
688
+ "is-extglob": "^2.1.1"
689
+ },
690
+ "engines": {
691
+ "node": ">=0.10.0"
692
+ }
693
+ },
694
+ "node_modules/is-number": {
695
+ "version": "7.0.0",
696
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
697
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
698
+ "license": "MIT",
699
+ "engines": {
700
+ "node": ">=0.12.0"
701
+ }
702
+ },
703
+ "node_modules/is-promise": {
704
+ "version": "4.0.0",
705
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
706
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
707
+ "license": "MIT"
708
+ },
709
+ "node_modules/jsonfile": {
710
+ "version": "6.2.0",
711
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
712
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
713
+ "license": "MIT",
714
+ "dependencies": {
715
+ "universalify": "^2.0.0"
716
+ },
717
+ "optionalDependencies": {
718
+ "graceful-fs": "^4.1.6"
719
+ }
720
+ },
721
+ "node_modules/math-intrinsics": {
722
+ "version": "1.1.0",
723
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
724
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
725
+ "license": "MIT",
726
+ "engines": {
727
+ "node": ">= 0.4"
728
+ }
729
+ },
730
+ "node_modules/media-typer": {
731
+ "version": "1.1.0",
732
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
733
+ "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
734
+ "license": "MIT",
735
+ "engines": {
736
+ "node": ">= 0.8"
737
+ }
738
+ },
739
+ "node_modules/merge-descriptors": {
740
+ "version": "2.0.0",
741
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
742
+ "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
743
+ "license": "MIT",
744
+ "engines": {
745
+ "node": ">=18"
746
+ },
747
+ "funding": {
748
+ "url": "https://github.com/sponsors/sindresorhus"
749
+ }
750
+ },
751
+ "node_modules/mime-db": {
752
+ "version": "1.54.0",
753
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
754
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
755
+ "license": "MIT",
756
+ "engines": {
757
+ "node": ">= 0.6"
758
+ }
759
+ },
760
+ "node_modules/mime-types": {
761
+ "version": "3.0.2",
762
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
763
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
764
+ "license": "MIT",
765
+ "dependencies": {
766
+ "mime-db": "^1.54.0"
767
+ },
768
+ "engines": {
769
+ "node": ">=18"
770
+ },
771
+ "funding": {
772
+ "type": "opencollective",
773
+ "url": "https://opencollective.com/express"
774
+ }
775
+ },
776
+ "node_modules/minimatch": {
777
+ "version": "3.1.2",
778
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
779
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
780
+ "license": "ISC",
781
+ "dependencies": {
782
+ "brace-expansion": "^1.1.7"
783
+ },
784
+ "engines": {
785
+ "node": "*"
786
+ }
787
+ },
788
+ "node_modules/minimist": {
789
+ "version": "1.2.8",
790
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
791
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
792
+ "license": "MIT",
793
+ "funding": {
794
+ "url": "https://github.com/sponsors/ljharb"
795
+ }
796
+ },
797
+ "node_modules/mkdirp": {
798
+ "version": "0.5.6",
799
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
800
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
801
+ "license": "MIT",
802
+ "dependencies": {
803
+ "minimist": "^1.2.6"
804
+ },
805
+ "bin": {
806
+ "mkdirp": "bin/cmd.js"
807
+ }
808
+ },
809
+ "node_modules/ms": {
810
+ "version": "2.1.3",
811
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
812
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
813
+ "license": "MIT"
814
+ },
815
+ "node_modules/multer": {
816
+ "version": "2.0.2",
817
+ "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz",
818
+ "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==",
819
+ "license": "MIT",
820
+ "dependencies": {
821
+ "append-field": "^1.0.0",
822
+ "busboy": "^1.6.0",
823
+ "concat-stream": "^2.0.0",
824
+ "mkdirp": "^0.5.6",
825
+ "object-assign": "^4.1.1",
826
+ "type-is": "^1.6.18",
827
+ "xtend": "^4.0.2"
828
+ },
829
+ "engines": {
830
+ "node": ">= 10.16.0"
831
+ }
832
+ },
833
+ "node_modules/multer/node_modules/media-typer": {
834
+ "version": "0.3.0",
835
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
836
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
837
+ "license": "MIT",
838
+ "engines": {
839
+ "node": ">= 0.6"
840
+ }
841
+ },
842
+ "node_modules/multer/node_modules/mime-db": {
843
+ "version": "1.52.0",
844
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
845
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
846
+ "license": "MIT",
847
+ "engines": {
848
+ "node": ">= 0.6"
849
+ }
850
+ },
851
+ "node_modules/multer/node_modules/mime-types": {
852
+ "version": "2.1.35",
853
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
854
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
855
+ "license": "MIT",
856
+ "dependencies": {
857
+ "mime-db": "1.52.0"
858
+ },
859
+ "engines": {
860
+ "node": ">= 0.6"
861
+ }
862
+ },
863
+ "node_modules/multer/node_modules/type-is": {
864
+ "version": "1.6.18",
865
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
866
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
867
+ "license": "MIT",
868
+ "dependencies": {
869
+ "media-typer": "0.3.0",
870
+ "mime-types": "~2.1.24"
871
+ },
872
+ "engines": {
873
+ "node": ">= 0.6"
874
+ }
875
+ },
876
+ "node_modules/negotiator": {
877
+ "version": "1.0.0",
878
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
879
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
880
+ "license": "MIT",
881
+ "engines": {
882
+ "node": ">= 0.6"
883
+ }
884
+ },
885
+ "node_modules/nodemon": {
886
+ "version": "3.1.11",
887
+ "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz",
888
+ "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==",
889
+ "license": "MIT",
890
+ "dependencies": {
891
+ "chokidar": "^3.5.2",
892
+ "debug": "^4",
893
+ "ignore-by-default": "^1.0.1",
894
+ "minimatch": "^3.1.2",
895
+ "pstree.remy": "^1.1.8",
896
+ "semver": "^7.5.3",
897
+ "simple-update-notifier": "^2.0.0",
898
+ "supports-color": "^5.5.0",
899
+ "touch": "^3.1.0",
900
+ "undefsafe": "^2.0.5"
901
+ },
902
+ "bin": {
903
+ "nodemon": "bin/nodemon.js"
904
+ },
905
+ "engines": {
906
+ "node": ">=10"
907
+ },
908
+ "funding": {
909
+ "type": "opencollective",
910
+ "url": "https://opencollective.com/nodemon"
911
+ }
912
+ },
913
+ "node_modules/normalize-path": {
914
+ "version": "3.0.0",
915
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
916
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
917
+ "license": "MIT",
918
+ "engines": {
919
+ "node": ">=0.10.0"
920
+ }
921
+ },
922
+ "node_modules/object-assign": {
923
+ "version": "4.1.1",
924
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
925
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
926
+ "license": "MIT",
927
+ "engines": {
928
+ "node": ">=0.10.0"
929
+ }
930
+ },
931
+ "node_modules/object-inspect": {
932
+ "version": "1.13.4",
933
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
934
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
935
+ "license": "MIT",
936
+ "engines": {
937
+ "node": ">= 0.4"
938
+ },
939
+ "funding": {
940
+ "url": "https://github.com/sponsors/ljharb"
941
+ }
942
+ },
943
+ "node_modules/on-finished": {
944
+ "version": "2.4.1",
945
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
946
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
947
+ "license": "MIT",
948
+ "dependencies": {
949
+ "ee-first": "1.1.1"
950
+ },
951
+ "engines": {
952
+ "node": ">= 0.8"
953
+ }
954
+ },
955
+ "node_modules/once": {
956
+ "version": "1.4.0",
957
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
958
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
959
+ "license": "ISC",
960
+ "dependencies": {
961
+ "wrappy": "1"
962
+ }
963
+ },
964
+ "node_modules/parseurl": {
965
+ "version": "1.3.3",
966
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
967
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
968
+ "license": "MIT",
969
+ "engines": {
970
+ "node": ">= 0.8"
971
+ }
972
+ },
973
+ "node_modules/path-to-regexp": {
974
+ "version": "8.3.0",
975
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
976
+ "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
977
+ "license": "MIT",
978
+ "funding": {
979
+ "type": "opencollective",
980
+ "url": "https://opencollective.com/express"
981
+ }
982
+ },
983
+ "node_modules/picomatch": {
984
+ "version": "2.3.1",
985
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
986
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
987
+ "license": "MIT",
988
+ "engines": {
989
+ "node": ">=8.6"
990
+ },
991
+ "funding": {
992
+ "url": "https://github.com/sponsors/jonschlinkert"
993
+ }
994
+ },
995
+ "node_modules/proxy-addr": {
996
+ "version": "2.0.7",
997
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
998
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
999
+ "license": "MIT",
1000
+ "dependencies": {
1001
+ "forwarded": "0.2.0",
1002
+ "ipaddr.js": "1.9.1"
1003
+ },
1004
+ "engines": {
1005
+ "node": ">= 0.10"
1006
+ }
1007
+ },
1008
+ "node_modules/pstree.remy": {
1009
+ "version": "1.1.8",
1010
+ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
1011
+ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
1012
+ "license": "MIT"
1013
+ },
1014
+ "node_modules/qs": {
1015
+ "version": "6.14.1",
1016
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
1017
+ "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
1018
+ "license": "BSD-3-Clause",
1019
+ "dependencies": {
1020
+ "side-channel": "^1.1.0"
1021
+ },
1022
+ "engines": {
1023
+ "node": ">=0.6"
1024
+ },
1025
+ "funding": {
1026
+ "url": "https://github.com/sponsors/ljharb"
1027
+ }
1028
+ },
1029
+ "node_modules/range-parser": {
1030
+ "version": "1.2.1",
1031
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1032
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
1033
+ "license": "MIT",
1034
+ "engines": {
1035
+ "node": ">= 0.6"
1036
+ }
1037
+ },
1038
+ "node_modules/raw-body": {
1039
+ "version": "3.0.2",
1040
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
1041
+ "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
1042
+ "license": "MIT",
1043
+ "dependencies": {
1044
+ "bytes": "~3.1.2",
1045
+ "http-errors": "~2.0.1",
1046
+ "iconv-lite": "~0.7.0",
1047
+ "unpipe": "~1.0.0"
1048
+ },
1049
+ "engines": {
1050
+ "node": ">= 0.10"
1051
+ }
1052
+ },
1053
+ "node_modules/readable-stream": {
1054
+ "version": "3.6.2",
1055
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
1056
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
1057
+ "license": "MIT",
1058
+ "dependencies": {
1059
+ "inherits": "^2.0.3",
1060
+ "string_decoder": "^1.1.1",
1061
+ "util-deprecate": "^1.0.1"
1062
+ },
1063
+ "engines": {
1064
+ "node": ">= 6"
1065
+ }
1066
+ },
1067
+ "node_modules/readdirp": {
1068
+ "version": "3.6.0",
1069
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
1070
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
1071
+ "license": "MIT",
1072
+ "dependencies": {
1073
+ "picomatch": "^2.2.1"
1074
+ },
1075
+ "engines": {
1076
+ "node": ">=8.10.0"
1077
+ }
1078
+ },
1079
+ "node_modules/router": {
1080
+ "version": "2.2.0",
1081
+ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
1082
+ "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
1083
+ "license": "MIT",
1084
+ "dependencies": {
1085
+ "debug": "^4.4.0",
1086
+ "depd": "^2.0.0",
1087
+ "is-promise": "^4.0.0",
1088
+ "parseurl": "^1.3.3",
1089
+ "path-to-regexp": "^8.0.0"
1090
+ },
1091
+ "engines": {
1092
+ "node": ">= 18"
1093
+ }
1094
+ },
1095
+ "node_modules/safe-buffer": {
1096
+ "version": "5.2.1",
1097
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1098
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1099
+ "funding": [
1100
+ {
1101
+ "type": "github",
1102
+ "url": "https://github.com/sponsors/feross"
1103
+ },
1104
+ {
1105
+ "type": "patreon",
1106
+ "url": "https://www.patreon.com/feross"
1107
+ },
1108
+ {
1109
+ "type": "consulting",
1110
+ "url": "https://feross.org/support"
1111
+ }
1112
+ ],
1113
+ "license": "MIT"
1114
+ },
1115
+ "node_modules/safer-buffer": {
1116
+ "version": "2.1.2",
1117
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1118
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
1119
+ "license": "MIT"
1120
+ },
1121
+ "node_modules/semver": {
1122
+ "version": "7.7.3",
1123
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
1124
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
1125
+ "license": "ISC",
1126
+ "bin": {
1127
+ "semver": "bin/semver.js"
1128
+ },
1129
+ "engines": {
1130
+ "node": ">=10"
1131
+ }
1132
+ },
1133
+ "node_modules/send": {
1134
+ "version": "1.2.1",
1135
+ "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
1136
+ "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==",
1137
+ "license": "MIT",
1138
+ "dependencies": {
1139
+ "debug": "^4.4.3",
1140
+ "encodeurl": "^2.0.0",
1141
+ "escape-html": "^1.0.3",
1142
+ "etag": "^1.8.1",
1143
+ "fresh": "^2.0.0",
1144
+ "http-errors": "^2.0.1",
1145
+ "mime-types": "^3.0.2",
1146
+ "ms": "^2.1.3",
1147
+ "on-finished": "^2.4.1",
1148
+ "range-parser": "^1.2.1",
1149
+ "statuses": "^2.0.2"
1150
+ },
1151
+ "engines": {
1152
+ "node": ">= 18"
1153
+ },
1154
+ "funding": {
1155
+ "type": "opencollective",
1156
+ "url": "https://opencollective.com/express"
1157
+ }
1158
+ },
1159
+ "node_modules/serve-static": {
1160
+ "version": "2.2.1",
1161
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
1162
+ "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==",
1163
+ "license": "MIT",
1164
+ "dependencies": {
1165
+ "encodeurl": "^2.0.0",
1166
+ "escape-html": "^1.0.3",
1167
+ "parseurl": "^1.3.3",
1168
+ "send": "^1.2.0"
1169
+ },
1170
+ "engines": {
1171
+ "node": ">= 18"
1172
+ },
1173
+ "funding": {
1174
+ "type": "opencollective",
1175
+ "url": "https://opencollective.com/express"
1176
+ }
1177
+ },
1178
+ "node_modules/setprototypeof": {
1179
+ "version": "1.2.0",
1180
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1181
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
1182
+ "license": "ISC"
1183
+ },
1184
+ "node_modules/side-channel": {
1185
+ "version": "1.1.0",
1186
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
1187
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
1188
+ "license": "MIT",
1189
+ "dependencies": {
1190
+ "es-errors": "^1.3.0",
1191
+ "object-inspect": "^1.13.3",
1192
+ "side-channel-list": "^1.0.0",
1193
+ "side-channel-map": "^1.0.1",
1194
+ "side-channel-weakmap": "^1.0.2"
1195
+ },
1196
+ "engines": {
1197
+ "node": ">= 0.4"
1198
+ },
1199
+ "funding": {
1200
+ "url": "https://github.com/sponsors/ljharb"
1201
+ }
1202
+ },
1203
+ "node_modules/side-channel-list": {
1204
+ "version": "1.0.0",
1205
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
1206
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
1207
+ "license": "MIT",
1208
+ "dependencies": {
1209
+ "es-errors": "^1.3.0",
1210
+ "object-inspect": "^1.13.3"
1211
+ },
1212
+ "engines": {
1213
+ "node": ">= 0.4"
1214
+ },
1215
+ "funding": {
1216
+ "url": "https://github.com/sponsors/ljharb"
1217
+ }
1218
+ },
1219
+ "node_modules/side-channel-map": {
1220
+ "version": "1.0.1",
1221
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
1222
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
1223
+ "license": "MIT",
1224
+ "dependencies": {
1225
+ "call-bound": "^1.0.2",
1226
+ "es-errors": "^1.3.0",
1227
+ "get-intrinsic": "^1.2.5",
1228
+ "object-inspect": "^1.13.3"
1229
+ },
1230
+ "engines": {
1231
+ "node": ">= 0.4"
1232
+ },
1233
+ "funding": {
1234
+ "url": "https://github.com/sponsors/ljharb"
1235
+ }
1236
+ },
1237
+ "node_modules/side-channel-weakmap": {
1238
+ "version": "1.0.2",
1239
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
1240
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
1241
+ "license": "MIT",
1242
+ "dependencies": {
1243
+ "call-bound": "^1.0.2",
1244
+ "es-errors": "^1.3.0",
1245
+ "get-intrinsic": "^1.2.5",
1246
+ "object-inspect": "^1.13.3",
1247
+ "side-channel-map": "^1.0.1"
1248
+ },
1249
+ "engines": {
1250
+ "node": ">= 0.4"
1251
+ },
1252
+ "funding": {
1253
+ "url": "https://github.com/sponsors/ljharb"
1254
+ }
1255
+ },
1256
+ "node_modules/simple-update-notifier": {
1257
+ "version": "2.0.0",
1258
+ "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
1259
+ "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
1260
+ "license": "MIT",
1261
+ "dependencies": {
1262
+ "semver": "^7.5.3"
1263
+ },
1264
+ "engines": {
1265
+ "node": ">=10"
1266
+ }
1267
+ },
1268
+ "node_modules/statuses": {
1269
+ "version": "2.0.2",
1270
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
1271
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
1272
+ "license": "MIT",
1273
+ "engines": {
1274
+ "node": ">= 0.8"
1275
+ }
1276
+ },
1277
+ "node_modules/streamsearch": {
1278
+ "version": "1.1.0",
1279
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
1280
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
1281
+ "engines": {
1282
+ "node": ">=10.0.0"
1283
+ }
1284
+ },
1285
+ "node_modules/string_decoder": {
1286
+ "version": "1.3.0",
1287
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
1288
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
1289
+ "license": "MIT",
1290
+ "dependencies": {
1291
+ "safe-buffer": "~5.2.0"
1292
+ }
1293
+ },
1294
+ "node_modules/supports-color": {
1295
+ "version": "5.5.0",
1296
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
1297
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
1298
+ "license": "MIT",
1299
+ "dependencies": {
1300
+ "has-flag": "^3.0.0"
1301
+ },
1302
+ "engines": {
1303
+ "node": ">=4"
1304
+ }
1305
+ },
1306
+ "node_modules/to-regex-range": {
1307
+ "version": "5.0.1",
1308
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
1309
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1310
+ "license": "MIT",
1311
+ "dependencies": {
1312
+ "is-number": "^7.0.0"
1313
+ },
1314
+ "engines": {
1315
+ "node": ">=8.0"
1316
+ }
1317
+ },
1318
+ "node_modules/toidentifier": {
1319
+ "version": "1.0.1",
1320
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
1321
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
1322
+ "license": "MIT",
1323
+ "engines": {
1324
+ "node": ">=0.6"
1325
+ }
1326
+ },
1327
+ "node_modules/touch": {
1328
+ "version": "3.1.1",
1329
+ "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
1330
+ "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
1331
+ "license": "ISC",
1332
+ "bin": {
1333
+ "nodetouch": "bin/nodetouch.js"
1334
+ }
1335
+ },
1336
+ "node_modules/type-is": {
1337
+ "version": "2.0.1",
1338
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
1339
+ "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
1340
+ "license": "MIT",
1341
+ "dependencies": {
1342
+ "content-type": "^1.0.5",
1343
+ "media-typer": "^1.1.0",
1344
+ "mime-types": "^3.0.0"
1345
+ },
1346
+ "engines": {
1347
+ "node": ">= 0.6"
1348
+ }
1349
+ },
1350
+ "node_modules/typedarray": {
1351
+ "version": "0.0.6",
1352
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
1353
+ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
1354
+ "license": "MIT"
1355
+ },
1356
+ "node_modules/undefsafe": {
1357
+ "version": "2.0.5",
1358
+ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
1359
+ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
1360
+ "license": "MIT"
1361
+ },
1362
+ "node_modules/universalify": {
1363
+ "version": "2.0.1",
1364
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
1365
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
1366
+ "license": "MIT",
1367
+ "engines": {
1368
+ "node": ">= 10.0.0"
1369
+ }
1370
+ },
1371
+ "node_modules/unpipe": {
1372
+ "version": "1.0.0",
1373
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1374
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
1375
+ "license": "MIT",
1376
+ "engines": {
1377
+ "node": ">= 0.8"
1378
+ }
1379
+ },
1380
+ "node_modules/util-deprecate": {
1381
+ "version": "1.0.2",
1382
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
1383
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
1384
+ "license": "MIT"
1385
+ },
1386
+ "node_modules/uuid": {
1387
+ "version": "13.0.0",
1388
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
1389
+ "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
1390
+ "funding": [
1391
+ "https://github.com/sponsors/broofa",
1392
+ "https://github.com/sponsors/ctavan"
1393
+ ],
1394
+ "license": "MIT",
1395
+ "bin": {
1396
+ "uuid": "dist-node/bin/uuid"
1397
+ }
1398
+ },
1399
+ "node_modules/vary": {
1400
+ "version": "1.1.2",
1401
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1402
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
1403
+ "license": "MIT",
1404
+ "engines": {
1405
+ "node": ">= 0.8"
1406
+ }
1407
+ },
1408
+ "node_modules/wrappy": {
1409
+ "version": "1.0.2",
1410
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1411
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
1412
+ "license": "ISC"
1413
+ },
1414
+ "node_modules/xtend": {
1415
+ "version": "4.0.2",
1416
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
1417
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
1418
+ "license": "MIT",
1419
+ "engines": {
1420
+ "node": ">=0.4"
1421
+ }
1422
+ }
1423
+ }
1424
+ }
backend/package.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "backend",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "start": "nodemon index.js",
8
+ "test": "echo \"Error: no test specified\" && exit 1"
9
+ },
10
+ "keywords": [],
11
+ "author": "",
12
+ "license": "ISC",
13
+ "type": "commonjs",
14
+ "dependencies": {
15
+ "cors": "^2.8.6",
16
+ "express": "^5.2.1",
17
+ "fs-extra": "^11.3.3",
18
+ "multer": "^2.0.2",
19
+ "nodemon": "^3.1.11",
20
+ "uuid": "^13.0.0"
21
+ }
22
+ }
backend/routes/audio.js ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require("express");
2
+ const multer = require("multer");
3
+ const path = require("path");
4
+ const fs = require("fs");
5
+ const { v4: uuidv4 } = require("uuid");
6
+
7
+ const router = express.Router();
8
+ const UPLOAD_DIR = path.join(__dirname, "../storage/uploads");
9
+
10
+ const allowedExt = [".wav", ".mp3", ".ogg", ".flac", ".m4a"];
11
+
12
+ const storage = multer.diskStorage({
13
+ destination: (req, file, cb) => {
14
+ cb(null, UPLOAD_DIR);
15
+ },
16
+ filename: (req, file, cb) => {
17
+ const id = uuidv4();
18
+ cb(null, `${id}.wav`);
19
+ }
20
+ });
21
+
22
+ const upload = multer({
23
+ storage,
24
+ limits: { fileSize: 20 * 1024 * 1024 },
25
+ fileFilter: (req, file, cb) => {
26
+ const ext = path.extname(file.originalname).toLowerCase();
27
+ if (!allowedExt.includes(ext)) {
28
+ return cb(new Error("Only audio files allowed"));
29
+ }
30
+ cb(null, true);
31
+ }
32
+ });
33
+
34
+ // POST /api/audio/upload
35
+ router.post("/upload", upload.single("audio"), (req, res) => {
36
+ if (!req.file) {
37
+ return res.status(400).json({ error: "No audio file uploaded" });
38
+ }
39
+
40
+ res.json({
41
+ audioId: req.file.filename,
42
+ size: req.file.size
43
+ });
44
+ });
45
+
46
+ // GET /api/audio
47
+ router.get("/", (req, res) => {
48
+ const files = fs.readdirSync(UPLOAD_DIR)
49
+ .filter(f => f.endsWith(".wav"))
50
+ .map(f => ({ audioId: f }));
51
+
52
+ res.json(files);
53
+ });
54
+
55
+ module.exports = router;
backend/routes/create.js ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require("express");
2
+ const router = express.Router();
3
+ const {
4
+ createJob,
5
+ getJobStatus,
6
+ listJobs
7
+ } = require("../services/jobService");
8
+
9
+ // POST /api/create
10
+ router.post("/", (req, res) => {
11
+ try {
12
+ const { modelId, audioId, settings } = req.body;
13
+
14
+ if (!modelId || !audioId || !settings) {
15
+ return res.status(400).json({ error: "Invalid payload" });
16
+ }
17
+
18
+ const jobId = createJob({ modelId, audioId, settings });
19
+ res.json({ jobId });
20
+ } catch (err) {
21
+ res.status(500).json({ error: err.message });
22
+ }
23
+ });
24
+
25
+ // GET /api/create/:jobId/status
26
+ router.get("/:jobId/status", (req, res) => {
27
+ try {
28
+ const status = getJobStatus(req.params.jobId);
29
+ res.json(status);
30
+ } catch (err) {
31
+ res.status(404).json({ error: err.message });
32
+ }
33
+ });
34
+
35
+ // GET /api/create
36
+ router.get("/", (req, res) => {
37
+ const jobs = listJobs();
38
+ res.json(jobs);
39
+ });
40
+
41
+ module.exports = router;
backend/routes/models.js ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require("express");
2
+ const router = express.Router();
3
+ const { loadModels } = require("../services/modelService");
4
+
5
+ router.get("/", (req, res) => {
6
+ const models = loadModels();
7
+ res.json(models);
8
+ });
9
+
10
+ module.exports = router;
backend/services/jobService.js ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { v4: uuidv4 } = require("uuid");
4
+
5
+ const CREATIONS_DIR = path.join(__dirname, "../storage/creations");
6
+ const UPLOAD_DIR = path.join(__dirname, "../storage/uploads");
7
+ const MODELS_DIR = path.join(__dirname, "../storage/models");
8
+
9
+ function createJob({ modelId, audioId, settings }) {
10
+ const jobId = uuidv4();
11
+ const jobDir = path.join(CREATIONS_DIR, jobId);
12
+
13
+ fs.mkdirSync(jobDir, { recursive: true });
14
+
15
+ // copy audio
16
+ const srcAudio = path.join(UPLOAD_DIR, audioId);
17
+ const dstAudio = path.join(jobDir, "input.wav");
18
+
19
+ if (!fs.existsSync(srcAudio)) {
20
+ throw new Error("Audio file not found");
21
+ }
22
+
23
+ fs.copyFileSync(srcAudio, dstAudio);
24
+
25
+ // load model metadata
26
+ const modelMetaPath = path.join(MODELS_DIR, modelId, "metadata.json");
27
+ if (!fs.existsSync(modelMetaPath)) {
28
+ throw new Error("Model metadata not found");
29
+ }
30
+
31
+ const modelMeta = JSON.parse(fs.readFileSync(modelMetaPath, "utf-8"));
32
+
33
+ // save model.json
34
+ fs.writeFileSync(
35
+ path.join(jobDir, "model.json"),
36
+ JSON.stringify(
37
+ {
38
+ id: modelId,
39
+ title: modelMeta.title,
40
+ engine: "RVC",
41
+ sampleRate: modelMeta.torchMetadata?.config?.sr
42
+ },
43
+ null,
44
+ 2
45
+ )
46
+ );
47
+
48
+ // save settings.json
49
+ fs.writeFileSync(
50
+ path.join(jobDir, "settings.json"),
51
+ JSON.stringify(settings, null, 2)
52
+ );
53
+
54
+ // init status
55
+ fs.writeFileSync(
56
+ path.join(jobDir, "status.json"),
57
+ JSON.stringify(
58
+ {
59
+ state: "queued",
60
+ progress: 0,
61
+ createdAt: new Date().toISOString()
62
+ },
63
+ null,
64
+ 2
65
+ )
66
+ );
67
+
68
+ return jobId;
69
+ }
70
+
71
+ function getJobStatus(jobId) {
72
+ const statusPath = path.join(CREATIONS_DIR, jobId, "status.json");
73
+ if (!fs.existsSync(statusPath)) {
74
+ throw new Error("Job not found");
75
+ }
76
+ return JSON.parse(fs.readFileSync(statusPath, "utf-8"));
77
+ }
78
+
79
+ function listJobs() {
80
+ if (!fs.existsSync(CREATIONS_DIR)) return [];
81
+
82
+ return fs.readdirSync(CREATIONS_DIR).map(jobId => {
83
+ const statusPath = path.join(CREATIONS_DIR, jobId, "status.json");
84
+ if (!fs.existsSync(statusPath)) return null;
85
+
86
+ const status = JSON.parse(fs.readFileSync(statusPath, "utf-8"));
87
+ return {
88
+ jobId,
89
+ ...status
90
+ };
91
+ }).filter(Boolean);
92
+ }
93
+
94
+ module.exports = {
95
+ createJob,
96
+ getJobStatus,
97
+ listJobs
98
+ };
backend/services/modelService.js ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ const MODELS_DIR = path.join(__dirname, "../storage/models");
5
+
6
+ function loadModels() {
7
+ if (!fs.existsSync(MODELS_DIR)) return [];
8
+
9
+ return fs.readdirSync(MODELS_DIR).map((folder) => {
10
+ const metaPath = path.join(MODELS_DIR, folder, "metadata.json");
11
+ if (!fs.existsSync(metaPath)) return null;
12
+
13
+ const meta = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
14
+
15
+ return {
16
+ id: folder,
17
+ title: meta.title,
18
+ tags: meta.tags || [],
19
+ engine: "RVC",
20
+ sampleRate: meta.torchMetadata?.config?.sr,
21
+ author: meta.author?.name,
22
+ };
23
+ }).filter(Boolean);
24
+ }
25
+
26
+ module.exports = { loadModels };
worker/README.md ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Worker – RVC Processing Engine
2
+
3
+ Worker bertugas:
4
+ - Membaca job dari backend
5
+ - Menjalankan voice conversion menggunakan RVC
6
+ - Menyimpan output audio
7
+ - Update status job
8
+
9
+ Worker berjalan **terpisah dari backend**.
10
+
11
+ ---
12
+
13
+ ## Requirements
14
+ - Python **3.10 (RECOMMENDED)**
15
+ - FFmpeg (tersedia di PATH)
16
+ - (Optional) NVIDIA GPU + CUDA
17
+
18
+ ### Python 3.13 **tidak didukung** oleh sebagian library audio & ML.
19
+
20
+ ---
21
+
22
+ ## Install dependencies
23
+ ```bash
24
+ pip install -r requirements.txt
25
+ ```
26
+
27
+ Jika terjadi error terkait pip:
28
+ ```bash
29
+ python -m pip install "pip<24.1"
30
+ ```
31
+
32
+ ## Run worker
33
+ ```bash
34
+ python main.py
35
+ ```
36
+
37
+ Worker akan:
38
+ - mencari job dengan status queued pada `status.json`
39
+ - memproses audio
40
+ - menghasilkan `output.wav`
41
+ - mengubah status menjadi done pada `status.json`
worker/audio_utils.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import librosa
2
+ import soundfile as sf
3
+
4
+ def load_audio(path, target_sr):
5
+ audio, sr = librosa.load(path, sr=target_sr, mono=True)
6
+ return audio, sr
7
+
8
+ def save_audio(path, audio, sr):
9
+ sf.write(path, audio, sr)
worker/main.py ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # worker/main.py
2
+ import os
3
+ import json
4
+ import time
5
+ import subprocess
6
+ import shlex
7
+
8
+ BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
9
+ CREATIONS_DIR = os.path.join(BASE_DIR, "backend/storage/creations")
10
+ MODELS_DIR = os.path.join(BASE_DIR, "backend/storage/models")
11
+
12
+ def update_status(job_dir, state, progress, message=None):
13
+ status_path = os.path.join(job_dir, "status.json")
14
+ data = {
15
+ "state": state,
16
+ "progress": progress,
17
+ "updatedAt": time.strftime("%Y-%m-%dT%H:%M:%SZ")
18
+ }
19
+ if message:
20
+ data["message"] = str(message)
21
+ with open(status_path, "w") as f:
22
+ json.dump(data, f, indent=2)
23
+
24
+ def find_model_files(model_id):
25
+ model_dir = os.path.join(MODELS_DIR, model_id)
26
+ if not os.path.isdir(model_dir):
27
+ raise FileNotFoundError(f"Model folder not found: {model_dir}")
28
+ pth = None
29
+ idx = None
30
+ for fname in os.listdir(model_dir):
31
+ if fname.endswith(".pth") or fname.endswith(".pt") or fname.endswith(".safetensors"):
32
+ pth = os.path.join(model_dir, fname)
33
+ if fname.endswith(".index") or fname.endswith(".ivf") or fname.endswith(".npy"):
34
+ idx = os.path.join(model_dir, fname)
35
+ return pth, idx, model_dir
36
+
37
+ def run_rvc_cli(input_wav, output_wav, pth_path, index_path=None, device="cpu", extra_args=None):
38
+ """
39
+ Calls: python -m rvc_python cli -i input_wav -o output_wav -mp pth_path [-ip index_path] -de device ...
40
+ """
41
+ cmd = [
42
+ "python", "-m", "rvc_python", "cli",
43
+ "-i", input_wav,
44
+ "-o", output_wav,
45
+ "-mp", pth_path,
46
+ "-de", device
47
+ ]
48
+ if index_path:
49
+ cmd += ["-ip", index_path]
50
+ if extra_args:
51
+ cmd += extra_args
52
+ # for logging/debugging, join as string
53
+ cmd_str = " ".join(shlex.quote(x) for x in cmd)
54
+ print("[WORKER] Running RVC CLI:", cmd_str)
55
+ # run and stream output
56
+ proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
57
+ return proc.returncode, proc.stdout, proc.stderr
58
+
59
+ def run_job(job_id):
60
+ job_dir = os.path.join(CREATIONS_DIR, job_id)
61
+ print(f"[WORKER] Processing job {job_id}")
62
+ update_status(job_dir, "processing", 5)
63
+
64
+ # load settings and model info
65
+ with open(os.path.join(job_dir, "settings.json")) as f:
66
+ settings = json.load(f)
67
+ with open(os.path.join(job_dir, "model.json")) as f:
68
+ model_meta = json.load(f)
69
+
70
+ model_id = model_meta.get("id")
71
+ pth_path, index_path, model_dir = find_model_files(model_id)
72
+
73
+ # fail-safe: index opsional
74
+ if index_path and not os.path.exists(index_path):
75
+ index_path = None
76
+
77
+ if pth_path is None:
78
+ update_status(job_dir, "error", 0, "Model .pth not found")
79
+ return
80
+
81
+ input_audio_path = os.path.join(job_dir, "input.wav")
82
+ output_audio_path = os.path.join(job_dir, "output.wav")
83
+
84
+ update_status(job_dir, "processing", 15, "Preparing RVC inference")
85
+
86
+ # ===== MAP SETTINGS → CLI ARGS =====
87
+ extra_args = []
88
+
89
+ # pitch
90
+ pitch = settings.get("pitch", 0)
91
+ if isinstance(pitch, (int, float)) and pitch != 0:
92
+ extra_args += ["-pi", str(int(pitch))]
93
+
94
+ # index rate
95
+ index_rate = settings.get("indexRate", 0.75)
96
+ extra_args += ["-ir", str(index_rate)]
97
+
98
+ # protect
99
+ protect = settings.get("protect", 0.33)
100
+ extra_args += ["-pr", str(protect)]
101
+
102
+ # RMS mix
103
+ rms_mix = settings.get("rmsMix", 1.0)
104
+ extra_args += ["-rm", str(rms_mix)]
105
+
106
+ # device
107
+ device = os.environ.get("RVC_DEVICE", settings.get("device", "cpu"))
108
+
109
+ update_status(job_dir, "processing", 35, f"Running RVC (device={device})")
110
+
111
+ # ===== RUN RVC (ONCE!) =====
112
+ try:
113
+ rc, out, err = run_rvc_cli(
114
+ input_audio_path,
115
+ output_audio_path,
116
+ pth_path,
117
+ index_path=index_path,
118
+ device=device,
119
+ extra_args=extra_args
120
+ )
121
+
122
+ if rc != 0:
123
+ update_status(
124
+ job_dir,
125
+ "error",
126
+ 0,
127
+ f"RVC failed (code={rc}): {err[:500]}"
128
+ )
129
+ print("[WORKER] RVC CLI failed:", err)
130
+ return
131
+
132
+ except Exception as e:
133
+ update_status(job_dir, "error", 0, str(e))
134
+ print("[WORKER] Exception during RVC run:", e)
135
+ return
136
+
137
+ update_status(job_dir, "processing", 85, "Saving output")
138
+
139
+ if not os.path.exists(output_audio_path):
140
+ update_status(job_dir, "error", 0, "output.wav missing after RVC")
141
+ return
142
+
143
+ update_status(job_dir, "done", 100, "Finished")
144
+ print(f"[WORKER] Job {job_id} done")
145
+
146
+ def main():
147
+ print("[WORKER] Started (RVC mode)")
148
+ while True:
149
+ for job_id in os.listdir(CREATIONS_DIR):
150
+ job_dir = os.path.join(CREATIONS_DIR, job_id)
151
+ status_path = os.path.join(job_dir, "status.json")
152
+ if not os.path.exists(status_path):
153
+ continue
154
+ with open(status_path) as f:
155
+ status = json.load(f)
156
+ if status.get("state") == "queued":
157
+ try:
158
+ run_job(job_id)
159
+ except Exception as e:
160
+ print(f"[ERROR] {job_id}: {e}")
161
+ update_status(job_dir, "error", 0, str(e))
162
+ time.sleep(1)
163
+
164
+ if __name__ == "__main__":
165
+ main()
worker/requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Core
2
+ numpy
3
+ soundfile
4
+ librosa
5
+ scipy
6
+
7
+ # RVC engine
8
+ rvc-python
9
+
10
+ # Torch
11
+ torch==2.1.1+cu118
12
+ torchaudio==2.1.1+cu118
13
+ --index-url https://download.pytorch.org/whl/cu118
worker/rvc_engine.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import librosa
2
+ import numpy as np
3
+
4
+ def process(audio, sr, settings):
5
+ pitch = settings.get("pitch", 0)
6
+
7
+ if pitch != 0:
8
+ audio = librosa.effects.pitch_shift(
9
+ audio,
10
+ sr=sr,
11
+ n_steps=pitch
12
+ )
13
+
14
+ # normalize
15
+ audio = audio / max(1e-6, np.max(np.abs(audio)))
16
+
17
+ return audio