import { load as parseHTML } from "cheerio";
import { fetchApi, fetchFile } from "@libs/fetch";
import { Chapter, Novel, Plugin } from "@typings/plugin";
const pluginId = "lnmtl";
const baseUrl = "https://lnmtl.com/";
export const popularNovels: Plugin.popularNovels = async function (page) {
let url = baseUrl + "novel?page=" + page;
const body = await fetchApi(url, {}, pluginId).then((r) => r.text());
const loadedCheerio = parseHTML(body);
let novels: Novel.Item[] = [];
loadedCheerio(".media").each(function () {
const novelName = loadedCheerio(this).find("h4").text();
const novelCover = loadedCheerio(this).find("img").attr("src");
const novelUrl = loadedCheerio(this).find("h4 > 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());
const loadedCheerio = parseHTML(body);
const novel: Novel.instance = {
url,
chapters: [],
};
novel.name = loadedCheerio(".novel-name").text();
novel.cover = loadedCheerio("div.novel").find("img").attr("src");
novel.summary = loadedCheerio("div.description").text().trim();
loadedCheerio(".panel-body > dl").each(function () {
let detailName = loadedCheerio(this).find("dt").text().trim();
let detail = loadedCheerio(this).find("dd").text().trim();
switch (detailName) {
case "Authors":
novel.author = detail;
break;
case "Current status":
novel.status = detail;
break;
}
});
novel.genres = loadedCheerio('.panel-heading:contains(" Genres ")')
.next()
.text()
.trim()
.replace(/\s\s/g, ",");
let volumes = JSON.parse(
loadedCheerio("main")
.next()
.html()
.match(/lnmtl.volumes = \[(.*?)\]/).[0]
.replace("lnmtl.volumes = ", "")
);
let chapters: Chapter.Item[] = [];
volumes = volumes.map((volume) => volume.id);
for (const volume of volumes) {
let volumeData = await fetchApi(
`https://lnmtl.com/chapter?page=1&volumeId=${volume}`
);
volumeData = await volumeData.json();
// volumeData = volumeData.data.map((volume) => volume.slug);
for (let i = 1; i <= volumeData.last_page; i++) {
let chapterData = await fetchApi(
`https://lnmtl.com/chapter?page=${i}&volumeId=${volume}`
);
chapterData = await chapterData.json();
chapterData = chapterData.data.map((chapter) => ({
name: `#${chapter.number} ${chapter.title}`,
url: `${baseUrl}chapter/${chapter.slug}`,
releaseTime: chapter.created_at,
}));
chapters.push(...chapterData);
}
}
novel.chapters = chapters;
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(".original").replaceWith("
");
let chapterText = loadedCheerio(".chapter-body").html();
if (!chapterText) {
chapterText = loadedCheerio(".alert.alert-warning").text();
}
return chapterText;
};
export const searchNovels: Plugin.searchNovels = async function (searchTerm) {
const url = "https://lnmtl.com/term";
const body = await fetchApi(url, {}, pluginId).then((r) => r.text());
const loadedCheerio = parseHTML(body);
let novels:Novel.Item[] = loadedCheerio("footer")
.next()
.next()
.html()
.match(/local: \[(.*?)\]/)[0]
.replace("local: ", "");
novels = JSON.parse(novels);
novels = novels.filter((novel) =>
novel.name.toLowerCase().includes(searchTerm.toLowerCase())
);
novels = novels.map((novel) => ({
name: novel.name,
url: novel.slug,
cover: novel.image,
}));
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: "LNMTL",
version: "1.0.0",
icon: "src/en/lnmtl/icon.png",
site: baseUrl,
protected: true,
fetchImage,
popularNovels,
parseNovelAndChapters,
parseChapter,
searchNovels,
};