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
This commit is contained in:
nyagami
2024-01-21 21:21:35 +07:00
committed by GitHub
parent 5aecd7b282
commit 2830ae68dd
39 changed files with 4227 additions and 2020 deletions
+281 -191
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -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";
+161
View File
@@ -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();
+219
View File
@@ -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();
+200
View File
@@ -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();
+219
View File
@@ -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();
+208
View File
@@ -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();
+177
View File
@@ -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();
+227
View File
@@ -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();
+183
View File
@@ -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();
+467
View File
@@ -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();
+192
View File
@@ -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();
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

+1 -1
View File
@@ -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;
-111
View File
@@ -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;
+115
View File
@@ -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();
-213
View File
@@ -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 });
};
+173
View File
@@ -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 });
};
+173
View File
@@ -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();
-180
View File
@@ -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,
};
+155
View File
@@ -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();
-133
View File
@@ -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 });
};
+126
View File
@@ -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();
-172
View File
@@ -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 });
};
+168
View File
@@ -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();
+138
View File
@@ -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();
-148
View File
@@ -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 });
};
-189
View File
@@ -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;
-492
View File
@@ -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,
};
+463
View File
@@ -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();
-146
View File
@@ -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 });
};
+145
View File
@@ -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();