Almost english sources (#709)
* fix: LightNovelPub * fix: novelpub -> lightnovelworld * still broken, wuxiaworld keep html in js variables * fix: NovelsOnline * fix: novelhole -> novelhall * fix: novelfull * fix: mtl reader * refactor: mtlnovel * temporary fix: lightnovelupdates * still cant work * cant work * cant parseNovel and chapters: Wuxiaworld
@@ -41,7 +41,7 @@ var fetch_1 = require("@libs/fetch");
|
||||
var NovelFull = /** @class */ (function () {
|
||||
function NovelFull() {
|
||||
this.id = "NF.me";
|
||||
this.name = "NovelFull";
|
||||
this.name = "NovelFullMe";
|
||||
this.site = "https://novelfull.me/";
|
||||
this.version = "1.0.0";
|
||||
this.icon = "src/en/novelfullme/icon.png";
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var cheerio_1 = require("cheerio");
|
||||
var fetch_1 = require("@libs/fetch");
|
||||
var NovelsOnline = /** @class */ (function () {
|
||||
function NovelsOnline() {
|
||||
this.id = "NO.net";
|
||||
this.name = "novelsOnline";
|
||||
this.site = "https://novelsonline.net";
|
||||
this.icon = "src/coverNotAvailable.jpg";
|
||||
this.version = "1.0.0";
|
||||
}
|
||||
NovelsOnline.prototype.popularNovels = function (pageNo, options) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
return [2 /*return*/, []]; /** TO DO */
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelsOnline.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var novel, result, $;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
novel = {
|
||||
url: novelUrl,
|
||||
chapters: [],
|
||||
};
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(novelUrl).then(function (res) { return res.text(); })];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
$ = (0, cheerio_1.load)(result);
|
||||
novel.name = $("h1").text();
|
||||
novel.cover = $(".novel-cover").find("a > img").attr("src");
|
||||
novel.author = $("div.novel-details > div:nth-child(5) > div.novel-detail-body")
|
||||
.find("li")
|
||||
.map(function (_, el) { return $(el).text(); })
|
||||
.get()
|
||||
.join(", ");
|
||||
novel.genres = $("div.novel-details > div:nth-child(2) > div.novel-detail-body")
|
||||
.find("li")
|
||||
.map(function (_, el) { return $(el).text(); })
|
||||
.get()
|
||||
.join(",");
|
||||
novel.summary = $("div.novel-right > div > div:nth-child(1) > div.novel-detail-body").text();
|
||||
novel.chapters = $("ul.chapter-chs > li > a")
|
||||
.map(function (_, el) {
|
||||
var chapterUrl = $(el).attr("href");
|
||||
var chapterName = $(el).text();
|
||||
return {
|
||||
name: chapterName,
|
||||
releaseTime: "",
|
||||
url: chapterUrl,
|
||||
};
|
||||
})
|
||||
.get();
|
||||
return [2 /*return*/, novel];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelsOnline.prototype.parseChapter = function (chapterUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var result, loadedCheerio, chapterText;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl).then(function (res) { return res.text(); })];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(result);
|
||||
chapterText = loadedCheerio("#contentall").html() || "";
|
||||
return [2 /*return*/, chapterText];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelsOnline.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var result, $, headers;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)("https://novelsonline.net/sResults.php", {
|
||||
headers: {
|
||||
Accept: "*/*",
|
||||
"Accept-Language": "pl,en-US;q=0.7,en;q=0.3",
|
||||
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
},
|
||||
method: "POST",
|
||||
body: "q=" + encodeURIComponent(searchTerm),
|
||||
}).then(function (res) { return res.text(); })];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
$ = (0, cheerio_1.load)(result);
|
||||
headers = $("li");
|
||||
return [2 /*return*/, headers
|
||||
.map(function (i, h) {
|
||||
var novelName = $(h).text();
|
||||
var novelUrl = $(h).find("a").attr("href");
|
||||
var novelCover = $(h).find("img").attr("src");
|
||||
if (!novelUrl) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
})
|
||||
.get()
|
||||
.filter(function (sr) { return sr !== null; })];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelsOnline.prototype.fetchImage = function (url) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
return [2 /*return*/, (0, fetch_1.fetchFile)(url)];
|
||||
});
|
||||
});
|
||||
};
|
||||
return NovelsOnline;
|
||||
}());
|
||||
exports.default = new NovelsOnline();
|
||||
@@ -0,0 +1,219 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var cheerio_1 = require("cheerio");
|
||||
var fetch_1 = require("@libs/fetch");
|
||||
var LightNovelPub = /** @class */ (function () {
|
||||
function LightNovelPub() {
|
||||
this.id = "lightnovelpub";
|
||||
this.name = "LightNovelPub";
|
||||
this.version = "1.0.0";
|
||||
this.icon = "src/en/lightnovelpub/icon.png";
|
||||
this.site = "https://www.lightnovelpub.com/";
|
||||
this.baseUrl = this.site;
|
||||
this.headers = {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
}
|
||||
LightNovelPub.prototype.popularNovels = function (pageNo, options) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, body, loadedCheerio, novels;
|
||||
var _this = this;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = "".concat(this.baseUrl, "browse/all/popular/all/").concat(pageNo);
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novels = [];
|
||||
loadedCheerio(".novel-item.ads").remove();
|
||||
loadedCheerio(".novel-item").each(function (idx, ele) {
|
||||
var _a;
|
||||
var novelName = loadedCheerio(ele)
|
||||
.find(".novel-title")
|
||||
.text()
|
||||
.trim();
|
||||
var novelCover = loadedCheerio(ele).find("img").attr("data-src");
|
||||
var novelUrl = _this.baseUrl +
|
||||
((_a = loadedCheerio(ele)
|
||||
.find(".novel-title > a")
|
||||
.attr("href")) === null || _a === void 0 ? void 0 : _a.substring(1));
|
||||
var novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
novels.push(novel);
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
LightNovelPub.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||
var _a;
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, body, loadedCheerio, novel, delay, chapterListUrl, chapterListBody, loadedChapterList, lastNumber, lastChapterUrl, chapterUrlPrefix, chapters, i;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
url = novelUrl;
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _b.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novel = {
|
||||
url: url,
|
||||
chapters: [],
|
||||
};
|
||||
novel.name = loadedCheerio("h1.novel-title").text().trim();
|
||||
novel.cover = loadedCheerio("figure.cover > img").attr("data-src");
|
||||
novel.genres = "";
|
||||
loadedCheerio(".categories > ul > li > a").each(function () {
|
||||
novel.genres += loadedCheerio(this).text() + ",";
|
||||
});
|
||||
novel.genres = novel.genres.slice(0, -1);
|
||||
loadedCheerio("div.header-stats > span").each(function () {
|
||||
if (loadedCheerio(this).find("small").text() === "Status") {
|
||||
novel.status = loadedCheerio(this).find("strong").text();
|
||||
}
|
||||
});
|
||||
novel.author = loadedCheerio(".author > a > span").text();
|
||||
novel.summary = loadedCheerio(".summary > .content").text().trim();
|
||||
delay = function (ms) { return new Promise(function (res) { return setTimeout(res, ms); }); };
|
||||
return [4 /*yield*/, delay(1000)];
|
||||
case 2:
|
||||
_b.sent();
|
||||
chapterListUrl = novelUrl + '/chapters?chorder=desc';
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterListUrl).then(function (r) { return r.text(); })];
|
||||
case 3:
|
||||
chapterListBody = _b.sent();
|
||||
loadedChapterList = (0, cheerio_1.load)(chapterListBody);
|
||||
lastNumber = parseInt((_a = loadedChapterList('ul.chapter-list > li:nth-child(1)').attr('data-chapterno')) !== null && _a !== void 0 ? _a : '0');
|
||||
lastChapterUrl = loadedChapterList('ul.chapter-list > li:nth-child(1) > a').attr('href');
|
||||
if (lastChapterUrl) {
|
||||
chapterUrlPrefix = this.baseUrl + lastChapterUrl.split('/').slice(1, -1).join('/');
|
||||
chapters = [];
|
||||
for (i = 1; i <= lastNumber; i++) {
|
||||
chapters.push({
|
||||
chapterNumber: i,
|
||||
name: 'Chapter ' + i,
|
||||
url: chapterUrlPrefix + '/chapter-' + i,
|
||||
});
|
||||
}
|
||||
novel.chapters = chapters;
|
||||
}
|
||||
return [2 /*return*/, novel];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
LightNovelPub.prototype.parseChapter = function (chapterUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var body, loadedCheerio, chapterText;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
chapterText = loadedCheerio("#chapter-container").html() || '';
|
||||
return [2 /*return*/, chapterText];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
LightNovelPub.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, link, response, token, verifytoken, formData, body, loadedCheerio, novels, results;
|
||||
var _this = this;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = "".concat(this.baseUrl, "lnsearchlive");
|
||||
link = "".concat(this.baseUrl, "search");
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(link).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
response = _a.sent();
|
||||
token = (0, cheerio_1.load)(response);
|
||||
verifytoken = token("#novelSearchForm > input").attr("value");
|
||||
formData = new FormData();
|
||||
formData.append("inputContent", searchTerm);
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url, {
|
||||
method: "POST",
|
||||
headers: { LNRequestVerifyToken: verifytoken },
|
||||
body: formData,
|
||||
}).then(function (r) { return r.text(); })];
|
||||
case 2:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novels = [];
|
||||
results = JSON.parse(loadedCheerio("body").text());
|
||||
loadedCheerio = (0, cheerio_1.load)(results.resultview);
|
||||
loadedCheerio(".novel-item").each(function (idx, ele) {
|
||||
var _a;
|
||||
var novelName = loadedCheerio(ele)
|
||||
.find("h4.novel-title")
|
||||
.text()
|
||||
.trim();
|
||||
var novelCover = loadedCheerio(ele).find("img").attr("src");
|
||||
var novelUrl = _this.baseUrl + ((_a = loadedCheerio(ele).find("a").attr("href")) === null || _a === void 0 ? void 0 : _a.substring(1));
|
||||
novels.push({
|
||||
name: novelName,
|
||||
url: novelUrl,
|
||||
cover: novelCover,
|
||||
});
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
LightNovelPub.prototype.fetchImage = function (url) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
return [2 /*return*/, (0, fetch_1.fetchFile)(url, { headers: this.headers })];
|
||||
});
|
||||
});
|
||||
};
|
||||
return LightNovelPub;
|
||||
}());
|
||||
exports.default = new LightNovelPub();
|
||||
@@ -0,0 +1,200 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var cheerio_1 = require("cheerio");
|
||||
var fetch_1 = require("@libs/fetch");
|
||||
var LightNovelUpdates = /** @class */ (function () {
|
||||
function LightNovelUpdates() {
|
||||
this.id = "LightNovelUpdates";
|
||||
this.name = "Light Novel Updates";
|
||||
this.version = "1.0.0";
|
||||
this.icon = "src/en/lightnovelupdates/icon.png";
|
||||
this.site = "https://www.lightnovelupdates.com/";
|
||||
this.baseUrl = this.site;
|
||||
}
|
||||
LightNovelUpdates.prototype.popularNovels = function (pageNo, options) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, body, loadedCheerio, novels;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = "".concat(this.baseUrl, "novel/page/").concat(pageNo, "/?m_orderby=rating");
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novels = [];
|
||||
loadedCheerio(".page-item-detail").each(function () {
|
||||
var novelName = loadedCheerio(this).find(".h5 > a").text();
|
||||
var novelCover = loadedCheerio(this).find("img").attr("src");
|
||||
var novelUrl = loadedCheerio(this).find(".h5 > a").attr("href");
|
||||
if (!novelUrl)
|
||||
return;
|
||||
var novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
novels.push(novel);
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
LightNovelUpdates.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, body, loadedCheerio, novel, chapter, novelId, formData, data, text;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = novelUrl;
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novel = {
|
||||
url: url,
|
||||
chapters: [],
|
||||
};
|
||||
novel.name = loadedCheerio(".post-title > h1").text().trim();
|
||||
novel.cover =
|
||||
loadedCheerio(".summary_image > a > img").attr("src") || undefined;
|
||||
loadedCheerio(".post-content_item").each(function () {
|
||||
var detailName = loadedCheerio(this)
|
||||
.find(".summary-heading > h5")
|
||||
.text()
|
||||
.trim();
|
||||
var detail = loadedCheerio(this)
|
||||
.find(".summary-content")
|
||||
.text()
|
||||
.trim();
|
||||
switch (detailName) {
|
||||
case "Genre(s)":
|
||||
novel.genres = detail.trim().replace(/[\t\n]/g, ",");
|
||||
break;
|
||||
case "Author(s)":
|
||||
novel.author = detail.trim();
|
||||
break;
|
||||
case "Status":
|
||||
novel.status = detail.replace(/G/g, "g");
|
||||
break;
|
||||
}
|
||||
});
|
||||
novel.summary = loadedCheerio("div.summary__content").text();
|
||||
chapter = [];
|
||||
novelId = loadedCheerio(".rating-post-id").attr("value");
|
||||
formData = new FormData();
|
||||
formData.append("action", "manga_get_chapters");
|
||||
formData.append("manga", novelId);
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)("https://www.lightnovelupdates.com/wp-admin/admin-ajax.php", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
})];
|
||||
case 2:
|
||||
data = _a.sent();
|
||||
return [4 /*yield*/, data.text()];
|
||||
case 3:
|
||||
text = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(text);
|
||||
loadedCheerio(".wp-manga-chapter").each(function () {
|
||||
var chapterName = loadedCheerio(this).find("a").text().trim();
|
||||
var releaseDate = loadedCheerio(this).find("span").text().trim();
|
||||
var chapterUrl = loadedCheerio(this).find("a").attr("href");
|
||||
if (!chapterUrl)
|
||||
return;
|
||||
chapter.push({
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
url: chapterUrl,
|
||||
});
|
||||
});
|
||||
novel.chapters = chapter.reverse();
|
||||
return [2 /*return*/, novel];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
LightNovelUpdates.prototype.parseChapter = function (chapterUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var chapterText;
|
||||
return __generator(this, function (_a) {
|
||||
chapterText = 'unable to load';
|
||||
return [2 /*return*/, chapterText];
|
||||
});
|
||||
});
|
||||
};
|
||||
LightNovelUpdates.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, body, loadedCheerio, novels;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = "".concat(this.baseUrl, "?s=").concat(searchTerm, "&post_type=wp-manga&m_orderby=rating");
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novels = [];
|
||||
loadedCheerio(".c-tabs-item__content").each(function () {
|
||||
var novelName = loadedCheerio(this).find(".h4 > a").text();
|
||||
var novelCover = loadedCheerio(this).find("img").attr("src");
|
||||
var novelUrl = loadedCheerio(this).find(".h4 > a").attr("href");
|
||||
if (!novelUrl)
|
||||
return;
|
||||
novels.push({
|
||||
url: novelUrl,
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
});
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
LightNovelUpdates.prototype.fetchImage = function (url) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
return [2 /*return*/, (0, fetch_1.fetchFile)(url)];
|
||||
});
|
||||
});
|
||||
};
|
||||
return LightNovelUpdates;
|
||||
}());
|
||||
exports.default = new LightNovelUpdates();
|
||||
@@ -0,0 +1,219 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var cheerio_1 = require("cheerio");
|
||||
var fetch_1 = require("@libs/fetch");
|
||||
var LightNovelWorld = /** @class */ (function () {
|
||||
function LightNovelWorld() {
|
||||
this.id = "lightnovelworld";
|
||||
this.name = "LightNovelWorld";
|
||||
this.site = "https://www.webnovelworld.org/";
|
||||
this.version = "1.0.0";
|
||||
this.icon = "src/en/lightnovelworld/icon.png";
|
||||
this.baseUrl = this.site;
|
||||
this.headers = {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
}
|
||||
LightNovelWorld.prototype.popularNovels = function (pageNo, options) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, body, loadedCheerio, novels;
|
||||
var _this = this;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = "".concat(this.baseUrl, "browse/all/popular/all/").concat(pageNo);
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novels = [];
|
||||
loadedCheerio(".novel-item.ads").remove();
|
||||
loadedCheerio(".novel-item").each(function (idx, ele) {
|
||||
var _a;
|
||||
var novelName = loadedCheerio(ele)
|
||||
.find(".novel-title")
|
||||
.text()
|
||||
.trim();
|
||||
var novelCover = loadedCheerio(ele).find("img").attr("data-src");
|
||||
var novelUrl = _this.baseUrl +
|
||||
((_a = loadedCheerio(ele)
|
||||
.find(".novel-title > a")
|
||||
.attr("href")) === null || _a === void 0 ? void 0 : _a.substring(1));
|
||||
var novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
novels.push(novel);
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
LightNovelWorld.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||
var _a;
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, body, loadedCheerio, novel, delay, chapterListUrl, chapterListBody, loadedChapterList, lastNumber, lastChapterUrl, chapterUrlPrefix, chapters, i;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
url = novelUrl;
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _b.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novel = {
|
||||
url: url,
|
||||
chapters: [],
|
||||
};
|
||||
novel.name = loadedCheerio("h1.novel-title").text().trim();
|
||||
novel.cover = loadedCheerio("figure.cover > img").attr("data-src");
|
||||
novel.genres = "";
|
||||
loadedCheerio(".categories > ul > li > a").each(function () {
|
||||
novel.genres += loadedCheerio(this).text() + ",";
|
||||
});
|
||||
novel.genres = novel.genres.slice(0, -1);
|
||||
loadedCheerio("div.header-stats > span").each(function () {
|
||||
if (loadedCheerio(this).find("small").text() === "Status") {
|
||||
novel.status = loadedCheerio(this).find("strong").text();
|
||||
}
|
||||
});
|
||||
novel.author = loadedCheerio(".author > a > span").text();
|
||||
novel.summary = loadedCheerio(".summary > .content").text().trim();
|
||||
delay = function (ms) { return new Promise(function (res) { return setTimeout(res, ms); }); };
|
||||
return [4 /*yield*/, delay(1000)];
|
||||
case 2:
|
||||
_b.sent();
|
||||
chapterListUrl = novelUrl + '/chapters?chorder=desc';
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterListUrl).then(function (r) { return r.text(); })];
|
||||
case 3:
|
||||
chapterListBody = _b.sent();
|
||||
loadedChapterList = (0, cheerio_1.load)(chapterListBody);
|
||||
lastNumber = parseInt((_a = loadedChapterList('ul.chapter-list > li:nth-child(1)').attr('data-chapterno')) !== null && _a !== void 0 ? _a : '0');
|
||||
lastChapterUrl = loadedChapterList('ul.chapter-list > li:nth-child(1) > a').attr('href');
|
||||
if (lastChapterUrl) {
|
||||
chapterUrlPrefix = this.baseUrl + lastChapterUrl.split('/').slice(1, -1).join('/');
|
||||
chapters = [];
|
||||
for (i = 1; i <= lastNumber; i++) {
|
||||
chapters.push({
|
||||
chapterNumber: i,
|
||||
name: 'Chapter ' + i,
|
||||
url: chapterUrlPrefix + '/chapter-' + i,
|
||||
});
|
||||
}
|
||||
novel.chapters = chapters;
|
||||
}
|
||||
return [2 /*return*/, novel];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
LightNovelWorld.prototype.parseChapter = function (chapterUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var body, loadedCheerio, chapterText;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
chapterText = loadedCheerio("#chapter-container").html() || '';
|
||||
return [2 /*return*/, chapterText];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
LightNovelWorld.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, link, response, token, verifytoken, formData, body, loadedCheerio, novels, results;
|
||||
var _this = this;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = "".concat(this.baseUrl, "lnsearchlive");
|
||||
link = "".concat(this.baseUrl, "search");
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(link).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
response = _a.sent();
|
||||
token = (0, cheerio_1.load)(response);
|
||||
verifytoken = token("#novelSearchForm > input").attr("value");
|
||||
formData = new FormData();
|
||||
formData.append("inputContent", searchTerm);
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url, {
|
||||
method: "POST",
|
||||
headers: { LNRequestVerifyToken: verifytoken },
|
||||
body: formData,
|
||||
}).then(function (r) { return r.text(); })];
|
||||
case 2:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novels = [];
|
||||
results = JSON.parse(loadedCheerio("body").text());
|
||||
loadedCheerio = (0, cheerio_1.load)(results.resultview);
|
||||
loadedCheerio(".novel-item").each(function (idx, ele) {
|
||||
var _a;
|
||||
var novelName = loadedCheerio(ele)
|
||||
.find("h4.novel-title")
|
||||
.text()
|
||||
.trim();
|
||||
var novelCover = loadedCheerio(ele).find("img").attr("src");
|
||||
var novelUrl = _this.baseUrl + ((_a = loadedCheerio(ele).find("a").attr("href")) === null || _a === void 0 ? void 0 : _a.substring(1));
|
||||
novels.push({
|
||||
name: novelName,
|
||||
url: novelUrl,
|
||||
cover: novelCover,
|
||||
});
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
LightNovelWorld.prototype.fetchImage = function (url) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
return [2 /*return*/, (0, fetch_1.fetchFile)(url, { headers: this.headers })];
|
||||
});
|
||||
});
|
||||
};
|
||||
return LightNovelWorld;
|
||||
}());
|
||||
exports.default = new LightNovelWorld();
|
||||
@@ -0,0 +1,208 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var cheerio_1 = require("cheerio");
|
||||
var fetch_1 = require("@libs/fetch");
|
||||
var MTLNovel = /** @class */ (function () {
|
||||
function MTLNovel() {
|
||||
this.id = "mtlnovel";
|
||||
this.name = "MTL Novel";
|
||||
this.version = "1.0.0";
|
||||
this.icon = "src/en/mtlnovel/icon.png";
|
||||
this.site = "https://www.mtlnovel.com/";
|
||||
this.baseUrl = this.site;
|
||||
}
|
||||
MTLNovel.prototype.popularNovels = function (pageNo, options) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, body, loadedCheerio, novels;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = "".concat(this.baseUrl, "alltime-rank/page/").concat(pageNo);
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (result) { return result.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
console.log(body);
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novels = [];
|
||||
loadedCheerio("div.box.wide").each(function () {
|
||||
var novelName = loadedCheerio(this)
|
||||
.find("a.list-title")
|
||||
.text()
|
||||
.slice(4);
|
||||
var novelCover = loadedCheerio(this).find("amp-img").attr("src");
|
||||
var novelUrl = loadedCheerio(this).find("a.list-title").attr("href");
|
||||
if (!novelUrl)
|
||||
return;
|
||||
var novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
novels.push(novel);
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
MTLNovel.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
function getChapters() {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var listResult, listBody, chapter;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, fetch(chapterListUrl)];
|
||||
case 1:
|
||||
listResult = _a.sent();
|
||||
return [4 /*yield*/, listResult.text()];
|
||||
case 2:
|
||||
listBody = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(listBody);
|
||||
chapter = [];
|
||||
loadedCheerio("div.ch-list")
|
||||
.find("a.ch-link")
|
||||
.each(function () {
|
||||
var chapterName = loadedCheerio(this)
|
||||
.text()
|
||||
.replace("~ ", "");
|
||||
var releaseDate = null;
|
||||
var chapterUrl = loadedCheerio(this).attr("href");
|
||||
if (chapterUrl) {
|
||||
chapter.push({
|
||||
url: chapterUrl,
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
});
|
||||
}
|
||||
});
|
||||
return [2 /*return*/, chapter.reverse()];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
var url, headers, body, loadedCheerio, novel, chapterListUrl, _a;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
url = novelUrl;
|
||||
headers = {
|
||||
Referer: "".concat(this.baseUrl, "alltime-rank/"),
|
||||
};
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url, {
|
||||
method: "GET",
|
||||
headers: headers,
|
||||
}).then(function (result) { return result.text(); })];
|
||||
case 1:
|
||||
body = _b.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novel = {
|
||||
url: url,
|
||||
chapters: [],
|
||||
};
|
||||
novel.name = loadedCheerio("h1.entry-title").text();
|
||||
novel.cover = loadedCheerio(".nov-head > amp-img").attr("src");
|
||||
novel.summary = loadedCheerio("div.desc > h2").next().text().trim();
|
||||
novel.author = loadedCheerio("#author").text();
|
||||
novel.status = loadedCheerio("#status").text();
|
||||
novel.genres = loadedCheerio("#genre").text().replace(/\s+/g, "");
|
||||
chapterListUrl = url + "chapter-list/";
|
||||
_a = novel;
|
||||
return [4 /*yield*/, getChapters()];
|
||||
case 2:
|
||||
_a.chapters = _b.sent();
|
||||
return [2 /*return*/, novel];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
MTLNovel.prototype.parseChapter = function (chapterUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var body, loadedCheerio, chapterText;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
chapterText = loadedCheerio("div.par").html() || '';
|
||||
return [2 /*return*/, chapterText];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
MTLNovel.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var searchUrl, res, result, novels;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
searchUrl = this.baseUrl +
|
||||
"wp-admin/admin-ajax.php?action=autosuggest&q=" +
|
||||
searchTerm +
|
||||
"&__amp_source_origin=https%3A%2F%2Fwww.mtlnovel.com";
|
||||
return [4 /*yield*/, fetch(searchUrl)];
|
||||
case 1:
|
||||
res = _a.sent();
|
||||
return [4 /*yield*/, res.json()];
|
||||
case 2:
|
||||
result = _a.sent();
|
||||
novels = [];
|
||||
result.items[0].results.map(function (item) {
|
||||
var novelName = item.title.replace(/<\/?strong>/g, "");
|
||||
var novelCover = item.thumbnail;
|
||||
var novelUrl = item.permalink.replace("https://www.mtlnovel.com/", "");
|
||||
var novel = { name: novelName, cover: novelCover, url: novelUrl };
|
||||
novels.push(novel);
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
MTLNovel.prototype.fetchImage = function (url) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
return [2 /*return*/, (0, fetch_1.fetchFile)(url)];
|
||||
});
|
||||
});
|
||||
};
|
||||
return MTLNovel;
|
||||
}());
|
||||
exports.default = new MTLNovel();
|
||||
@@ -0,0 +1,177 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var cheerio_1 = require("cheerio");
|
||||
var fetch_1 = require("@libs/fetch");
|
||||
var MTLReader = /** @class */ (function () {
|
||||
function MTLReader() {
|
||||
this.id = "mtlreader";
|
||||
this.name = "MTL Reader";
|
||||
this.version = "1.0.0";
|
||||
this.icon = "src/en/mtlreader/icon.png";
|
||||
this.site = "https://mtlreader.com/";
|
||||
this.baseUrl = this.site;
|
||||
}
|
||||
MTLReader.prototype.popularNovels = function (pageNo, options) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, body, loadedCheerio, novels;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = "".concat(this.baseUrl, "novels?page=").concat(pageNo);
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novels = [];
|
||||
loadedCheerio(".col-md-4.col-sm-4").each(function () {
|
||||
var novelName = loadedCheerio(this).find("h5").text();
|
||||
var novelCover = loadedCheerio(this).find("img").attr("src");
|
||||
var novelUrl = loadedCheerio(this).find("h5 > a").attr("href");
|
||||
if (!novelUrl)
|
||||
return;
|
||||
var novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
novels.push(novel);
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
MTLReader.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, body, loadedCheerio, novel, chapter;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = novelUrl;
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novel = {
|
||||
url: url,
|
||||
chapters: [],
|
||||
};
|
||||
novel.name = loadedCheerio(".agent-title").text().trim();
|
||||
novel.cover = loadedCheerio(".agent-p-img > img").attr("src");
|
||||
novel.summary = loadedCheerio("#editdescription").text().trim();
|
||||
novel.author = loadedCheerio("i.fa-user")
|
||||
.parent()
|
||||
.text()
|
||||
.replace("Author: ", "")
|
||||
.trim();
|
||||
chapter = [];
|
||||
loadedCheerio("tr.spaceUnder").each(function () {
|
||||
var chapterName = loadedCheerio(this).find("a").text().trim();
|
||||
var releaseDate = null;
|
||||
var chapterUrl = loadedCheerio(this).find("a").attr("href");
|
||||
if (!chapterUrl)
|
||||
return;
|
||||
chapter.push({
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
url: chapterUrl,
|
||||
});
|
||||
});
|
||||
novel.chapters = chapter;
|
||||
return [2 /*return*/, novel];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
MTLReader.prototype.parseChapter = function (chapterUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var body, loadedCheerio, chapterText;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
loadedCheerio(".container ins,script,p.mtlreader").remove();
|
||||
chapterText = loadedCheerio(".container").html() || '';
|
||||
return [2 /*return*/, chapterText];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
MTLReader.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, body, loadedCheerio, novels;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = "".concat(this.baseUrl, "search?input=").concat(searchTerm);
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novels = [];
|
||||
loadedCheerio(".col-md-4.col-sm-4").each(function () {
|
||||
var novelName = loadedCheerio(this).find("h5").text();
|
||||
var novelCover = loadedCheerio(this).find("img").attr("src");
|
||||
var novelUrl = loadedCheerio(this).find("h5 > a").attr("href");
|
||||
if (!novelUrl)
|
||||
return;
|
||||
var novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
novels.push(novel);
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
MTLReader.prototype.fetchImage = function (url) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
return [2 /*return*/, (0, fetch_1.fetchFile)(url)];
|
||||
});
|
||||
});
|
||||
};
|
||||
return MTLReader;
|
||||
}());
|
||||
exports.default = new MTLReader();
|
||||
@@ -0,0 +1,227 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var cheerio_1 = require("cheerio");
|
||||
var fetch_1 = require("@libs/fetch");
|
||||
var NovelFull = /** @class */ (function () {
|
||||
function NovelFull() {
|
||||
this.id = "novelfull";
|
||||
this.name = "NovelFull";
|
||||
this.version = "1.0.0";
|
||||
this.icon = "src/en/novelfull/icon.png";
|
||||
this.site = "https://novelfull.com/";
|
||||
this.baseUrl = this.site;
|
||||
}
|
||||
NovelFull.prototype.popularNovels = function (pageNo, options) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, result, body, loadedCheerio, novels;
|
||||
var _this = this;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = "".concat(this.baseUrl, "most-popular?page=").concat(pageNo);
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url)];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
return [4 /*yield*/, result.text()];
|
||||
case 2:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novels = [];
|
||||
loadedCheerio(".col-truyen-main .list-truyen .row").each(function (idx, ele) {
|
||||
var _a, _b;
|
||||
var novelName = loadedCheerio(ele)
|
||||
.find("h3.truyen-title > a")
|
||||
.text();
|
||||
var novelCover = _this.baseUrl + ((_a = loadedCheerio(ele).find("img").attr("src")) === null || _a === void 0 ? void 0 : _a.slice(1));
|
||||
var novelUrl = _this.baseUrl +
|
||||
((_b = loadedCheerio(ele)
|
||||
.find("h3.truyen-title > a")
|
||||
.attr("href")) === null || _b === void 0 ? void 0 : _b.slice(1));
|
||||
var novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
novels.push(novel);
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelFull.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
function getChapters(id, baseUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var chapterListUrl, data, chapterlist, chapter;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
chapterListUrl = baseUrl + "ajax/chapter-option?novelId=" + id;
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterListUrl)];
|
||||
case 1:
|
||||
data = _a.sent();
|
||||
return [4 /*yield*/, data.text()];
|
||||
case 2:
|
||||
chapterlist = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(chapterlist);
|
||||
chapter = [];
|
||||
loadedCheerio("select > option").each(function () {
|
||||
var _a;
|
||||
var chapterName = loadedCheerio(this).text();
|
||||
var releaseDate = null;
|
||||
var chapterUrl = baseUrl + ((_a = loadedCheerio(this).attr("value")) === null || _a === void 0 ? void 0 : _a.slice(1));
|
||||
chapter.push({
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
url: chapterUrl,
|
||||
});
|
||||
});
|
||||
return [2 /*return*/, chapter];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
var url, result, body, loadedCheerio, novel, novelId, _a;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
url = novelUrl;
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url)];
|
||||
case 1:
|
||||
result = _b.sent();
|
||||
return [4 /*yield*/, result.text()];
|
||||
case 2:
|
||||
body = _b.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novel = {
|
||||
url: url,
|
||||
chapters: [],
|
||||
};
|
||||
novel.name = loadedCheerio("div.book > img").attr("alt");
|
||||
novel.cover = this.baseUrl + loadedCheerio("div.book > img").attr("src");
|
||||
novel.summary = loadedCheerio("div.desc-text").text().trim();
|
||||
novel.author = loadedCheerio('h3:contains("Author")')
|
||||
.parent()
|
||||
.contents()
|
||||
.text()
|
||||
.replace("Author:", "");
|
||||
novel.genres = loadedCheerio('h3:contains("Genre")')
|
||||
.siblings()
|
||||
.map(function (i, el) { return loadedCheerio(el).text(); })
|
||||
.toArray()
|
||||
.join(",");
|
||||
novel.status = loadedCheerio('h3:contains("Status")').next().text();
|
||||
novelId = loadedCheerio("#rating").attr("data-novel-id");
|
||||
_a = novel;
|
||||
return [4 /*yield*/, getChapters(novelId, this.baseUrl)];
|
||||
case 3:
|
||||
_a.chapters = _b.sent();
|
||||
return [2 /*return*/, novel];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelFull.prototype.parseChapter = function (chapterUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var result, body, loadedCheerio, chapterText;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl)];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
return [4 /*yield*/, result.text()];
|
||||
case 2:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
loadedCheerio("#chapter-content div.ads").remove();
|
||||
chapterText = loadedCheerio("#chapter-content").html() || '';
|
||||
return [2 /*return*/, chapterText];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelFull.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var searchUrl, result, body, loadedCheerio, novels;
|
||||
var _this = this;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
searchUrl = "".concat(this.baseUrl, "search?keyword=").concat(searchTerm);
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(searchUrl)];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
return [4 /*yield*/, result.text()];
|
||||
case 2:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novels = [];
|
||||
loadedCheerio(".col-truyen-main .list-truyen .row").each(function (idx, ele) {
|
||||
var _a, _b;
|
||||
var novelName = loadedCheerio(ele)
|
||||
.find("h3.truyen-title > a")
|
||||
.text();
|
||||
var novelCover = _this.baseUrl + ((_a = loadedCheerio(ele).find("img").attr("src")) === null || _a === void 0 ? void 0 : _a.slice(1));
|
||||
var novelUrl = _this.baseUrl +
|
||||
((_b = loadedCheerio(ele)
|
||||
.find("h3.truyen-title > a")
|
||||
.attr("href")) === null || _b === void 0 ? void 0 : _b.slice(1));
|
||||
var novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
novels.push(novel);
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelFull.prototype.fetchImage = function (url) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
return [2 /*return*/, (0, fetch_1.fetchFile)(url)];
|
||||
});
|
||||
});
|
||||
};
|
||||
return NovelFull;
|
||||
}());
|
||||
exports.default = new NovelFull();
|
||||
@@ -0,0 +1,183 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var cheerio_1 = require("cheerio");
|
||||
var fetch_1 = require("@libs/fetch");
|
||||
var NovelHall = /** @class */ (function () {
|
||||
function NovelHall() {
|
||||
this.id = "novelhold";
|
||||
this.name = "Novel Hall";
|
||||
this.version = "1.0.0";
|
||||
this.icon = "src/en/novelhall/icon.png";
|
||||
this.site = "https://novelhall.com/";
|
||||
this.baseUrl = this.site;
|
||||
}
|
||||
NovelHall.prototype.popularNovels = function (pageNo, options) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, body, loadedCheerio, novels;
|
||||
var _this = this;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = "".concat(this.baseUrl, "all2022-").concat(pageNo, ".html");
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novels = [];
|
||||
loadedCheerio("li.btm").each(function (idx, ele) {
|
||||
var novelName = loadedCheerio(ele).text().trim();
|
||||
var novelUrl = _this.baseUrl + loadedCheerio(ele).find("a").attr("href");
|
||||
var novel = {
|
||||
name: novelName,
|
||||
url: novelUrl,
|
||||
};
|
||||
novels.push(novel);
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelHall.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||
var _a;
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, body, loadedCheerio, novel, chapter;
|
||||
var _this = this;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
url = novelUrl;
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _b.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novel = {
|
||||
url: url,
|
||||
chapters: [],
|
||||
};
|
||||
novel.name = loadedCheerio(".booknav2 > h1").text();
|
||||
novel.cover = loadedCheerio('meta[property="og:image"]').attr("content");
|
||||
novel.summary = loadedCheerio(".navtxt").text().trim();
|
||||
novel.author = loadedCheerio('p:contains("Author")')
|
||||
.text()
|
||||
.replace("Author:", "")
|
||||
.trim();
|
||||
novel.status = loadedCheerio('p:contains("Status")')
|
||||
.text()
|
||||
.replace("Status:", "")
|
||||
.replace("Active", "Ongoing")
|
||||
.trim();
|
||||
novel.genres = (_a = loadedCheerio('p:contains("Genre")')
|
||||
.text()) === null || _a === void 0 ? void 0 : _a.replace("Genre:", "").trim();
|
||||
chapter = [];
|
||||
loadedCheerio("#morelist ul > li").each(function (idx, ele) {
|
||||
var chapterName = loadedCheerio(ele).find("a").text().trim();
|
||||
var releaseDate = null;
|
||||
var chapterUrl = _this.baseUrl + loadedCheerio(ele).find("a").attr("href");
|
||||
chapter.push({
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
url: chapterUrl,
|
||||
});
|
||||
});
|
||||
novel.chapters = chapter;
|
||||
return [2 /*return*/, novel];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelHall.prototype.parseChapter = function (chapterUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var body, loadedCheerio, chapterText;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
chapterText = loadedCheerio(".content").html() || '';
|
||||
return [2 /*return*/, chapterText];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelHall.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, body, loadedCheerio, novels;
|
||||
var _this = this;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = "".concat(this.baseUrl, "index.php?s=so&module=book&keyword=").concat(searchTerm);
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url).then(function (r) { return r.text(); })];
|
||||
case 1:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novels = [];
|
||||
loadedCheerio("#article_list_content > li").each(function (idx, ele) {
|
||||
var _a;
|
||||
var novelName = loadedCheerio(ele)
|
||||
.find("h3")
|
||||
.text()
|
||||
.replace(/\t+/g, "")
|
||||
.replace(/\n/g, " ");
|
||||
var novelCover = loadedCheerio(ele).find("img").attr("data-src");
|
||||
var novelUrl = _this.baseUrl + ((_a = loadedCheerio(ele).find("a").attr("href")) === null || _a === void 0 ? void 0 : _a.slice(1));
|
||||
var novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
novels.push(novel);
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelHall.prototype.fetchImage = function (url) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
return [2 /*return*/, (0, fetch_1.fetchFile)(url)];
|
||||
});
|
||||
});
|
||||
};
|
||||
return NovelHall;
|
||||
}());
|
||||
exports.default = new NovelHall();
|
||||
@@ -0,0 +1,467 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var cheerio_1 = require("cheerio");
|
||||
var fetch_1 = require("@libs/fetch");
|
||||
var filterInputs_1 = require("@libs/filterInputs");
|
||||
var NovelUpdates = /** @class */ (function () {
|
||||
function NovelUpdates() {
|
||||
this.id = "novelupdates";
|
||||
this.name = "Novel Updates";
|
||||
this.version = "0.5.0";
|
||||
this.icon = "src/en/novelupdates/icon.png";
|
||||
this.site = "https://www.novelupdates.com/";
|
||||
this.baseUrl = this.site;
|
||||
this.filters = {
|
||||
sort: {
|
||||
label: "Sort Results By",
|
||||
type: filterInputs_1.FilterTypes.Picker,
|
||||
options: [
|
||||
{ label: "Last Updated", value: "sdate" },
|
||||
{ label: "Rating", value: "srate" },
|
||||
{ label: "Rank", value: "srank" },
|
||||
{ label: "Reviews", value: "sreview" },
|
||||
{ label: "Chapters", value: "srel" },
|
||||
{ label: "Title", value: "abc" },
|
||||
{ label: "Readers", value: "sread" },
|
||||
{ label: "Frequency", value: "sfrel" },
|
||||
],
|
||||
value: "sdate",
|
||||
},
|
||||
order: {
|
||||
label: "Order",
|
||||
type: filterInputs_1.FilterTypes.Picker,
|
||||
options: [
|
||||
{ label: "Descending", value: "desc" },
|
||||
{ label: "Ascending", value: "asc" },
|
||||
],
|
||||
value: "asc"
|
||||
},
|
||||
storyStatus: {
|
||||
label: "Story Status (Translation)",
|
||||
type: filterInputs_1.FilterTypes.Picker,
|
||||
options: [
|
||||
{ label: "All", value: "" },
|
||||
{ label: "Completed", value: "2" },
|
||||
{ label: "Ongoing", value: "3" },
|
||||
{ label: "Hiatus", value: "4" },
|
||||
],
|
||||
value: ""
|
||||
},
|
||||
language: {
|
||||
label: "Language",
|
||||
options: [
|
||||
{ label: "Chinese", value: "495" },
|
||||
{ label: "Filipino", value: "9181" },
|
||||
{ label: "Indonesian", value: "9179" },
|
||||
{ label: "Japanese", value: "496" },
|
||||
{ label: "Khmer", value: "18657" },
|
||||
{ label: "Korean", value: "497" },
|
||||
{ label: "Malaysian", value: "9183" },
|
||||
{ label: "Thai", value: "9954" },
|
||||
{ label: "Vietnamese", value: "9177" },
|
||||
],
|
||||
type: filterInputs_1.FilterTypes.Picker,
|
||||
value: ""
|
||||
},
|
||||
novelType: {
|
||||
label: "Novel Type",
|
||||
type: filterInputs_1.FilterTypes.CheckboxGroup,
|
||||
value: [],
|
||||
options: [
|
||||
{ label: "Light Novel", value: "2443" },
|
||||
{ label: "Published Novel", value: "26874" },
|
||||
{ label: "Web Novel", value: "2444" },
|
||||
]
|
||||
},
|
||||
genres: {
|
||||
label: "Genres",
|
||||
type: filterInputs_1.FilterTypes.CheckboxGroup,
|
||||
value: [],
|
||||
options: [
|
||||
{ label: "Action", value: "8" },
|
||||
{ label: "Adult", value: "280" },
|
||||
{ label: "Adventure", value: "13" },
|
||||
{ label: "Comedy", value: "17" },
|
||||
{ label: "Drama", value: "9" },
|
||||
{ label: "Ecchi", value: "292" },
|
||||
{ label: "Fantasy", value: "5" },
|
||||
{ label: "Gender Bender", value: "168" },
|
||||
{ label: "Harem", value: "3" },
|
||||
{ label: "Historical", value: "330" },
|
||||
{ label: "Horror", value: "343" },
|
||||
{ label: "Josei", value: "324" },
|
||||
{ label: "Martial Arts", value: "14" },
|
||||
{ label: "Mature", value: "4" },
|
||||
{ label: "Mecha", value: "10" },
|
||||
{ label: "Mystery", value: "245" },
|
||||
{ label: "Psychoical", value: "486" },
|
||||
{ label: "Romance", value: "15" },
|
||||
{ label: "School Life", value: "6" },
|
||||
{ label: "Sci-fi", value: "11" },
|
||||
{ label: "Seinen", value: "18" },
|
||||
{ label: "Shoujo", value: "157" },
|
||||
{ label: "Shoujo Ai", value: "851" },
|
||||
{ label: "Shounen", value: "12" },
|
||||
{ label: "Shounen Ai", value: "1692" },
|
||||
{ label: "Slice of Life", value: "7" },
|
||||
{ label: "Smut", value: "281" },
|
||||
{ label: "Sports", value: "1357" },
|
||||
{ label: "Supernatural", value: "16" },
|
||||
{ label: "Tragedy", value: "132" },
|
||||
{ label: "Wuxia", value: "479" },
|
||||
{ label: "Xianxia", value: "480" },
|
||||
{ label: "Xuanhuan", value: "3954" },
|
||||
{ label: "Yaoi", value: "560" },
|
||||
{ label: "Yuri", value: "922" },
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
NovelUpdates.prototype.getPopularNovelsUrl = function (page, _a) {
|
||||
var _b, _c, _d;
|
||||
var showLatestNovels = _a.showLatestNovels, filters = _a.filters;
|
||||
var url = "".concat(this.baseUrl).concat(filters
|
||||
? "series-finder"
|
||||
: showLatestNovels
|
||||
? "latest-series"
|
||||
: "series-ranking", "/");
|
||||
if (!filters) {
|
||||
url += "?rank=week";
|
||||
}
|
||||
else {
|
||||
url += "?sf=1";
|
||||
}
|
||||
if (filters) {
|
||||
if (Array.isArray(filters.novelType) && ((_b = filters.novelType) === null || _b === void 0 ? void 0 : _b.length)) {
|
||||
url += "&nt=" + (filters === null || filters === void 0 ? void 0 : filters.novelType.join(","));
|
||||
}
|
||||
if (Array.isArray(filters.genres) && ((_c = filters.genres) === null || _c === void 0 ? void 0 : _c.length)) {
|
||||
url += "&gi=" + (filters === null || filters === void 0 ? void 0 : filters.genres.join(",")) + "&mgi=and";
|
||||
}
|
||||
if (Array.isArray(filters.language) && ((_d = filters.language) === null || _d === void 0 ? void 0 : _d.length)) {
|
||||
url += "&org=" + (filters === null || filters === void 0 ? void 0 : filters.language.join(","));
|
||||
}
|
||||
if (filters.storyStatus) {
|
||||
url += "&ss=" + (filters === null || filters === void 0 ? void 0 : filters.storyStatus);
|
||||
}
|
||||
}
|
||||
if (!(filters === null || filters === void 0 ? void 0 : filters.sort)) {
|
||||
url += "&sort=" + "sdate";
|
||||
}
|
||||
else {
|
||||
url += "&sort=" + (filters === null || filters === void 0 ? void 0 : filters.sort);
|
||||
}
|
||||
if (!(filters === null || filters === void 0 ? void 0 : filters.order)) {
|
||||
url += "&order=" + "desc";
|
||||
}
|
||||
else {
|
||||
url += "&order=" + (filters === null || filters === void 0 ? void 0 : filters.order);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
NovelUpdates.prototype.popularNovels = function (pageNo, _a) {
|
||||
var showLatestNovels = _a.showLatestNovels, filters = _a.filters;
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, result, body, loadedCheerio, novels;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
url = this.getPopularNovelsUrl(pageNo, { showLatestNovels: showLatestNovels, filters: filters });
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url)];
|
||||
case 1:
|
||||
result = _b.sent();
|
||||
return [4 /*yield*/, result.text()];
|
||||
case 2:
|
||||
body = _b.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novels = [];
|
||||
loadedCheerio("div.search_main_box_nu").each(function (idx, ele) {
|
||||
var novelCover = loadedCheerio(ele).find("img").attr("src");
|
||||
var novelName = loadedCheerio(ele).find(".search_title > a").text();
|
||||
var novelUrl = loadedCheerio(ele)
|
||||
.find(".search_title > a")
|
||||
.attr("href");
|
||||
if (!novelUrl)
|
||||
return;
|
||||
var novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
novels.push(novel);
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelUpdates.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, result, body, loadedCheerio, novel, type, summary, chapter, novelId, formData, link, text;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = novelUrl;
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url)];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
return [4 /*yield*/, result.text()];
|
||||
case 2:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novel = {
|
||||
url: url,
|
||||
chapters: [],
|
||||
};
|
||||
novel.name = loadedCheerio(".seriestitlenu").text();
|
||||
novel.cover = loadedCheerio(".seriesimg > img").attr("src");
|
||||
novel.author = loadedCheerio("#showauthors").text().trim();
|
||||
novel.genres = loadedCheerio("#seriesgenre")
|
||||
.children("a")
|
||||
.map(function (i, el) { return loadedCheerio(el).text(); })
|
||||
.toArray()
|
||||
.join(",");
|
||||
novel.status = loadedCheerio("#editstatus").text().includes("Ongoing")
|
||||
? "Ongoing"
|
||||
: "Completed";
|
||||
type = loadedCheerio("#showtype").text().trim();
|
||||
summary = loadedCheerio("#editdescription").text().trim();
|
||||
novel.summary = summary + "\n\nType: ".concat(type);
|
||||
chapter = [];
|
||||
novelId = loadedCheerio("input#mypostid").attr("value");
|
||||
formData = new FormData();
|
||||
formData.append("action", "nd_getchapters");
|
||||
formData.append("mygrr", "0");
|
||||
formData.append("mypostid", novelId);
|
||||
link = "https://www.novelupdates.com/wp-admin/admin-ajax.php";
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(link, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
}).then(function (data) { return data.text(); })];
|
||||
case 3:
|
||||
text = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(text);
|
||||
loadedCheerio("li.sp_li_chp").each(function () {
|
||||
var chapterName = loadedCheerio(this).text().trim();
|
||||
var releaseDate = null;
|
||||
var chapterUrl = "https:" +
|
||||
loadedCheerio(this).find("a").first().next().attr("href");
|
||||
chapter.push({
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
url: chapterUrl,
|
||||
});
|
||||
});
|
||||
novel.chapters = chapter.reverse();
|
||||
return [2 /*return*/, novel];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelUpdates.prototype.getLocation = function (href) {
|
||||
var match = href.match(/^(https?:)\/\/(([^:/?#]*)(?::([0-9]+))?)([/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/);
|
||||
return match && "".concat(match[1], "//").concat(match[3]);
|
||||
};
|
||||
NovelUpdates.prototype.parseChapter = function (chapterUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var chapterText, result, body, loadedCheerio, isWuxiaWorld, isBlogspot, isTumblr, isWattpad, isLightNovelsTls, isiNovelTranslation, isTravisTranslation, isWordPressStr, isWordPress, isRainOfSnow, isWebNovel, isHostedNovel, isScribbleHub, bloatClasses, tags;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
chapterText = "";
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl)];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
return [4 /*yield*/, result.text()];
|
||||
case 2:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
isWuxiaWorld = chapterUrl.toLowerCase().includes("wuxiaworld");
|
||||
isBlogspot = chapterUrl.toLowerCase().includes("blogspot");
|
||||
isTumblr = chapterUrl.toLowerCase().includes("tumblr");
|
||||
isWattpad = chapterUrl.toLowerCase().includes("wattpad");
|
||||
isLightNovelsTls = chapterUrl
|
||||
.toLowerCase()
|
||||
.includes("lightnovelstranslations");
|
||||
isiNovelTranslation = chapterUrl
|
||||
.toLowerCase()
|
||||
.includes("inoveltranslation");
|
||||
isTravisTranslation = chapterUrl
|
||||
.toLowerCase()
|
||||
.includes("travistranslations");
|
||||
isWordPressStr = loadedCheerio('meta[name="generator"]').attr("content") ||
|
||||
loadedCheerio("footer").text();
|
||||
isWordPress = false;
|
||||
if (isWordPressStr) {
|
||||
isWordPress =
|
||||
isWordPressStr.toLowerCase().includes("wordpress") ||
|
||||
isWordPressStr.includes("Site Kit by Google") ||
|
||||
loadedCheerio(".powered-by")
|
||||
.text()
|
||||
.toLowerCase()
|
||||
.includes("wordpress");
|
||||
}
|
||||
isRainOfSnow = chapterUrl.toLowerCase().includes("rainofsnow");
|
||||
isWebNovel = chapterUrl.toLowerCase().includes("webnovel");
|
||||
isHostedNovel = chapterUrl.toLowerCase().includes("hostednovel");
|
||||
isScribbleHub = chapterUrl.toLowerCase().includes("scribblehub");
|
||||
if (isWuxiaWorld) {
|
||||
chapterText = loadedCheerio("#chapter-content").html();
|
||||
}
|
||||
else if (isRainOfSnow) {
|
||||
chapterText = loadedCheerio("div.content").html();
|
||||
}
|
||||
else if (isTumblr) {
|
||||
chapterText = loadedCheerio(".post").html();
|
||||
}
|
||||
else if (isBlogspot) {
|
||||
loadedCheerio(".post-share-buttons").remove();
|
||||
chapterText = loadedCheerio(".entry-content").html();
|
||||
}
|
||||
else if (isHostedNovel) {
|
||||
chapterText = loadedCheerio(".chapter").html();
|
||||
}
|
||||
else if (isScribbleHub) {
|
||||
chapterText = loadedCheerio("div.chp_raw").html();
|
||||
}
|
||||
else if (isWattpad) {
|
||||
chapterText = loadedCheerio(".container pre").html();
|
||||
}
|
||||
else if (isTravisTranslation) {
|
||||
chapterText = loadedCheerio(".reader-content").html();
|
||||
}
|
||||
else if (isLightNovelsTls) {
|
||||
chapterText = loadedCheerio(".text_story").html();
|
||||
}
|
||||
else if (isiNovelTranslation) {
|
||||
chapterText = loadedCheerio(".chakra-skeleton").html();
|
||||
}
|
||||
else if (isWordPress) {
|
||||
bloatClasses = [
|
||||
".c-ads",
|
||||
"#madara-comments",
|
||||
"#comments",
|
||||
".content-comments",
|
||||
".sharedaddy",
|
||||
".wp-dark-mode-switcher",
|
||||
".wp-next-post-navi",
|
||||
".wp-block-buttons",
|
||||
".wp-block-columns",
|
||||
".post-cats",
|
||||
".sidebar",
|
||||
".author-avatar",
|
||||
".ezoic-ad",
|
||||
];
|
||||
bloatClasses.map(function (tag) { return loadedCheerio(tag).remove(); });
|
||||
chapterText =
|
||||
loadedCheerio(".entry-content").html() ||
|
||||
loadedCheerio(".single_post").html() ||
|
||||
loadedCheerio(".post-entry").html() ||
|
||||
loadedCheerio(".main-content").html() ||
|
||||
loadedCheerio("article.post").html() ||
|
||||
loadedCheerio(".content").html() ||
|
||||
loadedCheerio("#content").html() ||
|
||||
loadedCheerio(".page-body").html() ||
|
||||
loadedCheerio(".td-page-content").html();
|
||||
}
|
||||
else if (isWebNovel) {
|
||||
chapterText = loadedCheerio(".cha-words").html();
|
||||
if (!chapterText) {
|
||||
chapterText = loadedCheerio("._content").html();
|
||||
}
|
||||
}
|
||||
else {
|
||||
tags = ["nav", "header", "footer", ".hidden"];
|
||||
tags.map(function (tag) { return loadedCheerio(tag).remove(); });
|
||||
chapterText = loadedCheerio("body").html();
|
||||
}
|
||||
if (chapterText) {
|
||||
/**
|
||||
* Convert relative urls to absolute
|
||||
*/
|
||||
chapterText = chapterText.replace(/href="\//g, "href=\"".concat(this.getLocation(chapterUrl), "/"));
|
||||
}
|
||||
return [2 /*return*/, chapterText];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelUpdates.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, result, body, loadedCheerio, novels;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = "https://www.novelupdates.com/?s=" +
|
||||
searchTerm +
|
||||
"&post_type=seriesplans";
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url)];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
return [4 /*yield*/, result.text()];
|
||||
case 2:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novels = [];
|
||||
loadedCheerio("div.search_main_box_nu").each(function () {
|
||||
var _a;
|
||||
var novelCover = loadedCheerio(this).find("img").attr("src");
|
||||
var novelName = loadedCheerio(this).find(".search_title > a").text();
|
||||
var novelUrl = (_a = loadedCheerio(this)
|
||||
.find(".search_title > a")
|
||||
.attr("href")) === null || _a === void 0 ? void 0 : _a.split("/")[4];
|
||||
if (!novelUrl)
|
||||
return;
|
||||
novels.push({
|
||||
name: novelName,
|
||||
url: novelUrl,
|
||||
cover: novelCover,
|
||||
});
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
NovelUpdates.prototype.fetchImage = function (url) {
|
||||
return (0, fetch_1.fetchFile)(url);
|
||||
};
|
||||
return NovelUpdates;
|
||||
}());
|
||||
exports.default = new NovelUpdates();
|
||||
@@ -0,0 +1,192 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var cheerio_1 = require("cheerio");
|
||||
var fetch_1 = require("@libs/fetch");
|
||||
var WuxiaWorld = /** @class */ (function () {
|
||||
function WuxiaWorld() {
|
||||
this.id = "wuxiaworld";
|
||||
this.name = "Wuxia World";
|
||||
this.icon = "src/en/wuxiaworld/icon.png";
|
||||
this.site = "https://www.wuxiaworld.com/";
|
||||
this.version = "0.5.0";
|
||||
this.baseUrl = this.site;
|
||||
}
|
||||
WuxiaWorld.prototype.popularNovels = function (pageNo, options) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var link, result, data, novels;
|
||||
var _this = this;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
link = "".concat(this.baseUrl, "api/novels");
|
||||
return [4 /*yield*/, fetch(link)];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
return [4 /*yield*/, result.json()];
|
||||
case 2:
|
||||
data = _a.sent();
|
||||
novels = [];
|
||||
data.items.map(function (novel) {
|
||||
var name = novel.name;
|
||||
var cover = novel.coverUrl;
|
||||
var url = _this.baseUrl + "novel/" + novel.slug + "/";
|
||||
novels.push({
|
||||
name: name,
|
||||
cover: cover,
|
||||
url: url,
|
||||
});
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
WuxiaWorld.prototype.parseNovelAndChapters = function (novelUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var url, result, body, loadedCheerio, novel, genres, chapter;
|
||||
var _this = this;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
url = novelUrl;
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url)];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
return [4 /*yield*/, result.text()];
|
||||
case 2:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
novel = {
|
||||
url: url,
|
||||
chapters: [],
|
||||
};
|
||||
novel.name = loadedCheerio("h1.line-clamp-2").text();
|
||||
novel.cover = loadedCheerio("img.absolute").attr("src");
|
||||
novel.summary = loadedCheerio("div.flex-col:nth-child(4) > div > div > span > span")
|
||||
.text()
|
||||
.trim();
|
||||
novel.author = loadedCheerio("div.MuiGrid-container > div > div > div")
|
||||
.filter(function () {
|
||||
return loadedCheerio(this).text().trim() === "Author:";
|
||||
})
|
||||
.next()
|
||||
.text();
|
||||
genres = [];
|
||||
loadedCheerio("a.MuiLink-underlineNone").each(function () {
|
||||
genres.push(loadedCheerio(this).find("div > div").text());
|
||||
});
|
||||
novel.genres = genres.join(",");
|
||||
novel.status = loadedCheerio("div.font-set-b10")
|
||||
.text();
|
||||
chapter = [];
|
||||
loadedCheerio("div.border-b.border-gray-line-base").each(function (idx, ele) {
|
||||
var _a;
|
||||
var name = loadedCheerio(ele)
|
||||
.find("a > div > div > div > span")
|
||||
.text();
|
||||
var releaseTime = loadedCheerio(ele)
|
||||
.find("a > div > div > div > div > span")
|
||||
.text();
|
||||
var url = (_a = loadedCheerio(ele).find("a").attr("href")) === null || _a === void 0 ? void 0 : _a.slice(1);
|
||||
url = "".concat(_this.baseUrl).concat(url);
|
||||
chapter.push({ name: name, releaseTime: releaseTime, url: url });
|
||||
});
|
||||
novel.chapters = chapter;
|
||||
return [2 /*return*/, novel];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
WuxiaWorld.prototype.parseChapter = function (chapterUrl) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var result, body, loadedCheerio, chapterText;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4 /*yield*/, (0, fetch_1.fetchApi)(chapterUrl)];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
return [4 /*yield*/, result.text()];
|
||||
case 2:
|
||||
body = _a.sent();
|
||||
loadedCheerio = (0, cheerio_1.load)(body);
|
||||
loadedCheerio(".chapter-nav").remove();
|
||||
loadedCheerio("#chapter-content > script").remove();
|
||||
chapterText = loadedCheerio("#chapter-content").html() || '';
|
||||
return [2 /*return*/, chapterText];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
WuxiaWorld.prototype.searchNovels = function (searchTerm, pageNo) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var searchUrl, url, result, data, novels;
|
||||
var _this = this;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
searchUrl = "https://www.wuxiaworld.com/api/novels/search?query=";
|
||||
url = "".concat(searchUrl).concat(searchTerm);
|
||||
return [4 /*yield*/, (0, fetch_1.fetchApi)(url)];
|
||||
case 1:
|
||||
result = _a.sent();
|
||||
return [4 /*yield*/, result.json()];
|
||||
case 2:
|
||||
data = _a.sent();
|
||||
novels = [];
|
||||
data.items.map(function (novel) {
|
||||
var name = novel.name;
|
||||
var cover = novel.coverUrl;
|
||||
var url = _this.baseUrl + "novel/" + novel.slug + "/";
|
||||
novels.push({
|
||||
name: name,
|
||||
url: url,
|
||||
cover: cover,
|
||||
});
|
||||
});
|
||||
return [2 /*return*/, novels];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
WuxiaWorld.prototype.fetchImage = function (url) {
|
||||
return (0, fetch_1.fetchFile)(url);
|
||||
};
|
||||
return WuxiaWorld;
|
||||
}());
|
||||
exports.default = new WuxiaWorld();
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
@@ -5,7 +5,7 @@ import { Filters } from "@libs/filterInputs";
|
||||
|
||||
class NovelFull implements Plugin.PluginBase {
|
||||
id = "NF.me";
|
||||
name = "NovelFull";
|
||||
name = "NovelFullMe";
|
||||
site = "https://novelfull.me/";
|
||||
version = "1.0.0";
|
||||
filters?: Filters | undefined;
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
||||
|
||||
export const id = "NO.net";
|
||||
export const name = "novelsOnline";
|
||||
export const site = "https://novelsonline.net";
|
||||
export const icon = "src/coverNotAvailable.jpg";
|
||||
export const version = "1.0.0";
|
||||
|
||||
const pluginId = id;
|
||||
|
||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
||||
const result = await fetchApi(
|
||||
"https://novelsonline.net/sResults.php",
|
||||
{
|
||||
headers: {
|
||||
Accept: "*/*",
|
||||
"Accept-Language": "pl,en-US;q=0.7,en;q=0.3",
|
||||
"Content-Type":
|
||||
"application/x-www-form-urlencoded; charset=UTF-8",
|
||||
},
|
||||
method: "POST",
|
||||
body: "q=" + encodeURIComponent(searchTerm),
|
||||
},
|
||||
pluginId
|
||||
).then((res) => res.text());
|
||||
let $ = parseHTML(result);
|
||||
|
||||
const headers = $("li");
|
||||
return headers
|
||||
.map((i, h) => {
|
||||
const novelName = $(h).text();
|
||||
const novelUrl = $(h).find("a").attr("href");
|
||||
const novelCover = $(h).find("img").attr("src");
|
||||
|
||||
if (!novelUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
})
|
||||
.get()
|
||||
.filter((sr) => sr !== null);
|
||||
};
|
||||
|
||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
||||
async function (novelUrl) {
|
||||
let novel: Novel.instance = {
|
||||
url: novelUrl,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
const result = await fetchApi(novelUrl).then((res) => res.text());
|
||||
let $ = parseHTML(result);
|
||||
|
||||
novel.name = $("h1").text();
|
||||
novel.cover = $(".novel-cover").find("a > img").attr("src");
|
||||
novel.author = $(
|
||||
"div.novel-details > div:nth-child(5) > div.novel-detail-body"
|
||||
)
|
||||
.find("li")
|
||||
.map((_, el) => $(el).text())
|
||||
.get()
|
||||
.join(", ");
|
||||
|
||||
novel.genres = $(
|
||||
"div.novel-details > div:nth-child(2) > div.novel-detail-body"
|
||||
)
|
||||
.find("li")
|
||||
.map((_, el) => $(el).text())
|
||||
.get()
|
||||
.join(",");
|
||||
|
||||
novel.summary = $(
|
||||
"div.novel-right > div > div:nth-child(1) > div.novel-detail-body"
|
||||
).text();
|
||||
|
||||
novel.chapters = $("ul.chapter-chs > li > a")
|
||||
.map((_, el) => {
|
||||
const chapterUrl = $(el).attr("href");
|
||||
const chapterName = $(el).text();
|
||||
|
||||
return {
|
||||
name: chapterName,
|
||||
releaseTime: "",
|
||||
url: chapterUrl,
|
||||
} as Chapter.Item;
|
||||
})
|
||||
.get();
|
||||
|
||||
return novel;
|
||||
};
|
||||
|
||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
||||
return []; /** TO DO */
|
||||
};
|
||||
|
||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
||||
const result = await fetchApi(chapterUrl).then((res) => res.text());
|
||||
let loadedCheerio = parseHTML(result);
|
||||
|
||||
const chapterText = loadedCheerio("#contentall").html() || "";
|
||||
|
||||
return chapterText;
|
||||
};
|
||||
export const fetchImage: Plugin.fetchImage = fetchFile;
|
||||
@@ -0,0 +1,115 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Plugin } from "@typings/plugin";
|
||||
import { Filters } from "@libs/filterInputs";
|
||||
|
||||
class NovelsOnline implements Plugin.PluginBase {
|
||||
id = "NO.net";
|
||||
name = "novelsOnline";
|
||||
site = "https://novelsonline.net";
|
||||
icon = "src/coverNotAvailable.jpg";
|
||||
filters?: Filters | undefined;
|
||||
version = "1.0.0";
|
||||
|
||||
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||
return []; /** TO DO */
|
||||
}
|
||||
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||
let novel: Plugin.SourceNovel = {
|
||||
url: novelUrl,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
const result = await fetchApi(novelUrl).then((res) => res.text());
|
||||
|
||||
let $ = parseHTML(result);
|
||||
|
||||
novel.name = $("h1").text();
|
||||
novel.cover = $(".novel-cover").find("a > img").attr("src");
|
||||
novel.author = $(
|
||||
"div.novel-details > div:nth-child(5) > div.novel-detail-body"
|
||||
)
|
||||
.find("li")
|
||||
.map((_, el) => $(el).text())
|
||||
.get()
|
||||
.join(", ");
|
||||
|
||||
novel.genres = $(
|
||||
"div.novel-details > div:nth-child(2) > div.novel-detail-body"
|
||||
)
|
||||
.find("li")
|
||||
.map((_, el) => $(el).text())
|
||||
.get()
|
||||
.join(",");
|
||||
|
||||
novel.summary = $(
|
||||
"div.novel-right > div > div:nth-child(1) > div.novel-detail-body"
|
||||
).text();
|
||||
|
||||
novel.chapters = $("ul.chapter-chs > li > a")
|
||||
.map((_, el) => {
|
||||
const chapterUrl = $(el).attr("href");
|
||||
const chapterName = $(el).text();
|
||||
|
||||
return {
|
||||
name: chapterName,
|
||||
releaseTime: "",
|
||||
url: chapterUrl,
|
||||
} as Plugin.ChapterItem;
|
||||
})
|
||||
.get();
|
||||
|
||||
return novel;
|
||||
}
|
||||
async parseChapter(chapterUrl: string): Promise<string> {
|
||||
const result = await fetchApi(chapterUrl).then((res) => res.text());
|
||||
let loadedCheerio = parseHTML(result);
|
||||
|
||||
const chapterText = loadedCheerio("#contentall").html() || "";
|
||||
|
||||
return chapterText;
|
||||
}
|
||||
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||
const result = await fetchApi(
|
||||
"https://novelsonline.net/sResults.php",
|
||||
{
|
||||
headers: {
|
||||
Accept: "*/*",
|
||||
"Accept-Language": "pl,en-US;q=0.7,en;q=0.3",
|
||||
"Content-Type":
|
||||
"application/x-www-form-urlencoded; charset=UTF-8",
|
||||
},
|
||||
method: "POST",
|
||||
body: "q=" + encodeURIComponent(searchTerm),
|
||||
},
|
||||
).then((res) => res.text());
|
||||
|
||||
let $ = parseHTML(result);
|
||||
|
||||
const headers = $("li");
|
||||
return headers
|
||||
.map((i, h) => {
|
||||
const novelName = $(h).text();
|
||||
const novelUrl = $(h).find("a").attr("href");
|
||||
const novelCover = $(h).find("img").attr("src");
|
||||
|
||||
if (!novelUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
})
|
||||
.get()
|
||||
.filter((sr) => sr !== null)
|
||||
}
|
||||
async fetchImage(url: string): Promise<string | undefined> {
|
||||
return fetchFile(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new NovelsOnline();
|
||||
@@ -1,213 +0,0 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
||||
|
||||
export const id = "lightnovelpub";
|
||||
export const name = "LightNovelPub";
|
||||
export const version = "1.0.0";
|
||||
export const icon = "src/en/lightnovelpub/icon.png";
|
||||
export const site = "https://www.lightnovelpub.com/";
|
||||
|
||||
|
||||
const baseUrl = site;
|
||||
const pluginId = id;
|
||||
|
||||
const headers = {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
|
||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
||||
const url = `${baseUrl}browse/all/popular/all/${page}`;
|
||||
|
||||
const body = await fetchApi(url, {}, pluginId).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
const novels: Novel.Item[] = [];
|
||||
|
||||
loadedCheerio(".novel-item.ads").remove();
|
||||
|
||||
loadedCheerio(".novel-item").each(function () {
|
||||
const novelName = loadedCheerio(this)
|
||||
.find(".novel-title")
|
||||
.text()
|
||||
.trim();
|
||||
const novelCover = loadedCheerio(this).find("img").attr("data-src");
|
||||
const novelUrl =
|
||||
baseUrl +
|
||||
loadedCheerio(this)
|
||||
.find(".novel-title > a")
|
||||
.attr("href")
|
||||
?.substring(1);
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
||||
async function (novelUrl) {
|
||||
const url = novelUrl;
|
||||
const body = await fetchApi(url, {}, pluginId).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
const novel: Novel.instance = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.name = loadedCheerio("h1.novel-title").text().trim();
|
||||
|
||||
novel.cover = loadedCheerio("figure.cover > img").attr("data-src");
|
||||
|
||||
novel.genres = "";
|
||||
|
||||
loadedCheerio(".categories > ul > li > a").each(function () {
|
||||
novel.genres += loadedCheerio(this).text() + ",";
|
||||
});
|
||||
|
||||
novel.genres = novel.genres.slice(0, -1);
|
||||
|
||||
loadedCheerio("div.header-stats > span").each(function () {
|
||||
if (loadedCheerio(this).find("small").text() === "Status") {
|
||||
novel.status = loadedCheerio(this).find("strong").text();
|
||||
}
|
||||
});
|
||||
|
||||
novel.author = loadedCheerio(".author > a > span").text();
|
||||
|
||||
novel.summary = loadedCheerio(".summary > .content").text().trim();
|
||||
|
||||
const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
|
||||
|
||||
let lastPage = 1;
|
||||
|
||||
lastPage = parseInt(
|
||||
loadedCheerio(
|
||||
"#novel > header > div.header-body.container > div.novel-info > div.header-stats > span:nth-child(1) > strong"
|
||||
)
|
||||
.text()
|
||||
?.trim()
|
||||
);
|
||||
|
||||
lastPage = Math.ceil(lastPage / 100);
|
||||
|
||||
async function getChapters() {
|
||||
let chapter: Chapter.Item[] = [];
|
||||
|
||||
for (let i = 1; i <= lastPage; i++) {
|
||||
const chaptersUrl = `${novelUrl}/chapters/page-${i}`;
|
||||
|
||||
const chaptersHtml = await fetchApi(
|
||||
chaptersUrl,
|
||||
{ headers: headers },
|
||||
pluginId
|
||||
).then((r) => r.text());
|
||||
|
||||
loadedCheerio = parseHTML(chaptersHtml);
|
||||
|
||||
loadedCheerio(".chapter-list li").each(function () {
|
||||
const chapterName = loadedCheerio(this)
|
||||
.find(".chapter-title")
|
||||
.text()
|
||||
.trim();
|
||||
|
||||
const releaseDate = loadedCheerio(this)
|
||||
.find(".chapter-update")
|
||||
.text()
|
||||
.trim();
|
||||
|
||||
const chapterUrl =
|
||||
baseUrl +
|
||||
loadedCheerio(this)
|
||||
.find("a")
|
||||
.attr("href")
|
||||
?.substring(1);
|
||||
|
||||
chapter.push({
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
url: chapterUrl,
|
||||
});
|
||||
});
|
||||
|
||||
await delay(1000);
|
||||
}
|
||||
|
||||
return chapter;
|
||||
}
|
||||
|
||||
novel.chapters = await getChapters();
|
||||
|
||||
return novel;
|
||||
};
|
||||
|
||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
||||
const body = await fetchApi(chapterUrl, {}, pluginId).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let chapterText = loadedCheerio("#chapter-container").html();
|
||||
|
||||
return chapterText;
|
||||
};
|
||||
|
||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
||||
const url = `${baseUrl}lnsearchlive`;
|
||||
const link = `${baseUrl}search`;
|
||||
const response = await fetchApi(link, {}, pluginId).then((r) => r.text());
|
||||
const token = parseHTML(response);
|
||||
let verifytoken = token("#novelSearchForm > input").attr("value");
|
||||
|
||||
let formData = new FormData();
|
||||
formData.append("inputContent", searchTerm);
|
||||
|
||||
const body = await fetchApi(
|
||||
url,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { LNRequestVerifyToken: verifytoken! },
|
||||
body: formData,
|
||||
},
|
||||
pluginId
|
||||
).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Novel.Item[] = [];
|
||||
|
||||
let results = JSON.parse(loadedCheerio("body").text());
|
||||
|
||||
loadedCheerio = parseHTML(results.resultview);
|
||||
|
||||
loadedCheerio(".novel-item").each(function () {
|
||||
const novelName = loadedCheerio(this)
|
||||
.find("h4.novel-title")
|
||||
.text()
|
||||
.trim();
|
||||
const novelCover = loadedCheerio(this).find("img").attr("src");
|
||||
const novelUrl =
|
||||
baseUrl + loadedCheerio(this).find("a").attr("href")?.substring(1);
|
||||
|
||||
novels.push({
|
||||
name: novelName,
|
||||
url: novelUrl,
|
||||
cover: novelCover,
|
||||
});
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
||||
return await fetchFile(url, { headers: headers });
|
||||
};
|
||||
@@ -0,0 +1,173 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Plugin } from "@typings/plugin";
|
||||
import { Filters } from "@libs/filterInputs";
|
||||
|
||||
|
||||
class LightNovelPub implements Plugin.PluginBase {
|
||||
id = "lightnovelpub";
|
||||
name = "LightNovelPub";
|
||||
version = "1.0.0";
|
||||
icon = "src/en/lightnovelpub/icon.png";
|
||||
filters?: Filters | undefined;
|
||||
site = "https://www.lightnovelpub.com/";
|
||||
baseUrl = this.site;
|
||||
headers = {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||
const url = `${this.baseUrl}browse/all/popular/all/${pageNo}`;
|
||||
|
||||
const body = await fetchApi(url).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
const novels: Plugin.NovelItem[] = [];
|
||||
|
||||
loadedCheerio(".novel-item.ads").remove();
|
||||
|
||||
loadedCheerio(".novel-item").each((idx, ele) => {
|
||||
const novelName = loadedCheerio(ele)
|
||||
.find(".novel-title")
|
||||
.text()
|
||||
.trim();
|
||||
const novelCover = loadedCheerio(ele).find("img").attr("data-src");
|
||||
const novelUrl =
|
||||
this.baseUrl +
|
||||
loadedCheerio(ele)
|
||||
.find(".novel-title > a")
|
||||
.attr("href")
|
||||
?.substring(1);
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||
const url = novelUrl;
|
||||
const body = await fetchApi(url).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
const novel: Plugin.SourceNovel = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.name = loadedCheerio("h1.novel-title").text().trim();
|
||||
|
||||
novel.cover = loadedCheerio("figure.cover > img").attr("data-src");
|
||||
|
||||
novel.genres = "";
|
||||
|
||||
loadedCheerio(".categories > ul > li > a").each(function () {
|
||||
novel.genres += loadedCheerio(this).text() + ",";
|
||||
});
|
||||
|
||||
novel.genres = novel.genres.slice(0, -1);
|
||||
|
||||
loadedCheerio("div.header-stats > span").each(function () {
|
||||
if (loadedCheerio(this).find("small").text() === "Status") {
|
||||
novel.status = loadedCheerio(this).find("strong").text();
|
||||
}
|
||||
});
|
||||
|
||||
novel.author = loadedCheerio(".author > a > span").text();
|
||||
|
||||
novel.summary = loadedCheerio(".summary > .content").text().trim();
|
||||
const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
|
||||
await delay(1000);
|
||||
const chapterListUrl = novelUrl + '/chapters?chorder=desc';
|
||||
|
||||
const chapterListBody = await fetchApi(chapterListUrl).then(r => r.text());
|
||||
const loadedChapterList = parseHTML(chapterListBody);
|
||||
|
||||
|
||||
const lastNumber = parseInt(
|
||||
loadedChapterList('ul.chapter-list > li:nth-child(1)').attr('data-chapterno') ?? '0'
|
||||
);
|
||||
const lastChapterUrl = loadedChapterList('ul.chapter-list > li:nth-child(1) > a').attr('href');
|
||||
|
||||
if(lastChapterUrl){
|
||||
const chapterUrlPrefix = this.baseUrl + lastChapterUrl.split('/').slice(1, -1).join('/');
|
||||
const chapters: Plugin.ChapterItem[] = [];
|
||||
for(let i = 1; i <= lastNumber; i++){
|
||||
chapters.push({
|
||||
chapterNumber: i,
|
||||
name: 'Chapter ' + i,
|
||||
url: chapterUrlPrefix + '/chapter-' + i,
|
||||
})
|
||||
}
|
||||
novel.chapters = chapters;
|
||||
}
|
||||
return novel;
|
||||
}
|
||||
async parseChapter(chapterUrl: string): Promise<string> {
|
||||
const body = await fetchApi(chapterUrl).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let chapterText = loadedCheerio("#chapter-container").html() || '';
|
||||
|
||||
return chapterText;
|
||||
}
|
||||
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||
const url = `${this.baseUrl}lnsearchlive`;
|
||||
const link = `${this.baseUrl}search`;
|
||||
const response = await fetchApi(link).then((r) => r.text());
|
||||
const token = parseHTML(response);
|
||||
let verifytoken = token("#novelSearchForm > input").attr("value");
|
||||
|
||||
let formData = new FormData();
|
||||
formData.append("inputContent", searchTerm);
|
||||
|
||||
const body = await fetchApi(
|
||||
url,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { LNRequestVerifyToken: verifytoken! },
|
||||
body: formData,
|
||||
},
|
||||
).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Plugin.NovelItem[] = [];
|
||||
|
||||
let results = JSON.parse(loadedCheerio("body").text());
|
||||
|
||||
loadedCheerio = parseHTML(results.resultview);
|
||||
|
||||
loadedCheerio(".novel-item").each((idx, ele) => {
|
||||
const novelName = loadedCheerio(ele)
|
||||
.find("h4.novel-title")
|
||||
.text()
|
||||
.trim();
|
||||
const novelCover = loadedCheerio(ele).find("img").attr("src");
|
||||
const novelUrl =
|
||||
this.baseUrl + loadedCheerio(ele).find("a").attr("href")?.substring(1);
|
||||
|
||||
novels.push({
|
||||
name: novelName,
|
||||
url: novelUrl,
|
||||
cover: novelCover,
|
||||
});
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
async fetchImage(url: string): Promise<string | undefined> {
|
||||
return fetchFile(url, {headers: this.headers});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new LightNovelPub();
|
||||
@@ -1,25 +1,23 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { 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";
|
||||
export const name = "Light Novel Updates";
|
||||
export const version = "1.0.0";
|
||||
export const icon = "src/en/lightnovelupdates/icon.png";
|
||||
export const site = "https://www.lightnovelupdates.com/";
|
||||
class LightNovelUpdates implements Plugin.PluginBase {
|
||||
id = "LightNovelUpdates";
|
||||
name = "Light Novel Updates";
|
||||
version = "1.0.0";
|
||||
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 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 body = await fetchApi(url).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Novel.Item[] = [];
|
||||
let novels: Plugin.NovelItem[] = [];
|
||||
|
||||
loadedCheerio(".page-item-detail").each(function () {
|
||||
const novelName = loadedCheerio(this).find(".h5 > a").text();
|
||||
@@ -38,17 +36,15 @@ export const popularNovels: Plugin.popularNovels = async function (page) {
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
||||
async function (novelUrl) {
|
||||
}
|
||||
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||
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);
|
||||
|
||||
const novel: Novel.instance = {
|
||||
const novel: Plugin.SourceNovel = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
@@ -82,7 +78,7 @@ export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
||||
|
||||
novel.summary = loadedCheerio("div.summary__content").text();
|
||||
|
||||
const chapter: Chapter.Item[] = [];
|
||||
const chapter: Plugin.ChapterItem[] = [];
|
||||
|
||||
const novelId = loadedCheerio(".rating-post-id").attr("value")!;
|
||||
|
||||
@@ -96,7 +92,6 @@ export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
||||
method: "POST",
|
||||
body: formData,
|
||||
},
|
||||
pluginId
|
||||
);
|
||||
const text = await data.text();
|
||||
|
||||
@@ -119,23 +114,20 @@ export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
||||
novel.chapters = chapter.reverse();
|
||||
|
||||
return novel;
|
||||
};
|
||||
}
|
||||
async parseChapter(chapterUrl: string): Promise<string> {
|
||||
const chapterText = 'unable to load';
|
||||
|
||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
||||
const chapterText = (await NovelUpdatesScraper.parseChapter(chapterUrl))
|
||||
.chapterText;
|
||||
return 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;
|
||||
};
|
||||
|
||||
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 body = await fetchApi(url).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Novel.Item[] = [];
|
||||
let novels: Plugin.NovelItem[] = [];
|
||||
|
||||
loadedCheerio(".c-tabs-item__content").each(function () {
|
||||
const novelName = loadedCheerio(this).find(".h4 > a").text();
|
||||
@@ -151,11 +143,11 @@ export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
||||
});
|
||||
});
|
||||
return novels;
|
||||
};
|
||||
}
|
||||
async fetchImage(url: string): Promise<string | undefined> {
|
||||
return fetchFile(url);
|
||||
}
|
||||
|
||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
||||
const headers = {
|
||||
Referer: baseUrl,
|
||||
};
|
||||
return await fetchFile(url, { headers: headers });
|
||||
};
|
||||
}
|
||||
|
||||
export default new LightNovelUpdates();
|
||||
@@ -0,0 +1,173 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Plugin } from "@typings/plugin";
|
||||
import { Filters } from "@libs/filterInputs";
|
||||
|
||||
|
||||
class LightNovelWorld implements Plugin.PluginBase {
|
||||
id = "lightnovelworld";
|
||||
name = "LightNovelWorld";
|
||||
site = "https://www.webnovelworld.org/";
|
||||
version = "1.0.0";
|
||||
filters?: Filters | undefined;
|
||||
icon = "src/en/lightnovelworld/icon.png";
|
||||
baseUrl = this.site;
|
||||
headers = {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||
const url = `${this.baseUrl}browse/all/popular/all/${pageNo}`;
|
||||
|
||||
const body = await fetchApi(url).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
const novels: Plugin.NovelItem[] = [];
|
||||
|
||||
loadedCheerio(".novel-item.ads").remove();
|
||||
|
||||
loadedCheerio(".novel-item").each((idx, ele) => {
|
||||
const novelName = loadedCheerio(ele)
|
||||
.find(".novel-title")
|
||||
.text()
|
||||
.trim();
|
||||
const novelCover = loadedCheerio(ele).find("img").attr("data-src");
|
||||
const novelUrl =
|
||||
this.baseUrl +
|
||||
loadedCheerio(ele)
|
||||
.find(".novel-title > a")
|
||||
.attr("href")
|
||||
?.substring(1);
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||
const url = novelUrl;
|
||||
const body = await fetchApi(url).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
const novel: Plugin.SourceNovel = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.name = loadedCheerio("h1.novel-title").text().trim();
|
||||
|
||||
novel.cover = loadedCheerio("figure.cover > img").attr("data-src");
|
||||
|
||||
novel.genres = "";
|
||||
|
||||
loadedCheerio(".categories > ul > li > a").each(function () {
|
||||
novel.genres += loadedCheerio(this).text() + ",";
|
||||
});
|
||||
|
||||
novel.genres = novel.genres.slice(0, -1);
|
||||
|
||||
loadedCheerio("div.header-stats > span").each(function () {
|
||||
if (loadedCheerio(this).find("small").text() === "Status") {
|
||||
novel.status = loadedCheerio(this).find("strong").text();
|
||||
}
|
||||
});
|
||||
|
||||
novel.author = loadedCheerio(".author > a > span").text();
|
||||
|
||||
novel.summary = loadedCheerio(".summary > .content").text().trim();
|
||||
const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
|
||||
await delay(1000);
|
||||
const chapterListUrl = novelUrl + '/chapters?chorder=desc';
|
||||
|
||||
const chapterListBody = await fetchApi(chapterListUrl).then(r => r.text());
|
||||
const loadedChapterList = parseHTML(chapterListBody);
|
||||
|
||||
|
||||
const lastNumber = parseInt(
|
||||
loadedChapterList('ul.chapter-list > li:nth-child(1)').attr('data-chapterno') ?? '0'
|
||||
);
|
||||
const lastChapterUrl = loadedChapterList('ul.chapter-list > li:nth-child(1) > a').attr('href');
|
||||
|
||||
if(lastChapterUrl){
|
||||
const chapterUrlPrefix = this.baseUrl + lastChapterUrl.split('/').slice(1, -1).join('/');
|
||||
const chapters: Plugin.ChapterItem[] = [];
|
||||
for(let i = 1; i <= lastNumber; i++){
|
||||
chapters.push({
|
||||
chapterNumber: i,
|
||||
name: 'Chapter ' + i,
|
||||
url: chapterUrlPrefix + '/chapter-' + i,
|
||||
})
|
||||
}
|
||||
novel.chapters = chapters;
|
||||
}
|
||||
return novel;
|
||||
}
|
||||
async parseChapter(chapterUrl: string): Promise<string> {
|
||||
const body = await fetchApi(chapterUrl).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let chapterText = loadedCheerio("#chapter-container").html() || '';
|
||||
|
||||
return chapterText;
|
||||
}
|
||||
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||
const url = `${this.baseUrl}lnsearchlive`;
|
||||
const link = `${this.baseUrl}search`;
|
||||
const response = await fetchApi(link).then((r) => r.text());
|
||||
const token = parseHTML(response);
|
||||
let verifytoken = token("#novelSearchForm > input").attr("value");
|
||||
|
||||
let formData = new FormData();
|
||||
formData.append("inputContent", searchTerm);
|
||||
|
||||
const body = await fetchApi(
|
||||
url,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { LNRequestVerifyToken: verifytoken! },
|
||||
body: formData,
|
||||
},
|
||||
).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Plugin.NovelItem[] = [];
|
||||
|
||||
let results = JSON.parse(loadedCheerio("body").text());
|
||||
|
||||
loadedCheerio = parseHTML(results.resultview);
|
||||
|
||||
loadedCheerio(".novel-item").each((idx, ele) => {
|
||||
const novelName = loadedCheerio(ele)
|
||||
.find("h4.novel-title")
|
||||
.text()
|
||||
.trim();
|
||||
const novelCover = loadedCheerio(ele).find("img").attr("src");
|
||||
const novelUrl =
|
||||
this.baseUrl + loadedCheerio(ele).find("a").attr("href")?.substring(1);
|
||||
|
||||
novels.push({
|
||||
name: novelName,
|
||||
url: novelUrl,
|
||||
cover: novelCover,
|
||||
});
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
async fetchImage(url: string): Promise<string | undefined> {
|
||||
return fetchFile(url, {headers: this.headers});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new LightNovelWorld();
|
||||
@@ -1,180 +0,0 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
||||
|
||||
const pluginId = "mtlnovel";
|
||||
const baseUrl = "https://www.mtlnovel.com/";
|
||||
|
||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
||||
const url = `${baseUrl}alltime-rank/page/${page}`;
|
||||
|
||||
const headers = {
|
||||
Referer: `${baseUrl}`,
|
||||
"User-Agent":
|
||||
"'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
|
||||
};
|
||||
|
||||
const body = await fetchApi(
|
||||
url,
|
||||
{
|
||||
method: "GET",
|
||||
headers: headers,
|
||||
},
|
||||
pluginId
|
||||
).then((result) => result.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Novel.Item[] = [];
|
||||
|
||||
loadedCheerio("div.box.wide").each(function () {
|
||||
const novelName = loadedCheerio(this)
|
||||
.find("a.list-title")
|
||||
.text()
|
||||
.slice(4);
|
||||
const novelCover = loadedCheerio(this).find("amp-img").attr("src");
|
||||
const novelUrl = loadedCheerio(this).find("a.list-title").attr("href");
|
||||
|
||||
if (!novelUrl) return;
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
||||
async function (novelUrl) {
|
||||
const url = novelUrl;
|
||||
|
||||
let headers = {
|
||||
Referer: `${baseUrl}alltime-rank/`,
|
||||
"User-Agent":
|
||||
"'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
|
||||
};
|
||||
|
||||
const body = await fetchApi(
|
||||
url,
|
||||
{
|
||||
method: "GET",
|
||||
headers: headers,
|
||||
},
|
||||
pluginId
|
||||
).then((result) => result.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
const novel: Novel.instance = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.name = loadedCheerio("h1.entry-title").text();
|
||||
|
||||
novel.cover = loadedCheerio(".nov-head > amp-img").attr("src");
|
||||
|
||||
novel.summary = loadedCheerio("div.desc > h2").next().text().trim();
|
||||
|
||||
novel.author = loadedCheerio("#author").text();
|
||||
|
||||
novel.status = loadedCheerio("#status").text();
|
||||
|
||||
novel.genres = loadedCheerio("#genre").text().replace(/\s+/g, "");
|
||||
|
||||
const chapterListUrl = url + "chapter-list/";
|
||||
|
||||
async function getChapters() {
|
||||
const listResult = await fetch(chapterListUrl);
|
||||
const listBody = await listResult.text();
|
||||
|
||||
loadedCheerio = parseHTML(listBody);
|
||||
|
||||
let chapter: Chapter.Item[] = [];
|
||||
|
||||
loadedCheerio("div.ch-list")
|
||||
.find("a.ch-link")
|
||||
.each(function () {
|
||||
const chapterName = loadedCheerio(this)
|
||||
.text()
|
||||
.replace("~ ", "");
|
||||
const releaseDate = null;
|
||||
const chapterUrl = loadedCheerio(this).attr("href");
|
||||
|
||||
chapter.push({
|
||||
url: chapterUrl,
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
});
|
||||
});
|
||||
return chapter.reverse();
|
||||
}
|
||||
|
||||
novel.chapters = await getChapters();
|
||||
|
||||
return novel;
|
||||
};
|
||||
|
||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
||||
const body = await fetchApi(chapterUrl, {}, pluginId).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
const chapterText = loadedCheerio("div.par").html();
|
||||
|
||||
return chapterText;
|
||||
};
|
||||
|
||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
||||
const searchUrl =
|
||||
baseUrl +
|
||||
"wp-admin/admin-ajax.php?action=autosuggest&q=" +
|
||||
searchTerm +
|
||||
"&__amp_source_origin=https%3A%2F%2Fwww.mtlnovel.com";
|
||||
|
||||
const res = await fetch(searchUrl);
|
||||
const result = await res.json();
|
||||
|
||||
let novels: Novel.Item[] = [];
|
||||
|
||||
result.items[0].results.map((item) => {
|
||||
const novelName = item.title.replace(/<\/?strong>/g, "");
|
||||
const novelCover = item.thumbnail;
|
||||
const novelUrl = item.permalink.replace(
|
||||
"https://www.mtlnovel.com/",
|
||||
""
|
||||
);
|
||||
|
||||
const novel = { name: novelName, cover: novelCover, url: novelUrl };
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
||||
const headers = {
|
||||
Referer: baseUrl,
|
||||
};
|
||||
return await fetchFile(url, { headers: headers });
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
id: pluginId,
|
||||
name: "MTL Novel",
|
||||
version: "1.0.0",
|
||||
icon: "src/en/mtlnovel/icon.png",
|
||||
site: baseUrl,
|
||||
protected: false,
|
||||
fetchImage,
|
||||
popularNovels,
|
||||
parseNovelAndChapters,
|
||||
parseChapter,
|
||||
searchNovels,
|
||||
};
|
||||
@@ -0,0 +1,155 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Plugin } from "@typings/plugin";
|
||||
import { Filters } from "@libs/filterInputs";
|
||||
|
||||
class MTLNovel implements Plugin.PluginBase {
|
||||
id = "mtlnovel";
|
||||
name = "MTL Novel";
|
||||
version = "1.0.0";
|
||||
icon = "src/en/mtlnovel/icon.png";
|
||||
site = "https://www.mtlnovel.com/";
|
||||
baseUrl = this.site;
|
||||
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||
const url = `${this.baseUrl}alltime-rank/page/${pageNo}`;
|
||||
const body = await fetchApi(url).then((result) => result.text());
|
||||
console.log(body);
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Plugin.NovelItem[] = [];
|
||||
|
||||
loadedCheerio("div.box.wide").each(function () {
|
||||
const novelName = loadedCheerio(this)
|
||||
.find("a.list-title")
|
||||
.text()
|
||||
.slice(4);
|
||||
const novelCover = loadedCheerio(this).find("amp-img").attr("src");
|
||||
const novelUrl = loadedCheerio(this).find("a.list-title").attr("href");
|
||||
|
||||
if (!novelUrl) return;
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||
const url = novelUrl;
|
||||
|
||||
let headers = {
|
||||
Referer: `${this.baseUrl}alltime-rank/`,
|
||||
};
|
||||
|
||||
const body = await fetchApi(
|
||||
url,
|
||||
{
|
||||
method: "GET",
|
||||
headers: headers,
|
||||
},
|
||||
).then((result) => result.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
const novel: Plugin.SourceNovel = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.name = loadedCheerio("h1.entry-title").text();
|
||||
|
||||
novel.cover = loadedCheerio(".nov-head > amp-img").attr("src");
|
||||
|
||||
novel.summary = loadedCheerio("div.desc > h2").next().text().trim();
|
||||
|
||||
novel.author = loadedCheerio("#author").text();
|
||||
|
||||
novel.status = loadedCheerio("#status").text();
|
||||
|
||||
novel.genres = loadedCheerio("#genre").text().replace(/\s+/g, "");
|
||||
|
||||
const chapterListUrl = url + "chapter-list/";
|
||||
|
||||
async function getChapters() {
|
||||
const listResult = await fetch(chapterListUrl);
|
||||
const listBody = await listResult.text();
|
||||
|
||||
loadedCheerio = parseHTML(listBody);
|
||||
|
||||
let chapter: Plugin.ChapterItem[] = [];
|
||||
|
||||
loadedCheerio("div.ch-list")
|
||||
.find("a.ch-link")
|
||||
.each(function () {
|
||||
const chapterName = loadedCheerio(this)
|
||||
.text()
|
||||
.replace("~ ", "");
|
||||
const releaseDate = null;
|
||||
const chapterUrl = loadedCheerio(this).attr("href");
|
||||
if (chapterUrl) {
|
||||
chapter.push({
|
||||
url: chapterUrl,
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
});
|
||||
}
|
||||
});
|
||||
return chapter.reverse();
|
||||
}
|
||||
|
||||
novel.chapters = await getChapters();
|
||||
|
||||
return novel;
|
||||
}
|
||||
async parseChapter(chapterUrl: string): Promise<string> {
|
||||
const body = await fetchApi(chapterUrl).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
const chapterText = loadedCheerio("div.par").html() || '';
|
||||
|
||||
return chapterText;
|
||||
}
|
||||
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||
const searchUrl =
|
||||
this.baseUrl +
|
||||
"wp-admin/admin-ajax.php?action=autosuggest&q=" +
|
||||
searchTerm +
|
||||
"&__amp_source_origin=https%3A%2F%2Fwww.mtlnovel.com";
|
||||
|
||||
const res = await fetch(searchUrl);
|
||||
const result = await res.json();
|
||||
|
||||
let novels: Plugin.NovelItem[] = [];
|
||||
interface SearchEntry {
|
||||
title: string;
|
||||
thumbnail: string;
|
||||
permalink: string;
|
||||
}
|
||||
result.items[0].results.map((item: SearchEntry) => {
|
||||
const novelName = item.title.replace(/<\/?strong>/g, "");
|
||||
const novelCover = item.thumbnail;
|
||||
const novelUrl = item.permalink.replace(
|
||||
"https://www.mtlnovel.com/",
|
||||
""
|
||||
);
|
||||
|
||||
const novel = { name: novelName, cover: novelCover, url: novelUrl };
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
async fetchImage(url: string): Promise<string | undefined> {
|
||||
return fetchFile(url);
|
||||
}
|
||||
|
||||
}
|
||||
export default new MTLNovel();
|
||||
@@ -1,133 +0,0 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
||||
|
||||
export const id = "mtlreader";
|
||||
export const name = "MTL Reader";
|
||||
export const version = "1.0.0";
|
||||
export const icon = "src/en/mtlreader/icon.png";
|
||||
export const site = "https://mtlreader.com/";
|
||||
|
||||
|
||||
const pluginId = id;
|
||||
const baseUrl = site;
|
||||
|
||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
||||
const url = `${baseUrl}novels?page=${page}`;
|
||||
|
||||
const body = await fetchApi(url, {}, pluginId).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Novel.Item[] = [];
|
||||
|
||||
loadedCheerio(".col-md-4.col-sm-4").each(function () {
|
||||
const novelName = loadedCheerio(this).find("h5").text();
|
||||
const novelCover = loadedCheerio(this).find("img").attr("src");
|
||||
const novelUrl = loadedCheerio(this).find("h5 > a").attr("href");
|
||||
|
||||
if (!novelUrl) return;
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
||||
async function (novelUrl) {
|
||||
const url = novelUrl;
|
||||
|
||||
const body = await fetchApi(url, {}, pluginId).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
const novel: Novel.instance = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.name = loadedCheerio(".agent-title").text().trim();
|
||||
|
||||
novel.cover = loadedCheerio(".agent-p-img > img").attr("src");
|
||||
|
||||
novel.summary = loadedCheerio("#editdescription").text().trim();
|
||||
|
||||
novel.author = loadedCheerio("i.fa-user")
|
||||
.parent()
|
||||
.text()
|
||||
.replace("Author: ", "")
|
||||
.trim();
|
||||
|
||||
let chapter: Chapter.Item[] = [];
|
||||
|
||||
loadedCheerio("tr.spaceUnder").each(function () {
|
||||
const chapterName = loadedCheerio(this).find("a").text().trim();
|
||||
const releaseDate = null;
|
||||
const chapterUrl = loadedCheerio(this).find("a").attr("href");
|
||||
|
||||
if (!chapterUrl) return;
|
||||
|
||||
chapter.push({
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
url: chapterUrl,
|
||||
});
|
||||
});
|
||||
|
||||
novel.chapters = chapter;
|
||||
|
||||
return novel;
|
||||
};
|
||||
|
||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
||||
const body = await fetchApi(chapterUrl, {}, pluginId).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
loadedCheerio(".container ins,script,p.mtlreader").remove();
|
||||
const chapterText = loadedCheerio(".container").html();
|
||||
|
||||
return chapterText;
|
||||
};
|
||||
|
||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
||||
const url = `${baseUrl}search?input=${searchTerm}`;
|
||||
|
||||
const body = await fetchApi(url, {}, pluginId).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Novel.Item[] = [];
|
||||
|
||||
loadedCheerio(".col-md-4.col-sm-4").each(function () {
|
||||
const novelName = loadedCheerio(this).find("h5").text();
|
||||
const novelCover = loadedCheerio(this).find("img").attr("src");
|
||||
const novelUrl = loadedCheerio(this).find("h5 > a").attr("href");
|
||||
|
||||
if (!novelUrl) return;
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
||||
const headers = {
|
||||
Referer: baseUrl,
|
||||
};
|
||||
return await fetchFile(url, { headers: headers });
|
||||
};
|
||||
@@ -0,0 +1,126 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Plugin } from "@typings/plugin";
|
||||
import { Filters } from "@libs/filterInputs";
|
||||
|
||||
class MTLReader implements Plugin.PluginBase {
|
||||
id = "mtlreader";
|
||||
name = "MTL Reader";
|
||||
version = "1.0.0";
|
||||
icon = "src/en/mtlreader/icon.png";
|
||||
site = "https://mtlreader.com/";
|
||||
baseUrl = this.site;
|
||||
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||
const url = `${this.baseUrl}novels?page=${pageNo}`;
|
||||
|
||||
const body = await fetchApi(url).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Plugin.NovelItem[] = [];
|
||||
|
||||
loadedCheerio(".col-md-4.col-sm-4").each(function () {
|
||||
const novelName = loadedCheerio(this).find("h5").text();
|
||||
const novelCover = loadedCheerio(this).find("img").attr("src");
|
||||
const novelUrl = loadedCheerio(this).find("h5 > a").attr("href");
|
||||
|
||||
if (!novelUrl) return;
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||
const url = novelUrl;
|
||||
|
||||
const body = await fetchApi(url).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
const novel: Plugin.SourceNovel = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.name = loadedCheerio(".agent-title").text().trim();
|
||||
|
||||
novel.cover = loadedCheerio(".agent-p-img > img").attr("src");
|
||||
|
||||
novel.summary = loadedCheerio("#editdescription").text().trim();
|
||||
|
||||
novel.author = loadedCheerio("i.fa-user")
|
||||
.parent()
|
||||
.text()
|
||||
.replace("Author: ", "")
|
||||
.trim();
|
||||
|
||||
let chapter: Plugin.ChapterItem[] = [];
|
||||
|
||||
loadedCheerio("tr.spaceUnder").each(function () {
|
||||
const chapterName = loadedCheerio(this).find("a").text().trim();
|
||||
const releaseDate = null;
|
||||
const chapterUrl = loadedCheerio(this).find("a").attr("href");
|
||||
|
||||
if (!chapterUrl) return;
|
||||
|
||||
chapter.push({
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
url: chapterUrl,
|
||||
});
|
||||
});
|
||||
|
||||
novel.chapters = chapter;
|
||||
|
||||
return novel;
|
||||
}
|
||||
async parseChapter(chapterUrl: string): Promise<string> {
|
||||
const body = await fetchApi(chapterUrl).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
loadedCheerio(".container ins,script,p.mtlreader").remove();
|
||||
const chapterText = loadedCheerio(".container").html() || '';
|
||||
|
||||
return chapterText;
|
||||
}
|
||||
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||
const url = `${this.baseUrl}search?input=${searchTerm}`;
|
||||
|
||||
const body = await fetchApi(url).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Plugin.NovelItem[] = [];
|
||||
|
||||
loadedCheerio(".col-md-4.col-sm-4").each(function () {
|
||||
const novelName = loadedCheerio(this).find("h5").text();
|
||||
const novelCover = loadedCheerio(this).find("img").attr("src");
|
||||
const novelUrl = loadedCheerio(this).find("h5 > a").attr("href");
|
||||
|
||||
if (!novelUrl) return;
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
async fetchImage(url: string): Promise<string | undefined> {
|
||||
return fetchFile(url);
|
||||
}
|
||||
|
||||
}
|
||||
export default new MTLReader();
|
||||
@@ -1,172 +0,0 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
||||
|
||||
export const id = "novelfull";
|
||||
export const name = "NovelFull";
|
||||
export const version = "1.0.0";
|
||||
export const icon = "src/en/novelfull/icon.png";
|
||||
export const site = "https://novelfull.com/";
|
||||
|
||||
|
||||
const baseUrl = site;
|
||||
|
||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
||||
const url = `${baseUrl}most-popular?page=${page}`;
|
||||
|
||||
const result = await fetchApi(url);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Novel.Item[] = [];
|
||||
|
||||
loadedCheerio(".col-truyen-main .list-truyen .row").each(function () {
|
||||
const novelName = loadedCheerio(this)
|
||||
.find("h3.truyen-title > a")
|
||||
.text();
|
||||
|
||||
const novelCover =
|
||||
baseUrl + loadedCheerio(this).find("img").attr("src")?.slice(1);
|
||||
|
||||
const novelUrl =
|
||||
baseUrl +
|
||||
loadedCheerio(this)
|
||||
.find("h3.truyen-title > a")
|
||||
.attr("href")
|
||||
?.slice(1);
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
||||
async function (novelUrl) {
|
||||
const url = novelUrl;
|
||||
|
||||
const result = await fetchApi(url);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
const novel: Novel.instance = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.name = loadedCheerio("div.book > img").attr("alt");
|
||||
|
||||
novel.cover = baseUrl + loadedCheerio("div.book > img").attr("src");
|
||||
|
||||
novel.summary = loadedCheerio("div.desc-text").text().trim();
|
||||
|
||||
novel.author = loadedCheerio('h3:contains("Author")')
|
||||
.parent()
|
||||
.contents()
|
||||
.text()
|
||||
.replace("Author:", "");
|
||||
|
||||
novel.genres = loadedCheerio('h3:contains("Genre")')
|
||||
.siblings()
|
||||
.map((i, el) => loadedCheerio(el).text())
|
||||
.toArray()
|
||||
.join(",");
|
||||
|
||||
novel.status = loadedCheerio('h3:contains("Status")').next().text();
|
||||
|
||||
const novelId = loadedCheerio("#rating").attr("data-novel-id")!;
|
||||
|
||||
async function getChapters(id: string) {
|
||||
const chapterListUrl =
|
||||
baseUrl + "ajax/chapter-option?novelId=" + id;
|
||||
|
||||
const data = await fetchApi(chapterListUrl);
|
||||
const chapterlist = await data.text();
|
||||
|
||||
loadedCheerio = parseHTML(chapterlist);
|
||||
|
||||
let chapter: Chapter.Item[] = [];
|
||||
|
||||
loadedCheerio("select > option").each(function () {
|
||||
const chapterName = loadedCheerio(this).text();
|
||||
const releaseDate = null;
|
||||
const chapterUrl =
|
||||
baseUrl + loadedCheerio(this).attr("value")?.slice(1);
|
||||
|
||||
chapter.push({
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
url: chapterUrl,
|
||||
});
|
||||
});
|
||||
return chapter;
|
||||
}
|
||||
|
||||
novel.chapters = await getChapters(novelId);
|
||||
|
||||
return novel;
|
||||
};
|
||||
|
||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
||||
const result = await fetchApi(chapterUrl);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
loadedCheerio("#chapter-content div.ads").remove();
|
||||
let chapterText = loadedCheerio("#chapter-content").html();
|
||||
|
||||
return chapterText;
|
||||
};
|
||||
|
||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
||||
const searchUrl = `${baseUrl}search?keyword=${searchTerm}`;
|
||||
|
||||
const result = await fetchApi(searchUrl);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Novel.Item[] = [];
|
||||
|
||||
loadedCheerio(".col-truyen-main .list-truyen .row").each(function () {
|
||||
const novelName = loadedCheerio(this)
|
||||
.find("h3.truyen-title > a")
|
||||
.text();
|
||||
|
||||
const novelCover =
|
||||
baseUrl + loadedCheerio(this).find("img").attr("src")?.slice(1);
|
||||
|
||||
const novelUrl =
|
||||
baseUrl +
|
||||
loadedCheerio(this)
|
||||
.find("h3.truyen-title > a")
|
||||
.attr("href")
|
||||
?.slice(1);
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
||||
const headers = {
|
||||
Referer: baseUrl,
|
||||
};
|
||||
return await fetchFile(url, { headers: headers });
|
||||
};
|
||||
@@ -0,0 +1,168 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Plugin } from "@typings/plugin";
|
||||
import { Filters } from "@libs/filterInputs";
|
||||
|
||||
|
||||
class NovelFull implements Plugin.PluginBase {
|
||||
id = "novelfull";
|
||||
name = "NovelFull";
|
||||
version = "1.0.0";
|
||||
icon = "src/en/novelfull/icon.png";
|
||||
site = "https://novelfull.com/";
|
||||
baseUrl = this.site;
|
||||
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||
const url = `${this.baseUrl}most-popular?page=${pageNo}`;
|
||||
|
||||
const result = await fetchApi(url);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Plugin.NovelItem[] = [];
|
||||
|
||||
loadedCheerio(".col-truyen-main .list-truyen .row").each((idx, ele) => {
|
||||
const novelName = loadedCheerio(ele)
|
||||
.find("h3.truyen-title > a")
|
||||
.text();
|
||||
|
||||
const novelCover =
|
||||
this.baseUrl + loadedCheerio(ele).find("img").attr("src")?.slice(1);
|
||||
|
||||
const novelUrl =
|
||||
this.baseUrl +
|
||||
loadedCheerio(ele)
|
||||
.find("h3.truyen-title > a")
|
||||
.attr("href")
|
||||
?.slice(1);
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||
const url = novelUrl;
|
||||
|
||||
const result = await fetchApi(url);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
const novel: Plugin.SourceNovel = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.name = loadedCheerio("div.book > img").attr("alt");
|
||||
|
||||
novel.cover = this.baseUrl + loadedCheerio("div.book > img").attr("src");
|
||||
|
||||
novel.summary = loadedCheerio("div.desc-text").text().trim();
|
||||
|
||||
novel.author = loadedCheerio('h3:contains("Author")')
|
||||
.parent()
|
||||
.contents()
|
||||
.text()
|
||||
.replace("Author:", "");
|
||||
|
||||
novel.genres = loadedCheerio('h3:contains("Genre")')
|
||||
.siblings()
|
||||
.map((i, el) => loadedCheerio(el).text())
|
||||
.toArray()
|
||||
.join(",");
|
||||
|
||||
novel.status = loadedCheerio('h3:contains("Status")').next().text();
|
||||
|
||||
const novelId = loadedCheerio("#rating").attr("data-novel-id")!;
|
||||
|
||||
async function getChapters(id: string, baseUrl: string) {
|
||||
const chapterListUrl =
|
||||
baseUrl + "ajax/chapter-option?novelId=" + id;
|
||||
|
||||
const data = await fetchApi(chapterListUrl);
|
||||
const chapterlist = await data.text();
|
||||
|
||||
loadedCheerio = parseHTML(chapterlist);
|
||||
|
||||
let chapter: Plugin.ChapterItem[] = [];
|
||||
|
||||
loadedCheerio("select > option").each(function () {
|
||||
const chapterName = loadedCheerio(this).text();
|
||||
const releaseDate = null;
|
||||
const chapterUrl =
|
||||
baseUrl + loadedCheerio(this).attr("value")?.slice(1);
|
||||
|
||||
chapter.push({
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
url: chapterUrl,
|
||||
});
|
||||
});
|
||||
return chapter;
|
||||
}
|
||||
|
||||
novel.chapters = await getChapters(novelId, this.baseUrl);
|
||||
|
||||
return novel;
|
||||
}
|
||||
async parseChapter(chapterUrl: string): Promise<string> {
|
||||
const result = await fetchApi(chapterUrl);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
loadedCheerio("#chapter-content div.ads").remove();
|
||||
let chapterText = loadedCheerio("#chapter-content").html() || '';
|
||||
|
||||
return chapterText;
|
||||
}
|
||||
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||
const searchUrl = `${this.baseUrl}search?keyword=${searchTerm}`;
|
||||
|
||||
const result = await fetchApi(searchUrl);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Plugin.NovelItem[] = [];
|
||||
|
||||
loadedCheerio(".col-truyen-main .list-truyen .row").each((idx, ele) => {
|
||||
const novelName = loadedCheerio(ele)
|
||||
.find("h3.truyen-title > a")
|
||||
.text();
|
||||
|
||||
const novelCover =
|
||||
this.baseUrl + loadedCheerio(ele).find("img").attr("src")?.slice(1);
|
||||
|
||||
const novelUrl =
|
||||
this.baseUrl +
|
||||
loadedCheerio(ele)
|
||||
.find("h3.truyen-title > a")
|
||||
.attr("href")
|
||||
?.slice(1);
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
async fetchImage(url: string): Promise<string | undefined> {
|
||||
return fetchFile(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new NovelFull();
|
||||
@@ -0,0 +1,138 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Plugin } from "@typings/plugin";
|
||||
import { Filters } from "@libs/filterInputs";
|
||||
|
||||
class NovelHall implements Plugin.PluginBase {
|
||||
id = "novelhold";
|
||||
name = "Novel Hall";
|
||||
version = "1.0.0";
|
||||
icon = "src/en/novelhall/icon.png";
|
||||
filters?: Filters | undefined;
|
||||
site = "https://novelhall.com/";
|
||||
baseUrl = this.site;
|
||||
|
||||
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||
const url = `${this.baseUrl}all2022-${pageNo}.html`;
|
||||
|
||||
const body = await fetchApi(url).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Plugin.NovelItem[] = [];
|
||||
|
||||
loadedCheerio("li.btm").each((idx, ele) => {
|
||||
const novelName = loadedCheerio(ele).text().trim()
|
||||
const novelUrl =
|
||||
this.baseUrl + loadedCheerio(ele).find("a").attr("href");
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||
const url = novelUrl;
|
||||
|
||||
const body = await fetchApi(url).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
const novel: Plugin.SourceNovel = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.name = loadedCheerio(".booknav2 > h1").text();
|
||||
|
||||
novel.cover = loadedCheerio('meta[property="og:image"]').attr(
|
||||
"content"
|
||||
);
|
||||
|
||||
novel.summary = loadedCheerio(".navtxt").text().trim();
|
||||
|
||||
novel.author = loadedCheerio('p:contains("Author")')
|
||||
.text()
|
||||
.replace("Author:", "")
|
||||
.trim();
|
||||
|
||||
novel.status = loadedCheerio('p:contains("Status")')
|
||||
.text()
|
||||
.replace("Status:", "")
|
||||
.replace("Active", "Ongoing")
|
||||
.trim();
|
||||
|
||||
novel.genres = loadedCheerio('p:contains("Genre")')
|
||||
.text()
|
||||
?.replace("Genre:", "")
|
||||
.trim();
|
||||
|
||||
let chapter: Plugin.ChapterItem[] = [];
|
||||
|
||||
loadedCheerio("#morelist ul > li").each((idx, ele) => {
|
||||
const chapterName = loadedCheerio(ele).find("a").text().trim();
|
||||
const releaseDate = null;
|
||||
const chapterUrl =
|
||||
this.baseUrl + loadedCheerio(ele).find("a").attr("href");
|
||||
|
||||
chapter.push({
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
url: chapterUrl,
|
||||
});
|
||||
});
|
||||
|
||||
novel.chapters = chapter;
|
||||
return novel;
|
||||
}
|
||||
async parseChapter(chapterUrl: string): Promise<string> {
|
||||
const body = await fetchApi(chapterUrl).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
const chapterText = loadedCheerio(".content").html() || '';
|
||||
|
||||
return chapterText;
|
||||
}
|
||||
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||
const url = `${this.baseUrl}index.php?s=so&module=book&keyword=${searchTerm}`;
|
||||
|
||||
const body = await fetchApi(url).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Plugin.NovelItem[] = [];
|
||||
|
||||
loadedCheerio("#article_list_content > li").each((idx, ele) => {
|
||||
const novelName = loadedCheerio(ele)
|
||||
.find("h3")
|
||||
.text()
|
||||
.replace(/\t+/g, "")
|
||||
.replace(/\n/g, " ");
|
||||
const novelCover = loadedCheerio(ele).find("img").attr("data-src");
|
||||
const novelUrl =
|
||||
this.baseUrl + loadedCheerio(ele).find("a").attr("href")?.slice(1);
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
async fetchImage(url: string): Promise<string | undefined> {
|
||||
return fetchFile(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new NovelHall();
|
||||
@@ -1,148 +0,0 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
||||
|
||||
export const id = "novelhold";
|
||||
export const name = "Novel Hold";
|
||||
export const version = "1.0.0";
|
||||
export const icon = "src/en/novelhold/icon.png";
|
||||
export const site = "https://novelhold.com/";
|
||||
|
||||
|
||||
const baseUrl = site;
|
||||
|
||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
||||
const url = `${baseUrl}all-${page}.html`;
|
||||
|
||||
const body = await fetchApi(url).then((r) => r.text());
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Novel.Item[] = [];
|
||||
|
||||
loadedCheerio("#article_list_content > li").each(function () {
|
||||
const novelName = loadedCheerio(this)
|
||||
.find("h3")
|
||||
.text()
|
||||
.replace(/\t+/g, "")
|
||||
.replace(/\n/g, " ");
|
||||
const novelCover = loadedCheerio(this).find("img").attr("data-src");
|
||||
const novelUrl =
|
||||
baseUrl + loadedCheerio(this).find("a").attr("href")?.slice(1);
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
||||
async function (novelUrl) {
|
||||
const url = novelUrl;
|
||||
|
||||
const body = await fetchApi(url).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
const novel: Novel.instance = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.name = loadedCheerio(".booknav2 > h1").text();
|
||||
|
||||
novel.cover = loadedCheerio('meta[property="og:image"]').attr(
|
||||
"content"
|
||||
);
|
||||
|
||||
novel.summary = loadedCheerio(".navtxt").text().trim();
|
||||
|
||||
novel.author = loadedCheerio('p:contains("Author")')
|
||||
.text()
|
||||
.replace("Author:", "")
|
||||
.trim();
|
||||
|
||||
novel.status = loadedCheerio('p:contains("Status")')
|
||||
.text()
|
||||
.replace("Status:", "")
|
||||
.replace("Active", "Ongoing")
|
||||
.trim();
|
||||
|
||||
novel.genres = loadedCheerio('p:contains("Genre")')
|
||||
.text()
|
||||
?.replace("Genre:", "")
|
||||
.trim();
|
||||
|
||||
let chapter: Chapter.Item[] = [];
|
||||
|
||||
loadedCheerio("ul.chapterlist > li").each(function () {
|
||||
const chapterName = loadedCheerio(this).find("a").text().trim();
|
||||
const releaseDate = null;
|
||||
const chapterUrl =
|
||||
baseUrl + loadedCheerio(this).find("a").attr("href")?.slice(1);
|
||||
|
||||
chapter.push({
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
url: chapterUrl,
|
||||
});
|
||||
});
|
||||
|
||||
novel.chapters = chapter;
|
||||
console.log(novel);
|
||||
return novel;
|
||||
};
|
||||
|
||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
||||
const body = await fetchApi(chapterUrl).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
const chapterText = loadedCheerio(".content").html();
|
||||
|
||||
return chapterText;
|
||||
};
|
||||
|
||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
||||
const url = `${baseUrl}index.php?s=so&module=book&keyword=${searchTerm}`;
|
||||
|
||||
const body = await fetchApi(url).then((r) => r.text());
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Novel.Item[] = [];
|
||||
|
||||
loadedCheerio("#article_list_content > li").each(function () {
|
||||
const novelName = loadedCheerio(this)
|
||||
.find("h3")
|
||||
.text()
|
||||
.replace(/\t+/g, "")
|
||||
.replace(/\n/g, " ");
|
||||
const novelCover = loadedCheerio(this).find("img").attr("data-src");
|
||||
const novelUrl =
|
||||
baseUrl + loadedCheerio(this).find("a").attr("href")?.slice(1);
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
||||
const headers = {
|
||||
Referer: baseUrl,
|
||||
};
|
||||
return await fetchFile(url, { headers: headers });
|
||||
};
|
||||
@@ -1,189 +0,0 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
||||
|
||||
export const id = "novelpub.com";
|
||||
export const name = "NovelPub";
|
||||
export const site = "https://www.novelpub.com/";
|
||||
export const version = "1.0.0";
|
||||
export const icon = "src/en/novelpub/icon.png";
|
||||
const baseUrl = site;
|
||||
|
||||
const headers = {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
|
||||
};
|
||||
|
||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
||||
let url = baseUrl + "browse/all/popular/all/" + page;
|
||||
|
||||
const result = await fetchApi(url, { method: "GET", headers });
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Novel.Item[] = [];
|
||||
|
||||
loadedCheerio(".novel-item.ads").remove();
|
||||
|
||||
loadedCheerio(".novel-item").each(function () {
|
||||
const novelName = loadedCheerio(this)
|
||||
.find(".novel-title")
|
||||
.text()
|
||||
.trim();
|
||||
const novelCover = loadedCheerio(this).find("img").attr("data-src");
|
||||
const novelUrl =
|
||||
baseUrl +
|
||||
loadedCheerio(this)
|
||||
.find(".novel-title > a")
|
||||
.attr("href")
|
||||
?.substring(1);
|
||||
|
||||
const novel = { name: novelName, cover: novelCover, url: novelUrl };
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
||||
async function (novelUrl) {
|
||||
const url = novelUrl;
|
||||
|
||||
const result = await fetchApi(url, { method: "GET", headers });
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
let novel: Novel.instance = { url, genres: "" };
|
||||
|
||||
novel.name = loadedCheerio("h1.novel-title").text().trim();
|
||||
|
||||
novel.cover = loadedCheerio("figure.cover > img").attr("data-src");
|
||||
|
||||
loadedCheerio("div.categories > ul > li").each(function () {
|
||||
novel.genres +=
|
||||
loadedCheerio(this)
|
||||
.text()
|
||||
.replace(/[\t\n]/g, "") + ",";
|
||||
});
|
||||
|
||||
loadedCheerio("div.header-stats > span").each(function () {
|
||||
if (loadedCheerio(this).find("small").text() === "Status") {
|
||||
novel.status = loadedCheerio(this).find("strong").text();
|
||||
}
|
||||
});
|
||||
|
||||
novel.genres = novel.genres?.slice(0, -1);
|
||||
|
||||
novel.author = loadedCheerio(".author > a > span").text();
|
||||
|
||||
novel.summary = loadedCheerio(".summary > .content").text().trim();
|
||||
|
||||
const delay = (ms: number) => new Promise((res) => setTimeout(res, ms));
|
||||
|
||||
let lastPage = 1;
|
||||
|
||||
lastPage = parseInt(
|
||||
loadedCheerio(
|
||||
"#novel > header > div.header-body.container > div.novel-info > div.header-stats > span:nth-child(1) > strong"
|
||||
)
|
||||
.text()
|
||||
?.trim()
|
||||
);
|
||||
|
||||
lastPage = Math.ceil(lastPage / 100);
|
||||
|
||||
const getChapters = async () => {
|
||||
let novelChapters: Chapter.Item[] = [];
|
||||
|
||||
for (let i = 1; i <= lastPage; i++) {
|
||||
const chaptersUrl = `${novelUrl}/chapters/page-${i}`;
|
||||
|
||||
const chaptersRequest = await fetchApi(chaptersUrl, {
|
||||
headers,
|
||||
});
|
||||
const chaptersHtml = await chaptersRequest.text();
|
||||
|
||||
loadedCheerio = parseHTML(chaptersHtml);
|
||||
|
||||
loadedCheerio(".chapter-list li").each(function () {
|
||||
const chapterName = loadedCheerio(this)
|
||||
.find(".chapter-title")
|
||||
.text()
|
||||
.trim();
|
||||
|
||||
const releaseDate = loadedCheerio(this)
|
||||
.find(".chapter-update")
|
||||
.text()
|
||||
.trim();
|
||||
|
||||
const chapterUrl =
|
||||
baseUrl +
|
||||
loadedCheerio(this)
|
||||
.find("a")
|
||||
.attr("href")
|
||||
?.substring(1);
|
||||
|
||||
novelChapters.push({
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
url: chapterUrl,
|
||||
});
|
||||
});
|
||||
|
||||
await delay(1000);
|
||||
}
|
||||
|
||||
return novelChapters;
|
||||
};
|
||||
|
||||
novel.chapters = await getChapters();
|
||||
|
||||
return novel;
|
||||
};
|
||||
|
||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
||||
const url = chapterUrl;
|
||||
|
||||
const result = await fetchApi(url, { method: "GET", headers });
|
||||
|
||||
const body = await result.text();
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
const chapterText = loadedCheerio("#chapter-container").html();
|
||||
return chapterText;
|
||||
};
|
||||
|
||||
// They have API to search, pls update it, im quite lazy xD
|
||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
||||
const url = `${baseUrl}lnwsearchlive?inputContent=${searchTerm}`;
|
||||
|
||||
const result = await fetchApi(url, { method: "GET", headers });
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Novel.Item[] = [];
|
||||
|
||||
// let results = JSON.parse(loadedCheerio('body').text());
|
||||
|
||||
// loadedCheerio = load(results.resultview);
|
||||
|
||||
// loadedCheerio('.novel-item').each(function () {
|
||||
// const novelName = loadedCheerio(this).find('.novel-title').text().trim();
|
||||
// const novelCover = loadedCheerio(this).find('img').attr('src');
|
||||
// const novelUrl =
|
||||
// baseUrl + loadedCheerio(this).find('a').attr('href').substring(1);
|
||||
|
||||
// const novel = { name: novelName, cover: novelCover, url: novelUrl };
|
||||
|
||||
// novels.push(novel);
|
||||
// });
|
||||
|
||||
return novels;
|
||||
};
|
||||
export const fetchImage: Plugin.fetchImage = fetchFile;
|
||||
@@ -1,492 +0,0 @@
|
||||
import cheerio from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { FilterInputs } from "@libs/filterInputs";
|
||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
||||
|
||||
const pluginId = "novelupdates";
|
||||
const baseUrl = "https://www.novelupdates.com/";
|
||||
|
||||
const headers = {
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
|
||||
};
|
||||
|
||||
function getPopularNovelsUrl(
|
||||
page: number,
|
||||
{ showLatestNovels, filters }: Plugin.Options
|
||||
) {
|
||||
let url = `${baseUrl}${
|
||||
filters
|
||||
? "series-finder"
|
||||
: showLatestNovels
|
||||
? "latest-series"
|
||||
: "series-ranking"
|
||||
}/`;
|
||||
|
||||
if (!filters) {
|
||||
url += "?rank=week";
|
||||
} else {
|
||||
url += "?sf=1";
|
||||
}
|
||||
|
||||
if (filters) {
|
||||
if (Array.isArray(filters.novelType) && filters.novelType?.length) {
|
||||
url += "&nt=" + filters?.novelType.join(",");
|
||||
}
|
||||
|
||||
if (Array.isArray(filters.genres) && filters.genres?.length) {
|
||||
url += "&gi=" + filters?.genres.join(",") + "&mgi=and";
|
||||
}
|
||||
|
||||
if (Array.isArray(filters.language) && filters.language?.length) {
|
||||
url += "&org=" + filters?.language.join(",");
|
||||
}
|
||||
|
||||
if (filters.storyStatus) {
|
||||
url += "&ss=" + filters?.storyStatus;
|
||||
}
|
||||
}
|
||||
|
||||
if (!filters?.sort) {
|
||||
url += "&sort=" + "sdate";
|
||||
} else {
|
||||
url += "&sort=" + filters?.sort;
|
||||
}
|
||||
|
||||
if (!filters?.order) {
|
||||
url += "&order=" + "desc";
|
||||
} else {
|
||||
url += "&order=" + filters?.order;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export const popularNovels: Plugin.popularNovels = async function (
|
||||
page,
|
||||
{ showLatestNovels, filters }
|
||||
) {
|
||||
const url = getPopularNovelsUrl(page, { showLatestNovels, filters });
|
||||
|
||||
const result = await fetchApi(url, { headers: headers }, pluginId);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
|
||||
const novels: Novel.Item[] = [];
|
||||
|
||||
loadedCheerio("div.search_main_box_nu").each(function (res) {
|
||||
const novelCover = loadedCheerio(this).find("img").attr("src");
|
||||
const novelName = loadedCheerio(this).find(".search_title > a").text();
|
||||
const novelUrl = loadedCheerio(this)
|
||||
.find(".search_title > a")
|
||||
.attr("href");
|
||||
|
||||
if (!novelUrl) return;
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
||||
async function (novelUrl) {
|
||||
const url = novelUrl;
|
||||
|
||||
const result = await fetchApi(url, { headers: headers }, pluginId);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = cheerio.load(body);
|
||||
|
||||
const novel: Novel.instance = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.name = loadedCheerio(".seriestitlenu").text();
|
||||
|
||||
novel.cover = loadedCheerio(".seriesimg > img").attr("src");
|
||||
|
||||
novel.author = loadedCheerio("#showauthors").text().trim();
|
||||
|
||||
novel.genres = loadedCheerio("#seriesgenre")
|
||||
.children("a")
|
||||
.map((i, el) => loadedCheerio(el).text())
|
||||
.toArray()
|
||||
.join(",");
|
||||
novel.status = loadedCheerio("#editstatus").text().includes("Ongoing")
|
||||
? "Ongoing"
|
||||
: "Completed";
|
||||
|
||||
let type = loadedCheerio("#showtype").text().trim();
|
||||
|
||||
let summary = loadedCheerio("#editdescription").text().trim();
|
||||
|
||||
novel.summary = summary + `\n\nType: ${type}`;
|
||||
|
||||
let chapter: Chapter.Item[] = [];
|
||||
|
||||
const novelId = loadedCheerio("input#mypostid").attr("value")!;
|
||||
|
||||
let formData = new FormData();
|
||||
formData.append("action", "nd_getchapters");
|
||||
formData.append("mygrr", "0");
|
||||
formData.append("mypostid", novelId);
|
||||
|
||||
let link = "https://www.novelupdates.com/wp-admin/admin-ajax.php";
|
||||
|
||||
const text = await fetchApi(
|
||||
link,
|
||||
{
|
||||
method: "POST",
|
||||
body: formData,
|
||||
headers: headers,
|
||||
},
|
||||
pluginId
|
||||
).then((data) => data.text());
|
||||
|
||||
loadedCheerio = cheerio.load(text);
|
||||
|
||||
loadedCheerio("li.sp_li_chp").each(function () {
|
||||
const chapterName = loadedCheerio(this).text().trim();
|
||||
|
||||
const releaseDate = null;
|
||||
|
||||
const chapterUrl =
|
||||
"https:" +
|
||||
loadedCheerio(this).find("a").first().next().attr("href");
|
||||
|
||||
chapter.push({
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
url: chapterUrl,
|
||||
});
|
||||
});
|
||||
|
||||
novel.chapters = chapter.reverse();
|
||||
|
||||
return novel;
|
||||
};
|
||||
|
||||
function getLocation(href: string) {
|
||||
var match = href.match(
|
||||
/^(https?:)\/\/(([^:/?#]*)(?::([0-9]+))?)([/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/
|
||||
);
|
||||
return match && `${match[1]}//${match[3]}`;
|
||||
}
|
||||
|
||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
||||
// console.log('Original URL: ', chapterUrl);
|
||||
|
||||
let chapterText = "";
|
||||
|
||||
const result = await fetchApi(
|
||||
chapterUrl,
|
||||
{
|
||||
method: "GET",
|
||||
headers: headers,
|
||||
},
|
||||
pluginId
|
||||
);
|
||||
const body = await result.text();
|
||||
|
||||
// console.log(result.chapterUrl);
|
||||
|
||||
// console.log('Redirected URL: ', result.chapterUrl);
|
||||
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
|
||||
let isWuxiaWorld = result.chapterUrl.toLowerCase().includes("wuxiaworld");
|
||||
|
||||
let isBlogspot = result.chapterUrl.toLowerCase().includes("blogspot");
|
||||
|
||||
let isTumblr = result.chapterUrl.toLowerCase().includes("tumblr");
|
||||
|
||||
let isWattpad = result.chapterUrl.toLowerCase().includes("wattpad");
|
||||
|
||||
let isLightNovelsTls = result.chapterUrl
|
||||
.toLowerCase()
|
||||
.includes("lightnovelstranslations");
|
||||
|
||||
let isiNovelTranslation = result.chapterUrl
|
||||
.toLowerCase()
|
||||
.includes("inoveltranslation");
|
||||
|
||||
let isTravisTranslation = result.chapterUrl
|
||||
.toLowerCase()
|
||||
.includes("travistranslations");
|
||||
|
||||
/**
|
||||
* Checks if its a wordpress site
|
||||
*/
|
||||
let isWordPressStr =
|
||||
loadedCheerio('meta[name="generator"]').attr("content") ||
|
||||
loadedCheerio("footer").text();
|
||||
|
||||
let isWordPress = false;
|
||||
|
||||
if (isWordPressStr) {
|
||||
isWordPress =
|
||||
isWordPressStr.toLowerCase().includes("wordpress") ||
|
||||
isWordPressStr.includes("Site Kit by Google") ||
|
||||
loadedCheerio(".powered-by")
|
||||
.text()
|
||||
.toLowerCase()
|
||||
.includes("wordpress");
|
||||
}
|
||||
|
||||
let isRainOfSnow = result.chapterUrl.toLowerCase().includes("rainofsnow");
|
||||
|
||||
let isWebNovel = result.chapterUrl.toLowerCase().includes("webnovel");
|
||||
|
||||
let isHostedNovel = result.chapterUrl.toLowerCase().includes("hostednovel");
|
||||
|
||||
let isScribbleHub = result.chapterUrl.toLowerCase().includes("scribblehub");
|
||||
|
||||
if (isWuxiaWorld) {
|
||||
chapterText = loadedCheerio("#chapter-content").html()!;
|
||||
} else if (isRainOfSnow) {
|
||||
chapterText = loadedCheerio("div.content").html()!;
|
||||
} else if (isTumblr) {
|
||||
chapterText = loadedCheerio(".post").html()!;
|
||||
} else if (isBlogspot) {
|
||||
loadedCheerio(".post-share-buttons").remove();
|
||||
chapterText = loadedCheerio(".entry-content").html()!;
|
||||
} else if (isHostedNovel) {
|
||||
chapterText = loadedCheerio(".chapter").html()!;
|
||||
} else if (isScribbleHub) {
|
||||
chapterText = loadedCheerio("div.chp_raw").html()!;
|
||||
} else if (isWattpad) {
|
||||
chapterText = loadedCheerio(".container pre").html()!;
|
||||
} else if (isTravisTranslation) {
|
||||
chapterText = loadedCheerio(".reader-content").html()!;
|
||||
} else if (isLightNovelsTls) {
|
||||
chapterText = loadedCheerio(".text_story").html()!;
|
||||
} else if (isiNovelTranslation) {
|
||||
chapterText = loadedCheerio(".chakra-skeleton").html()!;
|
||||
} else if (isWordPress) {
|
||||
/**
|
||||
* Remove wordpress bloat tags
|
||||
*/
|
||||
|
||||
const bloatClasses = [
|
||||
".c-ads",
|
||||
"#madara-comments",
|
||||
"#comments",
|
||||
".content-comments",
|
||||
".sharedaddy",
|
||||
".wp-dark-mode-switcher",
|
||||
".wp-next-post-navi",
|
||||
".wp-block-buttons",
|
||||
".wp-block-columns",
|
||||
".post-cats",
|
||||
".sidebar",
|
||||
".author-avatar",
|
||||
".ezoic-ad",
|
||||
];
|
||||
|
||||
bloatClasses.map((tag) => loadedCheerio(tag).remove());
|
||||
|
||||
chapterText =
|
||||
loadedCheerio(".entry-content").html() ||
|
||||
loadedCheerio(".single_post").html() ||
|
||||
loadedCheerio(".post-entry").html() ||
|
||||
loadedCheerio(".main-content").html() ||
|
||||
loadedCheerio("article.post").html() ||
|
||||
loadedCheerio(".content").html() ||
|
||||
loadedCheerio("#content").html() ||
|
||||
loadedCheerio(".page-body").html() ||
|
||||
loadedCheerio(".td-page-content").html()!;
|
||||
} else if (isWebNovel) {
|
||||
chapterText = loadedCheerio(".cha-words").html()!;
|
||||
|
||||
if (!chapterText) {
|
||||
chapterText = loadedCheerio("._content").html()!;
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* Remove unnecessary tags
|
||||
*/
|
||||
const tags = ["nav", "header", "footer", ".hidden"];
|
||||
|
||||
tags.map((tag) => loadedCheerio(tag).remove());
|
||||
|
||||
chapterText = loadedCheerio("body").html()!;
|
||||
}
|
||||
|
||||
if (chapterText) {
|
||||
/**
|
||||
* Convert relative urls to absolute
|
||||
*/
|
||||
chapterText = chapterText.replace(
|
||||
/href="\//g,
|
||||
`href="${getLocation(result.chapterUrl)}/`
|
||||
);
|
||||
}
|
||||
|
||||
return chapterText;
|
||||
};
|
||||
|
||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
||||
const url =
|
||||
"https://www.novelupdates.com/?s=" +
|
||||
searchTerm +
|
||||
"&post_type=seriesplans";
|
||||
|
||||
const result = await fetchApi(url, { headers: headers }, pluginId);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
|
||||
let novels: Novel.Item[] = [];
|
||||
|
||||
loadedCheerio("div.search_main_box_nu").each(function () {
|
||||
const novelCover = loadedCheerio(this).find("img").attr("src");
|
||||
const novelName = loadedCheerio(this).find(".search_title > a").text();
|
||||
const novelUrl = loadedCheerio(this)
|
||||
.find(".search_title > a")
|
||||
.attr("href")
|
||||
?.split("/")[4];
|
||||
|
||||
if (!novelUrl) return;
|
||||
|
||||
novels.push({
|
||||
name: novelName,
|
||||
url: novelUrl,
|
||||
cover: novelCover,
|
||||
});
|
||||
});
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
||||
return await fetchFile(url, { headers: headers });
|
||||
};
|
||||
|
||||
const filters = [
|
||||
{
|
||||
key: "sort",
|
||||
label: "Sort Results By",
|
||||
values: [
|
||||
{ label: "Last Updated", value: "sdate" },
|
||||
{ label: "Rating", value: "srate" },
|
||||
{ label: "Rank", value: "srank" },
|
||||
{ label: "Reviews", value: "sreview" },
|
||||
{ label: "Chapters", value: "srel" },
|
||||
{ label: "Title", value: "abc" },
|
||||
{ label: "Readers", value: "sread" },
|
||||
{ label: "Frequency", value: "sfrel" },
|
||||
],
|
||||
inputType: FilterInputs.Picker,
|
||||
},
|
||||
{
|
||||
key: "order",
|
||||
label: "Order",
|
||||
values: [
|
||||
{ label: "Descending", value: "desc" },
|
||||
{ label: "Ascending", value: "asc" },
|
||||
],
|
||||
inputType: FilterInputs.Picker,
|
||||
},
|
||||
{
|
||||
key: "storyStatus",
|
||||
label: "Story Status (Translation)",
|
||||
values: [
|
||||
{ label: "All", value: "" },
|
||||
{ label: "Completed", value: "2" },
|
||||
{ label: "Ongoing", value: "3" },
|
||||
{ label: "Hiatus", value: "4" },
|
||||
],
|
||||
inputType: FilterInputs.Picker,
|
||||
},
|
||||
{
|
||||
key: "language",
|
||||
label: "Language",
|
||||
values: [
|
||||
{ label: "Chinese", value: 495 },
|
||||
{ label: "Filipino", value: 9181 },
|
||||
{ label: "Indonesian", value: 9179 },
|
||||
{ label: "Japanese", value: 496 },
|
||||
{ label: "Khmer", value: 18657 },
|
||||
{ label: "Korean", value: 497 },
|
||||
{ label: "Malaysian", value: 9183 },
|
||||
{ label: "Thai", value: 9954 },
|
||||
{ label: "Vietnamese", value: 9177 },
|
||||
],
|
||||
inputType: FilterInputs.Checkbox,
|
||||
},
|
||||
{
|
||||
key: "novelType",
|
||||
label: "Novel Type",
|
||||
values: [
|
||||
{ label: "Light Novel", value: "2443" },
|
||||
{ label: "Published Novel", value: "26874" },
|
||||
{ label: "Web Novel", value: "2444" },
|
||||
],
|
||||
inputType: FilterInputs.Checkbox,
|
||||
},
|
||||
{
|
||||
key: "genres",
|
||||
label: "Genres",
|
||||
values: [
|
||||
{ label: "Action", value: 8 },
|
||||
{ label: "Adult", value: 280 },
|
||||
{ label: "Adventure", value: 13 },
|
||||
{ label: "Comedy", value: 17 },
|
||||
{ label: "Drama", value: 9 },
|
||||
{ label: "Ecchi", value: 292 },
|
||||
{ label: "Fantasy", value: 5 },
|
||||
{ label: "Gender Bender", value: 168 },
|
||||
{ label: "Harem", value: 3 },
|
||||
{ label: "Historical", value: 330 },
|
||||
{ label: "Horror", value: 343 },
|
||||
{ label: "Josei", value: 324 },
|
||||
{ label: "Martial Arts", value: 14 },
|
||||
{ label: "Mature", value: 4 },
|
||||
{ label: "Mecha", value: 10 },
|
||||
{ label: "Mystery", value: 245 },
|
||||
{ label: "Psychoical", value: 486 },
|
||||
{ label: "Romance", value: 15 },
|
||||
{ label: "School Life", value: 6 },
|
||||
{ label: "Sci-fi", value: 11 },
|
||||
{ label: "Seinen", value: 18 },
|
||||
{ label: "Shoujo", value: 157 },
|
||||
{ label: "Shoujo Ai", value: 851 },
|
||||
{ label: "Shounen", value: 12 },
|
||||
{ label: "Shounen Ai", value: 1692 },
|
||||
{ label: "Slice of Life", value: 7 },
|
||||
{ label: "Smut", value: 281 },
|
||||
{ label: "Sports", value: 1357 },
|
||||
{ label: "Supernatural", value: 16 },
|
||||
{ label: "Tragedy", value: 132 },
|
||||
{ label: "Wuxia", value: 479 },
|
||||
{ label: "Xianxia", value: 480 },
|
||||
{ label: "Xuanhuan", value: 3954 },
|
||||
{ label: "Yaoi", value: 560 },
|
||||
{ label: "Yuri", value: 922 },
|
||||
],
|
||||
inputType: FilterInputs.Checkbox,
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
id: pluginId,
|
||||
name: "Novel Updates",
|
||||
version: "0.5.0",
|
||||
icon: "src/en/novelupdates/icon.png",
|
||||
site: baseUrl,
|
||||
protected: false,
|
||||
fetchImage,
|
||||
popularNovels,
|
||||
parseNovelAndChapters,
|
||||
parseChapter,
|
||||
searchNovels,
|
||||
filters,
|
||||
};
|
||||
@@ -0,0 +1,463 @@
|
||||
import { load as parseHTML } from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Filters, FilterTypes } from "@libs/filterInputs";
|
||||
import { Plugin } from "@typings/plugin";
|
||||
|
||||
class NovelUpdates implements Plugin.PluginBase {
|
||||
id = "novelupdates";
|
||||
name = "Novel Updates";
|
||||
version = "0.5.0";
|
||||
icon = "src/en/novelupdates/icon.png";
|
||||
site = "https://www.novelupdates.com/";
|
||||
baseUrl = this.site;
|
||||
|
||||
getPopularNovelsUrl(
|
||||
page: number,
|
||||
{ showLatestNovels, filters }: Plugin.PopularNovelsOptions<Filters>
|
||||
) {
|
||||
let url = `${this.baseUrl}${filters
|
||||
? "series-finder"
|
||||
: showLatestNovels
|
||||
? "latest-series"
|
||||
: "series-ranking"
|
||||
}/`;
|
||||
|
||||
if (!filters) {
|
||||
url += "?rank=week";
|
||||
} else {
|
||||
url += "?sf=1";
|
||||
}
|
||||
|
||||
if (filters) {
|
||||
if (Array.isArray(filters.novelType) && filters.novelType?.length) {
|
||||
url += "&nt=" + filters?.novelType.join(",");
|
||||
}
|
||||
|
||||
if (Array.isArray(filters.genres) && filters.genres?.length) {
|
||||
url += "&gi=" + filters?.genres.join(",") + "&mgi=and";
|
||||
}
|
||||
|
||||
if (Array.isArray(filters.language) && filters.language?.length) {
|
||||
url += "&org=" + filters?.language.join(",");
|
||||
}
|
||||
|
||||
if (filters.storyStatus) {
|
||||
url += "&ss=" + filters?.storyStatus;
|
||||
}
|
||||
}
|
||||
|
||||
if (!filters?.sort) {
|
||||
url += "&sort=" + "sdate";
|
||||
} else {
|
||||
url += "&sort=" + filters?.sort;
|
||||
}
|
||||
|
||||
if (!filters?.order) {
|
||||
url += "&order=" + "desc";
|
||||
} else {
|
||||
url += "&order=" + filters?.order;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
async popularNovels(pageNo: number, { showLatestNovels, filters }: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||
const url = this.getPopularNovelsUrl(pageNo, { showLatestNovels, filters });
|
||||
|
||||
const result = await fetchApi(url);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
const novels: Plugin.NovelItem[] = [];
|
||||
|
||||
loadedCheerio("div.search_main_box_nu").each((idx, ele) => {
|
||||
const novelCover = loadedCheerio(ele).find("img").attr("src");
|
||||
const novelName = loadedCheerio(ele).find(".search_title > a").text();
|
||||
const novelUrl = loadedCheerio(ele)
|
||||
.find(".search_title > a")
|
||||
.attr("href");
|
||||
|
||||
if (!novelUrl) return;
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
url: novelUrl,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||
const url = novelUrl;
|
||||
|
||||
const result = await fetchApi(url);
|
||||
const body = await result.text();
|
||||
|
||||
let loadedCheerio = parseHTML(body);
|
||||
|
||||
const novel: Plugin.SourceNovel = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.name = loadedCheerio(".seriestitlenu").text();
|
||||
|
||||
novel.cover = loadedCheerio(".seriesimg > img").attr("src");
|
||||
|
||||
novel.author = loadedCheerio("#showauthors").text().trim();
|
||||
|
||||
novel.genres = loadedCheerio("#seriesgenre")
|
||||
.children("a")
|
||||
.map((i, el) => loadedCheerio(el).text())
|
||||
.toArray()
|
||||
.join(",");
|
||||
novel.status = loadedCheerio("#editstatus").text().includes("Ongoing")
|
||||
? "Ongoing"
|
||||
: "Completed";
|
||||
|
||||
let type = loadedCheerio("#showtype").text().trim();
|
||||
|
||||
let summary = loadedCheerio("#editdescription").text().trim();
|
||||
|
||||
novel.summary = summary + `\n\nType: ${type}`;
|
||||
|
||||
let chapter: Plugin.ChapterItem[] = [];
|
||||
|
||||
const novelId = loadedCheerio("input#mypostid").attr("value")!;
|
||||
|
||||
let formData = new FormData();
|
||||
formData.append("action", "nd_getchapters");
|
||||
formData.append("mygrr", "0");
|
||||
formData.append("mypostid", novelId);
|
||||
|
||||
let link = "https://www.novelupdates.com/wp-admin/admin-ajax.php";
|
||||
|
||||
const text = await fetchApi(
|
||||
link,
|
||||
{
|
||||
method: "POST",
|
||||
body: formData,
|
||||
},
|
||||
).then((data) => data.text());
|
||||
|
||||
loadedCheerio = parseHTML(text);
|
||||
|
||||
loadedCheerio("li.sp_li_chp").each(function () {
|
||||
const chapterName = loadedCheerio(this).text().trim();
|
||||
|
||||
const releaseDate = null;
|
||||
|
||||
const chapterUrl =
|
||||
"https:" +
|
||||
loadedCheerio(this).find("a").first().next().attr("href");
|
||||
|
||||
chapter.push({
|
||||
name: chapterName,
|
||||
releaseTime: releaseDate,
|
||||
url: chapterUrl,
|
||||
});
|
||||
});
|
||||
|
||||
novel.chapters = chapter.reverse();
|
||||
|
||||
return novel;
|
||||
}
|
||||
getLocation(href: string) {
|
||||
var match = href.match(
|
||||
/^(https?:)\/\/(([^:/?#]*)(?::([0-9]+))?)([/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/
|
||||
);
|
||||
return match && `${match[1]}//${match[3]}`;
|
||||
}
|
||||
|
||||
async parseChapter(chapterUrl: string): Promise<string> {
|
||||
let chapterText = "";
|
||||
|
||||
const result = await fetchApi(
|
||||
chapterUrl,
|
||||
);
|
||||
const body = await result.text();
|
||||
|
||||
// console.log(result.chapterUrl);
|
||||
|
||||
// console.log('Redirected URL: ', result.chapterUrl);
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let isWuxiaWorld = chapterUrl.toLowerCase().includes("wuxiaworld");
|
||||
|
||||
let isBlogspot = chapterUrl.toLowerCase().includes("blogspot");
|
||||
|
||||
let isTumblr = chapterUrl.toLowerCase().includes("tumblr");
|
||||
|
||||
let isWattpad = chapterUrl.toLowerCase().includes("wattpad");
|
||||
|
||||
let isLightNovelsTls = chapterUrl
|
||||
.toLowerCase()
|
||||
.includes("lightnovelstranslations");
|
||||
|
||||
let isiNovelTranslation = chapterUrl
|
||||
.toLowerCase()
|
||||
.includes("inoveltranslation");
|
||||
|
||||
let isTravisTranslation = chapterUrl
|
||||
.toLowerCase()
|
||||
.includes("travistranslations");
|
||||
|
||||
/**
|
||||
* Checks if its a wordpress site
|
||||
*/
|
||||
let isWordPressStr =
|
||||
loadedCheerio('meta[name="generator"]').attr("content") ||
|
||||
loadedCheerio("footer").text();
|
||||
|
||||
let isWordPress = false;
|
||||
|
||||
if (isWordPressStr) {
|
||||
isWordPress =
|
||||
isWordPressStr.toLowerCase().includes("wordpress") ||
|
||||
isWordPressStr.includes("Site Kit by Google") ||
|
||||
loadedCheerio(".powered-by")
|
||||
.text()
|
||||
.toLowerCase()
|
||||
.includes("wordpress");
|
||||
}
|
||||
|
||||
let isRainOfSnow = chapterUrl.toLowerCase().includes("rainofsnow");
|
||||
|
||||
let isWebNovel = chapterUrl.toLowerCase().includes("webnovel");
|
||||
|
||||
let isHostedNovel = chapterUrl.toLowerCase().includes("hostednovel");
|
||||
|
||||
let isScribbleHub = chapterUrl.toLowerCase().includes("scribblehub");
|
||||
|
||||
if (isWuxiaWorld) {
|
||||
chapterText = loadedCheerio("#chapter-content").html()!;
|
||||
} else if (isRainOfSnow) {
|
||||
chapterText = loadedCheerio("div.content").html()!;
|
||||
} else if (isTumblr) {
|
||||
chapterText = loadedCheerio(".post").html()!;
|
||||
} else if (isBlogspot) {
|
||||
loadedCheerio(".post-share-buttons").remove();
|
||||
chapterText = loadedCheerio(".entry-content").html()!;
|
||||
} else if (isHostedNovel) {
|
||||
chapterText = loadedCheerio(".chapter").html()!;
|
||||
} else if (isScribbleHub) {
|
||||
chapterText = loadedCheerio("div.chp_raw").html()!;
|
||||
} else if (isWattpad) {
|
||||
chapterText = loadedCheerio(".container pre").html()!;
|
||||
} else if (isTravisTranslation) {
|
||||
chapterText = loadedCheerio(".reader-content").html()!;
|
||||
} else if (isLightNovelsTls) {
|
||||
chapterText = loadedCheerio(".text_story").html()!;
|
||||
} else if (isiNovelTranslation) {
|
||||
chapterText = loadedCheerio(".chakra-skeleton").html()!;
|
||||
} else if (isWordPress) {
|
||||
/**
|
||||
* Remove wordpress bloat tags
|
||||
*/
|
||||
|
||||
const bloatClasses = [
|
||||
".c-ads",
|
||||
"#madara-comments",
|
||||
"#comments",
|
||||
".content-comments",
|
||||
".sharedaddy",
|
||||
".wp-dark-mode-switcher",
|
||||
".wp-next-post-navi",
|
||||
".wp-block-buttons",
|
||||
".wp-block-columns",
|
||||
".post-cats",
|
||||
".sidebar",
|
||||
".author-avatar",
|
||||
".ezoic-ad",
|
||||
];
|
||||
|
||||
bloatClasses.map((tag) => loadedCheerio(tag).remove());
|
||||
|
||||
chapterText =
|
||||
loadedCheerio(".entry-content").html() ||
|
||||
loadedCheerio(".single_post").html() ||
|
||||
loadedCheerio(".post-entry").html() ||
|
||||
loadedCheerio(".main-content").html() ||
|
||||
loadedCheerio("article.post").html() ||
|
||||
loadedCheerio(".content").html() ||
|
||||
loadedCheerio("#content").html() ||
|
||||
loadedCheerio(".page-body").html() ||
|
||||
loadedCheerio(".td-page-content").html()!;
|
||||
} else if (isWebNovel) {
|
||||
chapterText = loadedCheerio(".cha-words").html()!;
|
||||
|
||||
if (!chapterText) {
|
||||
chapterText = loadedCheerio("._content").html()!;
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* Remove unnecessary tags
|
||||
*/
|
||||
const tags = ["nav", "header", "footer", ".hidden"];
|
||||
|
||||
tags.map((tag) => loadedCheerio(tag).remove());
|
||||
|
||||
chapterText = loadedCheerio("body").html()!;
|
||||
}
|
||||
|
||||
if (chapterText) {
|
||||
/**
|
||||
* Convert relative urls to absolute
|
||||
*/
|
||||
chapterText = chapterText.replace(
|
||||
/href="\//g,
|
||||
`href="${this.getLocation(chapterUrl)}/`
|
||||
);
|
||||
}
|
||||
|
||||
return chapterText;
|
||||
}
|
||||
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||
const url =
|
||||
"https://www.novelupdates.com/?s=" +
|
||||
searchTerm +
|
||||
"&post_type=seriesplans";
|
||||
|
||||
const result = await fetchApi(url);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
let novels: Plugin.NovelItem[] = [];
|
||||
|
||||
loadedCheerio("div.search_main_box_nu").each(function () {
|
||||
const novelCover = loadedCheerio(this).find("img").attr("src");
|
||||
const novelName = loadedCheerio(this).find(".search_title > a").text();
|
||||
const novelUrl = loadedCheerio(this)
|
||||
.find(".search_title > a")
|
||||
.attr("href")
|
||||
?.split("/")[4];
|
||||
|
||||
if (!novelUrl) return;
|
||||
|
||||
novels.push({
|
||||
name: novelName,
|
||||
url: novelUrl,
|
||||
cover: novelCover,
|
||||
});
|
||||
});
|
||||
return novels;
|
||||
}
|
||||
fetchImage(url: string): Promise<string | undefined> {
|
||||
return fetchFile(url);
|
||||
}
|
||||
|
||||
filters: Filters = {
|
||||
sort: {
|
||||
label: "Sort Results By",
|
||||
type: FilterTypes.Picker,
|
||||
options: [
|
||||
{ label: "Last Updated", value: "sdate" },
|
||||
{ label: "Rating", value: "srate" },
|
||||
{ label: "Rank", value: "srank" },
|
||||
{ label: "Reviews", value: "sreview" },
|
||||
{ label: "Chapters", value: "srel" },
|
||||
{ label: "Title", value: "abc" },
|
||||
{ label: "Readers", value: "sread" },
|
||||
{ label: "Frequency", value: "sfrel" },
|
||||
],
|
||||
value: "sdate",
|
||||
},
|
||||
order: {
|
||||
label: "Order",
|
||||
type: FilterTypes.Picker,
|
||||
options: [
|
||||
{ label: "Descending", value: "desc" },
|
||||
{ label: "Ascending", value: "asc" },
|
||||
],
|
||||
value: "asc"
|
||||
},
|
||||
storyStatus: {
|
||||
label: "Story Status (Translation)",
|
||||
type: FilterTypes.Picker,
|
||||
options: [
|
||||
{ label: "All", value: "" },
|
||||
{ label: "Completed", value: "2" },
|
||||
{ label: "Ongoing", value: "3" },
|
||||
{ label: "Hiatus", value: "4" },
|
||||
],
|
||||
value: ""
|
||||
},
|
||||
language: {
|
||||
label: "Language",
|
||||
options: [
|
||||
{ label: "Chinese", value: "495" },
|
||||
{ label: "Filipino", value: "9181" },
|
||||
{ label: "Indonesian", value: "9179" },
|
||||
{ label: "Japanese", value: "496" },
|
||||
{ label: "Khmer", value: "18657" },
|
||||
{ label: "Korean", value: "497" },
|
||||
{ label: "Malaysian", value: "9183" },
|
||||
{ label: "Thai", value: "9954" },
|
||||
{ label: "Vietnamese", value: "9177" },
|
||||
],
|
||||
type: FilterTypes.Picker,
|
||||
value: ""
|
||||
},
|
||||
novelType: {
|
||||
label: "Novel Type",
|
||||
type: FilterTypes.CheckboxGroup,
|
||||
value: [],
|
||||
options: [
|
||||
{ label: "Light Novel", value: "2443" },
|
||||
{ label: "Published Novel", value: "26874" },
|
||||
{ label: "Web Novel", value: "2444" },
|
||||
]
|
||||
},
|
||||
genres: {
|
||||
label: "Genres",
|
||||
type: FilterTypes.CheckboxGroup,
|
||||
value: [],
|
||||
options: [
|
||||
{ label: "Action", value: "8" },
|
||||
{ label: "Adult", value: "280" },
|
||||
{ label: "Adventure", value: "13" },
|
||||
{ label: "Comedy", value: "17" },
|
||||
{ label: "Drama", value: "9" },
|
||||
{ label: "Ecchi", value: "292" },
|
||||
{ label: "Fantasy", value: "5" },
|
||||
{ label: "Gender Bender", value: "168" },
|
||||
{ label: "Harem", value: "3" },
|
||||
{ label: "Historical", value: "330" },
|
||||
{ label: "Horror", value: "343" },
|
||||
{ label: "Josei", value: "324" },
|
||||
{ label: "Martial Arts", value: "14" },
|
||||
{ label: "Mature", value: "4" },
|
||||
{ label: "Mecha", value: "10" },
|
||||
{ label: "Mystery", value: "245" },
|
||||
{ label: "Psychoical", value: "486" },
|
||||
{ label: "Romance", value: "15" },
|
||||
{ label: "School Life", value: "6" },
|
||||
{ label: "Sci-fi", value: "11" },
|
||||
{ label: "Seinen", value: "18" },
|
||||
{ label: "Shoujo", value: "157" },
|
||||
{ label: "Shoujo Ai", value: "851" },
|
||||
{ label: "Shounen", value: "12" },
|
||||
{ label: "Shounen Ai", value: "1692" },
|
||||
{ label: "Slice of Life", value: "7" },
|
||||
{ label: "Smut", value: "281" },
|
||||
{ label: "Sports", value: "1357" },
|
||||
{ label: "Supernatural", value: "16" },
|
||||
{ label: "Tragedy", value: "132" },
|
||||
{ label: "Wuxia", value: "479" },
|
||||
{ label: "Xianxia", value: "480" },
|
||||
{ label: "Xuanhuan", value: "3954" },
|
||||
{ label: "Yaoi", value: "560" },
|
||||
{ label: "Yuri", value: "922" },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new NovelUpdates();
|
||||
@@ -1,146 +0,0 @@
|
||||
import cheerio from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Chapter, Novel, Plugin } from "@typings/plugin";
|
||||
|
||||
export const id = "wuxiaworld";
|
||||
export const name = "Wuxia World (WIP)";
|
||||
export const version = "0.5.0";
|
||||
export const icon = "src/en/wuxiaworld/icon.png";
|
||||
export const site = "https://www.wuxiaworld.com/";
|
||||
|
||||
|
||||
const pluginId = id;
|
||||
const baseUrl = site;
|
||||
|
||||
export const popularNovels: Plugin.popularNovels = async function (page) {
|
||||
const link = `${baseUrl}api/novels`;
|
||||
|
||||
const result = await fetchApi(link, {}, pluginId);
|
||||
const data = await result.json();
|
||||
|
||||
let novels: Novel.Item[] = [];
|
||||
|
||||
data.items.map((novel) => {
|
||||
let name = novel.name;
|
||||
let cover = novel.coverUrl;
|
||||
let url = baseUrl + "novel/" + novel.slug + "/";
|
||||
|
||||
novels.push({
|
||||
name,
|
||||
cover,
|
||||
url,
|
||||
});
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const parseNovelAndChapters: Plugin.parseNovelAndChapters =
|
||||
async function (novelUrl) {
|
||||
const url = novelUrl;
|
||||
console.log(url);
|
||||
const result = await fetchApi(url, {}, pluginId);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
|
||||
const novel: Novel.instance = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.name = loadedCheerio("h1.line-clamp-2").text();
|
||||
|
||||
novel.cover = loadedCheerio("img.absolute").attr("src");
|
||||
|
||||
novel.summary = loadedCheerio(
|
||||
"div.flex-col:nth-child(4) > div > div > span > span"
|
||||
)
|
||||
.text()
|
||||
.trim();
|
||||
|
||||
novel.author = loadedCheerio("div.MuiGrid-container > div > div > div")
|
||||
.filter(function () {
|
||||
return loadedCheerio(this).text().trim() === "Author:";
|
||||
})
|
||||
.next()
|
||||
.text();
|
||||
|
||||
let genres: string[] = [];
|
||||
|
||||
loadedCheerio("a.MuiLink-underlineNone").each(function () {
|
||||
genres.push(loadedCheerio(this).find("div > div").text());
|
||||
});
|
||||
|
||||
novel.genres = genres.join(",");
|
||||
|
||||
novel.status = loadedCheerio("div.font-set-b10")
|
||||
.text()
|
||||
.includes("Complete");
|
||||
|
||||
let chapter: Chapter.Item[] = [];
|
||||
|
||||
loadedCheerio("div.border-b").each(function () {
|
||||
const name = loadedCheerio(this)
|
||||
.find("a > div > div > div > span")
|
||||
.text();
|
||||
const releaseTime = loadedCheerio(this)
|
||||
.find("a > div > div > div > div > span")
|
||||
.text();
|
||||
let url = loadedCheerio(this).find("a").attr("href")?.slice(1);
|
||||
url = `${baseUrl}${url}`;
|
||||
|
||||
chapter.push({ name, releaseTime, url });
|
||||
});
|
||||
|
||||
novel.chapters = chapter;
|
||||
|
||||
return novel;
|
||||
};
|
||||
|
||||
export const parseChapter: Plugin.parseChapter = async function (chapterUrl) {
|
||||
const result = await fetchApi(chapterUrl, {}, pluginId);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = cheerio.load(body);
|
||||
|
||||
loadedCheerio(".chapter-nav").remove();
|
||||
|
||||
loadedCheerio("#chapter-content > script").remove();
|
||||
|
||||
let chapterText = loadedCheerio("#chapter-content").html();
|
||||
|
||||
return chapterText;
|
||||
};
|
||||
|
||||
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
|
||||
const searchUrl = "https://www.wuxiaworld.com/api/novels/search?query=";
|
||||
|
||||
const url = `${searchUrl}${searchTerm}`;
|
||||
|
||||
const result = await fetchApi(url);
|
||||
const data = await result.json();
|
||||
|
||||
const novels: Novel.Item[] = [];
|
||||
|
||||
data.items.map((novel) => {
|
||||
let name = novel.name;
|
||||
let cover = novel.cover;
|
||||
let url = baseUrl + "novel/" + novel.slug + "/";
|
||||
|
||||
novels.push({
|
||||
name,
|
||||
url,
|
||||
cover,
|
||||
});
|
||||
});
|
||||
|
||||
return novels;
|
||||
};
|
||||
|
||||
export const fetchImage: Plugin.fetchImage = async function (url) {
|
||||
const headers = {
|
||||
Referer: baseUrl,
|
||||
};
|
||||
return await fetchFile(url, { headers: headers });
|
||||
};
|
||||
@@ -0,0 +1,145 @@
|
||||
import { load as parseHTML} from "cheerio";
|
||||
import { fetchApi, fetchFile } from "@libs/fetch";
|
||||
import { Plugin } from "@typings/plugin";
|
||||
import { Filters } from "@libs/filterInputs";
|
||||
|
||||
interface NovelEntry {
|
||||
name: string;
|
||||
coverUrl: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
class WuxiaWorld implements Plugin.PluginBase {
|
||||
id = "wuxiaworld";
|
||||
name = "Wuxia World";
|
||||
icon = "src/en/wuxiaworld/icon.png";
|
||||
site = "https://www.wuxiaworld.com/";
|
||||
filters?: Filters | undefined;
|
||||
version = "0.5.0";
|
||||
baseUrl = this.site;
|
||||
async popularNovels(pageNo: number, options: Plugin.PopularNovelsOptions<Filters>): Promise<Plugin.NovelItem[]> {
|
||||
const link = `${this.baseUrl}api/novels`;
|
||||
|
||||
const result = await fetch(link);
|
||||
const data = await result.json();
|
||||
|
||||
let novels: Plugin.NovelItem[] = [];
|
||||
|
||||
data.items.map((novel: NovelEntry) => {
|
||||
let name = novel.name;
|
||||
let cover = novel.coverUrl;
|
||||
let url = this.baseUrl + "novel/" + novel.slug + "/";
|
||||
|
||||
novels.push({
|
||||
name,
|
||||
cover,
|
||||
url,
|
||||
});
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||
const url = novelUrl;
|
||||
const result = await fetchApi(url);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
const novel: Plugin.SourceNovel = {
|
||||
url,
|
||||
chapters: [],
|
||||
};
|
||||
|
||||
novel.name = loadedCheerio("h1.line-clamp-2").text();
|
||||
|
||||
novel.cover = loadedCheerio("img.absolute").attr("src");
|
||||
|
||||
novel.summary = loadedCheerio(
|
||||
"div.flex-col:nth-child(4) > div > div > span > span"
|
||||
)
|
||||
.text()
|
||||
.trim();
|
||||
|
||||
novel.author = loadedCheerio("div.MuiGrid-container > div > div > div")
|
||||
.filter(function () {
|
||||
return loadedCheerio(this).text().trim() === "Author:";
|
||||
})
|
||||
.next()
|
||||
.text();
|
||||
|
||||
let genres: string[] = [];
|
||||
|
||||
loadedCheerio("a.MuiLink-underlineNone").each(function () {
|
||||
genres.push(loadedCheerio(this).find("div > div").text());
|
||||
});
|
||||
|
||||
novel.genres = genres.join(",");
|
||||
|
||||
novel.status = loadedCheerio("div.font-set-b10")
|
||||
.text();
|
||||
|
||||
let chapter: Plugin.ChapterItem[] = [];
|
||||
|
||||
loadedCheerio("div.border-b.border-gray-line-base").each((idx, ele) => {
|
||||
const name = loadedCheerio(ele)
|
||||
.find("a > div > div > div > span")
|
||||
.text();
|
||||
const releaseTime = loadedCheerio(ele)
|
||||
.find("a > div > div > div > div > span")
|
||||
.text();
|
||||
let url = loadedCheerio(ele).find("a").attr("href")?.slice(1);
|
||||
url = `${this.baseUrl}${url}`;
|
||||
|
||||
chapter.push({ name, releaseTime, url });
|
||||
});
|
||||
|
||||
novel.chapters = chapter;
|
||||
|
||||
return novel;
|
||||
}
|
||||
async parseChapter(chapterUrl: string): Promise<string> {
|
||||
const result = await fetchApi(chapterUrl);
|
||||
const body = await result.text();
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
loadedCheerio(".chapter-nav").remove();
|
||||
|
||||
loadedCheerio("#chapter-content > script").remove();
|
||||
|
||||
let chapterText = loadedCheerio("#chapter-content").html() || '';
|
||||
|
||||
return chapterText;
|
||||
}
|
||||
async searchNovels(searchTerm: string, pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||
const searchUrl = "https://www.wuxiaworld.com/api/novels/search?query=";
|
||||
|
||||
const url = `${searchUrl}${searchTerm}`;
|
||||
|
||||
const result = await fetchApi(url);
|
||||
const data = await result.json();
|
||||
|
||||
const novels: Plugin.NovelItem[] = [];
|
||||
|
||||
data.items.map((novel: NovelEntry) => {
|
||||
let name = novel.name;
|
||||
let cover = novel.coverUrl;
|
||||
let url = this.baseUrl + "novel/" + novel.slug + "/";
|
||||
|
||||
novels.push({
|
||||
name,
|
||||
url,
|
||||
cover,
|
||||
});
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
fetchImage(url: string): Promise<string | undefined> {
|
||||
return fetchFile(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new WuxiaWorld();
|
||||