import { Plugin } from "@typings/plugin"; import { fetchApi, fetchFile } from "@libs/fetch"; import { NovelStatus } from "@libs/novelStatus"; import { load as parseHTML } from "cheerio"; import qs from "qs"; class FreeWebNovel implements Plugin.PluginBase { id = "FWN.com"; name = "Free Web Novel"; site = "https://freewebnovel.com"; version = "1.0.0"; icon = "src/en/freewebnovel/icon.png"; async popularNovels( page: number, { showLatestNovels }: Plugin.PopularNovelsOptions, ): Promise { const sort = showLatestNovels ? "/latest-release-novels/" : "/completed-novels/"; const body = await fetchApi(this.site + sort + page).then((res) => res.text()); const loadedCheerio = parseHTML(body); const novels: Plugin.NovelItem[] = loadedCheerio(".li-row") .map((index, element) => ({ name: loadedCheerio(element).find(".tit").text() || "", cover: loadedCheerio(element).find("img").attr("src"), path: loadedCheerio(element).find("h3 > a").attr("href") || "", })) .get() .filter((novel) => novel.name && novel.path); return novels; } async parseNovel(novelPath: string): Promise { const body = await fetchApi(this.site + novelPath).then((res) => res.text()); const loadedCheerio = parseHTML(body); const novel: Plugin.SourceNovel = { path: novelPath, name: loadedCheerio("h1.tit").text(), cover: loadedCheerio(".pic > img").attr("src"), summary: loadedCheerio(".inner").text().trim(), }; novel.genres = loadedCheerio("[title=Genre]") .next() .text() .replace(/[\t\n]/g, ""); novel.author = loadedCheerio("[title=Author]") .next() .text() .replace(/[\t\n]/g, ""); novel.status = loadedCheerio("[title=Status]") .next() .text() .replace(/[\t\n]/g, ""); const chapters: Plugin.ChapterItem[] = loadedCheerio("#idData > li > a") .map((chapterIndex, element) => ({ name: loadedCheerio(element).attr("title") || "Chapter " + (chapterIndex + 1), path: loadedCheerio(element).attr("href") || novelPath.replace(".html", "/chapter-" + (chapterIndex + 1) + ".html"), releaseTime: null, chapterNumber: chapterIndex + 1, })) .get(); novel.chapters = chapters; return novel; } async parseChapter(chapterPath: string): Promise { const body = await fetchApi(this.site + chapterPath).then((res) => res.text()); const loadedCheerio = parseHTML(body); const chapterText = loadedCheerio("div.txt").html() || ""; return chapterText; } async searchNovels(searchTerm: string): Promise { const body = await fetchApi(this.site + "/search/", { headers: { "Content-Type": "application/x-www-form-urlencoded", Referer: this.site, Origin: this.site, }, method: "POST", body: qs.stringify({ searchkey: searchTerm }), }).then((res) => res.text()); const loadedCheerio = parseHTML(body); const novels: Plugin.NovelItem[] = loadedCheerio(".li-row > .li > .con") .map((index, element) => ({ name: loadedCheerio(element).find(".tit").text() || "", cover: loadedCheerio(element).find(".pic > a > img").attr("src"), path: loadedCheerio(element).find("h3 > a").attr("href") || "", })) .get() .filter((novel) => novel.name && novel.path); return novels; } fetchImage = fetchFile; } export default new FreeWebNovel();