Almost english sources (#709)
* fix: LightNovelPub * fix: novelpub -> lightnovelworld * still broken, wuxiaworld keep html in js variables * fix: NovelsOnline * fix: novelhole -> novelhall * fix: novelfull * fix: mtl reader * refactor: mtlnovel * temporary fix: lightnovelupdates * still cant work * cant work * cant parseNovel and chapters: Wuxiaworld
@@ -41,7 +41,7 @@ var fetch_1 = require("@libs/fetch");
|
|||||||
var NovelFull = /** @class */ (function () {
|
var NovelFull = /** @class */ (function () {
|
||||||
function NovelFull() {
|
function NovelFull() {
|
||||||
this.id = "NF.me";
|
this.id = "NF.me";
|
||||||
this.name = "NovelFull";
|
this.name = "NovelFullMe";
|
||||||
this.site = "https://novelfull.me/";
|
this.site = "https://novelfull.me/";
|
||||||
this.version = "1.0.0";
|
this.version = "1.0.0";
|
||||||
this.icon = "src/en/novelfullme/icon.png";
|
this.icon = "src/en/novelfullme/icon.png";
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||||
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var cheerio_1 = require("cheerio");
|
||||||
|
var fetch_1 = require("@libs/fetch");
|
||||||
|
var NovelsOnline = /** @class */ (function () {
|
||||||
|
function NovelsOnline() {
|
||||||
|
this.id = "NO.net";
|
||||||
|
this.name = "novelsOnline";
|
||||||
|
this.site = "https://novelsonline.net";
|
||||||
|
this.icon = "src/coverNotAvailable.jpg";
|
||||||
|
this.version = "1.0.0";
|
||||||
|
}
|
||||||
|
NovelsOnline.prototype.popularNovels = function (pageNo, options) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
return [2 /*return*/, []]; /** TO DO */
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelsOnline.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var novel, result, $;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
novel = {
|
||||||
|
url: novelUrl,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(novelUrl).then(function (res) { return res.text(); })];
|
||||||
|
case 1:
|
||||||
|
result = _a.sent();
|
||||||
|
$ = (0, cheerio_1.load)(result);
|
||||||
|
novel.name = $("h1").text();
|
||||||
|
novel.cover = $(".novel-cover").find("a > img").attr("src");
|
||||||
|
novel.author = $("div.novel-details > div:nth-child(5) > div.novel-detail-body")
|
||||||
|
.find("li")
|
||||||
|
.map(function (_, el) { return $(el).text(); })
|
||||||
|
.get()
|
||||||
|
.join(", ");
|
||||||
|
novel.genres = $("div.novel-details > div:nth-child(2) > div.novel-detail-body")
|
||||||
|
.find("li")
|
||||||
|
.map(function (_, el) { return $(el).text(); })
|
||||||
|
.get()
|
||||||
|
.join(",");
|
||||||
|
novel.summary = $("div.novel-right > div > div:nth-child(1) > div.novel-detail-body").text();
|
||||||
|
novel.chapters = $("ul.chapter-chs > li > a")
|
||||||
|
.map(function (_, el) {
|
||||||
|
var chapterUrl = $(el).attr("href");
|
||||||
|
var chapterName = $(el).text();
|
||||||
|
return {
|
||||||
|
name: chapterName,
|
||||||
|
releaseTime: "",
|
||||||
|
url: chapterUrl,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
return [2 /*return*/, novel];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelsOnline.prototype.parseChapter = function (chapterUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var result, loadedCheerio, chapterText;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl).then(function (res) { return res.text(); })];
|
||||||
|
case 1:
|
||||||
|
result = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(result);
|
||||||
|
chapterText = loadedCheerio("#contentall").html() || "";
|
||||||
|
return [2 /*return*/, chapterText];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelsOnline.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var result, $, headers;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)("https://novelsonline.net/sResults.php", {
|
||||||
|
headers: {
|
||||||
|
Accept: "*/*",
|
||||||
|
"Accept-Language": "pl,en-US;q=0.7,en;q=0.3",
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
body: "q=" + encodeURIComponent(searchTerm),
|
||||||
|
}).then(function (res) { return res.text(); })];
|
||||||
|
case 1:
|
||||||
|
result = _a.sent();
|
||||||
|
$ = (0, cheerio_1.load)(result);
|
||||||
|
headers = $("li");
|
||||||
|
return [2 /*return*/, headers
|
||||||
|
.map(function (i, h) {
|
||||||
|
var novelName = $(h).text();
|
||||||
|
var novelUrl = $(h).find("a").attr("href");
|
||||||
|
var novelCover = $(h).find("img").attr("src");
|
||||||
|
if (!novelUrl) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.get()
|
||||||
|
.filter(function (sr) { return sr !== null; })];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelsOnline.prototype.fetchImage = function (url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
return [2 /*return*/, (0, fetch_1.fetchFile)(url)];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return NovelsOnline;
|
||||||
|
}());
|
||||||
|
exports.default = new NovelsOnline();
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||||
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var cheerio_1 = require("cheerio");
|
||||||
|
var fetch_1 = require("@libs/fetch");
|
||||||
|
var LightNovelPub = /** @class */ (function () {
|
||||||
|
function LightNovelPub() {
|
||||||
|
this.id = "lightnovelpub";
|
||||||
|
this.name = "LightNovelPub";
|
||||||
|
this.version = "1.0.0";
|
||||||
|
this.icon = "src/en/lightnovelpub/icon.png";
|
||||||
|
this.site = "https://www.lightnovelpub.com/";
|
||||||
|
this.baseUrl = this.site;
|
||||||
|
this.headers = {
|
||||||
|
Accept: "application/json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
LightNovelPub.prototype.popularNovels = function (pageNo, options) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, body, loadedCheerio, novels;
|
||||||
|
var _this = this;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = "".concat(this.baseUrl, "browse/all/popular/all/").concat(pageNo);
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novels = [];
|
||||||
|
loadedCheerio(".novel-item.ads").remove();
|
||||||
|
loadedCheerio(".novel-item").each(function (idx, ele) {
|
||||||
|
var _a;
|
||||||
|
var novelName = loadedCheerio(ele)
|
||||||
|
.find(".novel-title")
|
||||||
|
.text()
|
||||||
|
.trim();
|
||||||
|
var novelCover = loadedCheerio(ele).find("img").attr("data-src");
|
||||||
|
var novelUrl = _this.baseUrl +
|
||||||
|
((_a = loadedCheerio(ele)
|
||||||
|
.find(".novel-title > a")
|
||||||
|
.attr("href")) === null || _a === void 0 ? void 0 : _a.substring(1));
|
||||||
|
var novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
LightNovelPub.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||||
|
var _a;
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, body, loadedCheerio, novel, delay, chapterListUrl, chapterListBody, loadedChapterList, lastNumber, lastChapterUrl, chapterUrlPrefix, chapters, i;
|
||||||
|
return __generator(this, function (_b) {
|
||||||
|
switch (_b.label) {
|
||||||
|
case 0:
|
||||||
|
url = novelUrl;
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _b.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novel = {
|
||||||
|
url: url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
novel.name = loadedCheerio("h1.novel-title").text().trim();
|
||||||
|
novel.cover = loadedCheerio("figure.cover > img").attr("data-src");
|
||||||
|
novel.genres = "";
|
||||||
|
loadedCheerio(".categories > ul > li > a").each(function () {
|
||||||
|
novel.genres += loadedCheerio(this).text() + ",";
|
||||||
|
});
|
||||||
|
novel.genres = novel.genres.slice(0, -1);
|
||||||
|
loadedCheerio("div.header-stats > span").each(function () {
|
||||||
|
if (loadedCheerio(this).find("small").text() === "Status") {
|
||||||
|
novel.status = loadedCheerio(this).find("strong").text();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
novel.author = loadedCheerio(".author > a > span").text();
|
||||||
|
novel.summary = loadedCheerio(".summary > .content").text().trim();
|
||||||
|
delay = function (ms) { return new Promise(function (res) { return setTimeout(res, ms); }); };
|
||||||
|
return [4 /*yield*/, delay(1000)];
|
||||||
|
case 2:
|
||||||
|
_b.sent();
|
||||||
|
chapterListUrl = novelUrl + '/chapters?chorder=desc';
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterListUrl).then(function (r) { return r.text(); })];
|
||||||
|
case 3:
|
||||||
|
chapterListBody = _b.sent();
|
||||||
|
loadedChapterList = (0, cheerio_1.load)(chapterListBody);
|
||||||
|
lastNumber = parseInt((_a = loadedChapterList('ul.chapter-list > li:nth-child(1)').attr('data-chapterno')) !== null && _a !== void 0 ? _a : '0');
|
||||||
|
lastChapterUrl = loadedChapterList('ul.chapter-list > li:nth-child(1) > a').attr('href');
|
||||||
|
if (lastChapterUrl) {
|
||||||
|
chapterUrlPrefix = this.baseUrl + lastChapterUrl.split('/').slice(1, -1).join('/');
|
||||||
|
chapters = [];
|
||||||
|
for (i = 1; i <= lastNumber; i++) {
|
||||||
|
chapters.push({
|
||||||
|
chapterNumber: i,
|
||||||
|
name: 'Chapter ' + i,
|
||||||
|
url: chapterUrlPrefix + '/chapter-' + i,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
novel.chapters = chapters;
|
||||||
|
}
|
||||||
|
return [2 /*return*/, novel];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
LightNovelPub.prototype.parseChapter = function (chapterUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var body, loadedCheerio, chapterText;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
chapterText = loadedCheerio("#chapter-container").html() || '';
|
||||||
|
return [2 /*return*/, chapterText];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
LightNovelPub.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, link, response, token, verifytoken, formData, body, loadedCheerio, novels, results;
|
||||||
|
var _this = this;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = "".concat(this.baseUrl, "lnsearchlive");
|
||||||
|
link = "".concat(this.baseUrl, "search");
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(link).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
response = _a.sent();
|
||||||
|
token = (0, cheerio_1.load)(response);
|
||||||
|
verifytoken = token("#novelSearchForm > input").attr("value");
|
||||||
|
formData = new FormData();
|
||||||
|
formData.append("inputContent", searchTerm);
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { LNRequestVerifyToken: verifytoken },
|
||||||
|
body: formData,
|
||||||
|
}).then(function (r) { return r.text(); })];
|
||||||
|
case 2:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novels = [];
|
||||||
|
results = JSON.parse(loadedCheerio("body").text());
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(results.resultview);
|
||||||
|
loadedCheerio(".novel-item").each(function (idx, ele) {
|
||||||
|
var _a;
|
||||||
|
var novelName = loadedCheerio(ele)
|
||||||
|
.find("h4.novel-title")
|
||||||
|
.text()
|
||||||
|
.trim();
|
||||||
|
var novelCover = loadedCheerio(ele).find("img").attr("src");
|
||||||
|
var novelUrl = _this.baseUrl + ((_a = loadedCheerio(ele).find("a").attr("href")) === null || _a === void 0 ? void 0 : _a.substring(1));
|
||||||
|
novels.push({
|
||||||
|
name: novelName,
|
||||||
|
url: novelUrl,
|
||||||
|
cover: novelCover,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
LightNovelPub.prototype.fetchImage = function (url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
return [2 /*return*/, (0, fetch_1.fetchFile)(url, { headers: this.headers })];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return LightNovelPub;
|
||||||
|
}());
|
||||||
|
exports.default = new LightNovelPub();
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||||
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var cheerio_1 = require("cheerio");
|
||||||
|
var fetch_1 = require("@libs/fetch");
|
||||||
|
var LightNovelUpdates = /** @class */ (function () {
|
||||||
|
function LightNovelUpdates() {
|
||||||
|
this.id = "LightNovelUpdates";
|
||||||
|
this.name = "Light Novel Updates";
|
||||||
|
this.version = "1.0.0";
|
||||||
|
this.icon = "src/en/lightnovelupdates/icon.png";
|
||||||
|
this.site = "https://www.lightnovelupdates.com/";
|
||||||
|
this.baseUrl = this.site;
|
||||||
|
}
|
||||||
|
LightNovelUpdates.prototype.popularNovels = function (pageNo, options) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, body, loadedCheerio, novels;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = "".concat(this.baseUrl, "novel/page/").concat(pageNo, "/?m_orderby=rating");
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novels = [];
|
||||||
|
loadedCheerio(".page-item-detail").each(function () {
|
||||||
|
var novelName = loadedCheerio(this).find(".h5 > a").text();
|
||||||
|
var novelCover = loadedCheerio(this).find("img").attr("src");
|
||||||
|
var novelUrl = loadedCheerio(this).find(".h5 > a").attr("href");
|
||||||
|
if (!novelUrl)
|
||||||
|
return;
|
||||||
|
var novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
LightNovelUpdates.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, body, loadedCheerio, novel, chapter, novelId, formData, data, text;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = novelUrl;
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novel = {
|
||||||
|
url: url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
novel.name = loadedCheerio(".post-title > h1").text().trim();
|
||||||
|
novel.cover =
|
||||||
|
loadedCheerio(".summary_image > a > img").attr("src") || undefined;
|
||||||
|
loadedCheerio(".post-content_item").each(function () {
|
||||||
|
var detailName = loadedCheerio(this)
|
||||||
|
.find(".summary-heading > h5")
|
||||||
|
.text()
|
||||||
|
.trim();
|
||||||
|
var detail = loadedCheerio(this)
|
||||||
|
.find(".summary-content")
|
||||||
|
.text()
|
||||||
|
.trim();
|
||||||
|
switch (detailName) {
|
||||||
|
case "Genre(s)":
|
||||||
|
novel.genres = detail.trim().replace(/[\t\n]/g, ",");
|
||||||
|
break;
|
||||||
|
case "Author(s)":
|
||||||
|
novel.author = detail.trim();
|
||||||
|
break;
|
||||||
|
case "Status":
|
||||||
|
novel.status = detail.replace(/G/g, "g");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
novel.summary = loadedCheerio("div.summary__content").text();
|
||||||
|
chapter = [];
|
||||||
|
novelId = loadedCheerio(".rating-post-id").attr("value");
|
||||||
|
formData = new FormData();
|
||||||
|
formData.append("action", "manga_get_chapters");
|
||||||
|
formData.append("manga", novelId);
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)("https://www.lightnovelupdates.com/wp-admin/admin-ajax.php", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
})];
|
||||||
|
case 2:
|
||||||
|
data = _a.sent();
|
||||||
|
return [4 /*yield*/, data.text()];
|
||||||
|
case 3:
|
||||||
|
text = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(text);
|
||||||
|
loadedCheerio(".wp-manga-chapter").each(function () {
|
||||||
|
var chapterName = loadedCheerio(this).find("a").text().trim();
|
||||||
|
var releaseDate = loadedCheerio(this).find("span").text().trim();
|
||||||
|
var chapterUrl = loadedCheerio(this).find("a").attr("href");
|
||||||
|
if (!chapterUrl)
|
||||||
|
return;
|
||||||
|
chapter.push({
|
||||||
|
name: chapterName,
|
||||||
|
releaseTime: releaseDate,
|
||||||
|
url: chapterUrl,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
novel.chapters = chapter.reverse();
|
||||||
|
return [2 /*return*/, novel];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
LightNovelUpdates.prototype.parseChapter = function (chapterUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var chapterText;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
chapterText = 'unable to load';
|
||||||
|
return [2 /*return*/, chapterText];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
LightNovelUpdates.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, body, loadedCheerio, novels;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = "".concat(this.baseUrl, "?s=").concat(searchTerm, "&post_type=wp-manga&m_orderby=rating");
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novels = [];
|
||||||
|
loadedCheerio(".c-tabs-item__content").each(function () {
|
||||||
|
var novelName = loadedCheerio(this).find(".h4 > a").text();
|
||||||
|
var novelCover = loadedCheerio(this).find("img").attr("src");
|
||||||
|
var novelUrl = loadedCheerio(this).find(".h4 > a").attr("href");
|
||||||
|
if (!novelUrl)
|
||||||
|
return;
|
||||||
|
novels.push({
|
||||||
|
url: novelUrl,
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
LightNovelUpdates.prototype.fetchImage = function (url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
return [2 /*return*/, (0, fetch_1.fetchFile)(url)];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return LightNovelUpdates;
|
||||||
|
}());
|
||||||
|
exports.default = new LightNovelUpdates();
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||||
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var cheerio_1 = require("cheerio");
|
||||||
|
var fetch_1 = require("@libs/fetch");
|
||||||
|
var LightNovelWorld = /** @class */ (function () {
|
||||||
|
function LightNovelWorld() {
|
||||||
|
this.id = "lightnovelworld";
|
||||||
|
this.name = "LightNovelWorld";
|
||||||
|
this.site = "https://www.webnovelworld.org/";
|
||||||
|
this.version = "1.0.0";
|
||||||
|
this.icon = "src/en/lightnovelworld/icon.png";
|
||||||
|
this.baseUrl = this.site;
|
||||||
|
this.headers = {
|
||||||
|
Accept: "application/json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
LightNovelWorld.prototype.popularNovels = function (pageNo, options) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, body, loadedCheerio, novels;
|
||||||
|
var _this = this;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = "".concat(this.baseUrl, "browse/all/popular/all/").concat(pageNo);
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novels = [];
|
||||||
|
loadedCheerio(".novel-item.ads").remove();
|
||||||
|
loadedCheerio(".novel-item").each(function (idx, ele) {
|
||||||
|
var _a;
|
||||||
|
var novelName = loadedCheerio(ele)
|
||||||
|
.find(".novel-title")
|
||||||
|
.text()
|
||||||
|
.trim();
|
||||||
|
var novelCover = loadedCheerio(ele).find("img").attr("data-src");
|
||||||
|
var novelUrl = _this.baseUrl +
|
||||||
|
((_a = loadedCheerio(ele)
|
||||||
|
.find(".novel-title > a")
|
||||||
|
.attr("href")) === null || _a === void 0 ? void 0 : _a.substring(1));
|
||||||
|
var novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
LightNovelWorld.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||||
|
var _a;
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, body, loadedCheerio, novel, delay, chapterListUrl, chapterListBody, loadedChapterList, lastNumber, lastChapterUrl, chapterUrlPrefix, chapters, i;
|
||||||
|
return __generator(this, function (_b) {
|
||||||
|
switch (_b.label) {
|
||||||
|
case 0:
|
||||||
|
url = novelUrl;
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _b.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novel = {
|
||||||
|
url: url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
novel.name = loadedCheerio("h1.novel-title").text().trim();
|
||||||
|
novel.cover = loadedCheerio("figure.cover > img").attr("data-src");
|
||||||
|
novel.genres = "";
|
||||||
|
loadedCheerio(".categories > ul > li > a").each(function () {
|
||||||
|
novel.genres += loadedCheerio(this).text() + ",";
|
||||||
|
});
|
||||||
|
novel.genres = novel.genres.slice(0, -1);
|
||||||
|
loadedCheerio("div.header-stats > span").each(function () {
|
||||||
|
if (loadedCheerio(this).find("small").text() === "Status") {
|
||||||
|
novel.status = loadedCheerio(this).find("strong").text();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
novel.author = loadedCheerio(".author > a > span").text();
|
||||||
|
novel.summary = loadedCheerio(".summary > .content").text().trim();
|
||||||
|
delay = function (ms) { return new Promise(function (res) { return setTimeout(res, ms); }); };
|
||||||
|
return [4 /*yield*/, delay(1000)];
|
||||||
|
case 2:
|
||||||
|
_b.sent();
|
||||||
|
chapterListUrl = novelUrl + '/chapters?chorder=desc';
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterListUrl).then(function (r) { return r.text(); })];
|
||||||
|
case 3:
|
||||||
|
chapterListBody = _b.sent();
|
||||||
|
loadedChapterList = (0, cheerio_1.load)(chapterListBody);
|
||||||
|
lastNumber = parseInt((_a = loadedChapterList('ul.chapter-list > li:nth-child(1)').attr('data-chapterno')) !== null && _a !== void 0 ? _a : '0');
|
||||||
|
lastChapterUrl = loadedChapterList('ul.chapter-list > li:nth-child(1) > a').attr('href');
|
||||||
|
if (lastChapterUrl) {
|
||||||
|
chapterUrlPrefix = this.baseUrl + lastChapterUrl.split('/').slice(1, -1).join('/');
|
||||||
|
chapters = [];
|
||||||
|
for (i = 1; i <= lastNumber; i++) {
|
||||||
|
chapters.push({
|
||||||
|
chapterNumber: i,
|
||||||
|
name: 'Chapter ' + i,
|
||||||
|
url: chapterUrlPrefix + '/chapter-' + i,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
novel.chapters = chapters;
|
||||||
|
}
|
||||||
|
return [2 /*return*/, novel];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
LightNovelWorld.prototype.parseChapter = function (chapterUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var body, loadedCheerio, chapterText;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
chapterText = loadedCheerio("#chapter-container").html() || '';
|
||||||
|
return [2 /*return*/, chapterText];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
LightNovelWorld.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, link, response, token, verifytoken, formData, body, loadedCheerio, novels, results;
|
||||||
|
var _this = this;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = "".concat(this.baseUrl, "lnsearchlive");
|
||||||
|
link = "".concat(this.baseUrl, "search");
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(link).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
response = _a.sent();
|
||||||
|
token = (0, cheerio_1.load)(response);
|
||||||
|
verifytoken = token("#novelSearchForm > input").attr("value");
|
||||||
|
formData = new FormData();
|
||||||
|
formData.append("inputContent", searchTerm);
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { LNRequestVerifyToken: verifytoken },
|
||||||
|
body: formData,
|
||||||
|
}).then(function (r) { return r.text(); })];
|
||||||
|
case 2:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novels = [];
|
||||||
|
results = JSON.parse(loadedCheerio("body").text());
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(results.resultview);
|
||||||
|
loadedCheerio(".novel-item").each(function (idx, ele) {
|
||||||
|
var _a;
|
||||||
|
var novelName = loadedCheerio(ele)
|
||||||
|
.find("h4.novel-title")
|
||||||
|
.text()
|
||||||
|
.trim();
|
||||||
|
var novelCover = loadedCheerio(ele).find("img").attr("src");
|
||||||
|
var novelUrl = _this.baseUrl + ((_a = loadedCheerio(ele).find("a").attr("href")) === null || _a === void 0 ? void 0 : _a.substring(1));
|
||||||
|
novels.push({
|
||||||
|
name: novelName,
|
||||||
|
url: novelUrl,
|
||||||
|
cover: novelCover,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
LightNovelWorld.prototype.fetchImage = function (url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
return [2 /*return*/, (0, fetch_1.fetchFile)(url, { headers: this.headers })];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return LightNovelWorld;
|
||||||
|
}());
|
||||||
|
exports.default = new LightNovelWorld();
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||||
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var cheerio_1 = require("cheerio");
|
||||||
|
var fetch_1 = require("@libs/fetch");
|
||||||
|
var MTLNovel = /** @class */ (function () {
|
||||||
|
function MTLNovel() {
|
||||||
|
this.id = "mtlnovel";
|
||||||
|
this.name = "MTL Novel";
|
||||||
|
this.version = "1.0.0";
|
||||||
|
this.icon = "src/en/mtlnovel/icon.png";
|
||||||
|
this.site = "https://www.mtlnovel.com/";
|
||||||
|
this.baseUrl = this.site;
|
||||||
|
}
|
||||||
|
MTLNovel.prototype.popularNovels = function (pageNo, options) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, body, loadedCheerio, novels;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = "".concat(this.baseUrl, "alltime-rank/page/").concat(pageNo);
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (result) { return result.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
console.log(body);
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novels = [];
|
||||||
|
loadedCheerio("div.box.wide").each(function () {
|
||||||
|
var novelName = loadedCheerio(this)
|
||||||
|
.find("a.list-title")
|
||||||
|
.text()
|
||||||
|
.slice(4);
|
||||||
|
var novelCover = loadedCheerio(this).find("amp-img").attr("src");
|
||||||
|
var novelUrl = loadedCheerio(this).find("a.list-title").attr("href");
|
||||||
|
if (!novelUrl)
|
||||||
|
return;
|
||||||
|
var novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
MTLNovel.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
function getChapters() {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var listResult, listBody, chapter;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0: return [4 /*yield*/, fetch(chapterListUrl)];
|
||||||
|
case 1:
|
||||||
|
listResult = _a.sent();
|
||||||
|
return [4 /*yield*/, listResult.text()];
|
||||||
|
case 2:
|
||||||
|
listBody = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(listBody);
|
||||||
|
chapter = [];
|
||||||
|
loadedCheerio("div.ch-list")
|
||||||
|
.find("a.ch-link")
|
||||||
|
.each(function () {
|
||||||
|
var chapterName = loadedCheerio(this)
|
||||||
|
.text()
|
||||||
|
.replace("~ ", "");
|
||||||
|
var releaseDate = null;
|
||||||
|
var chapterUrl = loadedCheerio(this).attr("href");
|
||||||
|
if (chapterUrl) {
|
||||||
|
chapter.push({
|
||||||
|
url: chapterUrl,
|
||||||
|
name: chapterName,
|
||||||
|
releaseTime: releaseDate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return [2 /*return*/, chapter.reverse()];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var url, headers, body, loadedCheerio, novel, chapterListUrl, _a;
|
||||||
|
return __generator(this, function (_b) {
|
||||||
|
switch (_b.label) {
|
||||||
|
case 0:
|
||||||
|
url = novelUrl;
|
||||||
|
headers = {
|
||||||
|
Referer: "".concat(this.baseUrl, "alltime-rank/"),
|
||||||
|
};
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url, {
|
||||||
|
method: "GET",
|
||||||
|
headers: headers,
|
||||||
|
}).then(function (result) { return result.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _b.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novel = {
|
||||||
|
url: url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
novel.name = loadedCheerio("h1.entry-title").text();
|
||||||
|
novel.cover = loadedCheerio(".nov-head > amp-img").attr("src");
|
||||||
|
novel.summary = loadedCheerio("div.desc > h2").next().text().trim();
|
||||||
|
novel.author = loadedCheerio("#author").text();
|
||||||
|
novel.status = loadedCheerio("#status").text();
|
||||||
|
novel.genres = loadedCheerio("#genre").text().replace(/\s+/g, "");
|
||||||
|
chapterListUrl = url + "chapter-list/";
|
||||||
|
_a = novel;
|
||||||
|
return [4 /*yield*/, getChapters()];
|
||||||
|
case 2:
|
||||||
|
_a.chapters = _b.sent();
|
||||||
|
return [2 /*return*/, novel];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
MTLNovel.prototype.parseChapter = function (chapterUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var body, loadedCheerio, chapterText;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
chapterText = loadedCheerio("div.par").html() || '';
|
||||||
|
return [2 /*return*/, chapterText];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
MTLNovel.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var searchUrl, res, result, novels;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
searchUrl = this.baseUrl +
|
||||||
|
"wp-admin/admin-ajax.php?action=autosuggest&q=" +
|
||||||
|
searchTerm +
|
||||||
|
"&__amp_source_origin=https%3A%2F%2Fwww.mtlnovel.com";
|
||||||
|
return [4 /*yield*/, fetch(searchUrl)];
|
||||||
|
case 1:
|
||||||
|
res = _a.sent();
|
||||||
|
return [4 /*yield*/, res.json()];
|
||||||
|
case 2:
|
||||||
|
result = _a.sent();
|
||||||
|
novels = [];
|
||||||
|
result.items[0].results.map(function (item) {
|
||||||
|
var novelName = item.title.replace(/<\/?strong>/g, "");
|
||||||
|
var novelCover = item.thumbnail;
|
||||||
|
var novelUrl = item.permalink.replace("https://www.mtlnovel.com/", "");
|
||||||
|
var novel = { name: novelName, cover: novelCover, url: novelUrl };
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
MTLNovel.prototype.fetchImage = function (url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
return [2 /*return*/, (0, fetch_1.fetchFile)(url)];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return MTLNovel;
|
||||||
|
}());
|
||||||
|
exports.default = new MTLNovel();
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||||
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var cheerio_1 = require("cheerio");
|
||||||
|
var fetch_1 = require("@libs/fetch");
|
||||||
|
var MTLReader = /** @class */ (function () {
|
||||||
|
function MTLReader() {
|
||||||
|
this.id = "mtlreader";
|
||||||
|
this.name = "MTL Reader";
|
||||||
|
this.version = "1.0.0";
|
||||||
|
this.icon = "src/en/mtlreader/icon.png";
|
||||||
|
this.site = "https://mtlreader.com/";
|
||||||
|
this.baseUrl = this.site;
|
||||||
|
}
|
||||||
|
MTLReader.prototype.popularNovels = function (pageNo, options) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, body, loadedCheerio, novels;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = "".concat(this.baseUrl, "novels?page=").concat(pageNo);
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novels = [];
|
||||||
|
loadedCheerio(".col-md-4.col-sm-4").each(function () {
|
||||||
|
var novelName = loadedCheerio(this).find("h5").text();
|
||||||
|
var novelCover = loadedCheerio(this).find("img").attr("src");
|
||||||
|
var novelUrl = loadedCheerio(this).find("h5 > a").attr("href");
|
||||||
|
if (!novelUrl)
|
||||||
|
return;
|
||||||
|
var novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
MTLReader.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, body, loadedCheerio, novel, chapter;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = novelUrl;
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novel = {
|
||||||
|
url: url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
novel.name = loadedCheerio(".agent-title").text().trim();
|
||||||
|
novel.cover = loadedCheerio(".agent-p-img > img").attr("src");
|
||||||
|
novel.summary = loadedCheerio("#editdescription").text().trim();
|
||||||
|
novel.author = loadedCheerio("i.fa-user")
|
||||||
|
.parent()
|
||||||
|
.text()
|
||||||
|
.replace("Author: ", "")
|
||||||
|
.trim();
|
||||||
|
chapter = [];
|
||||||
|
loadedCheerio("tr.spaceUnder").each(function () {
|
||||||
|
var chapterName = loadedCheerio(this).find("a").text().trim();
|
||||||
|
var releaseDate = null;
|
||||||
|
var chapterUrl = loadedCheerio(this).find("a").attr("href");
|
||||||
|
if (!chapterUrl)
|
||||||
|
return;
|
||||||
|
chapter.push({
|
||||||
|
name: chapterName,
|
||||||
|
releaseTime: releaseDate,
|
||||||
|
url: chapterUrl,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
novel.chapters = chapter;
|
||||||
|
return [2 /*return*/, novel];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
MTLReader.prototype.parseChapter = function (chapterUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var body, loadedCheerio, chapterText;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
loadedCheerio(".container ins,script,p.mtlreader").remove();
|
||||||
|
chapterText = loadedCheerio(".container").html() || '';
|
||||||
|
return [2 /*return*/, chapterText];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
MTLReader.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, body, loadedCheerio, novels;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = "".concat(this.baseUrl, "search?input=").concat(searchTerm);
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novels = [];
|
||||||
|
loadedCheerio(".col-md-4.col-sm-4").each(function () {
|
||||||
|
var novelName = loadedCheerio(this).find("h5").text();
|
||||||
|
var novelCover = loadedCheerio(this).find("img").attr("src");
|
||||||
|
var novelUrl = loadedCheerio(this).find("h5 > a").attr("href");
|
||||||
|
if (!novelUrl)
|
||||||
|
return;
|
||||||
|
var novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
MTLReader.prototype.fetchImage = function (url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
return [2 /*return*/, (0, fetch_1.fetchFile)(url)];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return MTLReader;
|
||||||
|
}());
|
||||||
|
exports.default = new MTLReader();
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||||
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var cheerio_1 = require("cheerio");
|
||||||
|
var fetch_1 = require("@libs/fetch");
|
||||||
|
var NovelFull = /** @class */ (function () {
|
||||||
|
function NovelFull() {
|
||||||
|
this.id = "novelfull";
|
||||||
|
this.name = "NovelFull";
|
||||||
|
this.version = "1.0.0";
|
||||||
|
this.icon = "src/en/novelfull/icon.png";
|
||||||
|
this.site = "https://novelfull.com/";
|
||||||
|
this.baseUrl = this.site;
|
||||||
|
}
|
||||||
|
NovelFull.prototype.popularNovels = function (pageNo, options) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, result, body, loadedCheerio, novels;
|
||||||
|
var _this = this;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = "".concat(this.baseUrl, "most-popular?page=").concat(pageNo);
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url)];
|
||||||
|
case 1:
|
||||||
|
result = _a.sent();
|
||||||
|
return [4 /*yield*/, result.text()];
|
||||||
|
case 2:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novels = [];
|
||||||
|
loadedCheerio(".col-truyen-main .list-truyen .row").each(function (idx, ele) {
|
||||||
|
var _a, _b;
|
||||||
|
var novelName = loadedCheerio(ele)
|
||||||
|
.find("h3.truyen-title > a")
|
||||||
|
.text();
|
||||||
|
var novelCover = _this.baseUrl + ((_a = loadedCheerio(ele).find("img").attr("src")) === null || _a === void 0 ? void 0 : _a.slice(1));
|
||||||
|
var novelUrl = _this.baseUrl +
|
||||||
|
((_b = loadedCheerio(ele)
|
||||||
|
.find("h3.truyen-title > a")
|
||||||
|
.attr("href")) === null || _b === void 0 ? void 0 : _b.slice(1));
|
||||||
|
var novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelFull.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
function getChapters(id, baseUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var chapterListUrl, data, chapterlist, chapter;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
chapterListUrl = baseUrl + "ajax/chapter-option?novelId=" + id;
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterListUrl)];
|
||||||
|
case 1:
|
||||||
|
data = _a.sent();
|
||||||
|
return [4 /*yield*/, data.text()];
|
||||||
|
case 2:
|
||||||
|
chapterlist = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(chapterlist);
|
||||||
|
chapter = [];
|
||||||
|
loadedCheerio("select > option").each(function () {
|
||||||
|
var _a;
|
||||||
|
var chapterName = loadedCheerio(this).text();
|
||||||
|
var releaseDate = null;
|
||||||
|
var chapterUrl = baseUrl + ((_a = loadedCheerio(this).attr("value")) === null || _a === void 0 ? void 0 : _a.slice(1));
|
||||||
|
chapter.push({
|
||||||
|
name: chapterName,
|
||||||
|
releaseTime: releaseDate,
|
||||||
|
url: chapterUrl,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return [2 /*return*/, chapter];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var url, result, body, loadedCheerio, novel, novelId, _a;
|
||||||
|
return __generator(this, function (_b) {
|
||||||
|
switch (_b.label) {
|
||||||
|
case 0:
|
||||||
|
url = novelUrl;
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url)];
|
||||||
|
case 1:
|
||||||
|
result = _b.sent();
|
||||||
|
return [4 /*yield*/, result.text()];
|
||||||
|
case 2:
|
||||||
|
body = _b.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novel = {
|
||||||
|
url: url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
novel.name = loadedCheerio("div.book > img").attr("alt");
|
||||||
|
novel.cover = this.baseUrl + loadedCheerio("div.book > img").attr("src");
|
||||||
|
novel.summary = loadedCheerio("div.desc-text").text().trim();
|
||||||
|
novel.author = loadedCheerio('h3:contains("Author")')
|
||||||
|
.parent()
|
||||||
|
.contents()
|
||||||
|
.text()
|
||||||
|
.replace("Author:", "");
|
||||||
|
novel.genres = loadedCheerio('h3:contains("Genre")')
|
||||||
|
.siblings()
|
||||||
|
.map(function (i, el) { return loadedCheerio(el).text(); })
|
||||||
|
.toArray()
|
||||||
|
.join(",");
|
||||||
|
novel.status = loadedCheerio('h3:contains("Status")').next().text();
|
||||||
|
novelId = loadedCheerio("#rating").attr("data-novel-id");
|
||||||
|
_a = novel;
|
||||||
|
return [4 /*yield*/, getChapters(novelId, this.baseUrl)];
|
||||||
|
case 3:
|
||||||
|
_a.chapters = _b.sent();
|
||||||
|
return [2 /*return*/, novel];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelFull.prototype.parseChapter = function (chapterUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var result, body, loadedCheerio, chapterText;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl)];
|
||||||
|
case 1:
|
||||||
|
result = _a.sent();
|
||||||
|
return [4 /*yield*/, result.text()];
|
||||||
|
case 2:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
loadedCheerio("#chapter-content div.ads").remove();
|
||||||
|
chapterText = loadedCheerio("#chapter-content").html() || '';
|
||||||
|
return [2 /*return*/, chapterText];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelFull.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var searchUrl, result, body, loadedCheerio, novels;
|
||||||
|
var _this = this;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
searchUrl = "".concat(this.baseUrl, "search?keyword=").concat(searchTerm);
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(searchUrl)];
|
||||||
|
case 1:
|
||||||
|
result = _a.sent();
|
||||||
|
return [4 /*yield*/, result.text()];
|
||||||
|
case 2:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novels = [];
|
||||||
|
loadedCheerio(".col-truyen-main .list-truyen .row").each(function (idx, ele) {
|
||||||
|
var _a, _b;
|
||||||
|
var novelName = loadedCheerio(ele)
|
||||||
|
.find("h3.truyen-title > a")
|
||||||
|
.text();
|
||||||
|
var novelCover = _this.baseUrl + ((_a = loadedCheerio(ele).find("img").attr("src")) === null || _a === void 0 ? void 0 : _a.slice(1));
|
||||||
|
var novelUrl = _this.baseUrl +
|
||||||
|
((_b = loadedCheerio(ele)
|
||||||
|
.find("h3.truyen-title > a")
|
||||||
|
.attr("href")) === null || _b === void 0 ? void 0 : _b.slice(1));
|
||||||
|
var novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelFull.prototype.fetchImage = function (url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
return [2 /*return*/, (0, fetch_1.fetchFile)(url)];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return NovelFull;
|
||||||
|
}());
|
||||||
|
exports.default = new NovelFull();
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||||
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var cheerio_1 = require("cheerio");
|
||||||
|
var fetch_1 = require("@libs/fetch");
|
||||||
|
var NovelHall = /** @class */ (function () {
|
||||||
|
function NovelHall() {
|
||||||
|
this.id = "novelhold";
|
||||||
|
this.name = "Novel Hall";
|
||||||
|
this.version = "1.0.0";
|
||||||
|
this.icon = "src/en/novelhall/icon.png";
|
||||||
|
this.site = "https://novelhall.com/";
|
||||||
|
this.baseUrl = this.site;
|
||||||
|
}
|
||||||
|
NovelHall.prototype.popularNovels = function (pageNo, options) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, body, loadedCheerio, novels;
|
||||||
|
var _this = this;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = "".concat(this.baseUrl, "all2022-").concat(pageNo, ".html");
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novels = [];
|
||||||
|
loadedCheerio("li.btm").each(function (idx, ele) {
|
||||||
|
var novelName = loadedCheerio(ele).text().trim();
|
||||||
|
var novelUrl = _this.baseUrl + loadedCheerio(ele).find("a").attr("href");
|
||||||
|
var novel = {
|
||||||
|
name: novelName,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelHall.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||||
|
var _a;
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, body, loadedCheerio, novel, chapter;
|
||||||
|
var _this = this;
|
||||||
|
return __generator(this, function (_b) {
|
||||||
|
switch (_b.label) {
|
||||||
|
case 0:
|
||||||
|
url = novelUrl;
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _b.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novel = {
|
||||||
|
url: url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
novel.name = loadedCheerio(".booknav2 > h1").text();
|
||||||
|
novel.cover = loadedCheerio('meta[property="og:image"]').attr("content");
|
||||||
|
novel.summary = loadedCheerio(".navtxt").text().trim();
|
||||||
|
novel.author = loadedCheerio('p:contains("Author")')
|
||||||
|
.text()
|
||||||
|
.replace("Author:", "")
|
||||||
|
.trim();
|
||||||
|
novel.status = loadedCheerio('p:contains("Status")')
|
||||||
|
.text()
|
||||||
|
.replace("Status:", "")
|
||||||
|
.replace("Active", "Ongoing")
|
||||||
|
.trim();
|
||||||
|
novel.genres = (_a = loadedCheerio('p:contains("Genre")')
|
||||||
|
.text()) === null || _a === void 0 ? void 0 : _a.replace("Genre:", "").trim();
|
||||||
|
chapter = [];
|
||||||
|
loadedCheerio("#morelist ul > li").each(function (idx, ele) {
|
||||||
|
var chapterName = loadedCheerio(ele).find("a").text().trim();
|
||||||
|
var releaseDate = null;
|
||||||
|
var chapterUrl = _this.baseUrl + loadedCheerio(ele).find("a").attr("href");
|
||||||
|
chapter.push({
|
||||||
|
name: chapterName,
|
||||||
|
releaseTime: releaseDate,
|
||||||
|
url: chapterUrl,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
novel.chapters = chapter;
|
||||||
|
return [2 /*return*/, novel];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelHall.prototype.parseChapter = function (chapterUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var body, loadedCheerio, chapterText;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
chapterText = loadedCheerio(".content").html() || '';
|
||||||
|
return [2 /*return*/, chapterText];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelHall.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, body, loadedCheerio, novels;
|
||||||
|
var _this = this;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = "".concat(this.baseUrl, "index.php?s=so&module=book&keyword=").concat(searchTerm);
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||||
|
case 1:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novels = [];
|
||||||
|
loadedCheerio("#article_list_content > li").each(function (idx, ele) {
|
||||||
|
var _a;
|
||||||
|
var novelName = loadedCheerio(ele)
|
||||||
|
.find("h3")
|
||||||
|
.text()
|
||||||
|
.replace(/\t+/g, "")
|
||||||
|
.replace(/\n/g, " ");
|
||||||
|
var novelCover = loadedCheerio(ele).find("img").attr("data-src");
|
||||||
|
var novelUrl = _this.baseUrl + ((_a = loadedCheerio(ele).find("a").attr("href")) === null || _a === void 0 ? void 0 : _a.slice(1));
|
||||||
|
var novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelHall.prototype.fetchImage = function (url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
return [2 /*return*/, (0, fetch_1.fetchFile)(url)];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return NovelHall;
|
||||||
|
}());
|
||||||
|
exports.default = new NovelHall();
|
||||||
@@ -0,0 +1,467 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||||
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var cheerio_1 = require("cheerio");
|
||||||
|
var fetch_1 = require("@libs/fetch");
|
||||||
|
var filterInputs_1 = require("@libs/filterInputs");
|
||||||
|
var NovelUpdates = /** @class */ (function () {
|
||||||
|
function NovelUpdates() {
|
||||||
|
this.id = "novelupdates";
|
||||||
|
this.name = "Novel Updates";
|
||||||
|
this.version = "0.5.0";
|
||||||
|
this.icon = "src/en/novelupdates/icon.png";
|
||||||
|
this.site = "https://www.novelupdates.com/";
|
||||||
|
this.baseUrl = this.site;
|
||||||
|
this.filters = {
|
||||||
|
sort: {
|
||||||
|
label: "Sort Results By",
|
||||||
|
type: filterInputs_1.FilterTypes.Picker,
|
||||||
|
options: [
|
||||||
|
{ label: "Last Updated", value: "sdate" },
|
||||||
|
{ label: "Rating", value: "srate" },
|
||||||
|
{ label: "Rank", value: "srank" },
|
||||||
|
{ label: "Reviews", value: "sreview" },
|
||||||
|
{ label: "Chapters", value: "srel" },
|
||||||
|
{ label: "Title", value: "abc" },
|
||||||
|
{ label: "Readers", value: "sread" },
|
||||||
|
{ label: "Frequency", value: "sfrel" },
|
||||||
|
],
|
||||||
|
value: "sdate",
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
label: "Order",
|
||||||
|
type: filterInputs_1.FilterTypes.Picker,
|
||||||
|
options: [
|
||||||
|
{ label: "Descending", value: "desc" },
|
||||||
|
{ label: "Ascending", value: "asc" },
|
||||||
|
],
|
||||||
|
value: "asc"
|
||||||
|
},
|
||||||
|
storyStatus: {
|
||||||
|
label: "Story Status (Translation)",
|
||||||
|
type: filterInputs_1.FilterTypes.Picker,
|
||||||
|
options: [
|
||||||
|
{ label: "All", value: "" },
|
||||||
|
{ label: "Completed", value: "2" },
|
||||||
|
{ label: "Ongoing", value: "3" },
|
||||||
|
{ label: "Hiatus", value: "4" },
|
||||||
|
],
|
||||||
|
value: ""
|
||||||
|
},
|
||||||
|
language: {
|
||||||
|
label: "Language",
|
||||||
|
options: [
|
||||||
|
{ label: "Chinese", value: "495" },
|
||||||
|
{ label: "Filipino", value: "9181" },
|
||||||
|
{ label: "Indonesian", value: "9179" },
|
||||||
|
{ label: "Japanese", value: "496" },
|
||||||
|
{ label: "Khmer", value: "18657" },
|
||||||
|
{ label: "Korean", value: "497" },
|
||||||
|
{ label: "Malaysian", value: "9183" },
|
||||||
|
{ label: "Thai", value: "9954" },
|
||||||
|
{ label: "Vietnamese", value: "9177" },
|
||||||
|
],
|
||||||
|
type: filterInputs_1.FilterTypes.Picker,
|
||||||
|
value: ""
|
||||||
|
},
|
||||||
|
novelType: {
|
||||||
|
label: "Novel Type",
|
||||||
|
type: filterInputs_1.FilterTypes.CheckboxGroup,
|
||||||
|
value: [],
|
||||||
|
options: [
|
||||||
|
{ label: "Light Novel", value: "2443" },
|
||||||
|
{ label: "Published Novel", value: "26874" },
|
||||||
|
{ label: "Web Novel", value: "2444" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
genres: {
|
||||||
|
label: "Genres",
|
||||||
|
type: filterInputs_1.FilterTypes.CheckboxGroup,
|
||||||
|
value: [],
|
||||||
|
options: [
|
||||||
|
{ label: "Action", value: "8" },
|
||||||
|
{ label: "Adult", value: "280" },
|
||||||
|
{ label: "Adventure", value: "13" },
|
||||||
|
{ label: "Comedy", value: "17" },
|
||||||
|
{ label: "Drama", value: "9" },
|
||||||
|
{ label: "Ecchi", value: "292" },
|
||||||
|
{ label: "Fantasy", value: "5" },
|
||||||
|
{ label: "Gender Bender", value: "168" },
|
||||||
|
{ label: "Harem", value: "3" },
|
||||||
|
{ label: "Historical", value: "330" },
|
||||||
|
{ label: "Horror", value: "343" },
|
||||||
|
{ label: "Josei", value: "324" },
|
||||||
|
{ label: "Martial Arts", value: "14" },
|
||||||
|
{ label: "Mature", value: "4" },
|
||||||
|
{ label: "Mecha", value: "10" },
|
||||||
|
{ label: "Mystery", value: "245" },
|
||||||
|
{ label: "Psychoical", value: "486" },
|
||||||
|
{ label: "Romance", value: "15" },
|
||||||
|
{ label: "School Life", value: "6" },
|
||||||
|
{ label: "Sci-fi", value: "11" },
|
||||||
|
{ label: "Seinen", value: "18" },
|
||||||
|
{ label: "Shoujo", value: "157" },
|
||||||
|
{ label: "Shoujo Ai", value: "851" },
|
||||||
|
{ label: "Shounen", value: "12" },
|
||||||
|
{ label: "Shounen Ai", value: "1692" },
|
||||||
|
{ label: "Slice of Life", value: "7" },
|
||||||
|
{ label: "Smut", value: "281" },
|
||||||
|
{ label: "Sports", value: "1357" },
|
||||||
|
{ label: "Supernatural", value: "16" },
|
||||||
|
{ label: "Tragedy", value: "132" },
|
||||||
|
{ label: "Wuxia", value: "479" },
|
||||||
|
{ label: "Xianxia", value: "480" },
|
||||||
|
{ label: "Xuanhuan", value: "3954" },
|
||||||
|
{ label: "Yaoi", value: "560" },
|
||||||
|
{ label: "Yuri", value: "922" },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
NovelUpdates.prototype.getPopularNovelsUrl = function (page, _a) {
|
||||||
|
var _b, _c, _d;
|
||||||
|
var showLatestNovels = _a.showLatestNovels, filters = _a.filters;
|
||||||
|
var url = "".concat(this.baseUrl).concat(filters
|
||||||
|
? "series-finder"
|
||||||
|
: showLatestNovels
|
||||||
|
? "latest-series"
|
||||||
|
: "series-ranking", "/");
|
||||||
|
if (!filters) {
|
||||||
|
url += "?rank=week";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
url += "?sf=1";
|
||||||
|
}
|
||||||
|
if (filters) {
|
||||||
|
if (Array.isArray(filters.novelType) && ((_b = filters.novelType) === null || _b === void 0 ? void 0 : _b.length)) {
|
||||||
|
url += "&nt=" + (filters === null || filters === void 0 ? void 0 : filters.novelType.join(","));
|
||||||
|
}
|
||||||
|
if (Array.isArray(filters.genres) && ((_c = filters.genres) === null || _c === void 0 ? void 0 : _c.length)) {
|
||||||
|
url += "&gi=" + (filters === null || filters === void 0 ? void 0 : filters.genres.join(",")) + "&mgi=and";
|
||||||
|
}
|
||||||
|
if (Array.isArray(filters.language) && ((_d = filters.language) === null || _d === void 0 ? void 0 : _d.length)) {
|
||||||
|
url += "&org=" + (filters === null || filters === void 0 ? void 0 : filters.language.join(","));
|
||||||
|
}
|
||||||
|
if (filters.storyStatus) {
|
||||||
|
url += "&ss=" + (filters === null || filters === void 0 ? void 0 : filters.storyStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(filters === null || filters === void 0 ? void 0 : filters.sort)) {
|
||||||
|
url += "&sort=" + "sdate";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
url += "&sort=" + (filters === null || filters === void 0 ? void 0 : filters.sort);
|
||||||
|
}
|
||||||
|
if (!(filters === null || filters === void 0 ? void 0 : filters.order)) {
|
||||||
|
url += "&order=" + "desc";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
url += "&order=" + (filters === null || filters === void 0 ? void 0 : filters.order);
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
NovelUpdates.prototype.popularNovels = function (pageNo, _a) {
|
||||||
|
var showLatestNovels = _a.showLatestNovels, filters = _a.filters;
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, result, body, loadedCheerio, novels;
|
||||||
|
return __generator(this, function (_b) {
|
||||||
|
switch (_b.label) {
|
||||||
|
case 0:
|
||||||
|
url = this.getPopularNovelsUrl(pageNo, { showLatestNovels: showLatestNovels, filters: filters });
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url)];
|
||||||
|
case 1:
|
||||||
|
result = _b.sent();
|
||||||
|
return [4 /*yield*/, result.text()];
|
||||||
|
case 2:
|
||||||
|
body = _b.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novels = [];
|
||||||
|
loadedCheerio("div.search_main_box_nu").each(function (idx, ele) {
|
||||||
|
var novelCover = loadedCheerio(ele).find("img").attr("src");
|
||||||
|
var novelName = loadedCheerio(ele).find(".search_title > a").text();
|
||||||
|
var novelUrl = loadedCheerio(ele)
|
||||||
|
.find(".search_title > a")
|
||||||
|
.attr("href");
|
||||||
|
if (!novelUrl)
|
||||||
|
return;
|
||||||
|
var novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelUpdates.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, result, body, loadedCheerio, novel, type, summary, chapter, novelId, formData, link, text;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = novelUrl;
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url)];
|
||||||
|
case 1:
|
||||||
|
result = _a.sent();
|
||||||
|
return [4 /*yield*/, result.text()];
|
||||||
|
case 2:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novel = {
|
||||||
|
url: url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
novel.name = loadedCheerio(".seriestitlenu").text();
|
||||||
|
novel.cover = loadedCheerio(".seriesimg > img").attr("src");
|
||||||
|
novel.author = loadedCheerio("#showauthors").text().trim();
|
||||||
|
novel.genres = loadedCheerio("#seriesgenre")
|
||||||
|
.children("a")
|
||||||
|
.map(function (i, el) { return loadedCheerio(el).text(); })
|
||||||
|
.toArray()
|
||||||
|
.join(",");
|
||||||
|
novel.status = loadedCheerio("#editstatus").text().includes("Ongoing")
|
||||||
|
? "Ongoing"
|
||||||
|
: "Completed";
|
||||||
|
type = loadedCheerio("#showtype").text().trim();
|
||||||
|
summary = loadedCheerio("#editdescription").text().trim();
|
||||||
|
novel.summary = summary + "\n\nType: ".concat(type);
|
||||||
|
chapter = [];
|
||||||
|
novelId = loadedCheerio("input#mypostid").attr("value");
|
||||||
|
formData = new FormData();
|
||||||
|
formData.append("action", "nd_getchapters");
|
||||||
|
formData.append("mygrr", "0");
|
||||||
|
formData.append("mypostid", novelId);
|
||||||
|
link = "https://www.novelupdates.com/wp-admin/admin-ajax.php";
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(link, {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
}).then(function (data) { return data.text(); })];
|
||||||
|
case 3:
|
||||||
|
text = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(text);
|
||||||
|
loadedCheerio("li.sp_li_chp").each(function () {
|
||||||
|
var chapterName = loadedCheerio(this).text().trim();
|
||||||
|
var releaseDate = null;
|
||||||
|
var chapterUrl = "https:" +
|
||||||
|
loadedCheerio(this).find("a").first().next().attr("href");
|
||||||
|
chapter.push({
|
||||||
|
name: chapterName,
|
||||||
|
releaseTime: releaseDate,
|
||||||
|
url: chapterUrl,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
novel.chapters = chapter.reverse();
|
||||||
|
return [2 /*return*/, novel];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelUpdates.prototype.getLocation = function (href) {
|
||||||
|
var match = href.match(/^(https?:)\/\/(([^:/?#]*)(?::([0-9]+))?)([/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/);
|
||||||
|
return match && "".concat(match[1], "//").concat(match[3]);
|
||||||
|
};
|
||||||
|
NovelUpdates.prototype.parseChapter = function (chapterUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var chapterText, result, body, loadedCheerio, isWuxiaWorld, isBlogspot, isTumblr, isWattpad, isLightNovelsTls, isiNovelTranslation, isTravisTranslation, isWordPressStr, isWordPress, isRainOfSnow, isWebNovel, isHostedNovel, isScribbleHub, bloatClasses, tags;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
chapterText = "";
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl)];
|
||||||
|
case 1:
|
||||||
|
result = _a.sent();
|
||||||
|
return [4 /*yield*/, result.text()];
|
||||||
|
case 2:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
isWuxiaWorld = chapterUrl.toLowerCase().includes("wuxiaworld");
|
||||||
|
isBlogspot = chapterUrl.toLowerCase().includes("blogspot");
|
||||||
|
isTumblr = chapterUrl.toLowerCase().includes("tumblr");
|
||||||
|
isWattpad = chapterUrl.toLowerCase().includes("wattpad");
|
||||||
|
isLightNovelsTls = chapterUrl
|
||||||
|
.toLowerCase()
|
||||||
|
.includes("lightnovelstranslations");
|
||||||
|
isiNovelTranslation = chapterUrl
|
||||||
|
.toLowerCase()
|
||||||
|
.includes("inoveltranslation");
|
||||||
|
isTravisTranslation = chapterUrl
|
||||||
|
.toLowerCase()
|
||||||
|
.includes("travistranslations");
|
||||||
|
isWordPressStr = loadedCheerio('meta[name="generator"]').attr("content") ||
|
||||||
|
loadedCheerio("footer").text();
|
||||||
|
isWordPress = false;
|
||||||
|
if (isWordPressStr) {
|
||||||
|
isWordPress =
|
||||||
|
isWordPressStr.toLowerCase().includes("wordpress") ||
|
||||||
|
isWordPressStr.includes("Site Kit by Google") ||
|
||||||
|
loadedCheerio(".powered-by")
|
||||||
|
.text()
|
||||||
|
.toLowerCase()
|
||||||
|
.includes("wordpress");
|
||||||
|
}
|
||||||
|
isRainOfSnow = chapterUrl.toLowerCase().includes("rainofsnow");
|
||||||
|
isWebNovel = chapterUrl.toLowerCase().includes("webnovel");
|
||||||
|
isHostedNovel = chapterUrl.toLowerCase().includes("hostednovel");
|
||||||
|
isScribbleHub = chapterUrl.toLowerCase().includes("scribblehub");
|
||||||
|
if (isWuxiaWorld) {
|
||||||
|
chapterText = loadedCheerio("#chapter-content").html();
|
||||||
|
}
|
||||||
|
else if (isRainOfSnow) {
|
||||||
|
chapterText = loadedCheerio("div.content").html();
|
||||||
|
}
|
||||||
|
else if (isTumblr) {
|
||||||
|
chapterText = loadedCheerio(".post").html();
|
||||||
|
}
|
||||||
|
else if (isBlogspot) {
|
||||||
|
loadedCheerio(".post-share-buttons").remove();
|
||||||
|
chapterText = loadedCheerio(".entry-content").html();
|
||||||
|
}
|
||||||
|
else if (isHostedNovel) {
|
||||||
|
chapterText = loadedCheerio(".chapter").html();
|
||||||
|
}
|
||||||
|
else if (isScribbleHub) {
|
||||||
|
chapterText = loadedCheerio("div.chp_raw").html();
|
||||||
|
}
|
||||||
|
else if (isWattpad) {
|
||||||
|
chapterText = loadedCheerio(".container pre").html();
|
||||||
|
}
|
||||||
|
else if (isTravisTranslation) {
|
||||||
|
chapterText = loadedCheerio(".reader-content").html();
|
||||||
|
}
|
||||||
|
else if (isLightNovelsTls) {
|
||||||
|
chapterText = loadedCheerio(".text_story").html();
|
||||||
|
}
|
||||||
|
else if (isiNovelTranslation) {
|
||||||
|
chapterText = loadedCheerio(".chakra-skeleton").html();
|
||||||
|
}
|
||||||
|
else if (isWordPress) {
|
||||||
|
bloatClasses = [
|
||||||
|
".c-ads",
|
||||||
|
"#madara-comments",
|
||||||
|
"#comments",
|
||||||
|
".content-comments",
|
||||||
|
".sharedaddy",
|
||||||
|
".wp-dark-mode-switcher",
|
||||||
|
".wp-next-post-navi",
|
||||||
|
".wp-block-buttons",
|
||||||
|
".wp-block-columns",
|
||||||
|
".post-cats",
|
||||||
|
".sidebar",
|
||||||
|
".author-avatar",
|
||||||
|
".ezoic-ad",
|
||||||
|
];
|
||||||
|
bloatClasses.map(function (tag) { return loadedCheerio(tag).remove(); });
|
||||||
|
chapterText =
|
||||||
|
loadedCheerio(".entry-content").html() ||
|
||||||
|
loadedCheerio(".single_post").html() ||
|
||||||
|
loadedCheerio(".post-entry").html() ||
|
||||||
|
loadedCheerio(".main-content").html() ||
|
||||||
|
loadedCheerio("article.post").html() ||
|
||||||
|
loadedCheerio(".content").html() ||
|
||||||
|
loadedCheerio("#content").html() ||
|
||||||
|
loadedCheerio(".page-body").html() ||
|
||||||
|
loadedCheerio(".td-page-content").html();
|
||||||
|
}
|
||||||
|
else if (isWebNovel) {
|
||||||
|
chapterText = loadedCheerio(".cha-words").html();
|
||||||
|
if (!chapterText) {
|
||||||
|
chapterText = loadedCheerio("._content").html();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tags = ["nav", "header", "footer", ".hidden"];
|
||||||
|
tags.map(function (tag) { return loadedCheerio(tag).remove(); });
|
||||||
|
chapterText = loadedCheerio("body").html();
|
||||||
|
}
|
||||||
|
if (chapterText) {
|
||||||
|
/**
|
||||||
|
* Convert relative urls to absolute
|
||||||
|
*/
|
||||||
|
chapterText = chapterText.replace(/href="\//g, "href=\"".concat(this.getLocation(chapterUrl), "/"));
|
||||||
|
}
|
||||||
|
return [2 /*return*/, chapterText];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelUpdates.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, result, body, loadedCheerio, novels;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = "https://www.novelupdates.com/?s=" +
|
||||||
|
searchTerm +
|
||||||
|
"&post_type=seriesplans";
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url)];
|
||||||
|
case 1:
|
||||||
|
result = _a.sent();
|
||||||
|
return [4 /*yield*/, result.text()];
|
||||||
|
case 2:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novels = [];
|
||||||
|
loadedCheerio("div.search_main_box_nu").each(function () {
|
||||||
|
var _a;
|
||||||
|
var novelCover = loadedCheerio(this).find("img").attr("src");
|
||||||
|
var novelName = loadedCheerio(this).find(".search_title > a").text();
|
||||||
|
var novelUrl = (_a = loadedCheerio(this)
|
||||||
|
.find(".search_title > a")
|
||||||
|
.attr("href")) === null || _a === void 0 ? void 0 : _a.split("/")[4];
|
||||||
|
if (!novelUrl)
|
||||||
|
return;
|
||||||
|
novels.push({
|
||||||
|
name: novelName,
|
||||||
|
url: novelUrl,
|
||||||
|
cover: novelCover,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
NovelUpdates.prototype.fetchImage = function (url) {
|
||||||
|
return (0, fetch_1.fetchFile)(url);
|
||||||
|
};
|
||||||
|
return NovelUpdates;
|
||||||
|
}());
|
||||||
|
exports.default = new NovelUpdates();
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||||
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var cheerio_1 = require("cheerio");
|
||||||
|
var fetch_1 = require("@libs/fetch");
|
||||||
|
var WuxiaWorld = /** @class */ (function () {
|
||||||
|
function WuxiaWorld() {
|
||||||
|
this.id = "wuxiaworld";
|
||||||
|
this.name = "Wuxia World";
|
||||||
|
this.icon = "src/en/wuxiaworld/icon.png";
|
||||||
|
this.site = "https://www.wuxiaworld.com/";
|
||||||
|
this.version = "0.5.0";
|
||||||
|
this.baseUrl = this.site;
|
||||||
|
}
|
||||||
|
WuxiaWorld.prototype.popularNovels = function (pageNo, options) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var link, result, data, novels;
|
||||||
|
var _this = this;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
link = "".concat(this.baseUrl, "api/novels");
|
||||||
|
return [4 /*yield*/, fetch(link)];
|
||||||
|
case 1:
|
||||||
|
result = _a.sent();
|
||||||
|
return [4 /*yield*/, result.json()];
|
||||||
|
case 2:
|
||||||
|
data = _a.sent();
|
||||||
|
novels = [];
|
||||||
|
data.items.map(function (novel) {
|
||||||
|
var name = novel.name;
|
||||||
|
var cover = novel.coverUrl;
|
||||||
|
var url = _this.baseUrl + "novel/" + novel.slug + "/";
|
||||||
|
novels.push({
|
||||||
|
name: name,
|
||||||
|
cover: cover,
|
||||||
|
url: url,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
WuxiaWorld.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var url, result, body, loadedCheerio, novel, genres, chapter;
|
||||||
|
var _this = this;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
url = novelUrl;
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url)];
|
||||||
|
case 1:
|
||||||
|
result = _a.sent();
|
||||||
|
return [4 /*yield*/, result.text()];
|
||||||
|
case 2:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
novel = {
|
||||||
|
url: url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
novel.name = loadedCheerio("h1.line-clamp-2").text();
|
||||||
|
novel.cover = loadedCheerio("img.absolute").attr("src");
|
||||||
|
novel.summary = loadedCheerio("div.flex-col:nth-child(4) > div > div > span > span")
|
||||||
|
.text()
|
||||||
|
.trim();
|
||||||
|
novel.author = loadedCheerio("div.MuiGrid-container > div > div > div")
|
||||||
|
.filter(function () {
|
||||||
|
return loadedCheerio(this).text().trim() === "Author:";
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.text();
|
||||||
|
genres = [];
|
||||||
|
loadedCheerio("a.MuiLink-underlineNone").each(function () {
|
||||||
|
genres.push(loadedCheerio(this).find("div > div").text());
|
||||||
|
});
|
||||||
|
novel.genres = genres.join(",");
|
||||||
|
novel.status = loadedCheerio("div.font-set-b10")
|
||||||
|
.text();
|
||||||
|
chapter = [];
|
||||||
|
loadedCheerio("div.border-b.border-gray-line-base").each(function (idx, ele) {
|
||||||
|
var _a;
|
||||||
|
var name = loadedCheerio(ele)
|
||||||
|
.find("a > div > div > div > span")
|
||||||
|
.text();
|
||||||
|
var releaseTime = loadedCheerio(ele)
|
||||||
|
.find("a > div > div > div > div > span")
|
||||||
|
.text();
|
||||||
|
var url = (_a = loadedCheerio(ele).find("a").attr("href")) === null || _a === void 0 ? void 0 : _a.slice(1);
|
||||||
|
url = "".concat(_this.baseUrl).concat(url);
|
||||||
|
chapter.push({ name: name, releaseTime: releaseTime, url: url });
|
||||||
|
});
|
||||||
|
novel.chapters = chapter;
|
||||||
|
return [2 /*return*/, novel];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
WuxiaWorld.prototype.parseChapter = function (chapterUrl) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var result, body, loadedCheerio, chapterText;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl)];
|
||||||
|
case 1:
|
||||||
|
result = _a.sent();
|
||||||
|
return [4 /*yield*/, result.text()];
|
||||||
|
case 2:
|
||||||
|
body = _a.sent();
|
||||||
|
loadedCheerio = (0, cheerio_1.load)(body);
|
||||||
|
loadedCheerio(".chapter-nav").remove();
|
||||||
|
loadedCheerio("#chapter-content > script").remove();
|
||||||
|
chapterText = loadedCheerio("#chapter-content").html() || '';
|
||||||
|
return [2 /*return*/, chapterText];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
WuxiaWorld.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var searchUrl, url, result, data, novels;
|
||||||
|
var _this = this;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
searchUrl = "https://www.wuxiaworld.com/api/novels/search?query=";
|
||||||
|
url = "".concat(searchUrl).concat(searchTerm);
|
||||||
|
return [4 /*yield*/, (0, fetch_1.fetchApi)(url)];
|
||||||
|
case 1:
|
||||||
|
result = _a.sent();
|
||||||
|
return [4 /*yield*/, result.json()];
|
||||||
|
case 2:
|
||||||
|
data = _a.sent();
|
||||||
|
novels = [];
|
||||||
|
data.items.map(function (novel) {
|
||||||
|
var name = novel.name;
|
||||||
|
var cover = novel.coverUrl;
|
||||||
|
var url = _this.baseUrl + "novel/" + novel.slug + "/";
|
||||||
|
novels.push({
|
||||||
|
name: name,
|
||||||
|
url: url,
|
||||||
|
cover: cover,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return [2 /*return*/, novels];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
WuxiaWorld.prototype.fetchImage = function (url) {
|
||||||
|
return (0, fetch_1.fetchFile)(url);
|
||||||
|
};
|
||||||
|
return WuxiaWorld;
|
||||||
|
}());
|
||||||
|
exports.default = new WuxiaWorld();
|
||||||
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
@@ -5,7 +5,7 @@ import { Filters } from "@libs/filterInputs";
|
|||||||
|
|
||||||
class NovelFull implements Plugin.PluginBase {
|
class NovelFull implements Plugin.PluginBase {
|
||||||
id = "NF.me";
|
id = "NF.me";
|
||||||
name = "NovelFull";
|
name = "NovelFullMe";
|
||||||
site = "https://novelfull.me/";
|
site = "https://novelfull.me/";
|
||||||
version = "1.0.0";
|
version = "1.0.0";
|
||||||
filters?: Filters | undefined;
|
filters?: Filters | undefined;
|
||||||
|
|||||||
@@ -1,111 +0,0 @@
|
|||||||
import { load as parseHTML } from "cheerio";
|
|
||||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
|
||||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
|
||||||
|
|
||||||
export const id = "NO.net";
|
|
||||||
export const name = "novelsOnline";
|
|
||||||
export const site = "https://novelsonline.net";
|
|
||||||
export const icon = "src/coverNotAvailable.jpg";
|
|
||||||
export const version = "1.0.0";
|
|
||||||
|
|
||||||
const pluginId = id;
|
|
||||||
|
|
||||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
|
||||||
const result = await fetchApi(
|
|
||||||
"https://novelsonline.net/sResults.php",
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Accept: "*/*",
|
|
||||||
"Accept-Language": "pl,en-US;q=0.7,en;q=0.3",
|
|
||||||
"Content-Type":
|
|
||||||
"application/x-www-form-urlencoded; charset=UTF-8",
|
|
||||||
},
|
|
||||||
method: "POST",
|
|
||||||
body: "q=" + encodeURIComponent(searchTerm),
|
|
||||||
},
|
|
||||||
pluginId
|
|
||||||
).then((res) => res.text());
|
|
||||||
let $ = parseHTML(result);
|
|
||||||
|
|
||||||
const headers = $("li");
|
|
||||||
return headers
|
|
||||||
.map((i, h) => {
|
|
||||||
const novelName = $(h).text();
|
|
||||||
const novelUrl = $(h).find("a").attr("href");
|
|
||||||
const novelCover = $(h).find("img").attr("src");
|
|
||||||
|
|
||||||
if (!novelUrl) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: novelName,
|
|
||||||
cover: novelCover,
|
|
||||||
url: novelUrl,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.get()
|
|
||||||
.filter((sr) => sr !== null);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
|
||||||
async function (novelUrl) {
|
|
||||||
let novel: Novel.instance = {
|
|
||||||
url: novelUrl,
|
|
||||||
chapters: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await fetchApi(novelUrl).then((res) => res.text());
|
|
||||||
let $ = parseHTML(result);
|
|
||||||
|
|
||||||
novel.name = $("h1").text();
|
|
||||||
novel.cover = $(".novel-cover").find("a > img").attr("src");
|
|
||||||
novel.author = $(
|
|
||||||
"div.novel-details > div:nth-child(5) > div.novel-detail-body"
|
|
||||||
)
|
|
||||||
.find("li")
|
|
||||||
.map((_, el) => $(el).text())
|
|
||||||
.get()
|
|
||||||
.join(", ");
|
|
||||||
|
|
||||||
novel.genres = $(
|
|
||||||
"div.novel-details > div:nth-child(2) > div.novel-detail-body"
|
|
||||||
)
|
|
||||||
.find("li")
|
|
||||||
.map((_, el) => $(el).text())
|
|
||||||
.get()
|
|
||||||
.join(",");
|
|
||||||
|
|
||||||
novel.summary = $(
|
|
||||||
"div.novel-right > div > div:nth-child(1) > div.novel-detail-body"
|
|
||||||
).text();
|
|
||||||
|
|
||||||
novel.chapters = $("ul.chapter-chs > li > a")
|
|
||||||
.map((_, el) => {
|
|
||||||
const chapterUrl = $(el).attr("href");
|
|
||||||
const chapterName = $(el).text();
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: chapterName,
|
|
||||||
releaseTime: "",
|
|
||||||
url: chapterUrl,
|
|
||||||
} as Chapter.Item;
|
|
||||||
})
|
|
||||||
.get();
|
|
||||||
|
|
||||||
return novel;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
|
||||||
return []; /** TO DO */
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
|
||||||
const result = await fetchApi(chapterUrl).then((res) => res.text());
|
|
||||||
let loadedCheerio = parseHTML(result);
|
|
||||||
|
|
||||||
const chapterText = loadedCheerio("#contentall").html() || "";
|
|
||||||
|
|
||||||
return chapterText;
|
|
||||||
};
|
|
||||||
export const fetchImage: Plugin.fetchImage = fetchFile;
|
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import { load as parseHTML } from "cheerio";
|
||||||
|
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||||
|
import { Plugin } from "@typings/plugin";
|
||||||
|
import { Filters } from "@libs/filterInputs";
|
||||||
|
|
||||||
|
class NovelsOnline implements Plugin.PluginBase {
|
||||||
|
id = "NO.net";
|
||||||
|
name = "novelsOnline";
|
||||||
|
site = "https://novelsonline.net";
|
||||||
|
icon = "src/coverNotAvailable.jpg";
|
||||||
|
filters?: Filters | undefined;
|
||||||
|
version = "1.0.0";
|
||||||
|
|
||||||
|
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||||
|
return []; /** TO DO */
|
||||||
|
}
|
||||||
|
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||||
|
let novel: Plugin.SourceNovel = {
|
||||||
|
url: novelUrl,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await fetchApi(novelUrl).then((res) => res.text());
|
||||||
|
|
||||||
|
let $ = parseHTML(result);
|
||||||
|
|
||||||
|
novel.name = $("h1").text();
|
||||||
|
novel.cover = $(".novel-cover").find("a > img").attr("src");
|
||||||
|
novel.author = $(
|
||||||
|
"div.novel-details > div:nth-child(5) > div.novel-detail-body"
|
||||||
|
)
|
||||||
|
.find("li")
|
||||||
|
.map((_, el) => $(el).text())
|
||||||
|
.get()
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
novel.genres = $(
|
||||||
|
"div.novel-details > div:nth-child(2) > div.novel-detail-body"
|
||||||
|
)
|
||||||
|
.find("li")
|
||||||
|
.map((_, el) => $(el).text())
|
||||||
|
.get()
|
||||||
|
.join(",");
|
||||||
|
|
||||||
|
novel.summary = $(
|
||||||
|
"div.novel-right > div > div:nth-child(1) > div.novel-detail-body"
|
||||||
|
).text();
|
||||||
|
|
||||||
|
novel.chapters = $("ul.chapter-chs > li > a")
|
||||||
|
.map((_, el) => {
|
||||||
|
const chapterUrl = $(el).attr("href");
|
||||||
|
const chapterName = $(el).text();
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: chapterName,
|
||||||
|
releaseTime: "",
|
||||||
|
url: chapterUrl,
|
||||||
|
} as Plugin.ChapterItem;
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
|
||||||
|
return novel;
|
||||||
|
}
|
||||||
|
async parseChapter(chapterUrl: string): Promise<string> {
|
||||||
|
const result = await fetchApi(chapterUrl).then((res) => res.text());
|
||||||
|
let loadedCheerio = parseHTML(result);
|
||||||
|
|
||||||
|
const chapterText = loadedCheerio("#contentall").html() || "";
|
||||||
|
|
||||||
|
return chapterText;
|
||||||
|
}
|
||||||
|
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
|
const result = await fetchApi(
|
||||||
|
"https://novelsonline.net/sResults.php",
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Accept: "*/*",
|
||||||
|
"Accept-Language": "pl,en-US;q=0.7,en;q=0.3",
|
||||||
|
"Content-Type":
|
||||||
|
"application/x-www-form-urlencoded; charset=UTF-8",
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
body: "q=" + encodeURIComponent(searchTerm),
|
||||||
|
},
|
||||||
|
).then((res) => res.text());
|
||||||
|
|
||||||
|
let $ = parseHTML(result);
|
||||||
|
|
||||||
|
const headers = $("li");
|
||||||
|
return headers
|
||||||
|
.map((i, h) => {
|
||||||
|
const novelName = $(h).text();
|
||||||
|
const novelUrl = $(h).find("a").attr("href");
|
||||||
|
const novelCover = $(h).find("img").attr("src");
|
||||||
|
|
||||||
|
if (!novelUrl) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.get()
|
||||||
|
.filter((sr) => sr !== null)
|
||||||
|
}
|
||||||
|
async fetchImage(url: string): Promise<string | undefined> {
|
||||||
|
return fetchFile(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new NovelsOnline();
|
||||||
@@ -1,213 +0,0 @@
|
|||||||
import { load as parseHTML } from "cheerio";
|
|
||||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
|
||||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
|
||||||
|
|
||||||
export const id = "lightnovelpub";
|
|
||||||
export const name = "LightNovelPub";
|
|
||||||
export const version = "1.0.0";
|
|
||||||
export const icon = "src/en/lightnovelpub/icon.png";
|
|
||||||
export const site = "https://www.lightnovelpub.com/";
|
|
||||||
|
|
||||||
|
|
||||||
const baseUrl = site;
|
|
||||||
const pluginId = id;
|
|
||||||
|
|
||||||
const headers = {
|
|
||||||
Accept: "application/json",
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
|
||||||
const url = `${baseUrl}browse/all/popular/all/${page}`;
|
|
||||||
|
|
||||||
const body = await fetchApi(url, {}, pluginId).then((r) => r.text());
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
const novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio(".novel-item.ads").remove();
|
|
||||||
|
|
||||||
loadedCheerio(".novel-item").each(function () {
|
|
||||||
const novelName = loadedCheerio(this)
|
|
||||||
.find(".novel-title")
|
|
||||||
.text()
|
|
||||||
.trim();
|
|
||||||
const novelCover = loadedCheerio(this).find("img").attr("data-src");
|
|
||||||
const novelUrl =
|
|
||||||
baseUrl +
|
|
||||||
loadedCheerio(this)
|
|
||||||
.find(".novel-title > a")
|
|
||||||
.attr("href")
|
|
||||||
?.substring(1);
|
|
||||||
|
|
||||||
const novel = {
|
|
||||||
name: novelName,
|
|
||||||
cover: novelCover,
|
|
||||||
url: novelUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
novels.push(novel);
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
|
||||||
async function (novelUrl) {
|
|
||||||
const url = novelUrl;
|
|
||||||
const body = await fetchApi(url, {}, pluginId).then((r) => r.text());
|
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
const novel: Novel.instance = {
|
|
||||||
url,
|
|
||||||
chapters: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
novel.name = loadedCheerio("h1.novel-title").text().trim();
|
|
||||||
|
|
||||||
novel.cover = loadedCheerio("figure.cover > img").attr("data-src");
|
|
||||||
|
|
||||||
novel.genres = "";
|
|
||||||
|
|
||||||
loadedCheerio(".categories > ul > li > a").each(function () {
|
|
||||||
novel.genres += loadedCheerio(this).text() + ",";
|
|
||||||
});
|
|
||||||
|
|
||||||
novel.genres = novel.genres.slice(0, -1);
|
|
||||||
|
|
||||||
loadedCheerio("div.header-stats > span").each(function () {
|
|
||||||
if (loadedCheerio(this).find("small").text() === "Status") {
|
|
||||||
novel.status = loadedCheerio(this).find("strong").text();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
novel.author = loadedCheerio(".author > a > span").text();
|
|
||||||
|
|
||||||
novel.summary = loadedCheerio(".summary > .content").text().trim();
|
|
||||||
|
|
||||||
const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
|
|
||||||
|
|
||||||
let lastPage = 1;
|
|
||||||
|
|
||||||
lastPage = parseInt(
|
|
||||||
loadedCheerio(
|
|
||||||
"#novel > header > div.header-body.container > div.novel-info > div.header-stats > span:nth-child(1) > strong"
|
|
||||||
)
|
|
||||||
.text()
|
|
||||||
?.trim()
|
|
||||||
);
|
|
||||||
|
|
||||||
lastPage = Math.ceil(lastPage / 100);
|
|
||||||
|
|
||||||
async function getChapters() {
|
|
||||||
let chapter: Chapter.Item[] = [];
|
|
||||||
|
|
||||||
for (let i = 1; i <= lastPage; i++) {
|
|
||||||
const chaptersUrl = `${novelUrl}/chapters/page-${i}`;
|
|
||||||
|
|
||||||
const chaptersHtml = await fetchApi(
|
|
||||||
chaptersUrl,
|
|
||||||
{ headers: headers },
|
|
||||||
pluginId
|
|
||||||
).then((r) => r.text());
|
|
||||||
|
|
||||||
loadedCheerio = parseHTML(chaptersHtml);
|
|
||||||
|
|
||||||
loadedCheerio(".chapter-list li").each(function () {
|
|
||||||
const chapterName = loadedCheerio(this)
|
|
||||||
.find(".chapter-title")
|
|
||||||
.text()
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
const releaseDate = loadedCheerio(this)
|
|
||||||
.find(".chapter-update")
|
|
||||||
.text()
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
const chapterUrl =
|
|
||||||
baseUrl +
|
|
||||||
loadedCheerio(this)
|
|
||||||
.find("a")
|
|
||||||
.attr("href")
|
|
||||||
?.substring(1);
|
|
||||||
|
|
||||||
chapter.push({
|
|
||||||
name: chapterName,
|
|
||||||
releaseTime: releaseDate,
|
|
||||||
url: chapterUrl,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await delay(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
return chapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
novel.chapters = await getChapters();
|
|
||||||
|
|
||||||
return novel;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
|
||||||
const body = await fetchApi(chapterUrl, {}, pluginId).then((r) => r.text());
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
let chapterText = loadedCheerio("#chapter-container").html();
|
|
||||||
|
|
||||||
return chapterText;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
|
||||||
const url = `${baseUrl}lnsearchlive`;
|
|
||||||
const link = `${baseUrl}search`;
|
|
||||||
const response = await fetchApi(link, {}, pluginId).then((r) => r.text());
|
|
||||||
const token = parseHTML(response);
|
|
||||||
let verifytoken = token("#novelSearchForm > input").attr("value");
|
|
||||||
|
|
||||||
let formData = new FormData();
|
|
||||||
formData.append("inputContent", searchTerm);
|
|
||||||
|
|
||||||
const body = await fetchApi(
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: { LNRequestVerifyToken: verifytoken! },
|
|
||||||
body: formData,
|
|
||||||
},
|
|
||||||
pluginId
|
|
||||||
).then((r) => r.text());
|
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
let novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
let results = JSON.parse(loadedCheerio("body").text());
|
|
||||||
|
|
||||||
loadedCheerio = parseHTML(results.resultview);
|
|
||||||
|
|
||||||
loadedCheerio(".novel-item").each(function () {
|
|
||||||
const novelName = loadedCheerio(this)
|
|
||||||
.find("h4.novel-title")
|
|
||||||
.text()
|
|
||||||
.trim();
|
|
||||||
const novelCover = loadedCheerio(this).find("img").attr("src");
|
|
||||||
const novelUrl =
|
|
||||||
baseUrl + loadedCheerio(this).find("a").attr("href")?.substring(1);
|
|
||||||
|
|
||||||
novels.push({
|
|
||||||
name: novelName,
|
|
||||||
url: novelUrl,
|
|
||||||
cover: novelCover,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
|
||||||
return await fetchFile(url, { headers: headers });
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
import { load as parseHTML } from "cheerio";
|
||||||
|
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||||
|
import { Plugin } from "@typings/plugin";
|
||||||
|
import { Filters } from "@libs/filterInputs";
|
||||||
|
|
||||||
|
|
||||||
|
class LightNovelPub implements Plugin.PluginBase {
|
||||||
|
id = "lightnovelpub";
|
||||||
|
name = "LightNovelPub";
|
||||||
|
version = "1.0.0";
|
||||||
|
icon = "src/en/lightnovelpub/icon.png";
|
||||||
|
filters?: Filters | undefined;
|
||||||
|
site = "https://www.lightnovelpub.com/";
|
||||||
|
baseUrl = this.site;
|
||||||
|
headers = {
|
||||||
|
Accept: "application/json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
};
|
||||||
|
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||||
|
const url = `${this.baseUrl}browse/all/popular/all/${pageNo}`;
|
||||||
|
|
||||||
|
const body = await fetchApi(url).then((r) => r.text());
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio(".novel-item.ads").remove();
|
||||||
|
|
||||||
|
loadedCheerio(".novel-item").each((idx, ele) => {
|
||||||
|
const novelName = loadedCheerio(ele)
|
||||||
|
.find(".novel-title")
|
||||||
|
.text()
|
||||||
|
.trim();
|
||||||
|
const novelCover = loadedCheerio(ele).find("img").attr("data-src");
|
||||||
|
const novelUrl =
|
||||||
|
this.baseUrl +
|
||||||
|
loadedCheerio(ele)
|
||||||
|
.find(".novel-title > a")
|
||||||
|
.attr("href")
|
||||||
|
?.substring(1);
|
||||||
|
|
||||||
|
const novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||||
|
const url = novelUrl;
|
||||||
|
const body = await fetchApi(url).then((r) => r.text());
|
||||||
|
|
||||||
|
let loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
const novel: Plugin.SourceNovel = {
|
||||||
|
url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
novel.name = loadedCheerio("h1.novel-title").text().trim();
|
||||||
|
|
||||||
|
novel.cover = loadedCheerio("figure.cover > img").attr("data-src");
|
||||||
|
|
||||||
|
novel.genres = "";
|
||||||
|
|
||||||
|
loadedCheerio(".categories > ul > li > a").each(function () {
|
||||||
|
novel.genres += loadedCheerio(this).text() + ",";
|
||||||
|
});
|
||||||
|
|
||||||
|
novel.genres = novel.genres.slice(0, -1);
|
||||||
|
|
||||||
|
loadedCheerio("div.header-stats > span").each(function () {
|
||||||
|
if (loadedCheerio(this).find("small").text() === "Status") {
|
||||||
|
novel.status = loadedCheerio(this).find("strong").text();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
novel.author = loadedCheerio(".author > a > span").text();
|
||||||
|
|
||||||
|
novel.summary = loadedCheerio(".summary > .content").text().trim();
|
||||||
|
const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
|
||||||
|
await delay(1000);
|
||||||
|
const chapterListUrl = novelUrl + '/chapters?chorder=desc';
|
||||||
|
|
||||||
|
const chapterListBody = await fetchApi(chapterListUrl).then(r => r.text());
|
||||||
|
const loadedChapterList = parseHTML(chapterListBody);
|
||||||
|
|
||||||
|
|
||||||
|
const lastNumber = parseInt(
|
||||||
|
loadedChapterList('ul.chapter-list > li:nth-child(1)').attr('data-chapterno') ?? '0'
|
||||||
|
);
|
||||||
|
const lastChapterUrl = loadedChapterList('ul.chapter-list > li:nth-child(1) > a').attr('href');
|
||||||
|
|
||||||
|
if(lastChapterUrl){
|
||||||
|
const chapterUrlPrefix = this.baseUrl + lastChapterUrl.split('/').slice(1, -1).join('/');
|
||||||
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
|
for(let i = 1; i <= lastNumber; i++){
|
||||||
|
chapters.push({
|
||||||
|
chapterNumber: i,
|
||||||
|
name: 'Chapter ' + i,
|
||||||
|
url: chapterUrlPrefix + '/chapter-' + i,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
novel.chapters = chapters;
|
||||||
|
}
|
||||||
|
return novel;
|
||||||
|
}
|
||||||
|
async parseChapter(chapterUrl: string): Promise<string> {
|
||||||
|
const body = await fetchApi(chapterUrl).then((r) => r.text());
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
let chapterText = loadedCheerio("#chapter-container").html() || '';
|
||||||
|
|
||||||
|
return chapterText;
|
||||||
|
}
|
||||||
|
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
|
const url = `${this.baseUrl}lnsearchlive`;
|
||||||
|
const link = `${this.baseUrl}search`;
|
||||||
|
const response = await fetchApi(link).then((r) => r.text());
|
||||||
|
const token = parseHTML(response);
|
||||||
|
let verifytoken = token("#novelSearchForm > input").attr("value");
|
||||||
|
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("inputContent", searchTerm);
|
||||||
|
|
||||||
|
const body = await fetchApi(
|
||||||
|
url,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: { LNRequestVerifyToken: verifytoken! },
|
||||||
|
body: formData,
|
||||||
|
},
|
||||||
|
).then((r) => r.text());
|
||||||
|
|
||||||
|
let loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
let novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
let results = JSON.parse(loadedCheerio("body").text());
|
||||||
|
|
||||||
|
loadedCheerio = parseHTML(results.resultview);
|
||||||
|
|
||||||
|
loadedCheerio(".novel-item").each((idx, ele) => {
|
||||||
|
const novelName = loadedCheerio(ele)
|
||||||
|
.find("h4.novel-title")
|
||||||
|
.text()
|
||||||
|
.trim();
|
||||||
|
const novelCover = loadedCheerio(ele).find("img").attr("src");
|
||||||
|
const novelUrl =
|
||||||
|
this.baseUrl + loadedCheerio(ele).find("a").attr("href")?.substring(1);
|
||||||
|
|
||||||
|
novels.push({
|
||||||
|
name: novelName,
|
||||||
|
url: novelUrl,
|
||||||
|
cover: novelCover,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
async fetchImage(url: string): Promise<string | undefined> {
|
||||||
|
return fetchFile(url, {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new LightNovelPub();
|
||||||
@@ -1,25 +1,23 @@
|
|||||||
import { load as parseHTML } from "cheerio";
|
import { load as parseHTML } from "cheerio";
|
||||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
import { Plugin } from "@typings/plugin";
|
||||||
|
import { Filters } from "@libs/filterInputs";
|
||||||
|
|
||||||
export const id = "LightNovelUpdates";
|
class LightNovelUpdates implements Plugin.PluginBase {
|
||||||
export const name = "Light Novel Updates";
|
id = "LightNovelUpdates";
|
||||||
export const version = "1.0.0";
|
name = "Light Novel Updates";
|
||||||
export const icon = "src/en/lightnovelupdates/icon.png";
|
version = "1.0.0";
|
||||||
export const site = "https://www.lightnovelupdates.com/";
|
icon = "src/en/lightnovelupdates/icon.png";
|
||||||
|
site = "https://www.lightnovelupdates.com/";
|
||||||
|
baseUrl = this.site;
|
||||||
|
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||||
|
let url = `${this.baseUrl}novel/page/${pageNo}/?m_orderby=rating`;
|
||||||
|
|
||||||
|
const body = await fetchApi(url).then((r) => r.text());
|
||||||
const pluginId = id;
|
|
||||||
const baseUrl = site;
|
|
||||||
|
|
||||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
|
||||||
let url = `${baseUrl}novel/page/${page}/?m_orderby=rating`;
|
|
||||||
|
|
||||||
const body = await fetchApi(url, {}, pluginId).then((r) => r.text());
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
let novels: Novel.Item[] = [];
|
let novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
loadedCheerio(".page-item-detail").each(function () {
|
loadedCheerio(".page-item-detail").each(function () {
|
||||||
const novelName = loadedCheerio(this).find(".h5 > a").text();
|
const novelName = loadedCheerio(this).find(".h5 > a").text();
|
||||||
@@ -38,17 +36,15 @@ export const popularNovels: Plugin.popularNovels = async function (page) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return novels;
|
return novels;
|
||||||
};
|
}
|
||||||
|
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
|
||||||
async function (novelUrl) {
|
|
||||||
const url = novelUrl;
|
const url = novelUrl;
|
||||||
|
|
||||||
const body = await fetchApi(url, {}, pluginId).then((r) => r.text());
|
const body = await fetchApi(url).then((r) => r.text());
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
let loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
const novel: Novel.instance = {
|
const novel: Plugin.SourceNovel = {
|
||||||
url,
|
url,
|
||||||
chapters: [],
|
chapters: [],
|
||||||
};
|
};
|
||||||
@@ -82,7 +78,7 @@ export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
|||||||
|
|
||||||
novel.summary = loadedCheerio("div.summary__content").text();
|
novel.summary = loadedCheerio("div.summary__content").text();
|
||||||
|
|
||||||
const chapter: Chapter.Item[] = [];
|
const chapter: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
const novelId = loadedCheerio(".rating-post-id").attr("value")!;
|
const novelId = loadedCheerio(".rating-post-id").attr("value")!;
|
||||||
|
|
||||||
@@ -96,7 +92,6 @@ export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData,
|
body: formData,
|
||||||
},
|
},
|
||||||
pluginId
|
|
||||||
);
|
);
|
||||||
const text = await data.text();
|
const text = await data.text();
|
||||||
|
|
||||||
@@ -119,23 +114,20 @@ export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
|||||||
novel.chapters = chapter.reverse();
|
novel.chapters = chapter.reverse();
|
||||||
|
|
||||||
return novel;
|
return novel;
|
||||||
};
|
}
|
||||||
|
async parseChapter(chapterUrl: string): Promise<string> {
|
||||||
|
const chapterText = 'unable to load';
|
||||||
|
|
||||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
return chapterText;
|
||||||
const chapterText = (await NovelUpdatesScraper.parseChapter(chapterUrl))
|
}
|
||||||
.chapterText;
|
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
|
const url = `${this.baseUrl}?s=${searchTerm}&post_type=wp-manga&m_orderby=rating`;
|
||||||
|
|
||||||
return chapterText;
|
const body = await fetchApi(url).then((r) => r.text());
|
||||||
};
|
|
||||||
|
|
||||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
|
||||||
const url = `${baseUrl}?s=${searchTerm}&post_type=wp-manga&m_orderby=rating`;
|
|
||||||
|
|
||||||
const body = await fetchApi(url, {}, pluginId).then((r) => r.text());
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
let novels: Novel.Item[] = [];
|
let novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
loadedCheerio(".c-tabs-item__content").each(function () {
|
loadedCheerio(".c-tabs-item__content").each(function () {
|
||||||
const novelName = loadedCheerio(this).find(".h4 > a").text();
|
const novelName = loadedCheerio(this).find(".h4 > a").text();
|
||||||
@@ -151,11 +143,11 @@ export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
return novels;
|
return novels;
|
||||||
};
|
}
|
||||||
|
async fetchImage(url: string): Promise<string | undefined> {
|
||||||
|
return fetchFile(url);
|
||||||
|
}
|
||||||
|
|
||||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
}
|
||||||
const headers = {
|
|
||||||
Referer: baseUrl,
|
export default new LightNovelUpdates();
|
||||||
};
|
|
||||||
return await fetchFile(url, { headers: headers });
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
import { load as parseHTML } from "cheerio";
|
||||||
|
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||||
|
import { Plugin } from "@typings/plugin";
|
||||||
|
import { Filters } from "@libs/filterInputs";
|
||||||
|
|
||||||
|
|
||||||
|
class LightNovelWorld implements Plugin.PluginBase {
|
||||||
|
id = "lightnovelworld";
|
||||||
|
name = "LightNovelWorld";
|
||||||
|
site = "https://www.webnovelworld.org/";
|
||||||
|
version = "1.0.0";
|
||||||
|
filters?: Filters | undefined;
|
||||||
|
icon = "src/en/lightnovelworld/icon.png";
|
||||||
|
baseUrl = this.site;
|
||||||
|
headers = {
|
||||||
|
Accept: "application/json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
};
|
||||||
|
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||||
|
const url = `${this.baseUrl}browse/all/popular/all/${pageNo}`;
|
||||||
|
|
||||||
|
const body = await fetchApi(url).then((r) => r.text());
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio(".novel-item.ads").remove();
|
||||||
|
|
||||||
|
loadedCheerio(".novel-item").each((idx, ele) => {
|
||||||
|
const novelName = loadedCheerio(ele)
|
||||||
|
.find(".novel-title")
|
||||||
|
.text()
|
||||||
|
.trim();
|
||||||
|
const novelCover = loadedCheerio(ele).find("img").attr("data-src");
|
||||||
|
const novelUrl =
|
||||||
|
this.baseUrl +
|
||||||
|
loadedCheerio(ele)
|
||||||
|
.find(".novel-title > a")
|
||||||
|
.attr("href")
|
||||||
|
?.substring(1);
|
||||||
|
|
||||||
|
const novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||||
|
const url = novelUrl;
|
||||||
|
const body = await fetchApi(url).then((r) => r.text());
|
||||||
|
|
||||||
|
let loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
const novel: Plugin.SourceNovel = {
|
||||||
|
url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
novel.name = loadedCheerio("h1.novel-title").text().trim();
|
||||||
|
|
||||||
|
novel.cover = loadedCheerio("figure.cover > img").attr("data-src");
|
||||||
|
|
||||||
|
novel.genres = "";
|
||||||
|
|
||||||
|
loadedCheerio(".categories > ul > li > a").each(function () {
|
||||||
|
novel.genres += loadedCheerio(this).text() + ",";
|
||||||
|
});
|
||||||
|
|
||||||
|
novel.genres = novel.genres.slice(0, -1);
|
||||||
|
|
||||||
|
loadedCheerio("div.header-stats > span").each(function () {
|
||||||
|
if (loadedCheerio(this).find("small").text() === "Status") {
|
||||||
|
novel.status = loadedCheerio(this).find("strong").text();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
novel.author = loadedCheerio(".author > a > span").text();
|
||||||
|
|
||||||
|
novel.summary = loadedCheerio(".summary > .content").text().trim();
|
||||||
|
const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
|
||||||
|
await delay(1000);
|
||||||
|
const chapterListUrl = novelUrl + '/chapters?chorder=desc';
|
||||||
|
|
||||||
|
const chapterListBody = await fetchApi(chapterListUrl).then(r => r.text());
|
||||||
|
const loadedChapterList = parseHTML(chapterListBody);
|
||||||
|
|
||||||
|
|
||||||
|
const lastNumber = parseInt(
|
||||||
|
loadedChapterList('ul.chapter-list > li:nth-child(1)').attr('data-chapterno') ?? '0'
|
||||||
|
);
|
||||||
|
const lastChapterUrl = loadedChapterList('ul.chapter-list > li:nth-child(1) > a').attr('href');
|
||||||
|
|
||||||
|
if(lastChapterUrl){
|
||||||
|
const chapterUrlPrefix = this.baseUrl + lastChapterUrl.split('/').slice(1, -1).join('/');
|
||||||
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
|
for(let i = 1; i <= lastNumber; i++){
|
||||||
|
chapters.push({
|
||||||
|
chapterNumber: i,
|
||||||
|
name: 'Chapter ' + i,
|
||||||
|
url: chapterUrlPrefix + '/chapter-' + i,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
novel.chapters = chapters;
|
||||||
|
}
|
||||||
|
return novel;
|
||||||
|
}
|
||||||
|
async parseChapter(chapterUrl: string): Promise<string> {
|
||||||
|
const body = await fetchApi(chapterUrl).then((r) => r.text());
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
let chapterText = loadedCheerio("#chapter-container").html() || '';
|
||||||
|
|
||||||
|
return chapterText;
|
||||||
|
}
|
||||||
|
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
|
const url = `${this.baseUrl}lnsearchlive`;
|
||||||
|
const link = `${this.baseUrl}search`;
|
||||||
|
const response = await fetchApi(link).then((r) => r.text());
|
||||||
|
const token = parseHTML(response);
|
||||||
|
let verifytoken = token("#novelSearchForm > input").attr("value");
|
||||||
|
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("inputContent", searchTerm);
|
||||||
|
|
||||||
|
const body = await fetchApi(
|
||||||
|
url,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: { LNRequestVerifyToken: verifytoken! },
|
||||||
|
body: formData,
|
||||||
|
},
|
||||||
|
).then((r) => r.text());
|
||||||
|
|
||||||
|
let loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
let novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
let results = JSON.parse(loadedCheerio("body").text());
|
||||||
|
|
||||||
|
loadedCheerio = parseHTML(results.resultview);
|
||||||
|
|
||||||
|
loadedCheerio(".novel-item").each((idx, ele) => {
|
||||||
|
const novelName = loadedCheerio(ele)
|
||||||
|
.find("h4.novel-title")
|
||||||
|
.text()
|
||||||
|
.trim();
|
||||||
|
const novelCover = loadedCheerio(ele).find("img").attr("src");
|
||||||
|
const novelUrl =
|
||||||
|
this.baseUrl + loadedCheerio(ele).find("a").attr("href")?.substring(1);
|
||||||
|
|
||||||
|
novels.push({
|
||||||
|
name: novelName,
|
||||||
|
url: novelUrl,
|
||||||
|
cover: novelCover,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
async fetchImage(url: string): Promise<string | undefined> {
|
||||||
|
return fetchFile(url, {headers: this.headers});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new LightNovelWorld();
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
import { load as parseHTML } from "cheerio";
|
|
||||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
|
||||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
|
||||||
|
|
||||||
const pluginId = "mtlnovel";
|
|
||||||
const baseUrl = "https://www.mtlnovel.com/";
|
|
||||||
|
|
||||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
|
||||||
const url = `${baseUrl}alltime-rank/page/${page}`;
|
|
||||||
|
|
||||||
const headers = {
|
|
||||||
Referer: `${baseUrl}`,
|
|
||||||
"User-Agent":
|
|
||||||
"'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
|
|
||||||
};
|
|
||||||
|
|
||||||
const body = await fetchApi(
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: headers,
|
|
||||||
},
|
|
||||||
pluginId
|
|
||||||
).then((result) => result.text());
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
let novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio("div.box.wide").each(function () {
|
|
||||||
const novelName = loadedCheerio(this)
|
|
||||||
.find("a.list-title")
|
|
||||||
.text()
|
|
||||||
.slice(4);
|
|
||||||
const novelCover = loadedCheerio(this).find("amp-img").attr("src");
|
|
||||||
const novelUrl = loadedCheerio(this).find("a.list-title").attr("href");
|
|
||||||
|
|
||||||
if (!novelUrl) return;
|
|
||||||
|
|
||||||
const novel = {
|
|
||||||
name: novelName,
|
|
||||||
cover: novelCover,
|
|
||||||
url: novelUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
novels.push(novel);
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
|
||||||
async function (novelUrl) {
|
|
||||||
const url = novelUrl;
|
|
||||||
|
|
||||||
let headers = {
|
|
||||||
Referer: `${baseUrl}alltime-rank/`,
|
|
||||||
"User-Agent":
|
|
||||||
"'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
|
|
||||||
};
|
|
||||||
|
|
||||||
const body = await fetchApi(
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: headers,
|
|
||||||
},
|
|
||||||
pluginId
|
|
||||||
).then((result) => result.text());
|
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
const novel: Novel.instance = {
|
|
||||||
url,
|
|
||||||
chapters: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
novel.name = loadedCheerio("h1.entry-title").text();
|
|
||||||
|
|
||||||
novel.cover = loadedCheerio(".nov-head > amp-img").attr("src");
|
|
||||||
|
|
||||||
novel.summary = loadedCheerio("div.desc > h2").next().text().trim();
|
|
||||||
|
|
||||||
novel.author = loadedCheerio("#author").text();
|
|
||||||
|
|
||||||
novel.status = loadedCheerio("#status").text();
|
|
||||||
|
|
||||||
novel.genres = loadedCheerio("#genre").text().replace(/\s+/g, "");
|
|
||||||
|
|
||||||
const chapterListUrl = url + "chapter-list/";
|
|
||||||
|
|
||||||
async function getChapters() {
|
|
||||||
const listResult = await fetch(chapterListUrl);
|
|
||||||
const listBody = await listResult.text();
|
|
||||||
|
|
||||||
loadedCheerio = parseHTML(listBody);
|
|
||||||
|
|
||||||
let chapter: Chapter.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio("div.ch-list")
|
|
||||||
.find("a.ch-link")
|
|
||||||
.each(function () {
|
|
||||||
const chapterName = loadedCheerio(this)
|
|
||||||
.text()
|
|
||||||
.replace("~ ", "");
|
|
||||||
const releaseDate = null;
|
|
||||||
const chapterUrl = loadedCheerio(this).attr("href");
|
|
||||||
|
|
||||||
chapter.push({
|
|
||||||
url: chapterUrl,
|
|
||||||
name: chapterName,
|
|
||||||
releaseTime: releaseDate,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return chapter.reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
novel.chapters = await getChapters();
|
|
||||||
|
|
||||||
return novel;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
|
||||||
const body = await fetchApi(chapterUrl, {}, pluginId).then((r) => r.text());
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
const chapterText = loadedCheerio("div.par").html();
|
|
||||||
|
|
||||||
return chapterText;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
|
||||||
const searchUrl =
|
|
||||||
baseUrl +
|
|
||||||
"wp-admin/admin-ajax.php?action=autosuggest&q=" +
|
|
||||||
searchTerm +
|
|
||||||
"&__amp_source_origin=https%3A%2F%2Fwww.mtlnovel.com";
|
|
||||||
|
|
||||||
const res = await fetch(searchUrl);
|
|
||||||
const result = await res.json();
|
|
||||||
|
|
||||||
let novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
result.items[0].results.map((item) => {
|
|
||||||
const novelName = item.title.replace(/<\/?strong>/g, "");
|
|
||||||
const novelCover = item.thumbnail;
|
|
||||||
const novelUrl = item.permalink.replace(
|
|
||||||
"https://www.mtlnovel.com/",
|
|
||||||
""
|
|
||||||
);
|
|
||||||
|
|
||||||
const novel = { name: novelName, cover: novelCover, url: novelUrl };
|
|
||||||
|
|
||||||
novels.push(novel);
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
|
||||||
const headers = {
|
|
||||||
Referer: baseUrl,
|
|
||||||
};
|
|
||||||
return await fetchFile(url, { headers: headers });
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
id: pluginId,
|
|
||||||
name: "MTL Novel",
|
|
||||||
version: "1.0.0",
|
|
||||||
icon: "src/en/mtlnovel/icon.png",
|
|
||||||
site: baseUrl,
|
|
||||||
protected: false,
|
|
||||||
fetchImage,
|
|
||||||
popularNovels,
|
|
||||||
parseNovelAndChapters,
|
|
||||||
parseChapter,
|
|
||||||
searchNovels,
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
import { load as parseHTML } from "cheerio";
|
||||||
|
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||||
|
import { Plugin } from "@typings/plugin";
|
||||||
|
import { Filters } from "@libs/filterInputs";
|
||||||
|
|
||||||
|
class MTLNovel implements Plugin.PluginBase {
|
||||||
|
id = "mtlnovel";
|
||||||
|
name = "MTL Novel";
|
||||||
|
version = "1.0.0";
|
||||||
|
icon = "src/en/mtlnovel/icon.png";
|
||||||
|
site = "https://www.mtlnovel.com/";
|
||||||
|
baseUrl = this.site;
|
||||||
|
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||||
|
const url = `${this.baseUrl}alltime-rank/page/${pageNo}`;
|
||||||
|
const body = await fetchApi(url).then((result) => result.text());
|
||||||
|
console.log(body);
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
let novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio("div.box.wide").each(function () {
|
||||||
|
const novelName = loadedCheerio(this)
|
||||||
|
.find("a.list-title")
|
||||||
|
.text()
|
||||||
|
.slice(4);
|
||||||
|
const novelCover = loadedCheerio(this).find("amp-img").attr("src");
|
||||||
|
const novelUrl = loadedCheerio(this).find("a.list-title").attr("href");
|
||||||
|
|
||||||
|
if (!novelUrl) return;
|
||||||
|
|
||||||
|
const novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||||
|
const url = novelUrl;
|
||||||
|
|
||||||
|
let headers = {
|
||||||
|
Referer: `${this.baseUrl}alltime-rank/`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const body = await fetchApi(
|
||||||
|
url,
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: headers,
|
||||||
|
},
|
||||||
|
).then((result) => result.text());
|
||||||
|
|
||||||
|
let loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
const novel: Plugin.SourceNovel = {
|
||||||
|
url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
novel.name = loadedCheerio("h1.entry-title").text();
|
||||||
|
|
||||||
|
novel.cover = loadedCheerio(".nov-head > amp-img").attr("src");
|
||||||
|
|
||||||
|
novel.summary = loadedCheerio("div.desc > h2").next().text().trim();
|
||||||
|
|
||||||
|
novel.author = loadedCheerio("#author").text();
|
||||||
|
|
||||||
|
novel.status = loadedCheerio("#status").text();
|
||||||
|
|
||||||
|
novel.genres = loadedCheerio("#genre").text().replace(/\s+/g, "");
|
||||||
|
|
||||||
|
const chapterListUrl = url + "chapter-list/";
|
||||||
|
|
||||||
|
async function getChapters() {
|
||||||
|
const listResult = await fetch(chapterListUrl);
|
||||||
|
const listBody = await listResult.text();
|
||||||
|
|
||||||
|
loadedCheerio = parseHTML(listBody);
|
||||||
|
|
||||||
|
let chapter: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio("div.ch-list")
|
||||||
|
.find("a.ch-link")
|
||||||
|
.each(function () {
|
||||||
|
const chapterName = loadedCheerio(this)
|
||||||
|
.text()
|
||||||
|
.replace("~ ", "");
|
||||||
|
const releaseDate = null;
|
||||||
|
const chapterUrl = loadedCheerio(this).attr("href");
|
||||||
|
if (chapterUrl) {
|
||||||
|
chapter.push({
|
||||||
|
url: chapterUrl,
|
||||||
|
name: chapterName,
|
||||||
|
releaseTime: releaseDate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return chapter.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
novel.chapters = await getChapters();
|
||||||
|
|
||||||
|
return novel;
|
||||||
|
}
|
||||||
|
async parseChapter(chapterUrl: string): Promise<string> {
|
||||||
|
const body = await fetchApi(chapterUrl).then((r) => r.text());
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
const chapterText = loadedCheerio("div.par").html() || '';
|
||||||
|
|
||||||
|
return chapterText;
|
||||||
|
}
|
||||||
|
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
|
const searchUrl =
|
||||||
|
this.baseUrl +
|
||||||
|
"wp-admin/admin-ajax.php?action=autosuggest&q=" +
|
||||||
|
searchTerm +
|
||||||
|
"&__amp_source_origin=https%3A%2F%2Fwww.mtlnovel.com";
|
||||||
|
|
||||||
|
const res = await fetch(searchUrl);
|
||||||
|
const result = await res.json();
|
||||||
|
|
||||||
|
let novels: Plugin.NovelItem[] = [];
|
||||||
|
interface SearchEntry {
|
||||||
|
title: string;
|
||||||
|
thumbnail: string;
|
||||||
|
permalink: string;
|
||||||
|
}
|
||||||
|
result.items[0].results.map((item: SearchEntry) => {
|
||||||
|
const novelName = item.title.replace(/<\/?strong>/g, "");
|
||||||
|
const novelCover = item.thumbnail;
|
||||||
|
const novelUrl = item.permalink.replace(
|
||||||
|
"https://www.mtlnovel.com/",
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
|
const novel = { name: novelName, cover: novelCover, url: novelUrl };
|
||||||
|
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
async fetchImage(url: string): Promise<string | undefined> {
|
||||||
|
return fetchFile(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export default new MTLNovel();
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
import { load as parseHTML } from "cheerio";
|
|
||||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
|
||||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
|
||||||
|
|
||||||
export const id = "mtlreader";
|
|
||||||
export const name = "MTL Reader";
|
|
||||||
export const version = "1.0.0";
|
|
||||||
export const icon = "src/en/mtlreader/icon.png";
|
|
||||||
export const site = "https://mtlreader.com/";
|
|
||||||
|
|
||||||
|
|
||||||
const pluginId = id;
|
|
||||||
const baseUrl = site;
|
|
||||||
|
|
||||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
|
||||||
const url = `${baseUrl}novels?page=${page}`;
|
|
||||||
|
|
||||||
const body = await fetchApi(url, {}, pluginId).then((r) => r.text());
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
let novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio(".col-md-4.col-sm-4").each(function () {
|
|
||||||
const novelName = loadedCheerio(this).find("h5").text();
|
|
||||||
const novelCover = loadedCheerio(this).find("img").attr("src");
|
|
||||||
const novelUrl = loadedCheerio(this).find("h5 > a").attr("href");
|
|
||||||
|
|
||||||
if (!novelUrl) return;
|
|
||||||
|
|
||||||
const novel = {
|
|
||||||
name: novelName,
|
|
||||||
cover: novelCover,
|
|
||||||
url: novelUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
novels.push(novel);
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
|
||||||
async function (novelUrl) {
|
|
||||||
const url = novelUrl;
|
|
||||||
|
|
||||||
const body = await fetchApi(url, {}, pluginId).then((r) => r.text());
|
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
const novel: Novel.instance = {
|
|
||||||
url,
|
|
||||||
chapters: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
novel.name = loadedCheerio(".agent-title").text().trim();
|
|
||||||
|
|
||||||
novel.cover = loadedCheerio(".agent-p-img > img").attr("src");
|
|
||||||
|
|
||||||
novel.summary = loadedCheerio("#editdescription").text().trim();
|
|
||||||
|
|
||||||
novel.author = loadedCheerio("i.fa-user")
|
|
||||||
.parent()
|
|
||||||
.text()
|
|
||||||
.replace("Author: ", "")
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
let chapter: Chapter.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio("tr.spaceUnder").each(function () {
|
|
||||||
const chapterName = loadedCheerio(this).find("a").text().trim();
|
|
||||||
const releaseDate = null;
|
|
||||||
const chapterUrl = loadedCheerio(this).find("a").attr("href");
|
|
||||||
|
|
||||||
if (!chapterUrl) return;
|
|
||||||
|
|
||||||
chapter.push({
|
|
||||||
name: chapterName,
|
|
||||||
releaseTime: releaseDate,
|
|
||||||
url: chapterUrl,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
novel.chapters = chapter;
|
|
||||||
|
|
||||||
return novel;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
|
||||||
const body = await fetchApi(chapterUrl, {}, pluginId).then((r) => r.text());
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
loadedCheerio(".container ins,script,p.mtlreader").remove();
|
|
||||||
const chapterText = loadedCheerio(".container").html();
|
|
||||||
|
|
||||||
return chapterText;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
|
||||||
const url = `${baseUrl}search?input=${searchTerm}`;
|
|
||||||
|
|
||||||
const body = await fetchApi(url, {}, pluginId).then((r) => r.text());
|
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
let novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio(".col-md-4.col-sm-4").each(function () {
|
|
||||||
const novelName = loadedCheerio(this).find("h5").text();
|
|
||||||
const novelCover = loadedCheerio(this).find("img").attr("src");
|
|
||||||
const novelUrl = loadedCheerio(this).find("h5 > a").attr("href");
|
|
||||||
|
|
||||||
if (!novelUrl) return;
|
|
||||||
|
|
||||||
const novel = {
|
|
||||||
name: novelName,
|
|
||||||
cover: novelCover,
|
|
||||||
url: novelUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
novels.push(novel);
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
|
||||||
const headers = {
|
|
||||||
Referer: baseUrl,
|
|
||||||
};
|
|
||||||
return await fetchFile(url, { headers: headers });
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
import { load as parseHTML } from "cheerio";
|
||||||
|
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||||
|
import { Plugin } from "@typings/plugin";
|
||||||
|
import { Filters } from "@libs/filterInputs";
|
||||||
|
|
||||||
|
class MTLReader implements Plugin.PluginBase {
|
||||||
|
id = "mtlreader";
|
||||||
|
name = "MTL Reader";
|
||||||
|
version = "1.0.0";
|
||||||
|
icon = "src/en/mtlreader/icon.png";
|
||||||
|
site = "https://mtlreader.com/";
|
||||||
|
baseUrl = this.site;
|
||||||
|
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||||
|
const url = `${this.baseUrl}novels?page=${pageNo}`;
|
||||||
|
|
||||||
|
const body = await fetchApi(url).then((r) => r.text());
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
let novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio(".col-md-4.col-sm-4").each(function () {
|
||||||
|
const novelName = loadedCheerio(this).find("h5").text();
|
||||||
|
const novelCover = loadedCheerio(this).find("img").attr("src");
|
||||||
|
const novelUrl = loadedCheerio(this).find("h5 > a").attr("href");
|
||||||
|
|
||||||
|
if (!novelUrl) return;
|
||||||
|
|
||||||
|
const novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||||
|
const url = novelUrl;
|
||||||
|
|
||||||
|
const body = await fetchApi(url).then((r) => r.text());
|
||||||
|
|
||||||
|
let loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
const novel: Plugin.SourceNovel = {
|
||||||
|
url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
novel.name = loadedCheerio(".agent-title").text().trim();
|
||||||
|
|
||||||
|
novel.cover = loadedCheerio(".agent-p-img > img").attr("src");
|
||||||
|
|
||||||
|
novel.summary = loadedCheerio("#editdescription").text().trim();
|
||||||
|
|
||||||
|
novel.author = loadedCheerio("i.fa-user")
|
||||||
|
.parent()
|
||||||
|
.text()
|
||||||
|
.replace("Author: ", "")
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
let chapter: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio("tr.spaceUnder").each(function () {
|
||||||
|
const chapterName = loadedCheerio(this).find("a").text().trim();
|
||||||
|
const releaseDate = null;
|
||||||
|
const chapterUrl = loadedCheerio(this).find("a").attr("href");
|
||||||
|
|
||||||
|
if (!chapterUrl) return;
|
||||||
|
|
||||||
|
chapter.push({
|
||||||
|
name: chapterName,
|
||||||
|
releaseTime: releaseDate,
|
||||||
|
url: chapterUrl,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
novel.chapters = chapter;
|
||||||
|
|
||||||
|
return novel;
|
||||||
|
}
|
||||||
|
async parseChapter(chapterUrl: string): Promise<string> {
|
||||||
|
const body = await fetchApi(chapterUrl).then((r) => r.text());
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
loadedCheerio(".container ins,script,p.mtlreader").remove();
|
||||||
|
const chapterText = loadedCheerio(".container").html() || '';
|
||||||
|
|
||||||
|
return chapterText;
|
||||||
|
}
|
||||||
|
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
|
const url = `${this.baseUrl}search?input=${searchTerm}`;
|
||||||
|
|
||||||
|
const body = await fetchApi(url).then((r) => r.text());
|
||||||
|
|
||||||
|
let loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
let novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio(".col-md-4.col-sm-4").each(function () {
|
||||||
|
const novelName = loadedCheerio(this).find("h5").text();
|
||||||
|
const novelCover = loadedCheerio(this).find("img").attr("src");
|
||||||
|
const novelUrl = loadedCheerio(this).find("h5 > a").attr("href");
|
||||||
|
|
||||||
|
if (!novelUrl) return;
|
||||||
|
|
||||||
|
const novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
async fetchImage(url: string): Promise<string | undefined> {
|
||||||
|
return fetchFile(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export default new MTLReader();
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
import { load as parseHTML } from "cheerio";
|
|
||||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
|
||||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
|
||||||
|
|
||||||
export const id = "novelfull";
|
|
||||||
export const name = "NovelFull";
|
|
||||||
export const version = "1.0.0";
|
|
||||||
export const icon = "src/en/novelfull/icon.png";
|
|
||||||
export const site = "https://novelfull.com/";
|
|
||||||
|
|
||||||
|
|
||||||
const baseUrl = site;
|
|
||||||
|
|
||||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
|
||||||
const url = `${baseUrl}most-popular?page=${page}`;
|
|
||||||
|
|
||||||
const result = await fetchApi(url);
|
|
||||||
const body = await result.text();
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
let novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio(".col-truyen-main .list-truyen .row").each(function () {
|
|
||||||
const novelName = loadedCheerio(this)
|
|
||||||
.find("h3.truyen-title > a")
|
|
||||||
.text();
|
|
||||||
|
|
||||||
const novelCover =
|
|
||||||
baseUrl + loadedCheerio(this).find("img").attr("src")?.slice(1);
|
|
||||||
|
|
||||||
const novelUrl =
|
|
||||||
baseUrl +
|
|
||||||
loadedCheerio(this)
|
|
||||||
.find("h3.truyen-title > a")
|
|
||||||
.attr("href")
|
|
||||||
?.slice(1);
|
|
||||||
|
|
||||||
const novel = {
|
|
||||||
name: novelName,
|
|
||||||
cover: novelCover,
|
|
||||||
url: novelUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
novels.push(novel);
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
|
||||||
async function (novelUrl) {
|
|
||||||
const url = novelUrl;
|
|
||||||
|
|
||||||
const result = await fetchApi(url);
|
|
||||||
const body = await result.text();
|
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
const novel: Novel.instance = {
|
|
||||||
url,
|
|
||||||
chapters: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
novel.name = loadedCheerio("div.book > img").attr("alt");
|
|
||||||
|
|
||||||
novel.cover = baseUrl + loadedCheerio("div.book > img").attr("src");
|
|
||||||
|
|
||||||
novel.summary = loadedCheerio("div.desc-text").text().trim();
|
|
||||||
|
|
||||||
novel.author = loadedCheerio('h3:contains("Author")')
|
|
||||||
.parent()
|
|
||||||
.contents()
|
|
||||||
.text()
|
|
||||||
.replace("Author:", "");
|
|
||||||
|
|
||||||
novel.genres = loadedCheerio('h3:contains("Genre")')
|
|
||||||
.siblings()
|
|
||||||
.map((i, el) => loadedCheerio(el).text())
|
|
||||||
.toArray()
|
|
||||||
.join(",");
|
|
||||||
|
|
||||||
novel.status = loadedCheerio('h3:contains("Status")').next().text();
|
|
||||||
|
|
||||||
const novelId = loadedCheerio("#rating").attr("data-novel-id")!;
|
|
||||||
|
|
||||||
async function getChapters(id: string) {
|
|
||||||
const chapterListUrl =
|
|
||||||
baseUrl + "ajax/chapter-option?novelId=" + id;
|
|
||||||
|
|
||||||
const data = await fetchApi(chapterListUrl);
|
|
||||||
const chapterlist = await data.text();
|
|
||||||
|
|
||||||
loadedCheerio = parseHTML(chapterlist);
|
|
||||||
|
|
||||||
let chapter: Chapter.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio("select > option").each(function () {
|
|
||||||
const chapterName = loadedCheerio(this).text();
|
|
||||||
const releaseDate = null;
|
|
||||||
const chapterUrl =
|
|
||||||
baseUrl + loadedCheerio(this).attr("value")?.slice(1);
|
|
||||||
|
|
||||||
chapter.push({
|
|
||||||
name: chapterName,
|
|
||||||
releaseTime: releaseDate,
|
|
||||||
url: chapterUrl,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return chapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
novel.chapters = await getChapters(novelId);
|
|
||||||
|
|
||||||
return novel;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
|
||||||
const result = await fetchApi(chapterUrl);
|
|
||||||
const body = await result.text();
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
loadedCheerio("#chapter-content div.ads").remove();
|
|
||||||
let chapterText = loadedCheerio("#chapter-content").html();
|
|
||||||
|
|
||||||
return chapterText;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
|
||||||
const searchUrl = `${baseUrl}search?keyword=${searchTerm}`;
|
|
||||||
|
|
||||||
const result = await fetchApi(searchUrl);
|
|
||||||
const body = await result.text();
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
let novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio(".col-truyen-main .list-truyen .row").each(function () {
|
|
||||||
const novelName = loadedCheerio(this)
|
|
||||||
.find("h3.truyen-title > a")
|
|
||||||
.text();
|
|
||||||
|
|
||||||
const novelCover =
|
|
||||||
baseUrl + loadedCheerio(this).find("img").attr("src")?.slice(1);
|
|
||||||
|
|
||||||
const novelUrl =
|
|
||||||
baseUrl +
|
|
||||||
loadedCheerio(this)
|
|
||||||
.find("h3.truyen-title > a")
|
|
||||||
.attr("href")
|
|
||||||
?.slice(1);
|
|
||||||
|
|
||||||
const novel = {
|
|
||||||
name: novelName,
|
|
||||||
cover: novelCover,
|
|
||||||
url: novelUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
novels.push(novel);
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
|
||||||
const headers = {
|
|
||||||
Referer: baseUrl,
|
|
||||||
};
|
|
||||||
return await fetchFile(url, { headers: headers });
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
import { load as parseHTML } from "cheerio";
|
||||||
|
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||||
|
import { Plugin } from "@typings/plugin";
|
||||||
|
import { Filters } from "@libs/filterInputs";
|
||||||
|
|
||||||
|
|
||||||
|
class NovelFull implements Plugin.PluginBase {
|
||||||
|
id = "novelfull";
|
||||||
|
name = "NovelFull";
|
||||||
|
version = "1.0.0";
|
||||||
|
icon = "src/en/novelfull/icon.png";
|
||||||
|
site = "https://novelfull.com/";
|
||||||
|
baseUrl = this.site;
|
||||||
|
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||||
|
const url = `${this.baseUrl}most-popular?page=${pageNo}`;
|
||||||
|
|
||||||
|
const result = await fetchApi(url);
|
||||||
|
const body = await result.text();
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
let novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio(".col-truyen-main .list-truyen .row").each((idx, ele) => {
|
||||||
|
const novelName = loadedCheerio(ele)
|
||||||
|
.find("h3.truyen-title > a")
|
||||||
|
.text();
|
||||||
|
|
||||||
|
const novelCover =
|
||||||
|
this.baseUrl + loadedCheerio(ele).find("img").attr("src")?.slice(1);
|
||||||
|
|
||||||
|
const novelUrl =
|
||||||
|
this.baseUrl +
|
||||||
|
loadedCheerio(ele)
|
||||||
|
.find("h3.truyen-title > a")
|
||||||
|
.attr("href")
|
||||||
|
?.slice(1);
|
||||||
|
|
||||||
|
const novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||||
|
const url = novelUrl;
|
||||||
|
|
||||||
|
const result = await fetchApi(url);
|
||||||
|
const body = await result.text();
|
||||||
|
|
||||||
|
let loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
const novel: Plugin.SourceNovel = {
|
||||||
|
url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
novel.name = loadedCheerio("div.book > img").attr("alt");
|
||||||
|
|
||||||
|
novel.cover = this.baseUrl + loadedCheerio("div.book > img").attr("src");
|
||||||
|
|
||||||
|
novel.summary = loadedCheerio("div.desc-text").text().trim();
|
||||||
|
|
||||||
|
novel.author = loadedCheerio('h3:contains("Author")')
|
||||||
|
.parent()
|
||||||
|
.contents()
|
||||||
|
.text()
|
||||||
|
.replace("Author:", "");
|
||||||
|
|
||||||
|
novel.genres = loadedCheerio('h3:contains("Genre")')
|
||||||
|
.siblings()
|
||||||
|
.map((i, el) => loadedCheerio(el).text())
|
||||||
|
.toArray()
|
||||||
|
.join(",");
|
||||||
|
|
||||||
|
novel.status = loadedCheerio('h3:contains("Status")').next().text();
|
||||||
|
|
||||||
|
const novelId = loadedCheerio("#rating").attr("data-novel-id")!;
|
||||||
|
|
||||||
|
async function getChapters(id: string, baseUrl: string) {
|
||||||
|
const chapterListUrl =
|
||||||
|
baseUrl + "ajax/chapter-option?novelId=" + id;
|
||||||
|
|
||||||
|
const data = await fetchApi(chapterListUrl);
|
||||||
|
const chapterlist = await data.text();
|
||||||
|
|
||||||
|
loadedCheerio = parseHTML(chapterlist);
|
||||||
|
|
||||||
|
let chapter: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio("select > option").each(function () {
|
||||||
|
const chapterName = loadedCheerio(this).text();
|
||||||
|
const releaseDate = null;
|
||||||
|
const chapterUrl =
|
||||||
|
baseUrl + loadedCheerio(this).attr("value")?.slice(1);
|
||||||
|
|
||||||
|
chapter.push({
|
||||||
|
name: chapterName,
|
||||||
|
releaseTime: releaseDate,
|
||||||
|
url: chapterUrl,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return chapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
novel.chapters = await getChapters(novelId, this.baseUrl);
|
||||||
|
|
||||||
|
return novel;
|
||||||
|
}
|
||||||
|
async parseChapter(chapterUrl: string): Promise<string> {
|
||||||
|
const result = await fetchApi(chapterUrl);
|
||||||
|
const body = await result.text();
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
loadedCheerio("#chapter-content div.ads").remove();
|
||||||
|
let chapterText = loadedCheerio("#chapter-content").html() || '';
|
||||||
|
|
||||||
|
return chapterText;
|
||||||
|
}
|
||||||
|
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
|
const searchUrl = `${this.baseUrl}search?keyword=${searchTerm}`;
|
||||||
|
|
||||||
|
const result = await fetchApi(searchUrl);
|
||||||
|
const body = await result.text();
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
let novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio(".col-truyen-main .list-truyen .row").each((idx, ele) => {
|
||||||
|
const novelName = loadedCheerio(ele)
|
||||||
|
.find("h3.truyen-title > a")
|
||||||
|
.text();
|
||||||
|
|
||||||
|
const novelCover =
|
||||||
|
this.baseUrl + loadedCheerio(ele).find("img").attr("src")?.slice(1);
|
||||||
|
|
||||||
|
const novelUrl =
|
||||||
|
this.baseUrl +
|
||||||
|
loadedCheerio(ele)
|
||||||
|
.find("h3.truyen-title > a")
|
||||||
|
.attr("href")
|
||||||
|
?.slice(1);
|
||||||
|
|
||||||
|
const novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
async fetchImage(url: string): Promise<string | undefined> {
|
||||||
|
return fetchFile(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new NovelFull();
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
import { load as parseHTML } from "cheerio";
|
||||||
|
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||||
|
import { Plugin } from "@typings/plugin";
|
||||||
|
import { Filters } from "@libs/filterInputs";
|
||||||
|
|
||||||
|
class NovelHall implements Plugin.PluginBase {
|
||||||
|
id = "novelhold";
|
||||||
|
name = "Novel Hall";
|
||||||
|
version = "1.0.0";
|
||||||
|
icon = "src/en/novelhall/icon.png";
|
||||||
|
filters?: Filters | undefined;
|
||||||
|
site = "https://novelhall.com/";
|
||||||
|
baseUrl = this.site;
|
||||||
|
|
||||||
|
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||||
|
const url = `${this.baseUrl}all2022-${pageNo}.html`;
|
||||||
|
|
||||||
|
const body = await fetchApi(url).then((r) => r.text());
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
let novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio("li.btm").each((idx, ele) => {
|
||||||
|
const novelName = loadedCheerio(ele).text().trim()
|
||||||
|
const novelUrl =
|
||||||
|
this.baseUrl + loadedCheerio(ele).find("a").attr("href");
|
||||||
|
|
||||||
|
const novel = {
|
||||||
|
name: novelName,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||||
|
const url = novelUrl;
|
||||||
|
|
||||||
|
const body = await fetchApi(url).then((r) => r.text());
|
||||||
|
|
||||||
|
let loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
const novel: Plugin.SourceNovel = {
|
||||||
|
url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
novel.name = loadedCheerio(".booknav2 > h1").text();
|
||||||
|
|
||||||
|
novel.cover = loadedCheerio('meta[property="og:image"]').attr(
|
||||||
|
"content"
|
||||||
|
);
|
||||||
|
|
||||||
|
novel.summary = loadedCheerio(".navtxt").text().trim();
|
||||||
|
|
||||||
|
novel.author = loadedCheerio('p:contains("Author")')
|
||||||
|
.text()
|
||||||
|
.replace("Author:", "")
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
novel.status = loadedCheerio('p:contains("Status")')
|
||||||
|
.text()
|
||||||
|
.replace("Status:", "")
|
||||||
|
.replace("Active", "Ongoing")
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
novel.genres = loadedCheerio('p:contains("Genre")')
|
||||||
|
.text()
|
||||||
|
?.replace("Genre:", "")
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
let chapter: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio("#morelist ul > li").each((idx, ele) => {
|
||||||
|
const chapterName = loadedCheerio(ele).find("a").text().trim();
|
||||||
|
const releaseDate = null;
|
||||||
|
const chapterUrl =
|
||||||
|
this.baseUrl + loadedCheerio(ele).find("a").attr("href");
|
||||||
|
|
||||||
|
chapter.push({
|
||||||
|
name: chapterName,
|
||||||
|
releaseTime: releaseDate,
|
||||||
|
url: chapterUrl,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
novel.chapters = chapter;
|
||||||
|
return novel;
|
||||||
|
}
|
||||||
|
async parseChapter(chapterUrl: string): Promise<string> {
|
||||||
|
const body = await fetchApi(chapterUrl).then((r) => r.text());
|
||||||
|
|
||||||
|
let loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
const chapterText = loadedCheerio(".content").html() || '';
|
||||||
|
|
||||||
|
return chapterText;
|
||||||
|
}
|
||||||
|
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
|
const url = `${this.baseUrl}index.php?s=so&module=book&keyword=${searchTerm}`;
|
||||||
|
|
||||||
|
const body = await fetchApi(url).then((r) => r.text());
|
||||||
|
|
||||||
|
let loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
let novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio("#article_list_content > li").each((idx, ele) => {
|
||||||
|
const novelName = loadedCheerio(ele)
|
||||||
|
.find("h3")
|
||||||
|
.text()
|
||||||
|
.replace(/\t+/g, "")
|
||||||
|
.replace(/\n/g, " ");
|
||||||
|
const novelCover = loadedCheerio(ele).find("img").attr("data-src");
|
||||||
|
const novelUrl =
|
||||||
|
this.baseUrl + loadedCheerio(ele).find("a").attr("href")?.slice(1);
|
||||||
|
|
||||||
|
const novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
async fetchImage(url: string): Promise<string | undefined> {
|
||||||
|
return fetchFile(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new NovelHall();
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
import { load as parseHTML } from "cheerio";
|
|
||||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
|
||||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
|
||||||
|
|
||||||
export const id = "novelhold";
|
|
||||||
export const name = "Novel Hold";
|
|
||||||
export const version = "1.0.0";
|
|
||||||
export const icon = "src/en/novelhold/icon.png";
|
|
||||||
export const site = "https://novelhold.com/";
|
|
||||||
|
|
||||||
|
|
||||||
const baseUrl = site;
|
|
||||||
|
|
||||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
|
||||||
const url = `${baseUrl}all-${page}.html`;
|
|
||||||
|
|
||||||
const body = await fetchApi(url).then((r) => r.text());
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
let novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio("#article_list_content > li").each(function () {
|
|
||||||
const novelName = loadedCheerio(this)
|
|
||||||
.find("h3")
|
|
||||||
.text()
|
|
||||||
.replace(/\t+/g, "")
|
|
||||||
.replace(/\n/g, " ");
|
|
||||||
const novelCover = loadedCheerio(this).find("img").attr("data-src");
|
|
||||||
const novelUrl =
|
|
||||||
baseUrl + loadedCheerio(this).find("a").attr("href")?.slice(1);
|
|
||||||
|
|
||||||
const novel = {
|
|
||||||
name: novelName,
|
|
||||||
cover: novelCover,
|
|
||||||
url: novelUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
novels.push(novel);
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
|
||||||
async function (novelUrl) {
|
|
||||||
const url = novelUrl;
|
|
||||||
|
|
||||||
const body = await fetchApi(url).then((r) => r.text());
|
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
const novel: Novel.instance = {
|
|
||||||
url,
|
|
||||||
chapters: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
novel.name = loadedCheerio(".booknav2 > h1").text();
|
|
||||||
|
|
||||||
novel.cover = loadedCheerio('meta[property="og:image"]').attr(
|
|
||||||
"content"
|
|
||||||
);
|
|
||||||
|
|
||||||
novel.summary = loadedCheerio(".navtxt").text().trim();
|
|
||||||
|
|
||||||
novel.author = loadedCheerio('p:contains("Author")')
|
|
||||||
.text()
|
|
||||||
.replace("Author:", "")
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
novel.status = loadedCheerio('p:contains("Status")')
|
|
||||||
.text()
|
|
||||||
.replace("Status:", "")
|
|
||||||
.replace("Active", "Ongoing")
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
novel.genres = loadedCheerio('p:contains("Genre")')
|
|
||||||
.text()
|
|
||||||
?.replace("Genre:", "")
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
let chapter: Chapter.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio("ul.chapterlist > li").each(function () {
|
|
||||||
const chapterName = loadedCheerio(this).find("a").text().trim();
|
|
||||||
const releaseDate = null;
|
|
||||||
const chapterUrl =
|
|
||||||
baseUrl + loadedCheerio(this).find("a").attr("href")?.slice(1);
|
|
||||||
|
|
||||||
chapter.push({
|
|
||||||
name: chapterName,
|
|
||||||
releaseTime: releaseDate,
|
|
||||||
url: chapterUrl,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
novel.chapters = chapter;
|
|
||||||
console.log(novel);
|
|
||||||
return novel;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
|
||||||
const body = await fetchApi(chapterUrl).then((r) => r.text());
|
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
const chapterText = loadedCheerio(".content").html();
|
|
||||||
|
|
||||||
return chapterText;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
|
||||||
const url = `${baseUrl}index.php?s=so&module=book&keyword=${searchTerm}`;
|
|
||||||
|
|
||||||
const body = await fetchApi(url).then((r) => r.text());
|
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
let novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio("#article_list_content > li").each(function () {
|
|
||||||
const novelName = loadedCheerio(this)
|
|
||||||
.find("h3")
|
|
||||||
.text()
|
|
||||||
.replace(/\t+/g, "")
|
|
||||||
.replace(/\n/g, " ");
|
|
||||||
const novelCover = loadedCheerio(this).find("img").attr("data-src");
|
|
||||||
const novelUrl =
|
|
||||||
baseUrl + loadedCheerio(this).find("a").attr("href")?.slice(1);
|
|
||||||
|
|
||||||
const novel = {
|
|
||||||
name: novelName,
|
|
||||||
cover: novelCover,
|
|
||||||
url: novelUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
novels.push(novel);
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
|
||||||
const headers = {
|
|
||||||
Referer: baseUrl,
|
|
||||||
};
|
|
||||||
return await fetchFile(url, { headers: headers });
|
|
||||||
};
|
|
||||||
@@ -1,189 +0,0 @@
|
|||||||
import { load as parseHTML } from "cheerio";
|
|
||||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
|
||||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
|
||||||
|
|
||||||
export const id = "novelpub.com";
|
|
||||||
export const name = "NovelPub";
|
|
||||||
export const site = "https://www.novelpub.com/";
|
|
||||||
export const version = "1.0.0";
|
|
||||||
export const icon = "src/en/novelpub/icon.png";
|
|
||||||
const baseUrl = site;
|
|
||||||
|
|
||||||
const headers = {
|
|
||||||
Accept: "application/json",
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"User-Agent":
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
|
|
||||||
};
|
|
||||||
|
|
||||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
|
||||||
let url = baseUrl + "browse/all/popular/all/" + page;
|
|
||||||
|
|
||||||
const result = await fetchApi(url, { method: "GET", headers });
|
|
||||||
const body = await result.text();
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
let novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio(".novel-item.ads").remove();
|
|
||||||
|
|
||||||
loadedCheerio(".novel-item").each(function () {
|
|
||||||
const novelName = loadedCheerio(this)
|
|
||||||
.find(".novel-title")
|
|
||||||
.text()
|
|
||||||
.trim();
|
|
||||||
const novelCover = loadedCheerio(this).find("img").attr("data-src");
|
|
||||||
const novelUrl =
|
|
||||||
baseUrl +
|
|
||||||
loadedCheerio(this)
|
|
||||||
.find(".novel-title > a")
|
|
||||||
.attr("href")
|
|
||||||
?.substring(1);
|
|
||||||
|
|
||||||
const novel = { name: novelName, cover: novelCover, url: novelUrl };
|
|
||||||
|
|
||||||
novels.push(novel);
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
|
||||||
async function (novelUrl) {
|
|
||||||
const url = novelUrl;
|
|
||||||
|
|
||||||
const result = await fetchApi(url, { method: "GET", headers });
|
|
||||||
const body = await result.text();
|
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
let novel: Novel.instance = { url, genres: "" };
|
|
||||||
|
|
||||||
novel.name = loadedCheerio("h1.novel-title").text().trim();
|
|
||||||
|
|
||||||
novel.cover = loadedCheerio("figure.cover > img").attr("data-src");
|
|
||||||
|
|
||||||
loadedCheerio("div.categories > ul > li").each(function () {
|
|
||||||
novel.genres +=
|
|
||||||
loadedCheerio(this)
|
|
||||||
.text()
|
|
||||||
.replace(/[\t\n]/g, "") + ",";
|
|
||||||
});
|
|
||||||
|
|
||||||
loadedCheerio("div.header-stats > span").each(function () {
|
|
||||||
if (loadedCheerio(this).find("small").text() === "Status") {
|
|
||||||
novel.status = loadedCheerio(this).find("strong").text();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
novel.genres = novel.genres?.slice(0, -1);
|
|
||||||
|
|
||||||
novel.author = loadedCheerio(".author > a > span").text();
|
|
||||||
|
|
||||||
novel.summary = loadedCheerio(".summary > .content").text().trim();
|
|
||||||
|
|
||||||
const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
|
|
||||||
|
|
||||||
let lastPage = 1;
|
|
||||||
|
|
||||||
lastPage = parseInt(
|
|
||||||
loadedCheerio(
|
|
||||||
"#novel > header > div.header-body.container > div.novel-info > div.header-stats > span:nth-child(1) > strong"
|
|
||||||
)
|
|
||||||
.text()
|
|
||||||
?.trim()
|
|
||||||
);
|
|
||||||
|
|
||||||
lastPage = Math.ceil(lastPage / 100);
|
|
||||||
|
|
||||||
const getChapters = async () => {
|
|
||||||
let novelChapters: Chapter.Item[] = [];
|
|
||||||
|
|
||||||
for (let i = 1; i <= lastPage; i++) {
|
|
||||||
const chaptersUrl = `${novelUrl}/chapters/page-${i}`;
|
|
||||||
|
|
||||||
const chaptersRequest = await fetchApi(chaptersUrl, {
|
|
||||||
headers,
|
|
||||||
});
|
|
||||||
const chaptersHtml = await chaptersRequest.text();
|
|
||||||
|
|
||||||
loadedCheerio = parseHTML(chaptersHtml);
|
|
||||||
|
|
||||||
loadedCheerio(".chapter-list li").each(function () {
|
|
||||||
const chapterName = loadedCheerio(this)
|
|
||||||
.find(".chapter-title")
|
|
||||||
.text()
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
const releaseDate = loadedCheerio(this)
|
|
||||||
.find(".chapter-update")
|
|
||||||
.text()
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
const chapterUrl =
|
|
||||||
baseUrl +
|
|
||||||
loadedCheerio(this)
|
|
||||||
.find("a")
|
|
||||||
.attr("href")
|
|
||||||
?.substring(1);
|
|
||||||
|
|
||||||
novelChapters.push({
|
|
||||||
name: chapterName,
|
|
||||||
releaseTime: releaseDate,
|
|
||||||
url: chapterUrl,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await delay(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
return novelChapters;
|
|
||||||
};
|
|
||||||
|
|
||||||
novel.chapters = await getChapters();
|
|
||||||
|
|
||||||
return novel;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
|
||||||
const url = chapterUrl;
|
|
||||||
|
|
||||||
const result = await fetchApi(url, { method: "GET", headers });
|
|
||||||
|
|
||||||
const body = await result.text();
|
|
||||||
const loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
const chapterText = loadedCheerio("#chapter-container").html();
|
|
||||||
return chapterText;
|
|
||||||
};
|
|
||||||
|
|
||||||
// They have API to search, pls update it, im quite lazy xD
|
|
||||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
|
||||||
const url = `${baseUrl}lnwsearchlive?inputContent=${searchTerm}`;
|
|
||||||
|
|
||||||
const result = await fetchApi(url, { method: "GET", headers });
|
|
||||||
const body = await result.text();
|
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
|
||||||
|
|
||||||
let novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
// let results = JSON.parse(loadedCheerio('body').text());
|
|
||||||
|
|
||||||
// loadedCheerio = load(results.resultview);
|
|
||||||
|
|
||||||
// loadedCheerio('.novel-item').each(function () {
|
|
||||||
// const novelName = loadedCheerio(this).find('.novel-title').text().trim();
|
|
||||||
// const novelCover = loadedCheerio(this).find('img').attr('src');
|
|
||||||
// const novelUrl =
|
|
||||||
// baseUrl + loadedCheerio(this).find('a').attr('href').substring(1);
|
|
||||||
|
|
||||||
// const novel = { name: novelName, cover: novelCover, url: novelUrl };
|
|
||||||
|
|
||||||
// novels.push(novel);
|
|
||||||
// });
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
export const fetchImage: Plugin.fetchImage = fetchFile;
|
|
||||||
@@ -1,492 +0,0 @@
|
|||||||
import cheerio from "cheerio";
|
|
||||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
|
||||||
import { FilterInputs } from "@libs/filterInputs";
|
|
||||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
|
||||||
|
|
||||||
const pluginId = "novelupdates";
|
|
||||||
const baseUrl = "https://www.novelupdates.com/";
|
|
||||||
|
|
||||||
const headers = {
|
|
||||||
"User-Agent":
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
|
|
||||||
};
|
|
||||||
|
|
||||||
function getPopularNovelsUrl(
|
|
||||||
page: number,
|
|
||||||
{ showLatestNovels, filters }: Plugin.Options
|
|
||||||
) {
|
|
||||||
let url = `${baseUrl}${
|
|
||||||
filters
|
|
||||||
? "series-finder"
|
|
||||||
: showLatestNovels
|
|
||||||
? "latest-series"
|
|
||||||
: "series-ranking"
|
|
||||||
}/`;
|
|
||||||
|
|
||||||
if (!filters) {
|
|
||||||
url += "?rank=week";
|
|
||||||
} else {
|
|
||||||
url += "?sf=1";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters) {
|
|
||||||
if (Array.isArray(filters.novelType) && filters.novelType?.length) {
|
|
||||||
url += "&nt=" + filters?.novelType.join(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(filters.genres) && filters.genres?.length) {
|
|
||||||
url += "&gi=" + filters?.genres.join(",") + "&mgi=and";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(filters.language) && filters.language?.length) {
|
|
||||||
url += "&org=" + filters?.language.join(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.storyStatus) {
|
|
||||||
url += "&ss=" + filters?.storyStatus;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filters?.sort) {
|
|
||||||
url += "&sort=" + "sdate";
|
|
||||||
} else {
|
|
||||||
url += "&sort=" + filters?.sort;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filters?.order) {
|
|
||||||
url += "&order=" + "desc";
|
|
||||||
} else {
|
|
||||||
url += "&order=" + filters?.order;
|
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const popularNovels: Plugin.popularNovels = async function (
|
|
||||||
page,
|
|
||||||
{ showLatestNovels, filters }
|
|
||||||
) {
|
|
||||||
const url = getPopularNovelsUrl(page, { showLatestNovels, filters });
|
|
||||||
|
|
||||||
const result = await fetchApi(url, { headers: headers }, pluginId);
|
|
||||||
const body = await result.text();
|
|
||||||
|
|
||||||
const loadedCheerio = cheerio.load(body);
|
|
||||||
|
|
||||||
const novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio("div.search_main_box_nu").each(function (res) {
|
|
||||||
const novelCover = loadedCheerio(this).find("img").attr("src");
|
|
||||||
const novelName = loadedCheerio(this).find(".search_title > a").text();
|
|
||||||
const novelUrl = loadedCheerio(this)
|
|
||||||
.find(".search_title > a")
|
|
||||||
.attr("href");
|
|
||||||
|
|
||||||
if (!novelUrl) return;
|
|
||||||
|
|
||||||
const novel = {
|
|
||||||
name: novelName,
|
|
||||||
cover: novelCover,
|
|
||||||
url: novelUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
novels.push(novel);
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
|
||||||
async function (novelUrl) {
|
|
||||||
const url = novelUrl;
|
|
||||||
|
|
||||||
const result = await fetchApi(url, { headers: headers }, pluginId);
|
|
||||||
const body = await result.text();
|
|
||||||
|
|
||||||
let loadedCheerio = cheerio.load(body);
|
|
||||||
|
|
||||||
const novel: Novel.instance = {
|
|
||||||
url,
|
|
||||||
chapters: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
novel.name = loadedCheerio(".seriestitlenu").text();
|
|
||||||
|
|
||||||
novel.cover = loadedCheerio(".seriesimg > img").attr("src");
|
|
||||||
|
|
||||||
novel.author = loadedCheerio("#showauthors").text().trim();
|
|
||||||
|
|
||||||
novel.genres = loadedCheerio("#seriesgenre")
|
|
||||||
.children("a")
|
|
||||||
.map((i, el) => loadedCheerio(el).text())
|
|
||||||
.toArray()
|
|
||||||
.join(",");
|
|
||||||
novel.status = loadedCheerio("#editstatus").text().includes("Ongoing")
|
|
||||||
? "Ongoing"
|
|
||||||
: "Completed";
|
|
||||||
|
|
||||||
let type = loadedCheerio("#showtype").text().trim();
|
|
||||||
|
|
||||||
let summary = loadedCheerio("#editdescription").text().trim();
|
|
||||||
|
|
||||||
novel.summary = summary + `\n\nType: ${type}`;
|
|
||||||
|
|
||||||
let chapter: Chapter.Item[] = [];
|
|
||||||
|
|
||||||
const novelId = loadedCheerio("input#mypostid").attr("value")!;
|
|
||||||
|
|
||||||
let formData = new FormData();
|
|
||||||
formData.append("action", "nd_getchapters");
|
|
||||||
formData.append("mygrr", "0");
|
|
||||||
formData.append("mypostid", novelId);
|
|
||||||
|
|
||||||
let link = "https://www.novelupdates.com/wp-admin/admin-ajax.php";
|
|
||||||
|
|
||||||
const text = await fetchApi(
|
|
||||||
link,
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
body: formData,
|
|
||||||
headers: headers,
|
|
||||||
},
|
|
||||||
pluginId
|
|
||||||
).then((data) => data.text());
|
|
||||||
|
|
||||||
loadedCheerio = cheerio.load(text);
|
|
||||||
|
|
||||||
loadedCheerio("li.sp_li_chp").each(function () {
|
|
||||||
const chapterName = loadedCheerio(this).text().trim();
|
|
||||||
|
|
||||||
const releaseDate = null;
|
|
||||||
|
|
||||||
const chapterUrl =
|
|
||||||
"https:" +
|
|
||||||
loadedCheerio(this).find("a").first().next().attr("href");
|
|
||||||
|
|
||||||
chapter.push({
|
|
||||||
name: chapterName,
|
|
||||||
releaseTime: releaseDate,
|
|
||||||
url: chapterUrl,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
novel.chapters = chapter.reverse();
|
|
||||||
|
|
||||||
return novel;
|
|
||||||
};
|
|
||||||
|
|
||||||
function getLocation(href: string) {
|
|
||||||
var match = href.match(
|
|
||||||
/^(https?:)\/\/(([^:/?#]*)(?::([0-9]+))?)([/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/
|
|
||||||
);
|
|
||||||
return match && `${match[1]}//${match[3]}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
|
||||||
// console.log('Original URL: ', chapterUrl);
|
|
||||||
|
|
||||||
let chapterText = "";
|
|
||||||
|
|
||||||
const result = await fetchApi(
|
|
||||||
chapterUrl,
|
|
||||||
{
|
|
||||||
method: "GET",
|
|
||||||
headers: headers,
|
|
||||||
},
|
|
||||||
pluginId
|
|
||||||
);
|
|
||||||
const body = await result.text();
|
|
||||||
|
|
||||||
// console.log(result.chapterUrl);
|
|
||||||
|
|
||||||
// console.log('Redirected URL: ', result.chapterUrl);
|
|
||||||
|
|
||||||
const loadedCheerio = cheerio.load(body);
|
|
||||||
|
|
||||||
let isWuxiaWorld = result.chapterUrl.toLowerCase().includes("wuxiaworld");
|
|
||||||
|
|
||||||
let isBlogspot = result.chapterUrl.toLowerCase().includes("blogspot");
|
|
||||||
|
|
||||||
let isTumblr = result.chapterUrl.toLowerCase().includes("tumblr");
|
|
||||||
|
|
||||||
let isWattpad = result.chapterUrl.toLowerCase().includes("wattpad");
|
|
||||||
|
|
||||||
let isLightNovelsTls = result.chapterUrl
|
|
||||||
.toLowerCase()
|
|
||||||
.includes("lightnovelstranslations");
|
|
||||||
|
|
||||||
let isiNovelTranslation = result.chapterUrl
|
|
||||||
.toLowerCase()
|
|
||||||
.includes("inoveltranslation");
|
|
||||||
|
|
||||||
let isTravisTranslation = result.chapterUrl
|
|
||||||
.toLowerCase()
|
|
||||||
.includes("travistranslations");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if its a wordpress site
|
|
||||||
*/
|
|
||||||
let isWordPressStr =
|
|
||||||
loadedCheerio('meta[name="generator"]').attr("content") ||
|
|
||||||
loadedCheerio("footer").text();
|
|
||||||
|
|
||||||
let isWordPress = false;
|
|
||||||
|
|
||||||
if (isWordPressStr) {
|
|
||||||
isWordPress =
|
|
||||||
isWordPressStr.toLowerCase().includes("wordpress") ||
|
|
||||||
isWordPressStr.includes("Site Kit by Google") ||
|
|
||||||
loadedCheerio(".powered-by")
|
|
||||||
.text()
|
|
||||||
.toLowerCase()
|
|
||||||
.includes("wordpress");
|
|
||||||
}
|
|
||||||
|
|
||||||
let isRainOfSnow = result.chapterUrl.toLowerCase().includes("rainofsnow");
|
|
||||||
|
|
||||||
let isWebNovel = result.chapterUrl.toLowerCase().includes("webnovel");
|
|
||||||
|
|
||||||
let isHostedNovel = result.chapterUrl.toLowerCase().includes("hostednovel");
|
|
||||||
|
|
||||||
let isScribbleHub = result.chapterUrl.toLowerCase().includes("scribblehub");
|
|
||||||
|
|
||||||
if (isWuxiaWorld) {
|
|
||||||
chapterText = loadedCheerio("#chapter-content").html()!;
|
|
||||||
} else if (isRainOfSnow) {
|
|
||||||
chapterText = loadedCheerio("div.content").html()!;
|
|
||||||
} else if (isTumblr) {
|
|
||||||
chapterText = loadedCheerio(".post").html()!;
|
|
||||||
} else if (isBlogspot) {
|
|
||||||
loadedCheerio(".post-share-buttons").remove();
|
|
||||||
chapterText = loadedCheerio(".entry-content").html()!;
|
|
||||||
} else if (isHostedNovel) {
|
|
||||||
chapterText = loadedCheerio(".chapter").html()!;
|
|
||||||
} else if (isScribbleHub) {
|
|
||||||
chapterText = loadedCheerio("div.chp_raw").html()!;
|
|
||||||
} else if (isWattpad) {
|
|
||||||
chapterText = loadedCheerio(".container pre").html()!;
|
|
||||||
} else if (isTravisTranslation) {
|
|
||||||
chapterText = loadedCheerio(".reader-content").html()!;
|
|
||||||
} else if (isLightNovelsTls) {
|
|
||||||
chapterText = loadedCheerio(".text_story").html()!;
|
|
||||||
} else if (isiNovelTranslation) {
|
|
||||||
chapterText = loadedCheerio(".chakra-skeleton").html()!;
|
|
||||||
} else if (isWordPress) {
|
|
||||||
/**
|
|
||||||
* Remove wordpress bloat tags
|
|
||||||
*/
|
|
||||||
|
|
||||||
const bloatClasses = [
|
|
||||||
".c-ads",
|
|
||||||
"#madara-comments",
|
|
||||||
"#comments",
|
|
||||||
".content-comments",
|
|
||||||
".sharedaddy",
|
|
||||||
".wp-dark-mode-switcher",
|
|
||||||
".wp-next-post-navi",
|
|
||||||
".wp-block-buttons",
|
|
||||||
".wp-block-columns",
|
|
||||||
".post-cats",
|
|
||||||
".sidebar",
|
|
||||||
".author-avatar",
|
|
||||||
".ezoic-ad",
|
|
||||||
];
|
|
||||||
|
|
||||||
bloatClasses.map((tag) => loadedCheerio(tag).remove());
|
|
||||||
|
|
||||||
chapterText =
|
|
||||||
loadedCheerio(".entry-content").html() ||
|
|
||||||
loadedCheerio(".single_post").html() ||
|
|
||||||
loadedCheerio(".post-entry").html() ||
|
|
||||||
loadedCheerio(".main-content").html() ||
|
|
||||||
loadedCheerio("article.post").html() ||
|
|
||||||
loadedCheerio(".content").html() ||
|
|
||||||
loadedCheerio("#content").html() ||
|
|
||||||
loadedCheerio(".page-body").html() ||
|
|
||||||
loadedCheerio(".td-page-content").html()!;
|
|
||||||
} else if (isWebNovel) {
|
|
||||||
chapterText = loadedCheerio(".cha-words").html()!;
|
|
||||||
|
|
||||||
if (!chapterText) {
|
|
||||||
chapterText = loadedCheerio("._content").html()!;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/**
|
|
||||||
* Remove unnecessary tags
|
|
||||||
*/
|
|
||||||
const tags = ["nav", "header", "footer", ".hidden"];
|
|
||||||
|
|
||||||
tags.map((tag) => loadedCheerio(tag).remove());
|
|
||||||
|
|
||||||
chapterText = loadedCheerio("body").html()!;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chapterText) {
|
|
||||||
/**
|
|
||||||
* Convert relative urls to absolute
|
|
||||||
*/
|
|
||||||
chapterText = chapterText.replace(
|
|
||||||
/href="\//g,
|
|
||||||
`href="${getLocation(result.chapterUrl)}/`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return chapterText;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
|
||||||
const url =
|
|
||||||
"https://www.novelupdates.com/?s=" +
|
|
||||||
searchTerm +
|
|
||||||
"&post_type=seriesplans";
|
|
||||||
|
|
||||||
const result = await fetchApi(url, { headers: headers }, pluginId);
|
|
||||||
const body = await result.text();
|
|
||||||
|
|
||||||
const loadedCheerio = cheerio.load(body);
|
|
||||||
|
|
||||||
let novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio("div.search_main_box_nu").each(function () {
|
|
||||||
const novelCover = loadedCheerio(this).find("img").attr("src");
|
|
||||||
const novelName = loadedCheerio(this).find(".search_title > a").text();
|
|
||||||
const novelUrl = loadedCheerio(this)
|
|
||||||
.find(".search_title > a")
|
|
||||||
.attr("href")
|
|
||||||
?.split("/")[4];
|
|
||||||
|
|
||||||
if (!novelUrl) return;
|
|
||||||
|
|
||||||
novels.push({
|
|
||||||
name: novelName,
|
|
||||||
url: novelUrl,
|
|
||||||
cover: novelCover,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
|
||||||
return await fetchFile(url, { headers: headers });
|
|
||||||
};
|
|
||||||
|
|
||||||
const filters = [
|
|
||||||
{
|
|
||||||
key: "sort",
|
|
||||||
label: "Sort Results By",
|
|
||||||
values: [
|
|
||||||
{ label: "Last Updated", value: "sdate" },
|
|
||||||
{ label: "Rating", value: "srate" },
|
|
||||||
{ label: "Rank", value: "srank" },
|
|
||||||
{ label: "Reviews", value: "sreview" },
|
|
||||||
{ label: "Chapters", value: "srel" },
|
|
||||||
{ label: "Title", value: "abc" },
|
|
||||||
{ label: "Readers", value: "sread" },
|
|
||||||
{ label: "Frequency", value: "sfrel" },
|
|
||||||
],
|
|
||||||
inputType: FilterInputs.Picker,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "order",
|
|
||||||
label: "Order",
|
|
||||||
values: [
|
|
||||||
{ label: "Descending", value: "desc" },
|
|
||||||
{ label: "Ascending", value: "asc" },
|
|
||||||
],
|
|
||||||
inputType: FilterInputs.Picker,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "storyStatus",
|
|
||||||
label: "Story Status (Translation)",
|
|
||||||
values: [
|
|
||||||
{ label: "All", value: "" },
|
|
||||||
{ label: "Completed", value: "2" },
|
|
||||||
{ label: "Ongoing", value: "3" },
|
|
||||||
{ label: "Hiatus", value: "4" },
|
|
||||||
],
|
|
||||||
inputType: FilterInputs.Picker,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "language",
|
|
||||||
label: "Language",
|
|
||||||
values: [
|
|
||||||
{ label: "Chinese", value: 495 },
|
|
||||||
{ label: "Filipino", value: 9181 },
|
|
||||||
{ label: "Indonesian", value: 9179 },
|
|
||||||
{ label: "Japanese", value: 496 },
|
|
||||||
{ label: "Khmer", value: 18657 },
|
|
||||||
{ label: "Korean", value: 497 },
|
|
||||||
{ label: "Malaysian", value: 9183 },
|
|
||||||
{ label: "Thai", value: 9954 },
|
|
||||||
{ label: "Vietnamese", value: 9177 },
|
|
||||||
],
|
|
||||||
inputType: FilterInputs.Checkbox,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "novelType",
|
|
||||||
label: "Novel Type",
|
|
||||||
values: [
|
|
||||||
{ label: "Light Novel", value: "2443" },
|
|
||||||
{ label: "Published Novel", value: "26874" },
|
|
||||||
{ label: "Web Novel", value: "2444" },
|
|
||||||
],
|
|
||||||
inputType: FilterInputs.Checkbox,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "genres",
|
|
||||||
label: "Genres",
|
|
||||||
values: [
|
|
||||||
{ label: "Action", value: 8 },
|
|
||||||
{ label: "Adult", value: 280 },
|
|
||||||
{ label: "Adventure", value: 13 },
|
|
||||||
{ label: "Comedy", value: 17 },
|
|
||||||
{ label: "Drama", value: 9 },
|
|
||||||
{ label: "Ecchi", value: 292 },
|
|
||||||
{ label: "Fantasy", value: 5 },
|
|
||||||
{ label: "Gender Bender", value: 168 },
|
|
||||||
{ label: "Harem", value: 3 },
|
|
||||||
{ label: "Historical", value: 330 },
|
|
||||||
{ label: "Horror", value: 343 },
|
|
||||||
{ label: "Josei", value: 324 },
|
|
||||||
{ label: "Martial Arts", value: 14 },
|
|
||||||
{ label: "Mature", value: 4 },
|
|
||||||
{ label: "Mecha", value: 10 },
|
|
||||||
{ label: "Mystery", value: 245 },
|
|
||||||
{ label: "Psychoical", value: 486 },
|
|
||||||
{ label: "Romance", value: 15 },
|
|
||||||
{ label: "School Life", value: 6 },
|
|
||||||
{ label: "Sci-fi", value: 11 },
|
|
||||||
{ label: "Seinen", value: 18 },
|
|
||||||
{ label: "Shoujo", value: 157 },
|
|
||||||
{ label: "Shoujo Ai", value: 851 },
|
|
||||||
{ label: "Shounen", value: 12 },
|
|
||||||
{ label: "Shounen Ai", value: 1692 },
|
|
||||||
{ label: "Slice of Life", value: 7 },
|
|
||||||
{ label: "Smut", value: 281 },
|
|
||||||
{ label: "Sports", value: 1357 },
|
|
||||||
{ label: "Supernatural", value: 16 },
|
|
||||||
{ label: "Tragedy", value: 132 },
|
|
||||||
{ label: "Wuxia", value: 479 },
|
|
||||||
{ label: "Xianxia", value: 480 },
|
|
||||||
{ label: "Xuanhuan", value: 3954 },
|
|
||||||
{ label: "Yaoi", value: 560 },
|
|
||||||
{ label: "Yuri", value: 922 },
|
|
||||||
],
|
|
||||||
inputType: FilterInputs.Checkbox,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
id: pluginId,
|
|
||||||
name: "Novel Updates",
|
|
||||||
version: "0.5.0",
|
|
||||||
icon: "src/en/novelupdates/icon.png",
|
|
||||||
site: baseUrl,
|
|
||||||
protected: false,
|
|
||||||
fetchImage,
|
|
||||||
popularNovels,
|
|
||||||
parseNovelAndChapters,
|
|
||||||
parseChapter,
|
|
||||||
searchNovels,
|
|
||||||
filters,
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,463 @@
|
|||||||
|
import { load as parseHTML } from "cheerio";
|
||||||
|
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||||
|
import { Filters, FilterTypes } from "@libs/filterInputs";
|
||||||
|
import { Plugin } from "@typings/plugin";
|
||||||
|
|
||||||
|
class NovelUpdates implements Plugin.PluginBase {
|
||||||
|
id = "novelupdates";
|
||||||
|
name = "Novel Updates";
|
||||||
|
version = "0.5.0";
|
||||||
|
icon = "src/en/novelupdates/icon.png";
|
||||||
|
site = "https://www.novelupdates.com/";
|
||||||
|
baseUrl = this.site;
|
||||||
|
|
||||||
|
getPopularNovelsUrl(
|
||||||
|
page: number,
|
||||||
|
{ showLatestNovels, filters }: Plugin.PopularNovelsOptions<Filters>
|
||||||
|
) {
|
||||||
|
let url = `${this.baseUrl}${filters
|
||||||
|
? "series-finder"
|
||||||
|
: showLatestNovels
|
||||||
|
? "latest-series"
|
||||||
|
: "series-ranking"
|
||||||
|
}/`;
|
||||||
|
|
||||||
|
if (!filters) {
|
||||||
|
url += "?rank=week";
|
||||||
|
} else {
|
||||||
|
url += "?sf=1";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filters) {
|
||||||
|
if (Array.isArray(filters.novelType) && filters.novelType?.length) {
|
||||||
|
url += "&nt=" + filters?.novelType.join(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(filters.genres) && filters.genres?.length) {
|
||||||
|
url += "&gi=" + filters?.genres.join(",") + "&mgi=and";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(filters.language) && filters.language?.length) {
|
||||||
|
url += "&org=" + filters?.language.join(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filters.storyStatus) {
|
||||||
|
url += "&ss=" + filters?.storyStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filters?.sort) {
|
||||||
|
url += "&sort=" + "sdate";
|
||||||
|
} else {
|
||||||
|
url += "&sort=" + filters?.sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filters?.order) {
|
||||||
|
url += "&order=" + "desc";
|
||||||
|
} else {
|
||||||
|
url += "&order=" + filters?.order;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
async popularNovels(pageNo: number, { showLatestNovels, filters }: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||||
|
const url = this.getPopularNovelsUrl(pageNo, { showLatestNovels, filters });
|
||||||
|
|
||||||
|
const result = await fetchApi(url);
|
||||||
|
const body = await result.text();
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio("div.search_main_box_nu").each((idx, ele) => {
|
||||||
|
const novelCover = loadedCheerio(ele).find("img").attr("src");
|
||||||
|
const novelName = loadedCheerio(ele).find(".search_title > a").text();
|
||||||
|
const novelUrl = loadedCheerio(ele)
|
||||||
|
.find(".search_title > a")
|
||||||
|
.attr("href");
|
||||||
|
|
||||||
|
if (!novelUrl) return;
|
||||||
|
|
||||||
|
const novel = {
|
||||||
|
name: novelName,
|
||||||
|
cover: novelCover,
|
||||||
|
url: novelUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
novels.push(novel);
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||||
|
const url = novelUrl;
|
||||||
|
|
||||||
|
const result = await fetchApi(url);
|
||||||
|
const body = await result.text();
|
||||||
|
|
||||||
|
let loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
const novel: Plugin.SourceNovel = {
|
||||||
|
url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
novel.name = loadedCheerio(".seriestitlenu").text();
|
||||||
|
|
||||||
|
novel.cover = loadedCheerio(".seriesimg > img").attr("src");
|
||||||
|
|
||||||
|
novel.author = loadedCheerio("#showauthors").text().trim();
|
||||||
|
|
||||||
|
novel.genres = loadedCheerio("#seriesgenre")
|
||||||
|
.children("a")
|
||||||
|
.map((i, el) => loadedCheerio(el).text())
|
||||||
|
.toArray()
|
||||||
|
.join(",");
|
||||||
|
novel.status = loadedCheerio("#editstatus").text().includes("Ongoing")
|
||||||
|
? "Ongoing"
|
||||||
|
: "Completed";
|
||||||
|
|
||||||
|
let type = loadedCheerio("#showtype").text().trim();
|
||||||
|
|
||||||
|
let summary = loadedCheerio("#editdescription").text().trim();
|
||||||
|
|
||||||
|
novel.summary = summary + `\n\nType: ${type}`;
|
||||||
|
|
||||||
|
let chapter: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
|
const novelId = loadedCheerio("input#mypostid").attr("value")!;
|
||||||
|
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("action", "nd_getchapters");
|
||||||
|
formData.append("mygrr", "0");
|
||||||
|
formData.append("mypostid", novelId);
|
||||||
|
|
||||||
|
let link = "https://www.novelupdates.com/wp-admin/admin-ajax.php";
|
||||||
|
|
||||||
|
const text = await fetchApi(
|
||||||
|
link,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
},
|
||||||
|
).then((data) => data.text());
|
||||||
|
|
||||||
|
loadedCheerio = parseHTML(text);
|
||||||
|
|
||||||
|
loadedCheerio("li.sp_li_chp").each(function () {
|
||||||
|
const chapterName = loadedCheerio(this).text().trim();
|
||||||
|
|
||||||
|
const releaseDate = null;
|
||||||
|
|
||||||
|
const chapterUrl =
|
||||||
|
"https:" +
|
||||||
|
loadedCheerio(this).find("a").first().next().attr("href");
|
||||||
|
|
||||||
|
chapter.push({
|
||||||
|
name: chapterName,
|
||||||
|
releaseTime: releaseDate,
|
||||||
|
url: chapterUrl,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
novel.chapters = chapter.reverse();
|
||||||
|
|
||||||
|
return novel;
|
||||||
|
}
|
||||||
|
getLocation(href: string) {
|
||||||
|
var match = href.match(
|
||||||
|
/^(https?:)\/\/(([^:/?#]*)(?::([0-9]+))?)([/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/
|
||||||
|
);
|
||||||
|
return match && `${match[1]}//${match[3]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async parseChapter(chapterUrl: string): Promise<string> {
|
||||||
|
let chapterText = "";
|
||||||
|
|
||||||
|
const result = await fetchApi(
|
||||||
|
chapterUrl,
|
||||||
|
);
|
||||||
|
const body = await result.text();
|
||||||
|
|
||||||
|
// console.log(result.chapterUrl);
|
||||||
|
|
||||||
|
// console.log('Redirected URL: ', result.chapterUrl);
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
let isWuxiaWorld = chapterUrl.toLowerCase().includes("wuxiaworld");
|
||||||
|
|
||||||
|
let isBlogspot = chapterUrl.toLowerCase().includes("blogspot");
|
||||||
|
|
||||||
|
let isTumblr = chapterUrl.toLowerCase().includes("tumblr");
|
||||||
|
|
||||||
|
let isWattpad = chapterUrl.toLowerCase().includes("wattpad");
|
||||||
|
|
||||||
|
let isLightNovelsTls = chapterUrl
|
||||||
|
.toLowerCase()
|
||||||
|
.includes("lightnovelstranslations");
|
||||||
|
|
||||||
|
let isiNovelTranslation = chapterUrl
|
||||||
|
.toLowerCase()
|
||||||
|
.includes("inoveltranslation");
|
||||||
|
|
||||||
|
let isTravisTranslation = chapterUrl
|
||||||
|
.toLowerCase()
|
||||||
|
.includes("travistranslations");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if its a wordpress site
|
||||||
|
*/
|
||||||
|
let isWordPressStr =
|
||||||
|
loadedCheerio('meta[name="generator"]').attr("content") ||
|
||||||
|
loadedCheerio("footer").text();
|
||||||
|
|
||||||
|
let isWordPress = false;
|
||||||
|
|
||||||
|
if (isWordPressStr) {
|
||||||
|
isWordPress =
|
||||||
|
isWordPressStr.toLowerCase().includes("wordpress") ||
|
||||||
|
isWordPressStr.includes("Site Kit by Google") ||
|
||||||
|
loadedCheerio(".powered-by")
|
||||||
|
.text()
|
||||||
|
.toLowerCase()
|
||||||
|
.includes("wordpress");
|
||||||
|
}
|
||||||
|
|
||||||
|
let isRainOfSnow = chapterUrl.toLowerCase().includes("rainofsnow");
|
||||||
|
|
||||||
|
let isWebNovel = chapterUrl.toLowerCase().includes("webnovel");
|
||||||
|
|
||||||
|
let isHostedNovel = chapterUrl.toLowerCase().includes("hostednovel");
|
||||||
|
|
||||||
|
let isScribbleHub = chapterUrl.toLowerCase().includes("scribblehub");
|
||||||
|
|
||||||
|
if (isWuxiaWorld) {
|
||||||
|
chapterText = loadedCheerio("#chapter-content").html()!;
|
||||||
|
} else if (isRainOfSnow) {
|
||||||
|
chapterText = loadedCheerio("div.content").html()!;
|
||||||
|
} else if (isTumblr) {
|
||||||
|
chapterText = loadedCheerio(".post").html()!;
|
||||||
|
} else if (isBlogspot) {
|
||||||
|
loadedCheerio(".post-share-buttons").remove();
|
||||||
|
chapterText = loadedCheerio(".entry-content").html()!;
|
||||||
|
} else if (isHostedNovel) {
|
||||||
|
chapterText = loadedCheerio(".chapter").html()!;
|
||||||
|
} else if (isScribbleHub) {
|
||||||
|
chapterText = loadedCheerio("div.chp_raw").html()!;
|
||||||
|
} else if (isWattpad) {
|
||||||
|
chapterText = loadedCheerio(".container pre").html()!;
|
||||||
|
} else if (isTravisTranslation) {
|
||||||
|
chapterText = loadedCheerio(".reader-content").html()!;
|
||||||
|
} else if (isLightNovelsTls) {
|
||||||
|
chapterText = loadedCheerio(".text_story").html()!;
|
||||||
|
} else if (isiNovelTranslation) {
|
||||||
|
chapterText = loadedCheerio(".chakra-skeleton").html()!;
|
||||||
|
} else if (isWordPress) {
|
||||||
|
/**
|
||||||
|
* Remove wordpress bloat tags
|
||||||
|
*/
|
||||||
|
|
||||||
|
const bloatClasses = [
|
||||||
|
".c-ads",
|
||||||
|
"#madara-comments",
|
||||||
|
"#comments",
|
||||||
|
".content-comments",
|
||||||
|
".sharedaddy",
|
||||||
|
".wp-dark-mode-switcher",
|
||||||
|
".wp-next-post-navi",
|
||||||
|
".wp-block-buttons",
|
||||||
|
".wp-block-columns",
|
||||||
|
".post-cats",
|
||||||
|
".sidebar",
|
||||||
|
".author-avatar",
|
||||||
|
".ezoic-ad",
|
||||||
|
];
|
||||||
|
|
||||||
|
bloatClasses.map((tag) => loadedCheerio(tag).remove());
|
||||||
|
|
||||||
|
chapterText =
|
||||||
|
loadedCheerio(".entry-content").html() ||
|
||||||
|
loadedCheerio(".single_post").html() ||
|
||||||
|
loadedCheerio(".post-entry").html() ||
|
||||||
|
loadedCheerio(".main-content").html() ||
|
||||||
|
loadedCheerio("article.post").html() ||
|
||||||
|
loadedCheerio(".content").html() ||
|
||||||
|
loadedCheerio("#content").html() ||
|
||||||
|
loadedCheerio(".page-body").html() ||
|
||||||
|
loadedCheerio(".td-page-content").html()!;
|
||||||
|
} else if (isWebNovel) {
|
||||||
|
chapterText = loadedCheerio(".cha-words").html()!;
|
||||||
|
|
||||||
|
if (!chapterText) {
|
||||||
|
chapterText = loadedCheerio("._content").html()!;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/**
|
||||||
|
* Remove unnecessary tags
|
||||||
|
*/
|
||||||
|
const tags = ["nav", "header", "footer", ".hidden"];
|
||||||
|
|
||||||
|
tags.map((tag) => loadedCheerio(tag).remove());
|
||||||
|
|
||||||
|
chapterText = loadedCheerio("body").html()!;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chapterText) {
|
||||||
|
/**
|
||||||
|
* Convert relative urls to absolute
|
||||||
|
*/
|
||||||
|
chapterText = chapterText.replace(
|
||||||
|
/href="\//g,
|
||||||
|
`href="${this.getLocation(chapterUrl)}/`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return chapterText;
|
||||||
|
}
|
||||||
|
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
|
const url =
|
||||||
|
"https://www.novelupdates.com/?s=" +
|
||||||
|
searchTerm +
|
||||||
|
"&post_type=seriesplans";
|
||||||
|
|
||||||
|
const result = await fetchApi(url);
|
||||||
|
const body = await result.text();
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
let novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio("div.search_main_box_nu").each(function () {
|
||||||
|
const novelCover = loadedCheerio(this).find("img").attr("src");
|
||||||
|
const novelName = loadedCheerio(this).find(".search_title > a").text();
|
||||||
|
const novelUrl = loadedCheerio(this)
|
||||||
|
.find(".search_title > a")
|
||||||
|
.attr("href")
|
||||||
|
?.split("/")[4];
|
||||||
|
|
||||||
|
if (!novelUrl) return;
|
||||||
|
|
||||||
|
novels.push({
|
||||||
|
name: novelName,
|
||||||
|
url: novelUrl,
|
||||||
|
cover: novelCover,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
fetchImage(url: string): Promise<string | undefined> {
|
||||||
|
return fetchFile(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
filters: Filters = {
|
||||||
|
sort: {
|
||||||
|
label: "Sort Results By",
|
||||||
|
type: FilterTypes.Picker,
|
||||||
|
options: [
|
||||||
|
{ label: "Last Updated", value: "sdate" },
|
||||||
|
{ label: "Rating", value: "srate" },
|
||||||
|
{ label: "Rank", value: "srank" },
|
||||||
|
{ label: "Reviews", value: "sreview" },
|
||||||
|
{ label: "Chapters", value: "srel" },
|
||||||
|
{ label: "Title", value: "abc" },
|
||||||
|
{ label: "Readers", value: "sread" },
|
||||||
|
{ label: "Frequency", value: "sfrel" },
|
||||||
|
],
|
||||||
|
value: "sdate",
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
label: "Order",
|
||||||
|
type: FilterTypes.Picker,
|
||||||
|
options: [
|
||||||
|
{ label: "Descending", value: "desc" },
|
||||||
|
{ label: "Ascending", value: "asc" },
|
||||||
|
],
|
||||||
|
value: "asc"
|
||||||
|
},
|
||||||
|
storyStatus: {
|
||||||
|
label: "Story Status (Translation)",
|
||||||
|
type: FilterTypes.Picker,
|
||||||
|
options: [
|
||||||
|
{ label: "All", value: "" },
|
||||||
|
{ label: "Completed", value: "2" },
|
||||||
|
{ label: "Ongoing", value: "3" },
|
||||||
|
{ label: "Hiatus", value: "4" },
|
||||||
|
],
|
||||||
|
value: ""
|
||||||
|
},
|
||||||
|
language: {
|
||||||
|
label: "Language",
|
||||||
|
options: [
|
||||||
|
{ label: "Chinese", value: "495" },
|
||||||
|
{ label: "Filipino", value: "9181" },
|
||||||
|
{ label: "Indonesian", value: "9179" },
|
||||||
|
{ label: "Japanese", value: "496" },
|
||||||
|
{ label: "Khmer", value: "18657" },
|
||||||
|
{ label: "Korean", value: "497" },
|
||||||
|
{ label: "Malaysian", value: "9183" },
|
||||||
|
{ label: "Thai", value: "9954" },
|
||||||
|
{ label: "Vietnamese", value: "9177" },
|
||||||
|
],
|
||||||
|
type: FilterTypes.Picker,
|
||||||
|
value: ""
|
||||||
|
},
|
||||||
|
novelType: {
|
||||||
|
label: "Novel Type",
|
||||||
|
type: FilterTypes.CheckboxGroup,
|
||||||
|
value: [],
|
||||||
|
options: [
|
||||||
|
{ label: "Light Novel", value: "2443" },
|
||||||
|
{ label: "Published Novel", value: "26874" },
|
||||||
|
{ label: "Web Novel", value: "2444" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
genres: {
|
||||||
|
label: "Genres",
|
||||||
|
type: FilterTypes.CheckboxGroup,
|
||||||
|
value: [],
|
||||||
|
options: [
|
||||||
|
{ label: "Action", value: "8" },
|
||||||
|
{ label: "Adult", value: "280" },
|
||||||
|
{ label: "Adventure", value: "13" },
|
||||||
|
{ label: "Comedy", value: "17" },
|
||||||
|
{ label: "Drama", value: "9" },
|
||||||
|
{ label: "Ecchi", value: "292" },
|
||||||
|
{ label: "Fantasy", value: "5" },
|
||||||
|
{ label: "Gender Bender", value: "168" },
|
||||||
|
{ label: "Harem", value: "3" },
|
||||||
|
{ label: "Historical", value: "330" },
|
||||||
|
{ label: "Horror", value: "343" },
|
||||||
|
{ label: "Josei", value: "324" },
|
||||||
|
{ label: "Martial Arts", value: "14" },
|
||||||
|
{ label: "Mature", value: "4" },
|
||||||
|
{ label: "Mecha", value: "10" },
|
||||||
|
{ label: "Mystery", value: "245" },
|
||||||
|
{ label: "Psychoical", value: "486" },
|
||||||
|
{ label: "Romance", value: "15" },
|
||||||
|
{ label: "School Life", value: "6" },
|
||||||
|
{ label: "Sci-fi", value: "11" },
|
||||||
|
{ label: "Seinen", value: "18" },
|
||||||
|
{ label: "Shoujo", value: "157" },
|
||||||
|
{ label: "Shoujo Ai", value: "851" },
|
||||||
|
{ label: "Shounen", value: "12" },
|
||||||
|
{ label: "Shounen Ai", value: "1692" },
|
||||||
|
{ label: "Slice of Life", value: "7" },
|
||||||
|
{ label: "Smut", value: "281" },
|
||||||
|
{ label: "Sports", value: "1357" },
|
||||||
|
{ label: "Supernatural", value: "16" },
|
||||||
|
{ label: "Tragedy", value: "132" },
|
||||||
|
{ label: "Wuxia", value: "479" },
|
||||||
|
{ label: "Xianxia", value: "480" },
|
||||||
|
{ label: "Xuanhuan", value: "3954" },
|
||||||
|
{ label: "Yaoi", value: "560" },
|
||||||
|
{ label: "Yuri", value: "922" },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new NovelUpdates();
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
import cheerio from "cheerio";
|
|
||||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
|
||||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
|
||||||
|
|
||||||
export const id = "wuxiaworld";
|
|
||||||
export const name = "Wuxia World (WIP)";
|
|
||||||
export const version = "0.5.0";
|
|
||||||
export const icon = "src/en/wuxiaworld/icon.png";
|
|
||||||
export const site = "https://www.wuxiaworld.com/";
|
|
||||||
|
|
||||||
|
|
||||||
const pluginId = id;
|
|
||||||
const baseUrl = site;
|
|
||||||
|
|
||||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
|
||||||
const link = `${baseUrl}api/novels`;
|
|
||||||
|
|
||||||
const result = await fetchApi(link, {}, pluginId);
|
|
||||||
const data = await result.json();
|
|
||||||
|
|
||||||
let novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
data.items.map((novel) => {
|
|
||||||
let name = novel.name;
|
|
||||||
let cover = novel.coverUrl;
|
|
||||||
let url = baseUrl + "novel/" + novel.slug + "/";
|
|
||||||
|
|
||||||
novels.push({
|
|
||||||
name,
|
|
||||||
cover,
|
|
||||||
url,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
|
||||||
async function (novelUrl) {
|
|
||||||
const url = novelUrl;
|
|
||||||
console.log(url);
|
|
||||||
const result = await fetchApi(url, {}, pluginId);
|
|
||||||
const body = await result.text();
|
|
||||||
|
|
||||||
const loadedCheerio = cheerio.load(body);
|
|
||||||
|
|
||||||
const novel: Novel.instance = {
|
|
||||||
url,
|
|
||||||
chapters: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
novel.name = loadedCheerio("h1.line-clamp-2").text();
|
|
||||||
|
|
||||||
novel.cover = loadedCheerio("img.absolute").attr("src");
|
|
||||||
|
|
||||||
novel.summary = loadedCheerio(
|
|
||||||
"div.flex-col:nth-child(4) > div > div > span > span"
|
|
||||||
)
|
|
||||||
.text()
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
novel.author = loadedCheerio("div.MuiGrid-container > div > div > div")
|
|
||||||
.filter(function () {
|
|
||||||
return loadedCheerio(this).text().trim() === "Author:";
|
|
||||||
})
|
|
||||||
.next()
|
|
||||||
.text();
|
|
||||||
|
|
||||||
let genres: string[] = [];
|
|
||||||
|
|
||||||
loadedCheerio("a.MuiLink-underlineNone").each(function () {
|
|
||||||
genres.push(loadedCheerio(this).find("div > div").text());
|
|
||||||
});
|
|
||||||
|
|
||||||
novel.genres = genres.join(",");
|
|
||||||
|
|
||||||
novel.status = loadedCheerio("div.font-set-b10")
|
|
||||||
.text()
|
|
||||||
.includes("Complete");
|
|
||||||
|
|
||||||
let chapter: Chapter.Item[] = [];
|
|
||||||
|
|
||||||
loadedCheerio("div.border-b").each(function () {
|
|
||||||
const name = loadedCheerio(this)
|
|
||||||
.find("a > div > div > div > span")
|
|
||||||
.text();
|
|
||||||
const releaseTime = loadedCheerio(this)
|
|
||||||
.find("a > div > div > div > div > span")
|
|
||||||
.text();
|
|
||||||
let url = loadedCheerio(this).find("a").attr("href")?.slice(1);
|
|
||||||
url = `${baseUrl}${url}`;
|
|
||||||
|
|
||||||
chapter.push({ name, releaseTime, url });
|
|
||||||
});
|
|
||||||
|
|
||||||
novel.chapters = chapter;
|
|
||||||
|
|
||||||
return novel;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
|
||||||
const result = await fetchApi(chapterUrl, {}, pluginId);
|
|
||||||
const body = await result.text();
|
|
||||||
|
|
||||||
const loadedCheerio = cheerio.load(body);
|
|
||||||
|
|
||||||
loadedCheerio(".chapter-nav").remove();
|
|
||||||
|
|
||||||
loadedCheerio("#chapter-content > script").remove();
|
|
||||||
|
|
||||||
let chapterText = loadedCheerio("#chapter-content").html();
|
|
||||||
|
|
||||||
return chapterText;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
|
||||||
const searchUrl = "https://www.wuxiaworld.com/api/novels/search?query=";
|
|
||||||
|
|
||||||
const url = `${searchUrl}${searchTerm}`;
|
|
||||||
|
|
||||||
const result = await fetchApi(url);
|
|
||||||
const data = await result.json();
|
|
||||||
|
|
||||||
const novels: Novel.Item[] = [];
|
|
||||||
|
|
||||||
data.items.map((novel) => {
|
|
||||||
let name = novel.name;
|
|
||||||
let cover = novel.cover;
|
|
||||||
let url = baseUrl + "novel/" + novel.slug + "/";
|
|
||||||
|
|
||||||
novels.push({
|
|
||||||
name,
|
|
||||||
url,
|
|
||||||
cover,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
|
||||||
const headers = {
|
|
||||||
Referer: baseUrl,
|
|
||||||
};
|
|
||||||
return await fetchFile(url, { headers: headers });
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
import { load as parseHTML} from "cheerio";
|
||||||
|
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||||
|
import { Plugin } from "@typings/plugin";
|
||||||
|
import { Filters } from "@libs/filterInputs";
|
||||||
|
|
||||||
|
interface NovelEntry {
|
||||||
|
name: string;
|
||||||
|
coverUrl: string;
|
||||||
|
slug: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class WuxiaWorld implements Plugin.PluginBase {
|
||||||
|
id = "wuxiaworld";
|
||||||
|
name = "Wuxia World";
|
||||||
|
icon = "src/en/wuxiaworld/icon.png";
|
||||||
|
site = "https://www.wuxiaworld.com/";
|
||||||
|
filters?: Filters | undefined;
|
||||||
|
version = "0.5.0";
|
||||||
|
baseUrl = this.site;
|
||||||
|
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||||
|
const link = `${this.baseUrl}api/novels`;
|
||||||
|
|
||||||
|
const result = await fetch(link);
|
||||||
|
const data = await result.json();
|
||||||
|
|
||||||
|
let novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
data.items.map((novel: NovelEntry) => {
|
||||||
|
let name = novel.name;
|
||||||
|
let cover = novel.coverUrl;
|
||||||
|
let url = this.baseUrl + "novel/" + novel.slug + "/";
|
||||||
|
|
||||||
|
novels.push({
|
||||||
|
name,
|
||||||
|
cover,
|
||||||
|
url,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||||
|
const url = novelUrl;
|
||||||
|
const result = await fetchApi(url);
|
||||||
|
const body = await result.text();
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
const novel: Plugin.SourceNovel = {
|
||||||
|
url,
|
||||||
|
chapters: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
novel.name = loadedCheerio("h1.line-clamp-2").text();
|
||||||
|
|
||||||
|
novel.cover = loadedCheerio("img.absolute").attr("src");
|
||||||
|
|
||||||
|
novel.summary = loadedCheerio(
|
||||||
|
"div.flex-col:nth-child(4) > div > div > span > span"
|
||||||
|
)
|
||||||
|
.text()
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
novel.author = loadedCheerio("div.MuiGrid-container > div > div > div")
|
||||||
|
.filter(function () {
|
||||||
|
return loadedCheerio(this).text().trim() === "Author:";
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.text();
|
||||||
|
|
||||||
|
let genres: string[] = [];
|
||||||
|
|
||||||
|
loadedCheerio("a.MuiLink-underlineNone").each(function () {
|
||||||
|
genres.push(loadedCheerio(this).find("div > div").text());
|
||||||
|
});
|
||||||
|
|
||||||
|
novel.genres = genres.join(",");
|
||||||
|
|
||||||
|
novel.status = loadedCheerio("div.font-set-b10")
|
||||||
|
.text();
|
||||||
|
|
||||||
|
let chapter: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
|
loadedCheerio("div.border-b.border-gray-line-base").each((idx, ele) => {
|
||||||
|
const name = loadedCheerio(ele)
|
||||||
|
.find("a > div > div > div > span")
|
||||||
|
.text();
|
||||||
|
const releaseTime = loadedCheerio(ele)
|
||||||
|
.find("a > div > div > div > div > span")
|
||||||
|
.text();
|
||||||
|
let url = loadedCheerio(ele).find("a").attr("href")?.slice(1);
|
||||||
|
url = `${this.baseUrl}${url}`;
|
||||||
|
|
||||||
|
chapter.push({ name, releaseTime, url });
|
||||||
|
});
|
||||||
|
|
||||||
|
novel.chapters = chapter;
|
||||||
|
|
||||||
|
return novel;
|
||||||
|
}
|
||||||
|
async parseChapter(chapterUrl: string): Promise<string> {
|
||||||
|
const result = await fetchApi(chapterUrl);
|
||||||
|
const body = await result.text();
|
||||||
|
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
loadedCheerio(".chapter-nav").remove();
|
||||||
|
|
||||||
|
loadedCheerio("#chapter-content > script").remove();
|
||||||
|
|
||||||
|
let chapterText = loadedCheerio("#chapter-content").html() || '';
|
||||||
|
|
||||||
|
return chapterText;
|
||||||
|
}
|
||||||
|
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
|
const searchUrl = "https://www.wuxiaworld.com/api/novels/search?query=";
|
||||||
|
|
||||||
|
const url = `${searchUrl}${searchTerm}`;
|
||||||
|
|
||||||
|
const result = await fetchApi(url);
|
||||||
|
const data = await result.json();
|
||||||
|
|
||||||
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
data.items.map((novel: NovelEntry) => {
|
||||||
|
let name = novel.name;
|
||||||
|
let cover = novel.coverUrl;
|
||||||
|
let url = this.baseUrl + "novel/" + novel.slug + "/";
|
||||||
|
|
||||||
|
novels.push({
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
cover,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
fetchImage(url: string): Promise<string | undefined> {
|
||||||
|
return fetchFile(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new WuxiaWorld();
|
||||||