import { load as parseHTML } from "cheerio"; import { fetchApi, fetchFile } from "@libs/fetch"; import { Plugin } from "@typings/plugin"; import { Filters, FilterTypes } 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/"; async popularNovels( page: number, { filters }: Plugin.PopularNovelsOptions ): Promise { let link = `${this.site}novel-list/?`; link += `orderby=${filters.order.value}`; link += `&order=${filters.sort.value}`; link += `&status=${filters.storyStatus.value}`; link += `&pg=${page}` const headers = new Headers(); headers.append('Alt-Used', 'www.mtlnovel.com'); const body = await fetch(link, {headers}).then((result) => result.text()); const loadedCheerio = parseHTML(body); const novels: Plugin.NovelItem[] = []; loadedCheerio("div.box.wide").each((i,el) => { const novelName = loadedCheerio(el) .find("a.list-title") .text() .slice(4); const novelCover = loadedCheerio(el).find("amp-img").attr("src"); const novelUrl = loadedCheerio(el).find("a.list-title").attr("href"); if (!novelUrl) return; const novel = { name: novelName, cover: novelCover, path: novelUrl.replace(this.site, ''), }; novels.push(novel); }); return novels; } async parseNovel(novelPath: string): Promise { const headers = new Headers(); headers.append('Referer', `${this.site}novel-list/`) headers.append('Alt-Used', 'www.mtlnovel.com'); const body = await fetch( this.site + novelPath, { headers }, ).then((result) => result.text()); let loadedCheerio = parseHTML(body); const novel: Plugin.SourceNovel = { path: novelPath, name: loadedCheerio("h1.entry-title").text() || 'Untitled', cover: loadedCheerio(".nov-head > amp-img").attr("src"), summary: loadedCheerio("div.desc > h2").next().text().trim(), author: loadedCheerio("#author").text(), status: loadedCheerio("#status").text(), genres: loadedCheerio("#genre").text().replace(/\s+/g, ""), chapters: [], }; const chapterListUrl = this.site + novelPath + "chapter-list/"; const getChapters = async () => { const listResult = await fetch(chapterListUrl, {headers}); const listBody = await listResult.text(); loadedCheerio = parseHTML(listBody); const chapter: Plugin.ChapterItem[] = []; loadedCheerio("div.ch-list") .find("a.ch-link") .each((i,el) => { const chapterName = loadedCheerio(el) .text() .replace("~ ", ""); const releaseDate = null; const chapterUrl = loadedCheerio(el).attr("href"); if (!chapterUrl) return; chapter.push({ path: chapterUrl.replace(this.site, ''), name: chapterName, releaseTime: releaseDate, }); }); return chapter.reverse(); } novel.chapters = await getChapters(); return novel; } async parseChapter(chapterPath: string): Promise { const headers = new Headers(); headers.append('Alt-Used', 'www.mtlnovel.com'); const body = await fetch(this.site + chapterPath, {headers}).then((r) => r.text()); const loadedCheerio = parseHTML(body); const chapterText = loadedCheerio("div.par").html() || ''; return chapterText; } async searchNovels(searchTerm: string, pageNo: number): Promise { const headers = new Headers(); headers.append('Alt-Used', 'www.mtlnovel.com'); const searchUrl = this.site + "wp-admin/admin-ajax.php?action=autosuggest&q=" + searchTerm + "&__amp_source_origin=https%3A%2F%2Fwww.mtlnovel.com"; const res = await fetch(searchUrl, {headers}); const result = await res.json(); const 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(this.site, ''); const novel = { name: novelName, cover: novelCover, path: novelUrl }; novels.push(novel); }); return novels; } async fetchImage(url: string): Promise { const headers = new Headers(); headers.append('Alt-Used', 'www.mtlnovel.com'); return fetchFile(url, {headers}); } filters = { order: { value: "view", label: "Order by", options: [ { label: "Date", value: "date" }, { label: "Name", value: "name" }, { label: "Rating", value: "rating" }, { label: "View", value: "view" }, ], type: FilterTypes.Picker, }, sort: { value: "desc", label: "Sort by", options: [ { label: "Descending", value: "desc" }, { label: "Ascending", value: "asc" }, ], type: FilterTypes.Picker, }, storyStatus: { value: "all", label: "Status", options: [ { label: "All", value: "all" }, { label: "Ongoing", value: "ongoing" }, { label: "Complete", value: "completed" }, ], type: FilterTypes.Picker, }, } satisfies Filters; } export default new MTLNovel();