support for novels and lot of formats for books
This commit is contained in:
578
desktop/package-lock.json
generated
578
desktop/package-lock.json
generated
@@ -17,6 +17,7 @@
|
|||||||
"cheerio": "^1.1.2",
|
"cheerio": "^1.1.2",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"electron-log": "^5.4.3",
|
"electron-log": "^5.4.3",
|
||||||
|
"epub": "^1.3.0",
|
||||||
"fastify": "^5.6.2",
|
"fastify": "^5.6.2",
|
||||||
"js-yaml": "^4.1.1",
|
"js-yaml": "^4.1.1",
|
||||||
"jsonwebtoken": "^9.0.3",
|
"jsonwebtoken": "^9.0.3",
|
||||||
@@ -2695,6 +2696,16 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/code-point-at": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
@@ -2897,7 +2908,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||||
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
|
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
@@ -3707,6 +3717,27 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/epub": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/epub/-/epub-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-6BL8gIitljkTf4HW52Ast6wenPTkMKllU28bRc5awVsT+xCaPl6nWSaqSmHbRgPrl1+5uekOPvOxy7DQzbhM8Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"adm-zip": "^0.4.11",
|
||||||
|
"xml2js": "^0.4.23"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"zipfile": "^0.5.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/epub/node_modules/adm-zip": {
|
||||||
|
"version": "0.4.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
|
||||||
|
"integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/err-code": {
|
"node_modules/err-code": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
|
||||||
@@ -4668,6 +4699,29 @@
|
|||||||
],
|
],
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/ignore-walk": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"minimatch": "^3.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ignore-walk/node_modules/minimatch": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/imurmurhash": {
|
"node_modules/imurmurhash": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
||||||
@@ -4790,6 +4844,13 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/isbinaryfile": {
|
"node_modules/isbinaryfile": {
|
||||||
"version": "5.0.7",
|
"version": "5.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz",
|
||||||
@@ -5420,12 +5481,60 @@
|
|||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/nan": {
|
||||||
|
"version": "2.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
|
||||||
|
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/napi-build-utils": {
|
"node_modules/napi-build-utils": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
|
||||||
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
|
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/needle": {
|
||||||
|
"version": "2.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz",
|
||||||
|
"integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^3.2.6",
|
||||||
|
"iconv-lite": "^0.4.4",
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"needle": "bin/needle"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.4.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/needle/node_modules/debug": {
|
||||||
|
"version": "3.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||||
|
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/needle/node_modules/iconv-lite": {
|
||||||
|
"version": "0.4.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
|
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/negotiator": {
|
"node_modules/negotiator": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
||||||
@@ -5577,6 +5686,348 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-pre-gyp": {
|
||||||
|
"version": "0.10.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz",
|
||||||
|
"integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==",
|
||||||
|
"deprecated": "Please upgrade to @mapbox/node-pre-gyp: the non-scoped node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in the future",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"detect-libc": "^1.0.2",
|
||||||
|
"mkdirp": "^0.5.1",
|
||||||
|
"needle": "^2.2.1",
|
||||||
|
"nopt": "^4.0.1",
|
||||||
|
"npm-packlist": "^1.1.6",
|
||||||
|
"npmlog": "^4.0.2",
|
||||||
|
"rc": "^1.2.7",
|
||||||
|
"rimraf": "^2.6.1",
|
||||||
|
"semver": "^5.3.0",
|
||||||
|
"tar": "^4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"node-pre-gyp": "bin/node-pre-gyp"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/abbrev": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/ansi-regex": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/aproba": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/are-we-there-yet": {
|
||||||
|
"version": "1.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
|
||||||
|
"integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"delegates": "^1.0.0",
|
||||||
|
"readable-stream": "^2.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/chownr": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/detect-libc": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"bin": {
|
||||||
|
"detect-libc": "bin/detect-libc.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/fs-minipass": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^2.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/gauge": {
|
||||||
|
"version": "2.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
|
||||||
|
"integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"aproba": "^1.0.3",
|
||||||
|
"console-control-strings": "^1.0.0",
|
||||||
|
"has-unicode": "^2.0.0",
|
||||||
|
"object-assign": "^4.1.0",
|
||||||
|
"signal-exit": "^3.0.0",
|
||||||
|
"string-width": "^1.0.1",
|
||||||
|
"strip-ansi": "^3.0.1",
|
||||||
|
"wide-align": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/is-fullwidth-code-point": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"number-is-nan": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/minimatch": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/minipass": {
|
||||||
|
"version": "2.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
|
||||||
|
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"yallist": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/minizlib": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^2.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/mkdirp": {
|
||||||
|
"version": "0.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"minimist": "^1.2.6"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"mkdirp": "bin/cmd.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/nopt": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"abbrev": "1",
|
||||||
|
"osenv": "^0.1.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"nopt": "bin/nopt.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/npmlog": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"are-we-there-yet": "~1.1.2",
|
||||||
|
"console-control-strings": "~1.1.0",
|
||||||
|
"gauge": "~2.7.3",
|
||||||
|
"set-blocking": "~2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/readable-stream": {
|
||||||
|
"version": "2.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||||
|
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.3",
|
||||||
|
"isarray": "~1.0.0",
|
||||||
|
"process-nextick-args": "~2.0.0",
|
||||||
|
"safe-buffer": "~5.1.1",
|
||||||
|
"string_decoder": "~1.1.1",
|
||||||
|
"util-deprecate": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/readable-stream/node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/rimraf": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||||
|
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rimraf": "bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/semver": {
|
||||||
|
"version": "5.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||||
|
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/signal-exit": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/string_decoder": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/string_decoder/node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/string-width": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"code-point-at": "^1.0.0",
|
||||||
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
|
"strip-ansi": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/strip-ansi": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/tar": {
|
||||||
|
"version": "4.4.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz",
|
||||||
|
"integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"chownr": "^1.1.4",
|
||||||
|
"fs-minipass": "^1.2.7",
|
||||||
|
"minipass": "^2.9.0",
|
||||||
|
"minizlib": "^1.3.3",
|
||||||
|
"mkdirp": "^0.5.5",
|
||||||
|
"safe-buffer": "^5.2.1",
|
||||||
|
"yallist": "^3.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/yallist": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/nopt": {
|
"node_modules/nopt": {
|
||||||
"version": "9.0.0",
|
"version": "9.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz",
|
||||||
@@ -5606,6 +6057,35 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/npm-bundled": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"npm-normalize-package-bin": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/npm-normalize-package-bin": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/npm-packlist": {
|
||||||
|
"version": "1.4.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
|
||||||
|
"integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ignore-walk": "^3.0.1",
|
||||||
|
"npm-bundled": "^1.0.1",
|
||||||
|
"npm-normalize-package-bin": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/npmlog": {
|
"node_modules/npmlog": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
|
||||||
@@ -5635,6 +6115,26 @@
|
|||||||
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/number-is-nan": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-keys": {
|
"node_modules/object-keys": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
||||||
@@ -5727,6 +6227,38 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/os-homedir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/os-tmpdir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/osenv": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
|
||||||
|
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"os-homedir": "^1.0.0",
|
||||||
|
"os-tmpdir": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/p-cancelable": {
|
"node_modules/p-cancelable": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
|
||||||
@@ -6044,6 +6576,13 @@
|
|||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^20.17.0 || >=22.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/process-nextick-args": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/process-warning": {
|
"node_modules/process-warning": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
|
||||||
@@ -6433,7 +6972,6 @@
|
|||||||
"version": "1.4.3",
|
"version": "1.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz",
|
||||||
"integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==",
|
"integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "BlueOak-1.0.0"
|
"license": "BlueOak-1.0.0"
|
||||||
},
|
},
|
||||||
"node_modules/secure-json-parse": {
|
"node_modules/secure-json-parse": {
|
||||||
@@ -7938,6 +8476,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xml2js": {
|
||||||
|
"version": "0.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
|
||||||
|
"integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"sax": ">=0.6.0",
|
||||||
|
"xmlbuilder": "~11.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xml2js/node_modules/xmlbuilder": {
|
||||||
|
"version": "11.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||||
|
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/xmlbuilder": {
|
"node_modules/xmlbuilder": {
|
||||||
"version": "15.1.1",
|
"version": "15.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
|
||||||
@@ -8071,6 +8631,20 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zipfile": {
|
||||||
|
"version": "0.5.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/zipfile/-/zipfile-0.5.12.tgz",
|
||||||
|
"integrity": "sha512-zA60gW+XgQBu/Q4qV3BCXNIDRald6Xi5UOPj3jWGlnkjmBHaKDwIz7kyXWV3kq7VEsQN/2t/IWjdXdKeVNm6Eg==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"nan": "~2.10.0",
|
||||||
|
"node-pre-gyp": "~0.10.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"unzip.js": "bin/unzip.js"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"cheerio": "^1.1.2",
|
"cheerio": "^1.1.2",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"electron-log": "^5.4.3",
|
"electron-log": "^5.4.3",
|
||||||
|
"epub": "^1.3.0",
|
||||||
"fastify": "^5.6.2",
|
"fastify": "^5.6.2",
|
||||||
"js-yaml": "^4.1.1",
|
"js-yaml": "^4.1.1",
|
||||||
"jsonwebtoken": "^9.0.3",
|
"jsonwebtoken": "^9.0.3",
|
||||||
|
|||||||
@@ -1,21 +1,14 @@
|
|||||||
import { FastifyRequest, FastifyReply } from 'fastify';
|
import { FastifyRequest, FastifyReply } from 'fastify';
|
||||||
import { getConfig as loadConfig, setConfig as saveConfig } from '../../shared/config.js';
|
import { getConfig as loadConfig } from '../../shared/config.js';
|
||||||
import { queryOne, queryAll, run } from '../../shared/database.js';
|
import { queryOne, queryAll, run } from '../../shared/database.js';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { PathLike } from "node:fs";
|
import { PathLike } from "node:fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import {getAnimeById, searchAnimeLocal} from "../anime/anime.service";
|
import {getAnimeById, searchAnimeLocal} from "../anime/anime.service";
|
||||||
import {getBookById, searchBooksAniList, searchBooksLocal} from "../books/books.service";
|
import {getBookById, searchBooksAniList} from "../books/books.service";
|
||||||
import AdmZip from 'adm-zip';
|
import AdmZip from 'adm-zip';
|
||||||
|
import EPub from 'epub';
|
||||||
type SetConfigBody = {
|
|
||||||
library?: {
|
|
||||||
anime?: string | null;
|
|
||||||
manga?: string | null;
|
|
||||||
novels?: string | null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type ScanQuery = {
|
type ScanQuery = {
|
||||||
mode?: 'full' | 'incremental';
|
mode?: 'full' | 'incremental';
|
||||||
@@ -37,10 +30,21 @@ async function resolveEntryMetadata(entry: any, type: string) {
|
|||||||
? await searchAnimeLocal(query)
|
? await searchAnimeLocal(query)
|
||||||
: await searchBooksAniList(query);
|
: await searchBooksAniList(query);
|
||||||
|
|
||||||
const first = results?.[0];
|
let picked = null;
|
||||||
|
|
||||||
if (first?.id) {
|
if (type !== 'anime' && Array.isArray(results)) {
|
||||||
matchedId = first.id;
|
console.log(type)
|
||||||
|
if (entry.type === 'novels') {
|
||||||
|
picked = results.find(r => r.format === 'NOVEL');
|
||||||
|
} else if (entry.type === 'manga') {
|
||||||
|
picked = results.find(r => r.format !== 'NOVEL');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
picked ??= results?.[0];
|
||||||
|
|
||||||
|
if (picked?.id) {
|
||||||
|
matchedId = picked.id;
|
||||||
|
|
||||||
await run(
|
await run(
|
||||||
`UPDATE local_entries
|
`UPDATE local_entries
|
||||||
@@ -104,9 +108,13 @@ export async function scanLibrary(request: FastifyRequest<{ Querystring: ScanQue
|
|||||||
}
|
}
|
||||||
|
|
||||||
const files = fs.readdirSync(fullPath, { withFileTypes: true })
|
const files = fs.readdirSync(fullPath, { withFileTypes: true })
|
||||||
.filter(f => f.isFile())
|
.filter(f =>
|
||||||
|
f.isFile() ||
|
||||||
|
(type === 'manga' && f.isDirectory())
|
||||||
|
)
|
||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
|
|
||||||
let unit = 1;
|
let unit = 1;
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
@@ -191,27 +199,30 @@ export async function streamUnit(request: FastifyRequest, reply: FastifyReply) {
|
|||||||
|
|
||||||
const parts = range.replace(/bytes=/, '').split('-');
|
const parts = range.replace(/bytes=/, '').split('-');
|
||||||
const start = Number(parts[0]);
|
const start = Number(parts[0]);
|
||||||
let end = parts[1] ? Number(parts[1]) : stat.size - 1;
|
const end = parts[1] ? Number(parts[1]) : stat.size - 1;
|
||||||
|
|
||||||
|
// Validate range values
|
||||||
if (
|
if (
|
||||||
Number.isNaN(start) ||
|
Number.isNaN(start) ||
|
||||||
Number.isNaN(end) ||
|
Number.isNaN(end) ||
|
||||||
start < 0 ||
|
start < 0 ||
|
||||||
|
start >= stat.size ||
|
||||||
end < start ||
|
end < start ||
|
||||||
end >= stat.size
|
end >= stat.size
|
||||||
) {
|
) {
|
||||||
end = stat.size - 1;
|
return reply.status(416).send({ error: 'INVALID_RANGE' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const contentLength = end - start + 1;
|
||||||
|
|
||||||
reply
|
reply
|
||||||
.status(206)
|
.status(206)
|
||||||
.header('Content-Range', `bytes ${start}-${end}/${stat.size}`)
|
.header('Content-Range', `bytes ${start}-${end}/${stat.size}`)
|
||||||
.header('Accept-Ranges', 'bytes')
|
.header('Accept-Ranges', 'bytes')
|
||||||
.header('Content-Length', end - start + 1)
|
.header('Content-Length', contentLength)
|
||||||
.header('Content-Type', 'video/mp4');
|
.header('Content-Type', 'video/mp4');
|
||||||
|
|
||||||
return fs.createReadStream(file.file_path, { start, end });
|
return fs.createReadStream(file.file_path, { start, end });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type MatchBody = {
|
type MatchBody = {
|
||||||
@@ -247,17 +258,13 @@ export async function matchEntry(
|
|||||||
return { status: 'OK', matched: !!matched_id };
|
return { status: 'OK', matched: !!matched_id };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUnits(
|
export async function getUnits(request: FastifyRequest<{ Params: Params }>, reply: FastifyReply) {
|
||||||
request: FastifyRequest<{ Params: Params }>,
|
|
||||||
reply: FastifyReply
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
const { type, id } = request.params as { type: string, id: string };
|
const { id } = request.params as { id: string };
|
||||||
|
|
||||||
// Buscar la entrada por matched_id
|
|
||||||
const entry = await queryOne(
|
const entry = await queryOne(
|
||||||
`SELECT id, type, matched_id FROM local_entries WHERE matched_id = ? AND type = ?`,
|
`SELECT id, type, matched_id FROM local_entries WHERE matched_id = ?`,
|
||||||
[Number(id), type],
|
[Number(id)],
|
||||||
'local_library'
|
'local_library'
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -274,24 +281,82 @@ export async function getUnits(
|
|||||||
'local_library'
|
'local_library'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Formatear la respuesta según el tipo
|
const MANGA_IMAGE_EXTS = ['.jpg', '.jpeg', '.png', '.webp'];
|
||||||
const units = files.map((file: any) => {
|
const MANGA_ARCHIVES = ['.cbz', '.cbr', '.zip'];
|
||||||
const fileName = path.basename(file.file_path);
|
|
||||||
const fileExt = path.extname(file.file_path).toLowerCase();
|
|
||||||
|
|
||||||
// Detectar si es un archivo comprimido (capítulo único) o carpeta
|
const NOVEL_EXTS = ['.epub', '.pdf', '.txt', '.md', '.docx', '.mobi'];
|
||||||
const isDirectory = fs.existsSync(file.file_path) &&
|
|
||||||
|
function isImageFolder(folderPath: string): boolean {
|
||||||
|
if (!fs.existsSync(folderPath)) return false;
|
||||||
|
if (!fs.statSync(folderPath).isDirectory()) return false;
|
||||||
|
|
||||||
|
const files = fs.readdirSync(folderPath);
|
||||||
|
return files.some(f => MANGA_IMAGE_EXTS.includes(path.extname(f).toLowerCase()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const units = files
|
||||||
|
.map((file: any) => {
|
||||||
|
const fileExt = path.extname(file.file_path).toLowerCase();
|
||||||
|
const isDir = fs.existsSync(file.file_path) &&
|
||||||
fs.statSync(file.file_path).isDirectory();
|
fs.statSync(file.file_path).isDirectory();
|
||||||
|
|
||||||
|
// ===== MANGA =====
|
||||||
|
if (entry.type === 'manga') {
|
||||||
|
if (MANGA_ARCHIVES.includes(fileExt)) {
|
||||||
return {
|
return {
|
||||||
id: file.id,
|
id: file.id,
|
||||||
number: file.unit_number,
|
number: file.unit_number,
|
||||||
name: fileName,
|
name: path.basename(file.file_path),
|
||||||
type: type === 'anime' ? 'episode' : 'chapter',
|
type: 'chapter',
|
||||||
format: fileExt === '.cbz' ? 'cbz' : 'file',
|
format: fileExt.replace('.', ''),
|
||||||
path: file.file_path
|
path: file.file_path
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
|
if (isDir && isImageFolder(file.file_path)) {
|
||||||
|
return {
|
||||||
|
id: file.id,
|
||||||
|
number: file.unit_number,
|
||||||
|
name: path.basename(file.file_path),
|
||||||
|
type: 'chapter',
|
||||||
|
format: 'folder',
|
||||||
|
path: file.file_path
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.type === 'novels') {
|
||||||
|
if (NOVEL_EXTS.includes(fileExt)) {
|
||||||
|
return {
|
||||||
|
id: file.id,
|
||||||
|
number: file.unit_number,
|
||||||
|
name: path.basename(file.file_path),
|
||||||
|
type: 'chapter',
|
||||||
|
format: fileExt.replace('.', ''),
|
||||||
|
path: file.file_path
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.type === 'anime') {
|
||||||
|
return {
|
||||||
|
id: file.id,
|
||||||
|
number: file.unit_number,
|
||||||
|
name: path.basename(file.file_path),
|
||||||
|
type: 'episode',
|
||||||
|
format: fileExt.replace('.', ''),
|
||||||
|
path: file.file_path
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entry_id: entry.id,
|
entry_id: entry.id,
|
||||||
@@ -306,7 +371,7 @@ export async function getUnits(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCbzPages(request: FastifyRequest, reply: FastifyReply) {
|
export async function getManifest(request: FastifyRequest, reply: FastifyReply) {
|
||||||
const { unitId } = request.params as any;
|
const { unitId } = request.params as any;
|
||||||
|
|
||||||
const file = await queryOne(
|
const file = await queryOne(
|
||||||
@@ -319,20 +384,73 @@ export async function getCbzPages(request: FastifyRequest, reply: FastifyReply)
|
|||||||
return reply.status(404).send({ error: 'FILE_NOT_FOUND' });
|
return reply.status(404).send({ error: 'FILE_NOT_FOUND' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ext = path.extname(file.file_path).toLowerCase();
|
||||||
|
|
||||||
|
// ===== MANGA =====
|
||||||
|
if (['.cbz', '.cbr', '.zip'].includes(ext)) {
|
||||||
const zip = new AdmZip(file.file_path);
|
const zip = new AdmZip(file.file_path);
|
||||||
|
|
||||||
const pages = zip.getEntries()
|
const pages = zip.getEntries()
|
||||||
.filter(e => !e.isDirectory && /\.(jpg|jpeg|png|webp)$/i.test(e.entryName))
|
.filter(e => !e.isDirectory && /\.(jpg|jpeg|png|webp)$/i.test(e.entryName))
|
||||||
.sort((a, b) => a.entryName.localeCompare(b.entryName, undefined, { numeric: true }))
|
.sort((a, b) => a.entryName.localeCompare(b.entryName, undefined, { numeric: true }))
|
||||||
.map((_, i) =>
|
.map((_, i) => ({
|
||||||
`/api/library/manga/cbz/${unitId}/page/${i}`
|
id: i,
|
||||||
);
|
url: `/api/library/${unitId}/resource/${i}`
|
||||||
|
}));
|
||||||
|
|
||||||
return { pages };
|
return {
|
||||||
|
type: 'manga',
|
||||||
|
format: 'archive',
|
||||||
|
pages
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCbzPage(request: FastifyRequest, reply: FastifyReply) {
|
if (fs.statSync(file.file_path).isDirectory()) {
|
||||||
const { unitId, page } = request.params as any;
|
const pages = fs.readdirSync(file.file_path)
|
||||||
|
.filter(f => /\.(jpg|jpeg|png|webp)$/i.test(f))
|
||||||
|
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
|
||||||
|
.map((_, i) => ({
|
||||||
|
id: i,
|
||||||
|
url: `/api/library/${unitId}/resource/${i}`
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'manga',
|
||||||
|
format: 'folder',
|
||||||
|
pages
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== NOVEL =====
|
||||||
|
if (ext === '.epub') {
|
||||||
|
return {
|
||||||
|
type: 'ln',
|
||||||
|
format: 'epub',
|
||||||
|
url: `/api/library/${unitId}/resource/epub`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['.txt', '.md'].includes(ext)) {
|
||||||
|
return {
|
||||||
|
type: 'ln',
|
||||||
|
format: 'text',
|
||||||
|
url: `/api/library/${unitId}/resource/text`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ext === '.pdf') {
|
||||||
|
return {
|
||||||
|
type: 'ln',
|
||||||
|
format: 'pdf',
|
||||||
|
url: `/api/library/${unitId}/resource/pdf`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.status(400).send({ error: 'UNSUPPORTED_FORMAT' });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPage(request: FastifyRequest, reply: FastifyReply) {
|
||||||
|
const { unitId, resId } = request.params as any;
|
||||||
|
|
||||||
const file = await queryOne(
|
const file = await queryOne(
|
||||||
`SELECT file_path FROM local_files WHERE id = ?`,
|
`SELECT file_path FROM local_files WHERE id = ?`,
|
||||||
@@ -342,16 +460,83 @@ export async function getCbzPage(request: FastifyRequest, reply: FastifyReply) {
|
|||||||
|
|
||||||
if (!file) return reply.status(404).send();
|
if (!file) return reply.status(404).send();
|
||||||
|
|
||||||
const zip = new AdmZip(file.file_path);
|
const ext = path.extname(file.file_path).toLowerCase();
|
||||||
|
|
||||||
|
// ===== CBZ PAGE =====
|
||||||
|
if (['.cbz', '.zip', '.cbr'].includes(ext)) {
|
||||||
|
const zip = new AdmZip(file.file_path);
|
||||||
const images = zip.getEntries()
|
const images = zip.getEntries()
|
||||||
.filter(e => !e.isDirectory && /\.(jpg|jpeg|png|webp)$/i.test(e.entryName))
|
.filter(e => !e.isDirectory && /\.(jpg|jpeg|png|webp)$/i.test(e.entryName))
|
||||||
.sort((a, b) => a.entryName.localeCompare(b.entryName, undefined, { numeric: true }));
|
.sort((a, b) => a.entryName.localeCompare(b.entryName, undefined, { numeric: true }));
|
||||||
|
|
||||||
const entry = images[page];
|
const entry = images[Number(resId)];
|
||||||
if (!entry) return reply.status(404).send();
|
if (!entry) return reply.status(404).send();
|
||||||
|
|
||||||
reply
|
return reply
|
||||||
.header('Content-Type', 'image/jpeg')
|
.header('Content-Type', 'image/jpeg')
|
||||||
.send(entry.getData());
|
.send(entry.getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fs.statSync(file.file_path).isDirectory()) {
|
||||||
|
const images = fs.readdirSync(file.file_path)
|
||||||
|
.filter(f => /\.(jpg|jpeg|png|webp)$/i.test(f))
|
||||||
|
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
|
||||||
|
|
||||||
|
const img = images[Number(resId)];
|
||||||
|
if (!img) return reply.status(404).send();
|
||||||
|
|
||||||
|
const imgPath = path.join(file.file_path, img);
|
||||||
|
const stat = fs.statSync(imgPath);
|
||||||
|
|
||||||
|
reply
|
||||||
|
.header('Content-Length', stat.size)
|
||||||
|
.header('Content-Type', 'image/jpeg');
|
||||||
|
|
||||||
|
return fs.createReadStream(imgPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ext === '.epub') {
|
||||||
|
const html = await parseEpubToHtml(file.file_path);
|
||||||
|
|
||||||
|
return reply
|
||||||
|
.header('Content-Type', 'text/html; charset=utf-8')
|
||||||
|
.send(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== TXT / MD =====
|
||||||
|
if (['.txt', '.md'].includes(ext)) {
|
||||||
|
const text = fs.readFileSync(file.file_path, 'utf8');
|
||||||
|
|
||||||
|
return reply
|
||||||
|
.header('Content-Type', 'text/html; charset=utf-8')
|
||||||
|
.send(`<div class="ln-content"><pre>${text}</pre></div>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.status(400).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseEpubToHtml(filePath: string): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const epub = new EPub(filePath);
|
||||||
|
|
||||||
|
epub.on('end', async () => {
|
||||||
|
let html = '';
|
||||||
|
|
||||||
|
for (const id of epub.flow.map(f => f.id)) {
|
||||||
|
const chapter = await new Promise<string>((res, rej) => {
|
||||||
|
epub.getChapter(id, (err, text) => {
|
||||||
|
if (err) rej(err);
|
||||||
|
else res(text);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
html += `<section class="ln-chapter">${chapter}</section>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(html);
|
||||||
|
});
|
||||||
|
|
||||||
|
epub.on('error', reject);
|
||||||
|
epub.parse();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ async function localRoutes(fastify: FastifyInstance) {
|
|||||||
fastify.get('/library/:type/:id', controller.getEntry);
|
fastify.get('/library/:type/:id', controller.getEntry);
|
||||||
fastify.get('/library/stream/:type/:id/:unit', controller.streamUnit);
|
fastify.get('/library/stream/:type/:id/:unit', controller.streamUnit);
|
||||||
fastify.post('/library/:type/:id/match', controller.matchEntry);
|
fastify.post('/library/:type/:id/match', controller.matchEntry);
|
||||||
fastify.get('/library/:type/:id/units', controller.getUnits);
|
fastify.get('/library/:id/units', controller.getUnits);
|
||||||
fastify.get('/library/:type/cbz/:unitId/pages', controller.getCbzPages);
|
fastify.get('/library/:unitId/manifest', controller.getManifest);
|
||||||
fastify.get('/library/:type/cbz/:unitId/page/:page', controller.getCbzPage);
|
fastify.get('/library/:unitId/resource/:resId', controller.getPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default localRoutes;
|
export default localRoutes;
|
||||||
@@ -32,12 +32,12 @@ let localEntryId = null;
|
|||||||
async function checkLocal() {
|
async function checkLocal() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/library/anime/${animeId}`);
|
const res = await fetch(`/api/library/anime/${animeId}`);
|
||||||
if (!res.ok) return;
|
if (!res.ok) return null;
|
||||||
|
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
localEntryId = data.id; // ← ID interna
|
return data.id;
|
||||||
|
} catch {
|
||||||
} catch {}
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadAniSkip(malId, episode, duration) {
|
async function loadAniSkip(malId, episode, duration) {
|
||||||
@@ -53,7 +53,7 @@ async function loadAniSkip(malId, episode, duration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadMetadata() {
|
async function loadMetadata() {
|
||||||
checkLocal();
|
localEntryId = await checkLocal();
|
||||||
try {
|
try {
|
||||||
const sourceQuery = (extName === 'local' || !extName) ? "source=anilist" : `source=${extName}`;
|
const sourceQuery = (extName === 'local' || !extName) ? "source=anilist" : `source=${extName}`;
|
||||||
const res = await fetch(`/api/anime/${animeId}?${sourceQuery}`);
|
const res = await fetch(`/api/anime/${animeId}?${sourceQuery}`);
|
||||||
@@ -133,6 +133,7 @@ async function loadMetadata() {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading metadata:', error);
|
console.error('Error loading metadata:', error);
|
||||||
}
|
}
|
||||||
|
await loadExtensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function applyAniSkip(video) {
|
async function applyAniSkip(video) {
|
||||||
@@ -484,4 +485,3 @@ setInterval(() => {
|
|||||||
}, 60000);
|
}, 60000);
|
||||||
|
|
||||||
loadMetadata();
|
loadMetadata();
|
||||||
loadExtensions();
|
|
||||||
@@ -18,7 +18,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
async function checkLocalLibraryEntry() {
|
async function checkLocalLibraryEntry() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/library/manga/${bookId}`);
|
const libraryType =
|
||||||
|
bookData?.entry_type === 'NOVEL' ? 'novels' : 'manga';
|
||||||
|
|
||||||
|
const res = await fetch(`/api/library/${libraryType}/${bookId}`);
|
||||||
if (!res.ok) return;
|
if (!res.ok) return;
|
||||||
|
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
@@ -38,18 +41,6 @@ async function checkLocalLibraryEntry() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function markAsLocal() {
|
|
||||||
isLocal = true;
|
|
||||||
const pill = document.getElementById('local-pill');
|
|
||||||
if (pill) {
|
|
||||||
pill.textContent = 'Local';
|
|
||||||
pill.style.display = 'inline-flex';
|
|
||||||
pill.style.background = 'rgba(34, 197, 94, 0.2)';
|
|
||||||
pill.style.color = '#22c55e';
|
|
||||||
pill.style.borderColor = 'rgba(34, 197, 94, 0.3)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
try {
|
try {
|
||||||
const urlData = URLUtils.parseEntityPath('book');
|
const urlData = URLUtils.parseEntityPath('book');
|
||||||
@@ -61,8 +52,8 @@ async function init() {
|
|||||||
extensionName = urlData.extensionName;
|
extensionName = urlData.extensionName;
|
||||||
bookId = urlData.entityId;
|
bookId = urlData.entityId;
|
||||||
bookSlug = urlData.slug;
|
bookSlug = urlData.slug;
|
||||||
await checkLocalLibraryEntry();
|
|
||||||
await loadBookMetadata();
|
await loadBookMetadata();
|
||||||
|
await checkLocalLibraryEntry();
|
||||||
|
|
||||||
await loadAvailableExtensions();
|
await loadAvailableExtensions();
|
||||||
await loadChapters();
|
await loadChapters();
|
||||||
@@ -220,7 +211,7 @@ async function loadChapters(targetProvider = null) {
|
|||||||
|
|
||||||
if (isLocalRequest) {
|
if (isLocalRequest) {
|
||||||
// Nuevo endpoint para archivos locales
|
// Nuevo endpoint para archivos locales
|
||||||
fetchUrl = `/api/library/manga/${bookId}/units`;
|
fetchUrl = `/api/library/${bookId}/units`;
|
||||||
} else {
|
} else {
|
||||||
const source = extensionName || 'anilist';
|
const source = extensionName || 'anilist';
|
||||||
fetchUrl = `/api/book/${bookId}/chapters?source=${source}`;
|
fetchUrl = `/api/book/${bookId}/chapters?source=${source}`;
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ async function loadChapter() {
|
|||||||
let newEndpoint;
|
let newEndpoint;
|
||||||
|
|
||||||
if (provider === 'local') {
|
if (provider === 'local') {
|
||||||
newEndpoint = `/api/library/manga/${bookId}/units`;
|
newEndpoint = `/api/library/${bookId}/units`;
|
||||||
} else {
|
} else {
|
||||||
newEndpoint = `/api/book/${bookId}/${chapter}/${provider}?source=${source}`;
|
newEndpoint = `/api/book/${bookId}/${chapter}/${provider}?source=${source}`;
|
||||||
}
|
}
|
||||||
@@ -142,29 +142,39 @@ async function loadChapter() {
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (provider === 'local') {
|
if (provider === 'local') {
|
||||||
const unit = data.units[Number(chapter)];
|
const unit = data.units[Number(chapter)];
|
||||||
|
if (!unit) return;
|
||||||
|
|
||||||
if (!unit) {
|
chapterLabel.textContent = unit.name;
|
||||||
reader.innerHTML = '<div class="loading-container"><span>Chapter not found</span></div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unit.format === 'cbz') {
|
|
||||||
chapterLabel.textContent = unit.name; // ✅
|
|
||||||
document.title = unit.name;
|
document.title = unit.name;
|
||||||
const pagesRes = await fetch(
|
|
||||||
`/api/library/manga/cbz/${unit.id}/pages`
|
|
||||||
);
|
|
||||||
const pagesData = await pagesRes.json();
|
|
||||||
|
|
||||||
|
const manifestRes = await fetch(`/api/library/${unit.id}/manifest`);
|
||||||
|
const manifest = await manifestRes.json();
|
||||||
|
|
||||||
|
reader.innerHTML = '';
|
||||||
|
|
||||||
|
// ===== MANGA =====
|
||||||
|
if (manifest.type === 'manga') {
|
||||||
currentType = 'manga';
|
currentType = 'manga';
|
||||||
updateSettingsVisibility();
|
updateSettingsVisibility();
|
||||||
applyStyles();
|
applyStyles();
|
||||||
|
|
||||||
currentPages = pagesData.pages.map(url => ({ url }));
|
currentPages = manifest.pages;
|
||||||
reader.innerHTML = '';
|
|
||||||
loadManga(currentPages);
|
loadManga(currentPages);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== LN =====
|
||||||
|
if (manifest.type === 'ln') {
|
||||||
|
currentType = 'ln';
|
||||||
|
updateSettingsVisibility();
|
||||||
|
applyStyles();
|
||||||
|
|
||||||
|
const contentRes = await fetch(manifest.url);
|
||||||
|
const html = await contentRes.text();
|
||||||
|
|
||||||
|
loadLN(html);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -205,13 +215,8 @@ async function loadChapter() {
|
|||||||
reader.innerHTML = '';
|
reader.innerHTML = '';
|
||||||
|
|
||||||
if (data.type === 'manga') {
|
if (data.type === 'manga') {
|
||||||
if (provider === 'local' && data.format === 'cbz') {
|
|
||||||
currentPages = data.pages.map(url => ({ url }));
|
|
||||||
loadManga(currentPages);
|
|
||||||
} else {
|
|
||||||
currentPages = data.pages || [];
|
currentPages = data.pages || [];
|
||||||
loadManga(currentPages);
|
loadManga(currentPages);
|
||||||
}
|
|
||||||
} else if (data.type === 'ln') {
|
} else if (data.type === 'ln') {
|
||||||
loadLN(data.content);
|
loadLN(data.content);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,21 +26,37 @@ async function loadLocalEntries() {
|
|||||||
grid.innerHTML = '<div class="skeleton-card"></div>'.repeat(6);
|
grid.innerHTML = '<div class="skeleton-card"></div>'.repeat(6);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Cambiado a endpoint de libros
|
const [mangaRes, novelRes] = await Promise.all([
|
||||||
const response = await fetch('/api/library/manga');
|
fetch('/api/library/manga'),
|
||||||
const entries = await response.json();
|
fetch('/api/library/novels')
|
||||||
localEntries = entries;
|
]);
|
||||||
|
|
||||||
if (entries.length === 0) {
|
const [manga, novel] = await Promise.all([
|
||||||
grid.innerHTML = '<p style="grid-column: 1/-1; text-align: center; color: var(--color-text-secondary); padding: 3rem;">No books found in your local library.</p>';
|
mangaRes.json(),
|
||||||
|
novelRes.json()
|
||||||
|
]);
|
||||||
|
|
||||||
|
localEntries = [
|
||||||
|
...manga.map(e => ({ ...e, type: 'manga' })),
|
||||||
|
...novel.map(e => ({ ...e, type: 'novel' }))
|
||||||
|
];
|
||||||
|
|
||||||
|
if (localEntries.length === 0) {
|
||||||
|
grid.innerHTML = '<p style="grid-column:1/-1;text-align:center;padding:3rem;">No books found.</p>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
renderLocalEntries(entries);
|
|
||||||
} catch (err) {
|
renderLocalEntries(localEntries);
|
||||||
grid.innerHTML = '<p style="grid-column: 1/-1; text-align: center; color: var(--color-danger); padding: 3rem;">Error loading local books.</p>';
|
} catch {
|
||||||
|
grid.innerHTML = '<p style="grid-column:1/-1;text-align:center;color:var(--color-danger);padding:3rem;">Error loading library.</p>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filterLocal(type) {
|
||||||
|
if (type === 'all') renderLocalEntries(localEntries);
|
||||||
|
else renderLocalEntries(localEntries.filter(e => e.type === type));
|
||||||
|
}
|
||||||
|
|
||||||
function renderLocalEntries(entries) {
|
function renderLocalEntries(entries) {
|
||||||
const grid = document.getElementById('local-entries-grid');
|
const grid = document.getElementById('local-entries-grid');
|
||||||
grid.innerHTML = entries.map(entry => {
|
grid.innerHTML = entries.map(entry => {
|
||||||
@@ -58,6 +74,7 @@ function renderLocalEntries(entries) {
|
|||||||
<p style="font-size: 0.85rem; color: var(--color-text-secondary); margin: 0;">
|
<p style="font-size: 0.85rem; color: var(--color-text-secondary); margin: 0;">
|
||||||
${chapters} Chapters
|
${chapters} Chapters
|
||||||
</p>
|
</p>
|
||||||
|
<div class="badge">${entry.type}</div>
|
||||||
<div class="match-status ${entry.matched ? 'status-linked' : 'status-unlinked'}">
|
<div class="match-status ${entry.matched ? 'status-linked' : 'status-unlinked'}">
|
||||||
${entry.matched ? '● Linked' : '○ Unlinked'}
|
${entry.matched ? '● Linked' : '○ Unlinked'}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
589
docker/package-lock.json
generated
589
docker/package-lock.json
generated
@@ -15,6 +15,7 @@
|
|||||||
"bindings": "^1.5.0",
|
"bindings": "^1.5.0",
|
||||||
"cheerio": "^1.1.2",
|
"cheerio": "^1.1.2",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
|
"epub": "^1.3.0",
|
||||||
"fastify": "^5.6.2",
|
"fastify": "^5.6.2",
|
||||||
"js-yaml": "^4.1.1",
|
"js-yaml": "^4.1.1",
|
||||||
"jsonwebtoken": "^9.0.3",
|
"jsonwebtoken": "^9.0.3",
|
||||||
@@ -874,6 +875,16 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/code-point-at": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
@@ -941,6 +952,13 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/core-util-is": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/create-require": {
|
"node_modules/create-require": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||||
@@ -1218,6 +1236,27 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/epub": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/epub/-/epub-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-6BL8gIitljkTf4HW52Ast6wenPTkMKllU28bRc5awVsT+xCaPl6nWSaqSmHbRgPrl1+5uekOPvOxy7DQzbhM8Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"adm-zip": "^0.4.11",
|
||||||
|
"xml2js": "^0.4.23"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"zipfile": "^0.5.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/epub/node_modules/adm-zip": {
|
||||||
|
"version": "0.4.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
|
||||||
|
"integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/err-code": {
|
"node_modules/err-code": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
|
||||||
@@ -1701,6 +1740,29 @@
|
|||||||
],
|
],
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/ignore-walk": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"minimatch": "^3.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ignore-walk/node_modules/minimatch": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/imurmurhash": {
|
"node_modules/imurmurhash": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
||||||
@@ -1787,6 +1849,13 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/isexe": {
|
"node_modules/isexe": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
@@ -2234,12 +2303,60 @@
|
|||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/nan": {
|
||||||
|
"version": "2.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
|
||||||
|
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/napi-build-utils": {
|
"node_modules/napi-build-utils": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
|
||||||
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
|
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/needle": {
|
||||||
|
"version": "2.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz",
|
||||||
|
"integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "^3.2.6",
|
||||||
|
"iconv-lite": "^0.4.4",
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"needle": "bin/needle"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.4.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/needle/node_modules/debug": {
|
||||||
|
"version": "3.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||||
|
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/needle/node_modules/iconv-lite": {
|
||||||
|
"version": "0.4.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
|
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/negotiator": {
|
"node_modules/negotiator": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
||||||
@@ -2392,6 +2509,348 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-pre-gyp": {
|
||||||
|
"version": "0.10.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz",
|
||||||
|
"integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==",
|
||||||
|
"deprecated": "Please upgrade to @mapbox/node-pre-gyp: the non-scoped node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in the future",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"detect-libc": "^1.0.2",
|
||||||
|
"mkdirp": "^0.5.1",
|
||||||
|
"needle": "^2.2.1",
|
||||||
|
"nopt": "^4.0.1",
|
||||||
|
"npm-packlist": "^1.1.6",
|
||||||
|
"npmlog": "^4.0.2",
|
||||||
|
"rc": "^1.2.7",
|
||||||
|
"rimraf": "^2.6.1",
|
||||||
|
"semver": "^5.3.0",
|
||||||
|
"tar": "^4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"node-pre-gyp": "bin/node-pre-gyp"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/abbrev": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/ansi-regex": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/aproba": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/are-we-there-yet": {
|
||||||
|
"version": "1.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
|
||||||
|
"integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"delegates": "^1.0.0",
|
||||||
|
"readable-stream": "^2.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/chownr": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/detect-libc": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"bin": {
|
||||||
|
"detect-libc": "bin/detect-libc.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/fs-minipass": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^2.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/gauge": {
|
||||||
|
"version": "2.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
|
||||||
|
"integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"aproba": "^1.0.3",
|
||||||
|
"console-control-strings": "^1.0.0",
|
||||||
|
"has-unicode": "^2.0.0",
|
||||||
|
"object-assign": "^4.1.0",
|
||||||
|
"signal-exit": "^3.0.0",
|
||||||
|
"string-width": "^1.0.1",
|
||||||
|
"strip-ansi": "^3.0.1",
|
||||||
|
"wide-align": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/is-fullwidth-code-point": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"number-is-nan": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/minimatch": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/minipass": {
|
||||||
|
"version": "2.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
|
||||||
|
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"yallist": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/minizlib": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^2.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/mkdirp": {
|
||||||
|
"version": "0.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"minimist": "^1.2.6"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"mkdirp": "bin/cmd.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/nopt": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"abbrev": "1",
|
||||||
|
"osenv": "^0.1.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"nopt": "bin/nopt.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/npmlog": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"are-we-there-yet": "~1.1.2",
|
||||||
|
"console-control-strings": "~1.1.0",
|
||||||
|
"gauge": "~2.7.3",
|
||||||
|
"set-blocking": "~2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/readable-stream": {
|
||||||
|
"version": "2.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||||
|
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.3",
|
||||||
|
"isarray": "~1.0.0",
|
||||||
|
"process-nextick-args": "~2.0.0",
|
||||||
|
"safe-buffer": "~5.1.1",
|
||||||
|
"string_decoder": "~1.1.1",
|
||||||
|
"util-deprecate": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/readable-stream/node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/rimraf": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||||
|
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rimraf": "bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/semver": {
|
||||||
|
"version": "5.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||||
|
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/signal-exit": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/string_decoder": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/string_decoder/node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/string-width": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"code-point-at": "^1.0.0",
|
||||||
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
|
"strip-ansi": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/strip-ansi": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/tar": {
|
||||||
|
"version": "4.4.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz",
|
||||||
|
"integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"chownr": "^1.1.4",
|
||||||
|
"fs-minipass": "^1.2.7",
|
||||||
|
"minipass": "^2.9.0",
|
||||||
|
"minizlib": "^1.3.3",
|
||||||
|
"mkdirp": "^0.5.5",
|
||||||
|
"safe-buffer": "^5.2.1",
|
||||||
|
"yallist": "^3.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-pre-gyp/node_modules/yallist": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/nopt": {
|
"node_modules/nopt": {
|
||||||
"version": "9.0.0",
|
"version": "9.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz",
|
||||||
@@ -2408,6 +2867,35 @@
|
|||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^20.17.0 || >=22.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/npm-bundled": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"npm-normalize-package-bin": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/npm-normalize-package-bin": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/npm-packlist": {
|
||||||
|
"version": "1.4.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
|
||||||
|
"integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ignore-walk": "^3.0.1",
|
||||||
|
"npm-bundled": "^1.0.1",
|
||||||
|
"npm-normalize-package-bin": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/npmlog": {
|
"node_modules/npmlog": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
|
||||||
@@ -2437,6 +2925,26 @@
|
|||||||
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/number-is-nan": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/on-exit-leak-free": {
|
"node_modules/on-exit-leak-free": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
|
||||||
@@ -2455,6 +2963,38 @@
|
|||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/os-homedir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/os-tmpdir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/osenv": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
|
||||||
|
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"license": "ISC",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"os-homedir": "^1.0.0",
|
||||||
|
"os-tmpdir": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/p-map": {
|
"node_modules/p-map": {
|
||||||
"version": "7.0.4",
|
"version": "7.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz",
|
||||||
@@ -2672,6 +3212,13 @@
|
|||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^20.17.0 || >=22.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/process-nextick-args": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/process-warning": {
|
"node_modules/process-warning": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
|
||||||
@@ -2913,6 +3460,12 @@
|
|||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/sax": {
|
||||||
|
"version": "1.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz",
|
||||||
|
"integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==",
|
||||||
|
"license": "BlueOak-1.0.0"
|
||||||
|
},
|
||||||
"node_modules/secure-json-parse": {
|
"node_modules/secure-json-parse": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz",
|
||||||
@@ -4005,6 +4558,28 @@
|
|||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/xml2js": {
|
||||||
|
"version": "0.4.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
|
||||||
|
"integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"sax": ">=0.6.0",
|
||||||
|
"xmlbuilder": "~11.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xmlbuilder": {
|
||||||
|
"version": "11.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||||
|
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
@@ -4020,6 +4595,20 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zipfile": {
|
||||||
|
"version": "0.5.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/zipfile/-/zipfile-0.5.12.tgz",
|
||||||
|
"integrity": "sha512-zA60gW+XgQBu/Q4qV3BCXNIDRald6Xi5UOPj3jWGlnkjmBHaKDwIz7kyXWV3kq7VEsQN/2t/IWjdXdKeVNm6Eg==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"nan": "~2.10.0",
|
||||||
|
"node-pre-gyp": "~0.10.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"unzip.js": "bin/unzip.js"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"bindings": "^1.5.0",
|
"bindings": "^1.5.0",
|
||||||
"cheerio": "^1.1.2",
|
"cheerio": "^1.1.2",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
|
"epub": "^1.3.0",
|
||||||
"fastify": "^5.6.2",
|
"fastify": "^5.6.2",
|
||||||
"js-yaml": "^4.1.1",
|
"js-yaml": "^4.1.1",
|
||||||
"jsonwebtoken": "^9.0.3",
|
"jsonwebtoken": "^9.0.3",
|
||||||
|
|||||||
@@ -1,21 +1,14 @@
|
|||||||
import { FastifyRequest, FastifyReply } from 'fastify';
|
import { FastifyRequest, FastifyReply } from 'fastify';
|
||||||
import { getConfig as loadConfig, setConfig as saveConfig } from '../../shared/config.js';
|
import { getConfig as loadConfig } from '../../shared/config.js';
|
||||||
import { queryOne, queryAll, run } from '../../shared/database.js';
|
import { queryOne, queryAll, run } from '../../shared/database.js';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { PathLike } from "node:fs";
|
import { PathLike } from "node:fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import {getAnimeById, searchAnimeLocal} from "../anime/anime.service";
|
import {getAnimeById, searchAnimeLocal} from "../anime/anime.service";
|
||||||
import {getBookById, searchBooksAniList, searchBooksLocal} from "../books/books.service";
|
import {getBookById, searchBooksAniList} from "../books/books.service";
|
||||||
import AdmZip from 'adm-zip';
|
import AdmZip from 'adm-zip';
|
||||||
|
import EPub from 'epub';
|
||||||
type SetConfigBody = {
|
|
||||||
library?: {
|
|
||||||
anime?: string | null;
|
|
||||||
manga?: string | null;
|
|
||||||
novels?: string | null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type ScanQuery = {
|
type ScanQuery = {
|
||||||
mode?: 'full' | 'incremental';
|
mode?: 'full' | 'incremental';
|
||||||
@@ -37,10 +30,21 @@ async function resolveEntryMetadata(entry: any, type: string) {
|
|||||||
? await searchAnimeLocal(query)
|
? await searchAnimeLocal(query)
|
||||||
: await searchBooksAniList(query);
|
: await searchBooksAniList(query);
|
||||||
|
|
||||||
const first = results?.[0];
|
let picked = null;
|
||||||
|
|
||||||
if (first?.id) {
|
if (type !== 'anime' && Array.isArray(results)) {
|
||||||
matchedId = first.id;
|
console.log(type)
|
||||||
|
if (entry.type === 'novels') {
|
||||||
|
picked = results.find(r => r.format === 'NOVEL');
|
||||||
|
} else if (entry.type === 'manga') {
|
||||||
|
picked = results.find(r => r.format !== 'NOVEL');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
picked ??= results?.[0];
|
||||||
|
|
||||||
|
if (picked?.id) {
|
||||||
|
matchedId = picked.id;
|
||||||
|
|
||||||
await run(
|
await run(
|
||||||
`UPDATE local_entries
|
`UPDATE local_entries
|
||||||
@@ -104,9 +108,13 @@ export async function scanLibrary(request: FastifyRequest<{ Querystring: ScanQue
|
|||||||
}
|
}
|
||||||
|
|
||||||
const files = fs.readdirSync(fullPath, { withFileTypes: true })
|
const files = fs.readdirSync(fullPath, { withFileTypes: true })
|
||||||
.filter(f => f.isFile())
|
.filter(f =>
|
||||||
|
f.isFile() ||
|
||||||
|
(type === 'manga' && f.isDirectory())
|
||||||
|
)
|
||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
|
|
||||||
let unit = 1;
|
let unit = 1;
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
@@ -191,27 +199,30 @@ export async function streamUnit(request: FastifyRequest, reply: FastifyReply) {
|
|||||||
|
|
||||||
const parts = range.replace(/bytes=/, '').split('-');
|
const parts = range.replace(/bytes=/, '').split('-');
|
||||||
const start = Number(parts[0]);
|
const start = Number(parts[0]);
|
||||||
let end = parts[1] ? Number(parts[1]) : stat.size - 1;
|
const end = parts[1] ? Number(parts[1]) : stat.size - 1;
|
||||||
|
|
||||||
|
// Validate range values
|
||||||
if (
|
if (
|
||||||
Number.isNaN(start) ||
|
Number.isNaN(start) ||
|
||||||
Number.isNaN(end) ||
|
Number.isNaN(end) ||
|
||||||
start < 0 ||
|
start < 0 ||
|
||||||
|
start >= stat.size ||
|
||||||
end < start ||
|
end < start ||
|
||||||
end >= stat.size
|
end >= stat.size
|
||||||
) {
|
) {
|
||||||
end = stat.size - 1;
|
return reply.status(416).send({ error: 'INVALID_RANGE' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const contentLength = end - start + 1;
|
||||||
|
|
||||||
reply
|
reply
|
||||||
.status(206)
|
.status(206)
|
||||||
.header('Content-Range', `bytes ${start}-${end}/${stat.size}`)
|
.header('Content-Range', `bytes ${start}-${end}/${stat.size}`)
|
||||||
.header('Accept-Ranges', 'bytes')
|
.header('Accept-Ranges', 'bytes')
|
||||||
.header('Content-Length', end - start + 1)
|
.header('Content-Length', contentLength)
|
||||||
.header('Content-Type', 'video/mp4');
|
.header('Content-Type', 'video/mp4');
|
||||||
|
|
||||||
return fs.createReadStream(file.file_path, { start, end });
|
return fs.createReadStream(file.file_path, { start, end });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type MatchBody = {
|
type MatchBody = {
|
||||||
@@ -247,17 +258,13 @@ export async function matchEntry(
|
|||||||
return { status: 'OK', matched: !!matched_id };
|
return { status: 'OK', matched: !!matched_id };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUnits(
|
export async function getUnits(request: FastifyRequest<{ Params: Params }>, reply: FastifyReply) {
|
||||||
request: FastifyRequest<{ Params: Params }>,
|
|
||||||
reply: FastifyReply
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
const { type, id } = request.params as { type: string, id: string };
|
const { id } = request.params as { id: string };
|
||||||
|
|
||||||
// Buscar la entrada por matched_id
|
|
||||||
const entry = await queryOne(
|
const entry = await queryOne(
|
||||||
`SELECT id, type, matched_id FROM local_entries WHERE matched_id = ? AND type = ?`,
|
`SELECT id, type, matched_id FROM local_entries WHERE matched_id = ?`,
|
||||||
[Number(id), type],
|
[Number(id)],
|
||||||
'local_library'
|
'local_library'
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -274,24 +281,82 @@ export async function getUnits(
|
|||||||
'local_library'
|
'local_library'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Formatear la respuesta según el tipo
|
const MANGA_IMAGE_EXTS = ['.jpg', '.jpeg', '.png', '.webp'];
|
||||||
const units = files.map((file: any) => {
|
const MANGA_ARCHIVES = ['.cbz', '.cbr', '.zip'];
|
||||||
const fileName = path.basename(file.file_path);
|
|
||||||
const fileExt = path.extname(file.file_path).toLowerCase();
|
|
||||||
|
|
||||||
// Detectar si es un archivo comprimido (capítulo único) o carpeta
|
const NOVEL_EXTS = ['.epub', '.pdf', '.txt', '.md', '.docx', '.mobi'];
|
||||||
const isDirectory = fs.existsSync(file.file_path) &&
|
|
||||||
|
function isImageFolder(folderPath: string): boolean {
|
||||||
|
if (!fs.existsSync(folderPath)) return false;
|
||||||
|
if (!fs.statSync(folderPath).isDirectory()) return false;
|
||||||
|
|
||||||
|
const files = fs.readdirSync(folderPath);
|
||||||
|
return files.some(f => MANGA_IMAGE_EXTS.includes(path.extname(f).toLowerCase()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const units = files
|
||||||
|
.map((file: any) => {
|
||||||
|
const fileExt = path.extname(file.file_path).toLowerCase();
|
||||||
|
const isDir = fs.existsSync(file.file_path) &&
|
||||||
fs.statSync(file.file_path).isDirectory();
|
fs.statSync(file.file_path).isDirectory();
|
||||||
|
|
||||||
|
// ===== MANGA =====
|
||||||
|
if (entry.type === 'manga') {
|
||||||
|
if (MANGA_ARCHIVES.includes(fileExt)) {
|
||||||
return {
|
return {
|
||||||
id: file.id,
|
id: file.id,
|
||||||
number: file.unit_number,
|
number: file.unit_number,
|
||||||
name: fileName,
|
name: path.basename(file.file_path),
|
||||||
type: type === 'anime' ? 'episode' : 'chapter',
|
type: 'chapter',
|
||||||
format: fileExt === '.cbz' ? 'cbz' : 'file',
|
format: fileExt.replace('.', ''),
|
||||||
path: file.file_path
|
path: file.file_path
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
|
if (isDir && isImageFolder(file.file_path)) {
|
||||||
|
return {
|
||||||
|
id: file.id,
|
||||||
|
number: file.unit_number,
|
||||||
|
name: path.basename(file.file_path),
|
||||||
|
type: 'chapter',
|
||||||
|
format: 'folder',
|
||||||
|
path: file.file_path
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.type === 'novels') {
|
||||||
|
if (NOVEL_EXTS.includes(fileExt)) {
|
||||||
|
return {
|
||||||
|
id: file.id,
|
||||||
|
number: file.unit_number,
|
||||||
|
name: path.basename(file.file_path),
|
||||||
|
type: 'chapter',
|
||||||
|
format: fileExt.replace('.', ''),
|
||||||
|
path: file.file_path
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.type === 'anime') {
|
||||||
|
return {
|
||||||
|
id: file.id,
|
||||||
|
number: file.unit_number,
|
||||||
|
name: path.basename(file.file_path),
|
||||||
|
type: 'episode',
|
||||||
|
format: fileExt.replace('.', ''),
|
||||||
|
path: file.file_path
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
entry_id: entry.id,
|
entry_id: entry.id,
|
||||||
@@ -306,7 +371,7 @@ export async function getUnits(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCbzPages(request: FastifyRequest, reply: FastifyReply) {
|
export async function getManifest(request: FastifyRequest, reply: FastifyReply) {
|
||||||
const { unitId } = request.params as any;
|
const { unitId } = request.params as any;
|
||||||
|
|
||||||
const file = await queryOne(
|
const file = await queryOne(
|
||||||
@@ -319,20 +384,73 @@ export async function getCbzPages(request: FastifyRequest, reply: FastifyReply)
|
|||||||
return reply.status(404).send({ error: 'FILE_NOT_FOUND' });
|
return reply.status(404).send({ error: 'FILE_NOT_FOUND' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ext = path.extname(file.file_path).toLowerCase();
|
||||||
|
|
||||||
|
// ===== MANGA =====
|
||||||
|
if (['.cbz', '.cbr', '.zip'].includes(ext)) {
|
||||||
const zip = new AdmZip(file.file_path);
|
const zip = new AdmZip(file.file_path);
|
||||||
|
|
||||||
const pages = zip.getEntries()
|
const pages = zip.getEntries()
|
||||||
.filter(e => !e.isDirectory && /\.(jpg|jpeg|png|webp)$/i.test(e.entryName))
|
.filter(e => !e.isDirectory && /\.(jpg|jpeg|png|webp)$/i.test(e.entryName))
|
||||||
.sort((a, b) => a.entryName.localeCompare(b.entryName, undefined, { numeric: true }))
|
.sort((a, b) => a.entryName.localeCompare(b.entryName, undefined, { numeric: true }))
|
||||||
.map((_, i) =>
|
.map((_, i) => ({
|
||||||
`/api/library/manga/cbz/${unitId}/page/${i}`
|
id: i,
|
||||||
);
|
url: `/api/library/${unitId}/resource/${i}`
|
||||||
|
}));
|
||||||
|
|
||||||
return { pages };
|
return {
|
||||||
|
type: 'manga',
|
||||||
|
format: 'archive',
|
||||||
|
pages
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCbzPage(request: FastifyRequest, reply: FastifyReply) {
|
if (fs.statSync(file.file_path).isDirectory()) {
|
||||||
const { unitId, page } = request.params as any;
|
const pages = fs.readdirSync(file.file_path)
|
||||||
|
.filter(f => /\.(jpg|jpeg|png|webp)$/i.test(f))
|
||||||
|
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
|
||||||
|
.map((_, i) => ({
|
||||||
|
id: i,
|
||||||
|
url: `/api/library/${unitId}/resource/${i}`
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'manga',
|
||||||
|
format: 'folder',
|
||||||
|
pages
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== NOVEL =====
|
||||||
|
if (ext === '.epub') {
|
||||||
|
return {
|
||||||
|
type: 'ln',
|
||||||
|
format: 'epub',
|
||||||
|
url: `/api/library/${unitId}/resource/epub`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['.txt', '.md'].includes(ext)) {
|
||||||
|
return {
|
||||||
|
type: 'ln',
|
||||||
|
format: 'text',
|
||||||
|
url: `/api/library/${unitId}/resource/text`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ext === '.pdf') {
|
||||||
|
return {
|
||||||
|
type: 'ln',
|
||||||
|
format: 'pdf',
|
||||||
|
url: `/api/library/${unitId}/resource/pdf`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.status(400).send({ error: 'UNSUPPORTED_FORMAT' });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPage(request: FastifyRequest, reply: FastifyReply) {
|
||||||
|
const { unitId, resId } = request.params as any;
|
||||||
|
|
||||||
const file = await queryOne(
|
const file = await queryOne(
|
||||||
`SELECT file_path FROM local_files WHERE id = ?`,
|
`SELECT file_path FROM local_files WHERE id = ?`,
|
||||||
@@ -342,16 +460,83 @@ export async function getCbzPage(request: FastifyRequest, reply: FastifyReply) {
|
|||||||
|
|
||||||
if (!file) return reply.status(404).send();
|
if (!file) return reply.status(404).send();
|
||||||
|
|
||||||
const zip = new AdmZip(file.file_path);
|
const ext = path.extname(file.file_path).toLowerCase();
|
||||||
|
|
||||||
|
// ===== CBZ PAGE =====
|
||||||
|
if (['.cbz', '.zip', '.cbr'].includes(ext)) {
|
||||||
|
const zip = new AdmZip(file.file_path);
|
||||||
const images = zip.getEntries()
|
const images = zip.getEntries()
|
||||||
.filter(e => !e.isDirectory && /\.(jpg|jpeg|png|webp)$/i.test(e.entryName))
|
.filter(e => !e.isDirectory && /\.(jpg|jpeg|png|webp)$/i.test(e.entryName))
|
||||||
.sort((a, b) => a.entryName.localeCompare(b.entryName, undefined, { numeric: true }));
|
.sort((a, b) => a.entryName.localeCompare(b.entryName, undefined, { numeric: true }));
|
||||||
|
|
||||||
const entry = images[page];
|
const entry = images[Number(resId)];
|
||||||
if (!entry) return reply.status(404).send();
|
if (!entry) return reply.status(404).send();
|
||||||
|
|
||||||
reply
|
return reply
|
||||||
.header('Content-Type', 'image/jpeg')
|
.header('Content-Type', 'image/jpeg')
|
||||||
.send(entry.getData());
|
.send(entry.getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fs.statSync(file.file_path).isDirectory()) {
|
||||||
|
const images = fs.readdirSync(file.file_path)
|
||||||
|
.filter(f => /\.(jpg|jpeg|png|webp)$/i.test(f))
|
||||||
|
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
|
||||||
|
|
||||||
|
const img = images[Number(resId)];
|
||||||
|
if (!img) return reply.status(404).send();
|
||||||
|
|
||||||
|
const imgPath = path.join(file.file_path, img);
|
||||||
|
const stat = fs.statSync(imgPath);
|
||||||
|
|
||||||
|
reply
|
||||||
|
.header('Content-Length', stat.size)
|
||||||
|
.header('Content-Type', 'image/jpeg');
|
||||||
|
|
||||||
|
return fs.createReadStream(imgPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ext === '.epub') {
|
||||||
|
const html = await parseEpubToHtml(file.file_path);
|
||||||
|
|
||||||
|
return reply
|
||||||
|
.header('Content-Type', 'text/html; charset=utf-8')
|
||||||
|
.send(html);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== TXT / MD =====
|
||||||
|
if (['.txt', '.md'].includes(ext)) {
|
||||||
|
const text = fs.readFileSync(file.file_path, 'utf8');
|
||||||
|
|
||||||
|
return reply
|
||||||
|
.header('Content-Type', 'text/html; charset=utf-8')
|
||||||
|
.send(`<div class="ln-content"><pre>${text}</pre></div>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.status(400).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseEpubToHtml(filePath: string): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const epub = new EPub(filePath);
|
||||||
|
|
||||||
|
epub.on('end', async () => {
|
||||||
|
let html = '';
|
||||||
|
|
||||||
|
for (const id of epub.flow.map(f => f.id)) {
|
||||||
|
const chapter = await new Promise<string>((res, rej) => {
|
||||||
|
epub.getChapter(id, (err, text) => {
|
||||||
|
if (err) rej(err);
|
||||||
|
else res(text);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
html += `<section class="ln-chapter">${chapter}</section>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(html);
|
||||||
|
});
|
||||||
|
|
||||||
|
epub.on('error', reject);
|
||||||
|
epub.parse();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ async function localRoutes(fastify: FastifyInstance) {
|
|||||||
fastify.get('/library/:type/:id', controller.getEntry);
|
fastify.get('/library/:type/:id', controller.getEntry);
|
||||||
fastify.get('/library/stream/:type/:id/:unit', controller.streamUnit);
|
fastify.get('/library/stream/:type/:id/:unit', controller.streamUnit);
|
||||||
fastify.post('/library/:type/:id/match', controller.matchEntry);
|
fastify.post('/library/:type/:id/match', controller.matchEntry);
|
||||||
fastify.get('/library/:type/:id/units', controller.getUnits);
|
fastify.get('/library/:id/units', controller.getUnits);
|
||||||
fastify.get('/library/:type/cbz/:unitId/pages', controller.getCbzPages);
|
fastify.get('/library/:unitId/manifest', controller.getManifest);
|
||||||
fastify.get('/library/:type/cbz/:unitId/page/:page', controller.getCbzPage);
|
fastify.get('/library/:unitId/resource/:resId', controller.getPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default localRoutes;
|
export default localRoutes;
|
||||||
@@ -32,12 +32,12 @@ let localEntryId = null;
|
|||||||
async function checkLocal() {
|
async function checkLocal() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/library/anime/${animeId}`);
|
const res = await fetch(`/api/library/anime/${animeId}`);
|
||||||
if (!res.ok) return;
|
if (!res.ok) return null;
|
||||||
|
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
localEntryId = data.id;
|
return data.id;
|
||||||
|
} catch {
|
||||||
} catch {}
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadAniSkip(malId, episode, duration) {
|
async function loadAniSkip(malId, episode, duration) {
|
||||||
@@ -53,7 +53,7 @@ async function loadAniSkip(malId, episode, duration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadMetadata() {
|
async function loadMetadata() {
|
||||||
checkLocal();
|
localEntryId = await checkLocal();
|
||||||
try {
|
try {
|
||||||
const sourceQuery = (extName === 'local' || !extName) ? "source=anilist" : `source=${extName}`;
|
const sourceQuery = (extName === 'local' || !extName) ? "source=anilist" : `source=${extName}`;
|
||||||
const res = await fetch(`/api/anime/${animeId}?${sourceQuery}`);
|
const res = await fetch(`/api/anime/${animeId}?${sourceQuery}`);
|
||||||
@@ -133,6 +133,7 @@ async function loadMetadata() {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading metadata:', error);
|
console.error('Error loading metadata:', error);
|
||||||
}
|
}
|
||||||
|
await loadExtensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function applyAniSkip(video) {
|
async function applyAniSkip(video) {
|
||||||
@@ -439,9 +440,9 @@ document.getElementById('next-btn').onclick = () => {
|
|||||||
|
|
||||||
if (currentEpisode <= 1) document.getElementById('prev-btn').disabled = true;
|
if (currentEpisode <= 1) document.getElementById('prev-btn').disabled = true;
|
||||||
|
|
||||||
|
// Actualizar progreso cada 1 minuto si el video está reproduciéndose
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
if (plyrInstance && !plyrInstance.paused) sendProgress();
|
if (plyrInstance && !plyrInstance.paused) sendProgress();
|
||||||
}, 60000);
|
}, 60000);
|
||||||
|
|
||||||
loadMetadata();
|
loadMetadata();
|
||||||
loadExtensions();
|
|
||||||
@@ -18,7 +18,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
async function checkLocalLibraryEntry() {
|
async function checkLocalLibraryEntry() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/library/manga/${bookId}`);
|
const libraryType =
|
||||||
|
bookData?.entry_type === 'NOVEL' ? 'novels' : 'manga';
|
||||||
|
|
||||||
|
const res = await fetch(`/api/library/${libraryType}/${bookId}`);
|
||||||
if (!res.ok) return;
|
if (!res.ok) return;
|
||||||
|
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
@@ -38,18 +41,6 @@ async function checkLocalLibraryEntry() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function markAsLocal() {
|
|
||||||
isLocal = true;
|
|
||||||
const pill = document.getElementById('local-pill');
|
|
||||||
if (pill) {
|
|
||||||
pill.textContent = 'Local';
|
|
||||||
pill.style.display = 'inline-flex';
|
|
||||||
pill.style.background = 'rgba(34, 197, 94, 0.2)';
|
|
||||||
pill.style.color = '#22c55e';
|
|
||||||
pill.style.borderColor = 'rgba(34, 197, 94, 0.3)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
try {
|
try {
|
||||||
const urlData = URLUtils.parseEntityPath('book');
|
const urlData = URLUtils.parseEntityPath('book');
|
||||||
@@ -61,8 +52,8 @@ async function init() {
|
|||||||
extensionName = urlData.extensionName;
|
extensionName = urlData.extensionName;
|
||||||
bookId = urlData.entityId;
|
bookId = urlData.entityId;
|
||||||
bookSlug = urlData.slug;
|
bookSlug = urlData.slug;
|
||||||
await checkLocalLibraryEntry();
|
|
||||||
await loadBookMetadata();
|
await loadBookMetadata();
|
||||||
|
await checkLocalLibraryEntry();
|
||||||
|
|
||||||
await loadAvailableExtensions();
|
await loadAvailableExtensions();
|
||||||
await loadChapters();
|
await loadChapters();
|
||||||
@@ -220,7 +211,7 @@ async function loadChapters(targetProvider = null) {
|
|||||||
|
|
||||||
if (isLocalRequest) {
|
if (isLocalRequest) {
|
||||||
// Nuevo endpoint para archivos locales
|
// Nuevo endpoint para archivos locales
|
||||||
fetchUrl = `/api/library/manga/${bookId}/units`;
|
fetchUrl = `/api/library/${bookId}/units`;
|
||||||
} else {
|
} else {
|
||||||
const source = extensionName || 'anilist';
|
const source = extensionName || 'anilist';
|
||||||
fetchUrl = `/api/book/${bookId}/chapters?source=${source}`;
|
fetchUrl = `/api/book/${bookId}/chapters?source=${source}`;
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ async function loadChapter() {
|
|||||||
let newEndpoint;
|
let newEndpoint;
|
||||||
|
|
||||||
if (provider === 'local') {
|
if (provider === 'local') {
|
||||||
newEndpoint = `/api/library/manga/${bookId}/units`;
|
newEndpoint = `/api/library/${bookId}/units`;
|
||||||
} else {
|
} else {
|
||||||
newEndpoint = `/api/book/${bookId}/${chapter}/${provider}?source=${source}`;
|
newEndpoint = `/api/book/${bookId}/${chapter}/${provider}?source=${source}`;
|
||||||
}
|
}
|
||||||
@@ -142,29 +142,39 @@ async function loadChapter() {
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (provider === 'local') {
|
if (provider === 'local') {
|
||||||
const unit = data.units[Number(chapter)];
|
const unit = data.units[Number(chapter)];
|
||||||
|
if (!unit) return;
|
||||||
|
|
||||||
if (!unit) {
|
chapterLabel.textContent = unit.name;
|
||||||
reader.innerHTML = '<div class="loading-container"><span>Chapter not found</span></div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unit.format === 'cbz') {
|
|
||||||
chapterLabel.textContent = unit.name; // ✅
|
|
||||||
document.title = unit.name;
|
document.title = unit.name;
|
||||||
const pagesRes = await fetch(
|
|
||||||
`/api/library/manga/cbz/${unit.id}/pages`
|
|
||||||
);
|
|
||||||
const pagesData = await pagesRes.json();
|
|
||||||
|
|
||||||
|
const manifestRes = await fetch(`/api/library/${unit.id}/manifest`);
|
||||||
|
const manifest = await manifestRes.json();
|
||||||
|
|
||||||
|
reader.innerHTML = '';
|
||||||
|
|
||||||
|
// ===== MANGA =====
|
||||||
|
if (manifest.type === 'manga') {
|
||||||
currentType = 'manga';
|
currentType = 'manga';
|
||||||
updateSettingsVisibility();
|
updateSettingsVisibility();
|
||||||
applyStyles();
|
applyStyles();
|
||||||
|
|
||||||
currentPages = pagesData.pages.map(url => ({ url }));
|
currentPages = manifest.pages;
|
||||||
reader.innerHTML = '';
|
|
||||||
loadManga(currentPages);
|
loadManga(currentPages);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== LN =====
|
||||||
|
if (manifest.type === 'ln') {
|
||||||
|
currentType = 'ln';
|
||||||
|
updateSettingsVisibility();
|
||||||
|
applyStyles();
|
||||||
|
|
||||||
|
const contentRes = await fetch(manifest.url);
|
||||||
|
const html = await contentRes.text();
|
||||||
|
|
||||||
|
loadLN(html);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -193,13 +203,8 @@ async function loadChapter() {
|
|||||||
reader.innerHTML = '';
|
reader.innerHTML = '';
|
||||||
|
|
||||||
if (data.type === 'manga') {
|
if (data.type === 'manga') {
|
||||||
if (provider === 'local' && data.format === 'cbz') {
|
|
||||||
currentPages = data.pages.map(url => ({ url }));
|
|
||||||
loadManga(currentPages);
|
|
||||||
} else {
|
|
||||||
currentPages = data.pages || [];
|
currentPages = data.pages || [];
|
||||||
loadManga(currentPages);
|
loadManga(currentPages);
|
||||||
}
|
|
||||||
} else if (data.type === 'ln') {
|
} else if (data.type === 'ln') {
|
||||||
loadLN(data.content);
|
loadLN(data.content);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,21 +26,37 @@ async function loadLocalEntries() {
|
|||||||
grid.innerHTML = '<div class="skeleton-card"></div>'.repeat(6);
|
grid.innerHTML = '<div class="skeleton-card"></div>'.repeat(6);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Cambiado a endpoint de libros
|
const [mangaRes, novelRes] = await Promise.all([
|
||||||
const response = await fetch('/api/library/manga');
|
fetch('/api/library/manga'),
|
||||||
const entries = await response.json();
|
fetch('/api/library/novels')
|
||||||
localEntries = entries;
|
]);
|
||||||
|
|
||||||
if (entries.length === 0) {
|
const [manga, novel] = await Promise.all([
|
||||||
grid.innerHTML = '<p style="grid-column: 1/-1; text-align: center; color: var(--color-text-secondary); padding: 3rem;">No books found in your local library.</p>';
|
mangaRes.json(),
|
||||||
|
novelRes.json()
|
||||||
|
]);
|
||||||
|
|
||||||
|
localEntries = [
|
||||||
|
...manga.map(e => ({ ...e, type: 'manga' })),
|
||||||
|
...novel.map(e => ({ ...e, type: 'novel' }))
|
||||||
|
];
|
||||||
|
|
||||||
|
if (localEntries.length === 0) {
|
||||||
|
grid.innerHTML = '<p style="grid-column:1/-1;text-align:center;padding:3rem;">No books found.</p>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
renderLocalEntries(entries);
|
|
||||||
} catch (err) {
|
renderLocalEntries(localEntries);
|
||||||
grid.innerHTML = '<p style="grid-column: 1/-1; text-align: center; color: var(--color-danger); padding: 3rem;">Error loading local books.</p>';
|
} catch {
|
||||||
|
grid.innerHTML = '<p style="grid-column:1/-1;text-align:center;color:var(--color-danger);padding:3rem;">Error loading library.</p>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filterLocal(type) {
|
||||||
|
if (type === 'all') renderLocalEntries(localEntries);
|
||||||
|
else renderLocalEntries(localEntries.filter(e => e.type === type));
|
||||||
|
}
|
||||||
|
|
||||||
function renderLocalEntries(entries) {
|
function renderLocalEntries(entries) {
|
||||||
const grid = document.getElementById('local-entries-grid');
|
const grid = document.getElementById('local-entries-grid');
|
||||||
grid.innerHTML = entries.map(entry => {
|
grid.innerHTML = entries.map(entry => {
|
||||||
@@ -58,6 +74,7 @@ function renderLocalEntries(entries) {
|
|||||||
<p style="font-size: 0.85rem; color: var(--color-text-secondary); margin: 0;">
|
<p style="font-size: 0.85rem; color: var(--color-text-secondary); margin: 0;">
|
||||||
${chapters} Chapters
|
${chapters} Chapters
|
||||||
</p>
|
</p>
|
||||||
|
<div class="badge">${entry.type}</div>
|
||||||
<div class="match-status ${entry.matched ? 'status-linked' : 'status-unlinked'}">
|
<div class="match-status ${entry.matched ? 'status-linked' : 'status-unlinked'}">
|
||||||
${entry.matched ? '● Linked' : '○ Unlinked'}
|
${entry.matched ? '● Linked' : '○ Unlinked'}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user