Feat: resolveUrl, add neobook (#803)
* neobook * add qs * fix * / * fix * expandURL -> resolveUrl * fix Agitoon * Update ranobelib.ts --------- Co-authored-by: nyagami <hoangquan05112002@gmail.com>
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
@@ -143,6 +143,18 @@ app.post("/fetchImage/", async (req, res) => {
|
|||||||
res.json({ error: String(err) });
|
res.json({ error: String(err) });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
app.post("/resolveUrl/", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const fullURL = await pluginApi.resolveUrl(
|
||||||
|
req.body["pluginRequirePath"],
|
||||||
|
req.body["path"],
|
||||||
|
req.body["isNovel"],
|
||||||
|
);
|
||||||
|
res.send(fullURL);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
res.json({ error: String(err) });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.get("/", (req, res) => {
|
app.get("/", (req, res) => {
|
||||||
res.sendFile(path.join(dirname, "test_web", "index.html"));
|
res.sendFile(path.join(dirname, "test_web", "index.html"));
|
||||||
|
|||||||
+1
-1
@@ -57,7 +57,7 @@ const makeInit = async (init?: FetchInit) => {
|
|||||||
export async function fetchApi(url: string, init?: FetchInit) {
|
export async function fetchApi(url: string, init?: FetchInit) {
|
||||||
init = await makeInit(init);
|
init = await makeInit(init);
|
||||||
// console.log(init.headers);
|
// console.log(init.headers);
|
||||||
// console.log(url, init);
|
console.log(url, init);
|
||||||
return await fetch(url, init as RequestInit);
|
return await fetch(url, init as RequestInit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
+65
-44
@@ -17,8 +17,9 @@
|
|||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"less": "^4.2.0",
|
"less": "^4.2.0",
|
||||||
"module-alias": "^2.2.3",
|
"module-alias": "^2.2.3",
|
||||||
"sanitize-html": "^2.11.0",
|
"qs": "^6.11.2",
|
||||||
"terser": "^5.27.2",
|
"sanitize-html": "^2.12.1",
|
||||||
|
"terser": "^5.28.1",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tsc-alias": "^1.8.8",
|
"tsc-alias": "^1.8.8",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
@@ -463,6 +464,20 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||||
},
|
},
|
||||||
|
"node_modules/body-parser/node_modules/qs": {
|
||||||
|
"version": "6.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||||
|
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"side-channel": "^1.0.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/boolbase": {
|
"node_modules/boolbase": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||||
@@ -1478,6 +1493,20 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||||
},
|
},
|
||||||
|
"node_modules/express/node_modules/qs": {
|
||||||
|
"version": "6.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||||
|
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"side-channel": "^1.0.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/express/node_modules/raw-body": {
|
"node_modules/express/node_modules/raw-body": {
|
||||||
"version": "2.5.1",
|
"version": "2.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||||
@@ -2861,9 +2890,9 @@
|
|||||||
"integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="
|
"integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="
|
||||||
},
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.11.0",
|
"version": "6.11.2",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
|
||||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
"integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"side-channel": "^1.0.4"
|
"side-channel": "^1.0.4"
|
||||||
},
|
},
|
||||||
@@ -3064,9 +3093,9 @@
|
|||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
},
|
},
|
||||||
"node_modules/sanitize-html": {
|
"node_modules/sanitize-html": {
|
||||||
"version": "2.11.0",
|
"version": "2.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.12.1.tgz",
|
||||||
"integrity": "sha512-BG68EDHRaGKqlsNjJ2xUB7gpInPA8gVx/mvjO743hZaeMCZ2DwzW7xvsqZ+KNU4QKwj86HJ3uu2liISf2qBBUA==",
|
"integrity": "sha512-Plh+JAn0UVDpBRP/xEjsk+xDCoOvMBwQUf/K+/cBAVuTbtX8bj2VB7S1sL1dssVpykqp0/KPSesHrqXtokVBpA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"deepmerge": "^4.2.2",
|
"deepmerge": "^4.2.2",
|
||||||
"escape-string-regexp": "^4.0.0",
|
"escape-string-regexp": "^4.0.0",
|
||||||
@@ -3436,9 +3465,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/terser": {
|
"node_modules/terser": {
|
||||||
"version": "5.27.2",
|
"version": "5.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.27.2.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.28.1.tgz",
|
||||||
"integrity": "sha512-sHXmLSkImesJ4p5apTeT63DsV4Obe1s37qT8qvwHRmVxKTBH7Rv9Wr26VcAMmLbmk9UliiwK8z+657NyJHHy/w==",
|
"integrity": "sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
"acorn": "^8.8.2",
|
"acorn": "^8.8.2",
|
||||||
@@ -3728,20 +3757,6 @@
|
|||||||
"qs": "^6.11.2"
|
"qs": "^6.11.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/url/node_modules/qs": {
|
|
||||||
"version": "6.11.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
|
|
||||||
"integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
|
|
||||||
"dependencies": {
|
|
||||||
"side-channel": "^1.0.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/urlencode": {
|
"node_modules/urlencode": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/urlencode/-/urlencode-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/urlencode/-/urlencode-2.0.0.tgz",
|
||||||
@@ -4224,6 +4239,14 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||||
|
},
|
||||||
|
"qs": {
|
||||||
|
"version": "6.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||||
|
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||||
|
"requires": {
|
||||||
|
"side-channel": "^1.0.4"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -5079,6 +5102,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||||
},
|
},
|
||||||
|
"qs": {
|
||||||
|
"version": "6.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||||
|
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||||
|
"requires": {
|
||||||
|
"side-channel": "^1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"raw-body": {
|
"raw-body": {
|
||||||
"version": "2.5.1",
|
"version": "2.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||||
@@ -6126,9 +6157,9 @@
|
|||||||
"integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="
|
"integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="
|
||||||
},
|
},
|
||||||
"qs": {
|
"qs": {
|
||||||
"version": "6.11.0",
|
"version": "6.11.2",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
|
||||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
"integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"side-channel": "^1.0.4"
|
"side-channel": "^1.0.4"
|
||||||
}
|
}
|
||||||
@@ -6261,9 +6292,9 @@
|
|||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
},
|
},
|
||||||
"sanitize-html": {
|
"sanitize-html": {
|
||||||
"version": "2.11.0",
|
"version": "2.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.12.1.tgz",
|
||||||
"integrity": "sha512-BG68EDHRaGKqlsNjJ2xUB7gpInPA8gVx/mvjO743hZaeMCZ2DwzW7xvsqZ+KNU4QKwj86HJ3uu2liISf2qBBUA==",
|
"integrity": "sha512-Plh+JAn0UVDpBRP/xEjsk+xDCoOvMBwQUf/K+/cBAVuTbtX8bj2VB7S1sL1dssVpykqp0/KPSesHrqXtokVBpA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"deepmerge": "^4.2.2",
|
"deepmerge": "^4.2.2",
|
||||||
"escape-string-regexp": "^4.0.0",
|
"escape-string-regexp": "^4.0.0",
|
||||||
@@ -6570,9 +6601,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"terser": {
|
"terser": {
|
||||||
"version": "5.27.2",
|
"version": "5.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.27.2.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.28.1.tgz",
|
||||||
"integrity": "sha512-sHXmLSkImesJ4p5apTeT63DsV4Obe1s37qT8qvwHRmVxKTBH7Rv9Wr26VcAMmLbmk9UliiwK8z+657NyJHHy/w==",
|
"integrity": "sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
"acorn": "^8.8.2",
|
"acorn": "^8.8.2",
|
||||||
@@ -6781,16 +6812,6 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"punycode": "^1.4.1",
|
"punycode": "^1.4.1",
|
||||||
"qs": "^6.11.2"
|
"qs": "^6.11.2"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"qs": {
|
|
||||||
"version": "6.11.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
|
|
||||||
"integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
|
|
||||||
"requires": {
|
|
||||||
"side-channel": "^1.0.4"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"urlencode": {
|
"urlencode": {
|
||||||
|
|||||||
+3
-2
@@ -24,8 +24,9 @@
|
|||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"less": "^4.2.0",
|
"less": "^4.2.0",
|
||||||
"module-alias": "^2.2.3",
|
"module-alias": "^2.2.3",
|
||||||
"sanitize-html": "^2.11.0",
|
"qs": "^6.11.2",
|
||||||
"terser": "^5.27.2",
|
"sanitize-html": "^2.12.1",
|
||||||
|
"terser": "^5.28.1",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tsc-alias": "^1.8.8",
|
"tsc-alias": "^1.8.8",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { load as parseHTML } from "cheerio";
|
|||||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||||
import { Plugin } from "@typings/plugin";
|
import { Plugin } from "@typings/plugin";
|
||||||
import { Filters, FilterTypes } from "@libs/filterInputs";
|
import { Filters, FilterTypes } from "@libs/filterInputs";
|
||||||
|
import qs from "qs";
|
||||||
|
|
||||||
class NovelsOnline implements Plugin.PluginBase {
|
class NovelsOnline implements Plugin.PluginBase {
|
||||||
id = "NO.net";
|
id = "NO.net";
|
||||||
@@ -115,7 +116,7 @@ class NovelsOnline implements Plugin.PluginBase {
|
|||||||
"application/x-www-form-urlencoded; charset=UTF-8",
|
"application/x-www-form-urlencoded; charset=UTF-8",
|
||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: "q=" + encodeURIComponent(searchTerm),
|
body: qs.stringify({ q: searchTerm }),
|
||||||
},
|
},
|
||||||
).then((res) => res.text());
|
).then((res) => res.text());
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Plugin } from "@typings/plugin";
|
|||||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||||
import { NovelStatus } from "@libs/novelStatus";
|
import { NovelStatus } from "@libs/novelStatus";
|
||||||
import { load as parseHTML } from "cheerio";
|
import { load as parseHTML } from "cheerio";
|
||||||
|
import qs from "qs";
|
||||||
|
|
||||||
class FreeWebNovel implements Plugin.PluginBase {
|
class FreeWebNovel implements Plugin.PluginBase {
|
||||||
id = "FWN.com";
|
id = "FWN.com";
|
||||||
@@ -92,7 +93,7 @@ class FreeWebNovel implements Plugin.PluginBase {
|
|||||||
Origin: this.site,
|
Origin: this.site,
|
||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: "searchkey=" + encodeURIComponent(searchTerm),
|
body: qs.stringify({ searchkey: searchTerm }),
|
||||||
}).then((res) => res.text());
|
}).then((res) => res.text());
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|||||||
@@ -1,186 +0,0 @@
|
|||||||
import { load as parseHTML } from "cheerio";
|
|
||||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
|
||||||
import { Plugin } from "@typings/plugin";
|
|
||||||
import { Filters } from "@libs/filterInputs";
|
|
||||||
|
|
||||||
export const id = "agit.xyz";
|
|
||||||
export const name = "Agitoon";
|
|
||||||
export const site = "https://agit501.xyz/";
|
|
||||||
export const version = "1.0.0";
|
|
||||||
export const icon = "src/kr/agitoon/agit.png";
|
|
||||||
|
|
||||||
const baseUrl = site;
|
|
||||||
|
|
||||||
class Agitoon implements Plugin.PluginBase {
|
|
||||||
id = "agit.xyz";
|
|
||||||
name = "Agitoon";
|
|
||||||
icon = "src/kr/agitoon/agit.png";
|
|
||||||
site = "https://agit501.xyz/";
|
|
||||||
filters?: Filters | undefined;
|
|
||||||
version = "1.0.0";
|
|
||||||
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
|
||||||
const list_limit = 20 * (pageNo - 1);
|
|
||||||
const day = new Date().getDay();
|
|
||||||
|
|
||||||
const res = await fetchApi(baseUrl + "novel/index.update.php", {
|
|
||||||
headers: {
|
|
||||||
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
||||||
},
|
|
||||||
body: `mode=get_data_novel_list_p&novel_menu=3&np_day=${day}&np_rank=1&np_distributor=0&np_genre=00&np_order=1&np_genre_ex_1=00&np_genre_ex_2=00&list_limit=${list_limit}&is_query_first=true`,
|
|
||||||
method: "POST",
|
|
||||||
});
|
|
||||||
const resJson = await res.json() as response;
|
|
||||||
|
|
||||||
const novels: Plugin.NovelItem[] = []
|
|
||||||
|
|
||||||
resJson?.list?.forEach((novel) => {
|
|
||||||
novels.push({
|
|
||||||
url: baseUrl + "novel/list/" + novel.wr_id,
|
|
||||||
name: novel.wr_subject,
|
|
||||||
cover: baseUrl + novel.np_dir + "/thumbnail/" + novel.np_thumbnail,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
}
|
|
||||||
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
|
||||||
const novelId = novelUrl.split("/").reverse()[0];
|
|
||||||
|
|
||||||
// cheerio
|
|
||||||
const result = await fetchApi(novelUrl);
|
|
||||||
const body = await result.text();
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body, { decodeEntities: false });
|
|
||||||
const name = loadedCheerio("h5.pt-2").text();
|
|
||||||
const summary = loadedCheerio(".pt-1.mt-1.pb-1.mb-1").text();
|
|
||||||
const author = loadedCheerio(".post-item-list-cate-v")
|
|
||||||
.first()
|
|
||||||
.text()
|
|
||||||
.split(" : ")
|
|
||||||
.reverse()[0];
|
|
||||||
const cover =
|
|
||||||
baseUrl.slice(0, baseUrl.length - 1) +
|
|
||||||
loadedCheerio("div.col-5.pr-0.pl-0 img").attr("src");
|
|
||||||
const genresTag = loadedCheerio(".col-7 > .post-item-list-cate > span");
|
|
||||||
let genres = "";
|
|
||||||
|
|
||||||
genresTag.each((_, element) => {
|
|
||||||
genres += loadedCheerio(element).text();
|
|
||||||
genres += ", ";
|
|
||||||
});
|
|
||||||
genres = genres.slice(0, genres.length - 2);
|
|
||||||
|
|
||||||
// normal REST HTTP requests
|
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
|
||||||
|
|
||||||
const res = await fetchApi(baseUrl + "novel/list.update.php", {
|
|
||||||
headers: {
|
|
||||||
"content-type":
|
|
||||||
"application/x-www-form-urlencoded; charset=UTF-8",
|
|
||||||
},
|
|
||||||
body: `mode=get_data_novel_list_c&wr_id_p=${novelId}&page_no=1&cnt_list=10000&order_type=Asc`,
|
|
||||||
method: "POST",
|
|
||||||
});
|
|
||||||
|
|
||||||
const resJson = await res.json() as responseBook
|
|
||||||
|
|
||||||
resJson?.list?.forEach((chapter) => {
|
|
||||||
chapters.push({
|
|
||||||
name: chapter.wr_subject,
|
|
||||||
url: baseUrl + `novel/view/${chapter.wr_id}/2`,
|
|
||||||
releaseTime: chapter.wr_datetime,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const novel: Plugin.SourceNovel = {
|
|
||||||
url: novelUrl,
|
|
||||||
name,
|
|
||||||
cover,
|
|
||||||
summary,
|
|
||||||
author,
|
|
||||||
status: "",
|
|
||||||
genres: genres,
|
|
||||||
chapters,
|
|
||||||
};
|
|
||||||
return novel;
|
|
||||||
}
|
|
||||||
async parseChapter(chapterUrl: string): Promise<string> {
|
|
||||||
const result = await fetchApi(chapterUrl);
|
|
||||||
const body = await result.text();
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
let content = loadedCheerio("#id_wr_content").html() || '';
|
|
||||||
|
|
||||||
// gets rid of the popup thingy
|
|
||||||
content = content.replace(
|
|
||||||
"팝업메뉴는 빈공간을 더치하거나 스크룰시 사라집니다",
|
|
||||||
""
|
|
||||||
);
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
|
||||||
const rawResults = await fetchApi("https://agit501.xyz/novel/search.php", {
|
|
||||||
headers: {
|
|
||||||
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
||||||
},
|
|
||||||
body: `mode=get_data_novel_list_p_sch&search_novel=${searchTerm}&list_limit=0`,
|
|
||||||
method: "POST",
|
|
||||||
});
|
|
||||||
const resJson = await rawResults.json() as response;
|
|
||||||
|
|
||||||
const novels: Plugin.NovelItem[] = []
|
|
||||||
|
|
||||||
resJson?.list?.forEach((novel) => {
|
|
||||||
novels.push({
|
|
||||||
url: baseUrl + "novel/list/" + novel.wr_id,
|
|
||||||
name: novel.wr_subject,
|
|
||||||
cover: baseUrl + novel.np_dir + "/thumbnail/" + novel.np_thumbnail,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
}
|
|
||||||
async fetchImage(url: string): Promise<string | undefined> {
|
|
||||||
return fetchFile(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default new Agitoon();
|
|
||||||
|
|
||||||
interface response {
|
|
||||||
list_limit: number;
|
|
||||||
list?: (ListEntity)[] | null;
|
|
||||||
list_count: number;
|
|
||||||
}
|
|
||||||
interface ListEntity {
|
|
||||||
wr_id: string;
|
|
||||||
wr_subject: string;
|
|
||||||
np_dir: string;
|
|
||||||
np_type_02: string;
|
|
||||||
np_thumbnail: string;
|
|
||||||
np_author: string;
|
|
||||||
wr_subject2: string;
|
|
||||||
wr_datetime: string;
|
|
||||||
np_distributor: string;
|
|
||||||
np_genre: string;
|
|
||||||
np_country: string;
|
|
||||||
np_age: string;
|
|
||||||
is_scrap: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface responseBook {
|
|
||||||
list?: (ListEntity2)[] | null;
|
|
||||||
download_time: string;
|
|
||||||
}
|
|
||||||
interface ListEntity2 {
|
|
||||||
wr_id: string;
|
|
||||||
wr_subject: string;
|
|
||||||
wr_datetime: string;
|
|
||||||
url_view1: string;
|
|
||||||
novel_c_style1: string;
|
|
||||||
novel_c_str1: string;
|
|
||||||
data_novel_c_view: string;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
import { fetchFile, fetchApi } from "@libs/fetch";
|
||||||
|
import { Filters } from "@libs/filterInputs";
|
||||||
|
import { Plugin } from "@typings/plugin";
|
||||||
|
import { load as parseHTML } from "cheerio";
|
||||||
|
import qs from "qs";
|
||||||
|
|
||||||
|
class Agitoon implements Plugin.PluginBase {
|
||||||
|
id = "agit.xyz";
|
||||||
|
name = "Agitoon";
|
||||||
|
icon = "src/kr/agitoon/agit.png";
|
||||||
|
site = "https://agit501.xyz";
|
||||||
|
version = "1.0.0";
|
||||||
|
|
||||||
|
async popularNovels(
|
||||||
|
pageNo: number,
|
||||||
|
{ filters, showLatestNovels }: Plugin.PopularNovelsOptions,
|
||||||
|
): Promise<Plugin.NovelItem[]> {
|
||||||
|
const res = await fetchApi(this.site + "/novel/index.update.php", {
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
body: qs.stringify({
|
||||||
|
mode: "get_data_novel_list_p",
|
||||||
|
novel_menu: showLatestNovels ? "1" : "3",
|
||||||
|
np_day: new Date().getDay(),
|
||||||
|
np_rank: "1",
|
||||||
|
np_distributor: "0",
|
||||||
|
np_genre: "00",
|
||||||
|
np_order: "1",
|
||||||
|
np_genre_ex_1: "00",
|
||||||
|
np_genre_ex_2: "00",
|
||||||
|
list_limit: 20 * (pageNo - 1),
|
||||||
|
is_query_first: pageNo == 1,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const resJson = (await res.json()) as response;
|
||||||
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
resJson?.list?.forEach((novel) =>
|
||||||
|
novels.push({
|
||||||
|
name: novel.wr_subject,
|
||||||
|
cover: this.site + novel.np_dir + "/thumbnail/" + novel.np_thumbnail,
|
||||||
|
path: novel.wr_id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
|
||||||
|
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||||
|
const result = await fetchApi(this.resolveUrl(novelPath, true))
|
||||||
|
.then((res) => res.text());
|
||||||
|
const loadedCheerio = parseHTML(result, { decodeEntities: false });
|
||||||
|
|
||||||
|
const novel: Plugin.SourceNovel = {
|
||||||
|
path: novelPath,
|
||||||
|
name: loadedCheerio("h5.pt-2").text(),
|
||||||
|
cover: this.site + loadedCheerio("div.col-5.pr-0.pl-0 img").attr("src"),
|
||||||
|
summary: loadedCheerio(".pt-1.mt-1.pb-1.mb-1").text(),
|
||||||
|
};
|
||||||
|
|
||||||
|
novel.author = loadedCheerio(".post-item-list-cate-v")
|
||||||
|
.first()
|
||||||
|
.text()
|
||||||
|
.split(" : ")[1];
|
||||||
|
|
||||||
|
const genres = loadedCheerio(".col-7 > .post-item-list-cate > span")
|
||||||
|
.map((index, element) => loadedCheerio(element).text().trim())
|
||||||
|
.get();
|
||||||
|
|
||||||
|
if (genres.length) {
|
||||||
|
novel.genres = genres.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
|
const res = await fetchApi(this.site + "/novel/list.update.php", {
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
body: qs.stringify({
|
||||||
|
mode: "get_data_novel_list_c",
|
||||||
|
wr_id_p: novelPath,
|
||||||
|
page_no: "1",
|
||||||
|
cnt_list: "10000",
|
||||||
|
order_type: "Asc",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const resJson = (await res.json()) as responseBook;
|
||||||
|
resJson?.list?.forEach((chapter) =>
|
||||||
|
chapters.push({
|
||||||
|
name: chapter.wr_subject,
|
||||||
|
path: chapter.wr_id + "/2",
|
||||||
|
releaseTime: chapter.wr_datetime,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
novel.chapters = chapters;
|
||||||
|
return novel;
|
||||||
|
}
|
||||||
|
|
||||||
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
|
const result = await fetchApi(this.resolveUrl(chapterPath))
|
||||||
|
.then((res) => res.text());
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(result);
|
||||||
|
let content = loadedCheerio("#id_wr_content").html() || "";
|
||||||
|
|
||||||
|
// gets rid of the popup thingy
|
||||||
|
content = content
|
||||||
|
.replace("팝업메뉴는 빈공간을 더치하거나 스크룰시 사라집니다", "")
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
|
const rawResults = await fetchApi("https://agit501.xyz/novel/search.php", {
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
body: qs.stringify({
|
||||||
|
mode: "get_data_novel_list_p_sch",
|
||||||
|
search_novel: searchTerm,
|
||||||
|
list_limit: 0,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const resJson = (await rawResults.json()) as response;
|
||||||
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
resJson?.list?.forEach((novel) =>
|
||||||
|
novels.push({
|
||||||
|
name: novel.wr_subject,
|
||||||
|
cover: this.site + "/" + novel.np_dir + "/thumbnail/" + novel.np_thumbnail,
|
||||||
|
path: novel.wr_id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchImage = fetchFile;
|
||||||
|
resolveUrl = (path: string, isNovel?: boolean) =>
|
||||||
|
this.site + (isNovel ? "/novel/list/" : "/novel/view/") + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Agitoon();
|
||||||
|
|
||||||
|
interface response {
|
||||||
|
list_limit: number;
|
||||||
|
list?: ListEntity[] | null;
|
||||||
|
list_count: number;
|
||||||
|
}
|
||||||
|
interface ListEntity {
|
||||||
|
wr_id: string;
|
||||||
|
wr_subject: string;
|
||||||
|
np_dir: string;
|
||||||
|
np_type_02: string;
|
||||||
|
np_thumbnail: string;
|
||||||
|
np_author: string;
|
||||||
|
wr_subject2: string;
|
||||||
|
wr_datetime: string;
|
||||||
|
np_distributor: string;
|
||||||
|
np_genre: string;
|
||||||
|
np_country: string;
|
||||||
|
np_age: string;
|
||||||
|
is_scrap: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface responseBook {
|
||||||
|
list?: ListEntity2[] | null;
|
||||||
|
download_time: string;
|
||||||
|
}
|
||||||
|
interface ListEntity2 {
|
||||||
|
wr_id: string;
|
||||||
|
wr_subject: string;
|
||||||
|
wr_datetime: string;
|
||||||
|
url_view1: string;
|
||||||
|
novel_c_style1: string;
|
||||||
|
novel_c_str1: string;
|
||||||
|
data_novel_c_view: string;
|
||||||
|
}
|
||||||
@@ -112,7 +112,7 @@ class freedlit implements Plugin.PluginBase {
|
|||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
loadedCheerio("#bookListBlock > div").each((index, element) => {
|
loadedCheerio("#bookGridBlock > div").each((index, element) => {
|
||||||
const name = loadedCheerio(element).find("h4 > a").text()?.trim();
|
const name = loadedCheerio(element).find("h4 > a").text()?.trim();
|
||||||
const cover = loadedCheerio(element).find("a > img").attr("src")?.trim();
|
const cover = loadedCheerio(element).find("a > img").attr("src")?.trim();
|
||||||
const url = loadedCheerio(element).find("h4 > a").attr("href")?.trim();
|
const url = loadedCheerio(element).find("h4 > a").attr("href")?.trim();
|
||||||
|
|||||||
@@ -53,15 +53,14 @@ class AuthorToday implements Plugin.PluginBase {
|
|||||||
cover: novel.coverUrl
|
cover: novel.coverUrl
|
||||||
? "https://cm.author.today/content/" + novel.coverUrl
|
? "https://cm.author.today/content/" + novel.coverUrl
|
||||||
: defaultCover,
|
: defaultCover,
|
||||||
path: "/work/" + novel.id,
|
path: novel.id.toString(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
async parseNovel(workID: string): Promise<Plugin.SourceNovel> {
|
||||||
const workID = novelPath.split("/")[2];
|
|
||||||
const result = await fetchApi(`${apiUrl}v1/work/${workID}/details`, {
|
const result = await fetchApi(`${apiUrl}v1/work/${workID}/details`, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: token,
|
Authorization: token,
|
||||||
@@ -70,7 +69,7 @@ class AuthorToday implements Plugin.PluginBase {
|
|||||||
|
|
||||||
const book = (await result.json()) as responseBook;
|
const book = (await result.json()) as responseBook;
|
||||||
const novel: Plugin.SourceNovel = {
|
const novel: Plugin.SourceNovel = {
|
||||||
path: novelPath,
|
path: workID,
|
||||||
name: book.title,
|
name: book.title,
|
||||||
cover: book.coverUrl ? book.coverUrl.split("?")[0] : defaultCover,
|
cover: book.coverUrl ? book.coverUrl.split("?")[0] : defaultCover,
|
||||||
genres: book.tags?.join(", "),
|
genres: book.tags?.join(", "),
|
||||||
@@ -105,7 +104,7 @@ class AuthorToday implements Plugin.PluginBase {
|
|||||||
if (chapter.isAvailable && !chapter.isDraft) {
|
if (chapter.isAvailable && !chapter.isDraft) {
|
||||||
chapters.push({
|
chapters.push({
|
||||||
name: chapter.title || "Глава " + (chapterIndex + 1),
|
name: chapter.title || "Глава " + (chapterIndex + 1),
|
||||||
path: `v1/work/${workID}/chapter/${chapter.id}/text`,
|
path: workID + "/" + chapter.id,
|
||||||
releaseTime: dayjs(
|
releaseTime: dayjs(
|
||||||
chapter.publishTime || chapter.lastModificationTime,
|
chapter.publishTime || chapter.lastModificationTime,
|
||||||
).format("LLL"),
|
).format("LLL"),
|
||||||
@@ -119,11 +118,15 @@ class AuthorToday implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
const result = await fetchApi(apiUrl + chapterPath, {
|
const [workID, chapterID] = chapterPath.split("/");
|
||||||
headers: {
|
const result = await fetchApi(
|
||||||
Authorization: token,
|
apiUrl + `v1/work/${workID}/chapter/${chapterID}/text`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: token,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
);
|
||||||
const json = (await result.json()) as encryptedСhapter;
|
const json = (await result.json()) as encryptedСhapter;
|
||||||
|
|
||||||
if (json.code) {
|
if (json.code) {
|
||||||
@@ -160,26 +163,26 @@ class AuthorToday implements Plugin.PluginBase {
|
|||||||
const loadedCheerio = parseHTML(result);
|
const loadedCheerio = parseHTML(result);
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
loadedCheerio("div.book-row").each((index, element) => {
|
loadedCheerio("a.work-row").each((index, element) => {
|
||||||
const name = loadedCheerio(element)
|
const name = loadedCheerio(element)
|
||||||
.find('div[class="book-title"] a')
|
.find('h4[class="work-title"]')
|
||||||
.text()
|
.text()
|
||||||
.trim();
|
.trim();
|
||||||
let cover = loadedCheerio(element).find("img").attr("src");
|
let cover = loadedCheerio(element).find("img").attr("data-src");
|
||||||
const path = loadedCheerio(element)
|
const path = loadedCheerio(element).attr("href");
|
||||||
.find('div[class="book-title"] a')
|
|
||||||
.attr("href");
|
|
||||||
|
|
||||||
cover = cover ? cover.split("?")[0] : defaultCover;
|
cover = cover ? cover.split("?")[0] : defaultCover;
|
||||||
|
|
||||||
if (!path) return;
|
if (!path) return;
|
||||||
novels.push({ name, cover, path });
|
novels.push({ name, cover, path: path.replace("/work/", "") });
|
||||||
});
|
});
|
||||||
|
|
||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchImage = fetchFile;
|
fetchImage = fetchFile;
|
||||||
|
resolveUrl = (path: string, isNovel?: boolean) =>
|
||||||
|
isNovel ? this.site + "/work/" + path : this.site + "/reader/" + path;
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
sort: {
|
sort: {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class Bookriver implements Plugin.PluginBase {
|
|||||||
novels.push({
|
novels.push({
|
||||||
name: novel.name,
|
name: novel.name,
|
||||||
cover: novel.coverImages[0].url,
|
cover: novel.coverImages[0].url,
|
||||||
path: "/book/" + novel.slug,
|
path: novel.slug,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,8 @@ class Bookriver implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||||
const result = await fetchApi(this.site + novelPath).then((res) => res.text());
|
const result = await fetchApi(this.resolveUrl(novelPath, true))
|
||||||
|
.then((res) => res.text());
|
||||||
const loadedCheerio = parseHTML(result);
|
const loadedCheerio = parseHTML(result);
|
||||||
|
|
||||||
const jsonRaw = loadedCheerio("#__NEXT_DATA__").html();
|
const jsonRaw = loadedCheerio("#__NEXT_DATA__").html();
|
||||||
@@ -72,7 +73,7 @@ class Bookriver implements Plugin.PluginBase {
|
|||||||
if (chapter.available) {
|
if (chapter.available) {
|
||||||
chapters.push({
|
chapters.push({
|
||||||
name: chapter.name,
|
name: chapter.name,
|
||||||
path: "/reader/" + book?.slug + "/" + chapter.chapterId,
|
path: book?.slug + "/" + chapter.chapterId,
|
||||||
releaseTime: dayjs(
|
releaseTime: dayjs(
|
||||||
chapter.firstPublishedAt || chapter.createdAt || undefined,
|
chapter.firstPublishedAt || chapter.createdAt || undefined,
|
||||||
).format("LLL"),
|
).format("LLL"),
|
||||||
@@ -112,7 +113,7 @@ class Bookriver implements Plugin.PluginBase {
|
|||||||
novels.push({
|
novels.push({
|
||||||
name: novel.name,
|
name: novel.name,
|
||||||
cover: novel.coverImages[0].url,
|
cover: novel.coverImages[0].url,
|
||||||
path: "/book/" + novel.slug,
|
path: novel.slug,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -120,6 +121,8 @@ class Bookriver implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fetchImage = fetchFile;
|
fetchImage = fetchFile;
|
||||||
|
resolveUrl = (path: string, isNovel?: boolean) =>
|
||||||
|
this.site + (isNovel ? "/book/" : "/reader/") + path;
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
sort: {
|
sort: {
|
||||||
|
|||||||
@@ -0,0 +1,450 @@
|
|||||||
|
import { Plugin } from "@typings/plugin";
|
||||||
|
import { FilterTypes, Filters } from "@libs/filterInputs";
|
||||||
|
import { defaultCover } from "@libs/defaultCover";
|
||||||
|
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||||
|
import { NovelStatus } from "@libs/novelStatus";
|
||||||
|
|
||||||
|
const statusKey: { [key: string]: string } = {
|
||||||
|
"0": NovelStatus.Unknown,
|
||||||
|
"1": NovelStatus.Ongoing,
|
||||||
|
"2": NovelStatus.Completed,
|
||||||
|
"3": NovelStatus.OnHiatus,
|
||||||
|
"4": NovelStatus.Cancelled,
|
||||||
|
};
|
||||||
|
|
||||||
|
class Neobook implements Plugin.PluginBase {
|
||||||
|
id = "neobook";
|
||||||
|
name = "Neobook";
|
||||||
|
site = "https://neobook.org";
|
||||||
|
version = "1.0.0";
|
||||||
|
icon = "src/ru/neobook/icon.png";
|
||||||
|
|
||||||
|
async fetchNovels(
|
||||||
|
page: number,
|
||||||
|
{ filters, showLatestNovels }: Plugin.PopularNovelsOptions<typeof this.filters>,
|
||||||
|
searchTerm?: string,
|
||||||
|
): Promise<Plugin.NovelItem[]> {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("version", "4.0");
|
||||||
|
formData.append("uid", "0");
|
||||||
|
formData.append("utoken", "");
|
||||||
|
formData.append("resource", "general");
|
||||||
|
formData.append("action", "get_bundle");
|
||||||
|
formData.append("bundle", "bundle_books");
|
||||||
|
formData.append("target", "feed");
|
||||||
|
formData.append("page", page.toString());
|
||||||
|
formData.append("filter_category_id", filters?.category?.value || "0");
|
||||||
|
formData.append("filter_completed", "-1");
|
||||||
|
formData.append("filter_search", searchTerm || "");
|
||||||
|
formData.append("filter_tags", filters?.tags?.value || "");
|
||||||
|
formData.append(
|
||||||
|
"filter_sort",
|
||||||
|
showLatestNovels ? "new" : filters?.sort?.value || "popular",
|
||||||
|
);
|
||||||
|
formData.append("filter_timeread", filters?.timeread?.value || "0-999999");
|
||||||
|
|
||||||
|
const result = await fetchApi("https://api.neobook.org/", {
|
||||||
|
method: "post",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
const json = (await result.json()) as { bundle_books: BundleBooks };
|
||||||
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
if (json.bundle_books.feed?.length) {
|
||||||
|
json.bundle_books.feed?.forEach((novel) =>
|
||||||
|
novels.push({
|
||||||
|
name: novel.title,
|
||||||
|
cover: novel?.attachment?.image?.m || defaultCover,
|
||||||
|
path: novel.token + "/",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
|
||||||
|
popularNovels = this.fetchNovels;
|
||||||
|
|
||||||
|
async searchNovels(
|
||||||
|
searchTerm: string,
|
||||||
|
page: number,
|
||||||
|
): Promise<Plugin.NovelItem[]> {
|
||||||
|
const defaultOptions: any = {
|
||||||
|
filters: undefined,
|
||||||
|
showLatestNovels: false,
|
||||||
|
};
|
||||||
|
return this.fetchNovels(page, defaultOptions, searchTerm);
|
||||||
|
}
|
||||||
|
|
||||||
|
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||||
|
const body = await fetchApi(this.resolveUrl(novelPath, true))
|
||||||
|
.then((res) => res.text());
|
||||||
|
|
||||||
|
const novel: Plugin.SourceNovel = {
|
||||||
|
path: novelPath,
|
||||||
|
name: "",
|
||||||
|
cover: defaultCover,
|
||||||
|
};
|
||||||
|
|
||||||
|
const bookRaw = body.match(/var postData = ({.*?});/);
|
||||||
|
if (bookRaw instanceof Array && bookRaw.length >= 2) {
|
||||||
|
const book = JSON.parse(bookRaw[1]) as Novels;
|
||||||
|
|
||||||
|
novel.name = book.title;
|
||||||
|
novel.summary = book.text?.replace?.(/<br>/g, "\n") || book.text_fix;
|
||||||
|
novel.author =
|
||||||
|
book.user && book.user.firstname && book.user.lastname
|
||||||
|
? book.user.firstname + " " + book.user.lastname
|
||||||
|
: book.user?.initials || "";
|
||||||
|
novel.status = statusKey[book.status || "0"] || NovelStatus.Unknown;
|
||||||
|
|
||||||
|
if (book.attachment?.image?.m) novel.cover = book.attachment.image.m;
|
||||||
|
if (book.tags?.length) novel.genres = book.tags.join(",");
|
||||||
|
|
||||||
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
|
book.chapters?.forEach((chapter, chapterIndex) => {
|
||||||
|
if (chapter.access == "1" && chapter.status == "1") {
|
||||||
|
chapters.push({
|
||||||
|
name: chapter.title || "Глава " + (chapterIndex + 1),
|
||||||
|
path: `?book=${book.token}&chapter=${chapter.token}`,
|
||||||
|
releaseTime: null,
|
||||||
|
chapterNumber: Number(chapter.sort) || chapterIndex + 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
novel.chapters = chapters;
|
||||||
|
}
|
||||||
|
return novel;
|
||||||
|
}
|
||||||
|
|
||||||
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
|
const body = await fetchApi(this.resolveUrl(chapterPath))
|
||||||
|
.then((res) => res.text());
|
||||||
|
|
||||||
|
const bookRaw = body.match(/var data = ({.*?});/);
|
||||||
|
let chapterText = "";
|
||||||
|
|
||||||
|
if (bookRaw instanceof Array && bookRaw.length >= 2) {
|
||||||
|
const book = JSON.parse(bookRaw[1]) as Novels;
|
||||||
|
const token = chapterPath.split("=")[2];
|
||||||
|
const chapter = book.chapters?.find?.(
|
||||||
|
(chapter) => chapter.token == token,
|
||||||
|
);
|
||||||
|
chapterText = (chapter?.data?.html || "").replace(/<br>/g, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
return chapterText;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchImage = fetchFile;
|
||||||
|
resolveUrl = (path: string, isNovel?: boolean) =>
|
||||||
|
this.site + (isNovel ? "/book/" : "/reader/") + path;
|
||||||
|
|
||||||
|
filters = {
|
||||||
|
sort: {
|
||||||
|
label: "Сортировка:",
|
||||||
|
value: "popular",
|
||||||
|
options: [
|
||||||
|
{ label: "Сначала популярные", value: "popular" },
|
||||||
|
{ label: "Сначала новые", value: "new" },
|
||||||
|
{ label: "В случайном порядке", value: "rand" },
|
||||||
|
],
|
||||||
|
type: FilterTypes.Picker,
|
||||||
|
},
|
||||||
|
timeread: {
|
||||||
|
label: "Время прочтения:",
|
||||||
|
value: "0-999999",
|
||||||
|
options: [
|
||||||
|
{ label: "Все", value: "0-999999" },
|
||||||
|
{ label: "Менее 15 минут", value: "0-900" },
|
||||||
|
{ label: "15-30 минут", value: "900-1800" },
|
||||||
|
{ label: "30-60 минут", value: "1800-3600" },
|
||||||
|
{ label: "1-2 часа", value: "3600-7200" },
|
||||||
|
{ label: "Более 2 часов", value: "7200-999999" },
|
||||||
|
],
|
||||||
|
type: FilterTypes.Picker,
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
label: "Жанр:",
|
||||||
|
value: "",
|
||||||
|
options: [
|
||||||
|
{ label: "Все", value: "" },
|
||||||
|
{ label: "Антиутопия", value: "10" },
|
||||||
|
{ label: "Детектив", value: "13" },
|
||||||
|
{ label: "Детские книги", value: "14" },
|
||||||
|
{ label: "Драма", value: "15" },
|
||||||
|
{ label: "Другое", value: "16" },
|
||||||
|
{ label: "История", value: "18" },
|
||||||
|
{ label: "Мелодрама", value: "21" },
|
||||||
|
{ label: "Мистика", value: "22" },
|
||||||
|
{ label: "Научная фантастика", value: "23" },
|
||||||
|
{ label: "Нон-фикшн", value: "35" },
|
||||||
|
{ label: "Подростки и молодежь", value: "26" },
|
||||||
|
{ label: "Постапокалипсис", value: "27" },
|
||||||
|
{ label: "Поэзия", value: "28" },
|
||||||
|
{ label: "Приключения", value: "29" },
|
||||||
|
{ label: "Рассказ", value: "34" },
|
||||||
|
{ label: "Роман", value: "36" },
|
||||||
|
{ label: "Творчество", value: "40" },
|
||||||
|
{ label: "Триллер", value: "91" },
|
||||||
|
{ label: "Ужасы", value: "90" },
|
||||||
|
{ label: "Фантастика", value: "41" },
|
||||||
|
{ label: "Фанфик", value: "42" },
|
||||||
|
{ label: "Фэнтези", value: "44" },
|
||||||
|
{ label: "Эротика", value: "46" },
|
||||||
|
{ label: "Юмор", value: "47" },
|
||||||
|
],
|
||||||
|
type: FilterTypes.Picker,
|
||||||
|
},
|
||||||
|
tags: {
|
||||||
|
label: "Тэги:",
|
||||||
|
value: "",
|
||||||
|
type: FilterTypes.TextInput,
|
||||||
|
},
|
||||||
|
} satisfies Filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Neobook();
|
||||||
|
|
||||||
|
interface BundleBooks {
|
||||||
|
categories: any[];
|
||||||
|
topSection: any[];
|
||||||
|
feed: Novels[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BottomSection {
|
||||||
|
id: string;
|
||||||
|
boost: string;
|
||||||
|
preferenceid: string;
|
||||||
|
type_id: string;
|
||||||
|
contentTypeid: string;
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
more: string;
|
||||||
|
part: string;
|
||||||
|
items: Novels[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Novels {
|
||||||
|
status?: string;
|
||||||
|
error?: string;
|
||||||
|
login?: Login;
|
||||||
|
requestid?: string;
|
||||||
|
bundleBooks?: BundleBooks;
|
||||||
|
available?: string;
|
||||||
|
boosted?: string;
|
||||||
|
id?: string;
|
||||||
|
token: string;
|
||||||
|
type?: string;
|
||||||
|
typeid?: string;
|
||||||
|
verified?: string;
|
||||||
|
audio?: string;
|
||||||
|
labelid?: string;
|
||||||
|
labelTitle?: string;
|
||||||
|
languageid?: string;
|
||||||
|
languageTitle?: string;
|
||||||
|
categoryid?: string;
|
||||||
|
categoryTitle?: string;
|
||||||
|
price?: Price;
|
||||||
|
priceSpecial?: Price;
|
||||||
|
priceDiscount?: string;
|
||||||
|
title: string;
|
||||||
|
text: string;
|
||||||
|
text_fix: string;
|
||||||
|
copyright?: string;
|
||||||
|
align?: string;
|
||||||
|
completed?: string;
|
||||||
|
timereadS?: string;
|
||||||
|
views?: string;
|
||||||
|
likes?: string;
|
||||||
|
comments?: string;
|
||||||
|
bookmarks?: string;
|
||||||
|
isLike?: string;
|
||||||
|
isBookmark?: string;
|
||||||
|
isPurchase?: string;
|
||||||
|
isSelf?: string;
|
||||||
|
dateEdit?: string;
|
||||||
|
datePublish?: string;
|
||||||
|
link_path?: string;
|
||||||
|
user?: NovelsUser;
|
||||||
|
attachment?: Attachment;
|
||||||
|
adult?: string;
|
||||||
|
adultPlaceholder?: string;
|
||||||
|
nft?: string;
|
||||||
|
feed?: Feed;
|
||||||
|
counters?: NovelsCounters;
|
||||||
|
rating?: Rating;
|
||||||
|
readTime?: ReadTime;
|
||||||
|
readProgress?: ReadProgress;
|
||||||
|
tags?: string[];
|
||||||
|
chapters?: Chapter[];
|
||||||
|
lastComments?: any[];
|
||||||
|
bottomSection?: BottomSection;
|
||||||
|
carouselItems?: Novels[];
|
||||||
|
book?: Novels;
|
||||||
|
activeChapterIndex?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Attachment {
|
||||||
|
has: string;
|
||||||
|
id: string;
|
||||||
|
typeid: string;
|
||||||
|
userid: string;
|
||||||
|
width: string;
|
||||||
|
height: string;
|
||||||
|
duration: string;
|
||||||
|
image: Image;
|
||||||
|
video: string;
|
||||||
|
origin: string;
|
||||||
|
ext: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Image {
|
||||||
|
s: string;
|
||||||
|
m: string;
|
||||||
|
l: string;
|
||||||
|
has?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Chapter {
|
||||||
|
id: string;
|
||||||
|
token: string;
|
||||||
|
status: string;
|
||||||
|
sort: string;
|
||||||
|
free: string;
|
||||||
|
title: string;
|
||||||
|
words: string;
|
||||||
|
access: string;
|
||||||
|
loaded: string;
|
||||||
|
data?: Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Data {
|
||||||
|
available: string;
|
||||||
|
access: string;
|
||||||
|
id: string;
|
||||||
|
token: string;
|
||||||
|
postid: string;
|
||||||
|
free: string;
|
||||||
|
title: string;
|
||||||
|
html: string;
|
||||||
|
dateEdit: string;
|
||||||
|
datePublish: string;
|
||||||
|
attachment: Attachment;
|
||||||
|
lastComments: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NovelsCounters {
|
||||||
|
impressions: Bookmarks;
|
||||||
|
views: Bookmarks;
|
||||||
|
likes: Bookmarks;
|
||||||
|
comments: Bookmarks;
|
||||||
|
bookmarks: Bookmarks;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Bookmarks {
|
||||||
|
value: string;
|
||||||
|
formatted: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Feed {
|
||||||
|
name: string;
|
||||||
|
canRemoved: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Login {
|
||||||
|
uid: string;
|
||||||
|
utoken: string;
|
||||||
|
has: string;
|
||||||
|
banned: string;
|
||||||
|
verified: string;
|
||||||
|
acc: string;
|
||||||
|
arm: string;
|
||||||
|
grm: string;
|
||||||
|
counters: LoginCounters;
|
||||||
|
user: LoginUser;
|
||||||
|
pro: any[];
|
||||||
|
earn: any[];
|
||||||
|
boost: any[];
|
||||||
|
deposit: any[];
|
||||||
|
affiliate: any[];
|
||||||
|
preferences: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LoginCounters {
|
||||||
|
notifications: string;
|
||||||
|
messages: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LoginUser {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
firstname: string;
|
||||||
|
lastname: string;
|
||||||
|
photo: string;
|
||||||
|
status: string;
|
||||||
|
balance: Balance;
|
||||||
|
connectedService: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Balance {
|
||||||
|
neo: Price;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Price {
|
||||||
|
has: string;
|
||||||
|
value: string;
|
||||||
|
currency: Currency;
|
||||||
|
formatted: string;
|
||||||
|
deadline: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Currency = "NEO";
|
||||||
|
|
||||||
|
interface Rating {
|
||||||
|
value: string;
|
||||||
|
count: string;
|
||||||
|
my: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReadProgress {
|
||||||
|
chapter: string;
|
||||||
|
progress: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReadTime {
|
||||||
|
hours: string;
|
||||||
|
minutes: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NovelsUser {
|
||||||
|
available: string;
|
||||||
|
boosted: string;
|
||||||
|
id: string;
|
||||||
|
status: string;
|
||||||
|
verified: string;
|
||||||
|
metrica: string;
|
||||||
|
cover: Image;
|
||||||
|
photo: Image;
|
||||||
|
username: string;
|
||||||
|
firstname: string;
|
||||||
|
lastname: string;
|
||||||
|
initials: string;
|
||||||
|
link: string;
|
||||||
|
about: string;
|
||||||
|
subscriptions: string;
|
||||||
|
subscribers: string;
|
||||||
|
publications: string;
|
||||||
|
books: string;
|
||||||
|
poems: string;
|
||||||
|
posts: string;
|
||||||
|
drafts: string;
|
||||||
|
isPro: string;
|
||||||
|
isAdult: string;
|
||||||
|
isBanned: string;
|
||||||
|
isSubscription: string;
|
||||||
|
isBlock: string;
|
||||||
|
gr: string;
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ class novelOvh implements Plugin.PluginBase {
|
|||||||
novels.push({
|
novels.push({
|
||||||
name: novel.name.ru,
|
name: novel.name.ru,
|
||||||
cover: novel.poster,
|
cover: novel.poster,
|
||||||
path: "/novel/" + novel.slug,
|
path: novel.slug,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ class novelOvh implements Plugin.PluginBase {
|
|||||||
|
|
||||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||||
const result = await fetchApi(
|
const result = await fetchApi(
|
||||||
this.site + novelPath + "?_data=routes/reader/book/$slug/index",
|
this.resolveUrl(novelPath, true) + "?_data=routes/reader/book/$slug/index",
|
||||||
);
|
);
|
||||||
const json = (await result.json()) as responseNovel;
|
const json = (await result.json()) as responseNovel;
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ class novelOvh implements Plugin.PluginBase {
|
|||||||
|
|
||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
const result = await fetchApi(
|
const result = await fetchApi(
|
||||||
"https://api.novel.ovh/v2/chapters/" + chapterPath.split("/")[3],
|
"https://api.novel.ovh/v2/chapters/" + chapterPath.split("/")[1],
|
||||||
);
|
);
|
||||||
const book = (await result.json()) as responseChapter;
|
const book = (await result.json()) as responseChapter;
|
||||||
const image = Object.fromEntries(
|
const image = Object.fromEntries(
|
||||||
@@ -106,7 +106,7 @@ class novelOvh implements Plugin.PluginBase {
|
|||||||
novels.push({
|
novels.push({
|
||||||
name: novel.name.ru,
|
name: novel.name.ru,
|
||||||
cover: novel.poster,
|
cover: novel.poster,
|
||||||
path: "/novel/" + novel.slug,
|
path: novel.slug,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -154,6 +154,7 @@ class novelOvh implements Plugin.PluginBase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fetchImage = fetchFile;
|
fetchImage = fetchFile;
|
||||||
|
resolveUrl = (path: string, isNovel?: boolean) => this.site + "/novel/" + path;
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
sort: {
|
sort: {
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ class TL implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fetchImage = fetchFile;
|
fetchImage = fetchFile;
|
||||||
|
resolveUrl = (path: string, isNovel?: boolean) => "https://" + path;
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
tags: {
|
tags: {
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class RNBH implements Plugin.PluginBase {
|
|||||||
novels.push({
|
novels.push({
|
||||||
name: novel.names.rus || novel.names.eng || novel.names.original,
|
name: novel.names.rus || novel.names.eng || novel.names.original,
|
||||||
cover: novel.poster.medium,
|
cover: novel.poster.medium,
|
||||||
path: "/ranobe/" + novel.id,
|
path: novel.id.toString(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ class RNBH implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||||
const result = await fetchApi(this.site + "/api" + novelPath);
|
const result = await fetchApi(this.site + "/api/ranobe/" + novelPath);
|
||||||
const json = (await result.json()) as { data: responseNovel };
|
const json = (await result.json()) as { data: responseNovel };
|
||||||
|
|
||||||
const novel: Plugin.SourceNovel = {
|
const novel: Plugin.SourceNovel = {
|
||||||
@@ -98,7 +98,7 @@ class RNBH implements Plugin.PluginBase {
|
|||||||
|
|
||||||
const chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
const chaptersRaw = await fetchApi(
|
const chaptersRaw = await fetchApi(
|
||||||
this.site + "/api" + novelPath + "/contents",
|
this.site + "/api/ranobe/" + novelPath + "/contents",
|
||||||
);
|
);
|
||||||
const chaptersJSON = (await chaptersRaw.json()) as {
|
const chaptersJSON = (await chaptersRaw.json()) as {
|
||||||
volumes: VolumesEntity[];
|
volumes: VolumesEntity[];
|
||||||
@@ -123,7 +123,7 @@ class RNBH implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
const result = await fetchApi(this.site + chapterPath);
|
const result = await fetchApi(this.resolveUrl(chapterPath));
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
@@ -161,7 +161,7 @@ class RNBH implements Plugin.PluginBase {
|
|||||||
novel.name ||
|
novel.name ||
|
||||||
novel?.names?.original ||
|
novel?.names?.original ||
|
||||||
"",
|
"",
|
||||||
path: "/ranobe/" + novel.id,
|
path: novel.id.toString(),
|
||||||
cover: novel?.image?.replace("/small", "/medium"),
|
cover: novel?.image?.replace("/small", "/medium"),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -170,6 +170,7 @@ class RNBH implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fetchImage = fetchFile;
|
fetchImage = fetchFile;
|
||||||
|
resolveUrl = (path: string, isNovel?: boolean) => this.site + "/ranobe/" + path;
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
sort: {
|
sort: {
|
||||||
|
|||||||
@@ -67,7 +67,8 @@ class RLIB implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||||
const body = await fetchApi(this.site + novelPath).then((res) => res.text());
|
const body = await fetchApi(this.resolveUrl(novelPath, true))
|
||||||
|
.then((res) => res.text());
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
const novel: Plugin.SourceNovel = {
|
const novel: Plugin.SourceNovel = {
|
||||||
@@ -104,6 +105,10 @@ class RLIB implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.ui = loadedCheerio("a.header-right-menu__item")
|
||||||
|
.attr("href")
|
||||||
|
?.replace?.(/[^0-9]/g, "");
|
||||||
|
|
||||||
const chaptersRaw = body.match(/window\.__DATA__ = ({.*?});/);
|
const chaptersRaw = body.match(/window\.__DATA__ = ({.*?});/);
|
||||||
if (chaptersRaw instanceof Array && chaptersRaw.length >= 2) {
|
if (chaptersRaw instanceof Array && chaptersRaw.length >= 2) {
|
||||||
const chaptersJson: responseBook = JSON.parse(chaptersRaw[1]);
|
const chaptersJson: responseBook = JSON.parse(chaptersRaw[1]);
|
||||||
@@ -136,7 +141,7 @@ class RLIB implements Plugin.PluginBase {
|
|||||||
customOrder[chapter.branch_id || 0] =
|
customOrder[chapter.branch_id || 0] =
|
||||||
(customOrder[chapter.branch_id || 0] || 1) + 1;
|
(customOrder[chapter.branch_id || 0] || 1) + 1;
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
const chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
chaptersJson.chapters.list.forEach((chapter, chapterIndex) =>
|
chaptersJson.chapters.list.forEach((chapter, chapterIndex) =>
|
||||||
@@ -146,7 +151,7 @@ class RLIB implements Plugin.PluginBase {
|
|||||||
" Глава " + chapter.chapter_number +
|
" Глава " + chapter.chapter_number +
|
||||||
(chapter.chapter_name ? " " + chapter.chapter_name.trim() : ""),
|
(chapter.chapter_name ? " " + chapter.chapter_name.trim() : ""),
|
||||||
path:
|
path:
|
||||||
"/" + chaptersJson.manga.slug +
|
novelPath +
|
||||||
"/v" + chapter.chapter_volume +
|
"/v" + chapter.chapter_volume +
|
||||||
"/c" + chapter.chapter_number +
|
"/c" + chapter.chapter_number +
|
||||||
"?bid=" + (chapter.branch_id || ""),
|
"?bid=" + (chapter.branch_id || ""),
|
||||||
@@ -164,16 +169,15 @@ class RLIB implements Plugin.PluginBase {
|
|||||||
if ((a.page || 0) > (b.page || 0)) return 1;
|
if ((a.page || 0) > (b.page || 0)) return 1;
|
||||||
if ((a.page || 0) < (b.page || 0)) return -1;
|
if ((a.page || 0) < (b.page || 0)) return -1;
|
||||||
return (a.chapterNumber || 0) - (b.chapterNumber || 0);
|
return (a.chapterNumber || 0) - (b.chapterNumber || 0);
|
||||||
})
|
})
|
||||||
: chapters.reverse();
|
: chapters.reverse();
|
||||||
}
|
}
|
||||||
return novel;
|
return novel;
|
||||||
}
|
}
|
||||||
|
|
||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
const result = await fetchApi(
|
const result = await fetchApi(this.resolveUrl(chapterPath))
|
||||||
this.site + chapterPath + (this.ui ? "&ui=" + this.ui : ""),
|
.then((res) => res.text());
|
||||||
).then((res) => res.text());
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(result);
|
const loadedCheerio = parseHTML(result);
|
||||||
loadedCheerio(".reader-container img").each((index, element) => {
|
loadedCheerio(".reader-container img").each((index, element) => {
|
||||||
@@ -211,6 +215,8 @@ class RLIB implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fetchImage = fetchFile;
|
fetchImage = fetchFile;
|
||||||
|
resolveUrl = (path: string, isNovel?: boolean) =>
|
||||||
|
this.site + path + (this.ui ? (isNovel ? "?" : "&") + "ui=" + this.ui : "");
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
sort: {
|
sort: {
|
||||||
|
|||||||
@@ -44,19 +44,18 @@ class ReN implements Plugin.PluginBase {
|
|||||||
const novels: Plugin.NovelItem[] = body.content.map((novel) => ({
|
const novels: Plugin.NovelItem[] = body.content.map((novel) => ({
|
||||||
name: novel.main_name || novel.secondary_name,
|
name: novel.main_name || novel.secondary_name,
|
||||||
cover: this.site + (novel.img.high || novel.img.mid || novel.img.low),
|
cover: this.site + (novel.img.high || novel.img.mid || novel.img.low),
|
||||||
path: "/novel/" + novel.dir,
|
path: novel.dir,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
async parseNovel(novelID: string): Promise<Plugin.SourceNovel> {
|
||||||
const novelID = novelPath.split("/")[2];
|
|
||||||
const result = await fetchApi(this.site + "/api/titles/" + novelID);
|
const result = await fetchApi(this.site + "/api/titles/" + novelID);
|
||||||
const body = (await result.json()) as { content: responseNovel };
|
const body = (await result.json()) as { content: responseNovel };
|
||||||
|
|
||||||
const novel: Plugin.SourceNovel = {
|
const novel: Plugin.SourceNovel = {
|
||||||
path: novelPath,
|
path: novelID,
|
||||||
name:
|
name:
|
||||||
body.content.main_name ||
|
body.content.main_name ||
|
||||||
body.content.secondary_name ||
|
body.content.secondary_name ||
|
||||||
@@ -104,7 +103,7 @@ class ReN implements Plugin.PluginBase {
|
|||||||
"Том " + chapter.tome +
|
"Том " + chapter.tome +
|
||||||
" Глава " + chapter.chapter +
|
" Глава " + chapter.chapter +
|
||||||
(chapter.name ? " " + chapter.name.trim() : ""),
|
(chapter.name ? " " + chapter.name.trim() : ""),
|
||||||
path: "/novel/" + novelID + "/" + chapter.id,
|
path: novelID + "/" + chapter.id,
|
||||||
releaseTime: dayjs(chapter.upload_date).format("LLL"),
|
releaseTime: dayjs(chapter.upload_date).format("LLL"),
|
||||||
chapterNumber: chapter.index,
|
chapterNumber: chapter.index,
|
||||||
});
|
});
|
||||||
@@ -117,7 +116,7 @@ class ReN implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
const url = this.site + "/api/titles/chapters/" + chapterPath.split("/")[3];
|
const url = this.site + "/api/titles/chapters/" + chapterPath.split("/")[1];
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
const body = (await result.json()) as { content: responseСhapter };
|
const body = (await result.json()) as { content: responseСhapter };
|
||||||
|
|
||||||
@@ -137,7 +136,7 @@ class ReN implements Plugin.PluginBase {
|
|||||||
novels.push({
|
novels.push({
|
||||||
name: novel.main_name || novel.secondary_name,
|
name: novel.main_name || novel.secondary_name,
|
||||||
cover: this.site + (novel.img.high || novel.img.mid || novel.img.low),
|
cover: this.site + (novel.img.high || novel.img.mid || novel.img.low),
|
||||||
path: "/novel/" + novel.dir,
|
path: novel.dir,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -145,6 +144,7 @@ class ReN implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fetchImage = fetchFile;
|
fetchImage = fetchFile;
|
||||||
|
resolveUrl = (path: string, isNovel?: boolean) => this.site + "/novel/" + path;
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
sort: {
|
sort: {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Plugin } from "@typings/plugin";
|
|||||||
import { NovelStatus } from "@libs/novelStatus";
|
import { NovelStatus } from "@libs/novelStatus";
|
||||||
import { load as parseHTML } from "cheerio";
|
import { load as parseHTML } from "cheerio";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import qs from "qs";
|
||||||
|
|
||||||
export interface ReadwnMetadata {
|
export interface ReadwnMetadata {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -191,9 +192,12 @@ class ReadwnPlugin implements Plugin.PluginBase {
|
|||||||
Origin: this.site,
|
Origin: this.site,
|
||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body:
|
body: qs.stringify({
|
||||||
"show=title&tempid=1&tbname=news&keyboard=" +
|
show: "title",
|
||||||
encodeURIComponent(searchTerm),
|
tempid: 1,
|
||||||
|
tbname: "news",
|
||||||
|
keyboard: searchTerm,
|
||||||
|
}),
|
||||||
}).then((res) => res.text());
|
}).then((res) => res.text());
|
||||||
const loadedCheerio = parseHTML(result);
|
const loadedCheerio = parseHTML(result);
|
||||||
|
|
||||||
@@ -205,7 +209,6 @@ class ReadwnPlugin implements Plugin.PluginBase {
|
|||||||
}))
|
}))
|
||||||
.get()
|
.get()
|
||||||
.filter((novel) => novel.name && novel.path);
|
.filter((novel) => novel.name && novel.path);
|
||||||
|
|
||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,3 +87,14 @@ export const parseChapter = async (
|
|||||||
|
|
||||||
export const fetchImage = async (pluginRequirePath: string, url: string) =>
|
export const fetchImage = async (pluginRequirePath: string, url: string) =>
|
||||||
(await getPlugin(pluginRequirePath))?.fetchImage(url);
|
(await getPlugin(pluginRequirePath))?.fetchImage(url);
|
||||||
|
|
||||||
|
export const resolveUrl = async (
|
||||||
|
pluginRequirePath: string,
|
||||||
|
path: string,
|
||||||
|
isNovel?: boolean,
|
||||||
|
) => {
|
||||||
|
const plugin = await getPlugin(pluginRequirePath);
|
||||||
|
if (!plugin) return path;
|
||||||
|
if (plugin.resolveUrl) return plugin.resolveUrl(path, isNovel);
|
||||||
|
return plugin.site + path;
|
||||||
|
};
|
||||||
@@ -357,6 +357,27 @@
|
|||||||
<img src="" style="min-height: 300px; max-width: 450px;" alt="">
|
<img src="" style="min-height: 300px; max-width: 450px;" alt="">
|
||||||
</accordion-content>
|
</accordion-content>
|
||||||
</accordion-box>
|
</accordion-box>
|
||||||
|
<accordion-box id="resolveUrl" collapsed>
|
||||||
|
<accordion-header>
|
||||||
|
resolveUrl
|
||||||
|
</accordion-header>
|
||||||
|
<accordion-content>
|
||||||
|
<div class="input-group">
|
||||||
|
<select class="form-control">
|
||||||
|
<option>novel</option>
|
||||||
|
<option>chapter</option>
|
||||||
|
</select>
|
||||||
|
<input type="path" class="form-control" placeholder="path">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-primary resolveUrl-btn" type="button">fetch</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="spinner-border" role="status">
|
||||||
|
<span class="sr-only">Loading...</span>
|
||||||
|
</div>
|
||||||
|
<input readonly>
|
||||||
|
</accordion-content>
|
||||||
|
</accordion-box>
|
||||||
</main-accordion>
|
</main-accordion>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ class AccordionHeader extends HTMLElement {
|
|||||||
return this.parentElement;
|
return this.parentElement;
|
||||||
}
|
}
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
console.log("I AM ON THE SCREEN, DO YOUR FUCKING JOB");
|
|
||||||
const childNodes = Array.from(this.childNodes);
|
const childNodes = Array.from(this.childNodes);
|
||||||
this.innerHTML = "";
|
this.innerHTML = "";
|
||||||
this.btn.append(...childNodes);
|
this.btn.append(...childNodes);
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ const parseChapter = $("#parseChapter");
|
|||||||
|
|
||||||
/** @type {JQuery<AccordionBox>} */
|
/** @type {JQuery<AccordionBox>} */
|
||||||
const fetchImage = $("#fetchImage");
|
const fetchImage = $("#fetchImage");
|
||||||
|
/** @type {JQuery<AccordionBox>} */
|
||||||
|
const resolveUrl = $("#resolveUrl");
|
||||||
|
|
||||||
/** @type {JQuery<HTMLSelectElement>}*/
|
/** @type {JQuery<HTMLSelectElement>}*/
|
||||||
const plugin_language_selection = $("#language");
|
const plugin_language_selection = $("#language");
|
||||||
@@ -1149,6 +1151,37 @@ class PluginWrapper {
|
|||||||
spinner.hide();
|
spinner.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async resolveUrl() {
|
||||||
|
const type = $("#resolveUrl select").val();
|
||||||
|
const path = $("#resolveUrl input").val();
|
||||||
|
const spinner = $("#resolveUrl .spinner-border");
|
||||||
|
spinner.show();
|
||||||
|
try {
|
||||||
|
const fullURL = await (
|
||||||
|
await fetchFromAPI("/resolveUrl/", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
pluginRequirePath: this.requirePath,
|
||||||
|
isNovel: type === "novel",
|
||||||
|
path,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
).text();
|
||||||
|
$("#resolveUrl input[readonly]").val(fullURL)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
if (e)
|
||||||
|
$("#resolveUrl").text(
|
||||||
|
`${typeof e === "object" && "message" in e ? e.message : e}`
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
spinner.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const currentWrapper = () => state.current_plugin || emptyPluginWrapper;
|
const currentWrapper = () => state.current_plugin || emptyPluginWrapper;
|
||||||
const emptyPluginWrapper = new PluginWrapper("");
|
const emptyPluginWrapper = new PluginWrapper("");
|
||||||
@@ -1566,6 +1599,7 @@ $(".parsePage-btn").on("click", () =>
|
|||||||
|
|
||||||
$(".parseChapter-btn").on("click", () => state.current_plugin?.getChapter());
|
$(".parseChapter-btn").on("click", () => state.current_plugin?.getChapter());
|
||||||
$(".fetchImage-btn").on("click", () => state.current_plugin?.fetchImage());
|
$(".fetchImage-btn").on("click", () => state.current_plugin?.fetchImage());
|
||||||
|
$(".resolveUrl-btn").on("click", () => state.current_plugin?.resolveUrl());
|
||||||
|
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -92,6 +92,7 @@ export namespace Plugin {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
fetchImage(url: string): Promise<string | undefined>;
|
fetchImage(url: string): Promise<string | undefined>;
|
||||||
|
resolveUrl?(path: string, isNovel?: boolean): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PagePlugin extends PluginBase {
|
export interface PagePlugin extends PluginBase {
|
||||||
@@ -104,4 +105,4 @@ export namespace Plugin {
|
|||||||
*/
|
*/
|
||||||
parsePage(novelPath: string, page: string): Promise<SourcePage>;
|
parsePage(novelPath: string, page: string): Promise<SourcePage>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user