From 38fc1860da6eb0d7bc68c00c52d17179e5d6b7a7 Mon Sep 17 00:00:00 2001 From: K1ngfish3r Date: Mon, 29 Jan 2024 23:27:42 +0800 Subject: [PATCH] (add src) LNMTL & ScribbleHub (#738) * quick * quick panda fix * remove console.log --- icons/src/en/lnmtl/icon.png | Bin 0 -> 2886 bytes icons/src/en/scribblehub/icon.png | Bin 0 -> 10734 bytes plugins/english/lnmtl.ts | 224 +++++++++++++++++++++ plugins/english/novelupdates.ts | 32 ++- plugins/english/pandaMTL.ts | 40 +--- plugins/english/scribblehub.ts | 311 ++++++++++++++++++++++++++++++ 6 files changed, 565 insertions(+), 42 deletions(-) create mode 100644 icons/src/en/lnmtl/icon.png create mode 100644 icons/src/en/scribblehub/icon.png create mode 100644 plugins/english/lnmtl.ts create mode 100644 plugins/english/scribblehub.ts diff --git a/icons/src/en/lnmtl/icon.png b/icons/src/en/lnmtl/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5796edcdf8b4ddd64649994664a9615afdbd1888 GIT binary patch literal 2886 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 literal 0 HcmV?d00001 diff --git a/plugins/english/lnmtl.ts b/plugins/english/lnmtl.ts new file mode 100644 index 0000000..23451e1 --- /dev/null +++ b/plugins/english/lnmtl.ts @@ -0,0 +1,224 @@ +import { load as parseHTML } from "cheerio"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { Plugin } from "@typings/plugin"; + +class LnMTLPlugin implements Plugin.PluginBase { + id = "lnmtl"; + name = "LnMTL"; + icon = "src/en/lnmtl/icon.png"; + site = "https://lnmtl.com/"; + version = "1.0.0"; + + async popularNovels( + page: number, + { filters }: Plugin.PopularNovelsOptions + ): Promise { + let link = this.site + "novel?"; + + link += `orderBy=${filters.order.value}`; + link += `&order=${filters.sort.value}`; + link += `&filter=${filters.storyStatus.value}`; + link += `&page=${page}`; + + const body = await fetchApi(link).then((result) => + result.text() + ); + + const loadedCheerio = parseHTML(body); + const novels: Plugin.NovelItem[] = []; + + 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; + }; + + async parseNovelAndChapters(novelUrl: string): Promise { + 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(".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 () { + const detailName = loadedCheerio(this).find("dt").text().trim(); + const 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() + .find('a') + .map((i,el) => loadedCheerio(el).text()) + .toArray() + .join(","); + + const delay = (ms: number) => new Promise((res) => setTimeout(res, ms)); + let volumes = JSON.parse( + loadedCheerio("main") + .next() + .html() + ?.match(/lnmtl.volumes = \[(.*?)\]/)![0] + ?.replace("lnmtl.volumes = ", "") || "" + ); + + const chapter: Plugin.ChapterItem[] = []; + + volumes = volumes.map((volume: { id: number; }) => volume.id); + + for (const volume of volumes) { + const volumeData = await fetchApi( + `${this.site}chapter?page=1&volumeId=${volume}` + ); + const volumePage = await volumeData.json(); + + // volumeData = volumeData.data.map((volume) => volume.slug); + + for (let i = 1; i <= volumePage.last_page; i++) { + const chapterData = await fetchApi( + `${this.site}chapter?page=${i}&volumeId=${volume}` + ); + const chapterInfo = await chapterData.json(); + + const chapterDetails = chapterInfo.data.map( + (chapter: { number: number; title: string; slug: string; created_at: string; }) => ({ + name: `#${chapter.number} ${chapter.title}`, + url: `${this.site}chapter/${chapter.slug}`, + releaseTime: new Date (chapter.created_at).toISOString(), //converts time obtained to UTC +0, TODO: Make it not convert + })); + + chapter.push(...chapterDetails); + } + await delay(1000); + } + + novel.chapters = chapter; + + return novel; + }; + + async parseChapter(chapterUrl: string): Promise { + const result = await fetchApi(chapterUrl); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + loadedCheerio('.original, script').remove(); + loadedCheerio('sentence.translated').wrap('

'); + + let chapterText = loadedCheerio('.chapter-body').html()?.replace(/„/g, '“'); + + if (!chapterText) { + chapterText = loadedCheerio(".alert.alert-warning").text(); + } + + return chapterText; + }; + + async searchNovels( + searchTerm: string, + pageNo: number + ): Promise { + const body = await fetchApi(this.site).then((r) => r.text()); + const loadedCheerio = parseHTML(body); + + const list = loadedCheerio('footer') + .next() + .next() + .html() + ?.match(/prefetch: '\/(.*json)/)![1]; + + const search = await fetch(`${this.site}${list}`); + const data = await search.json(); + + const nov = data.filter((res: { name: string; }) => + res.name.toLowerCase().includes(searchTerm.toLowerCase()), + ); + + const novels: Plugin.NovelItem[] = []; + nov.map((res: { name: string; slug: string; image: string; }) => { + const novelName = res.name; + const novelUrl = `${this.site}novel/${res.slug}`; + const novelCover = res.image; + + const novel = { + url: novelUrl, + name: novelName, + cover: novelCover, + }; + novels.push(novel); + }); + return novels; + }; + + async fetchImage(url: string): Promise { + return await fetchFile(url); + } + + filters = { + order: { + value: "favourites", + label: "Order by", + options: [ + { label: "Favourites", value: "favourites" }, + { label: "Name", value: "name" }, + { label: "Addition Date", value: "date" }, + ], + 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: "Finished", value: "finished" }, + ], + type: FilterTypes.Picker, + }, + } satisfies Filters; +} + +export default new LnMTLPlugin(); \ No newline at end of file diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index bc89ad9..d20d026 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -104,6 +104,7 @@ class NovelUpdates implements Plugin.PluginBase { .map((i, el) => loadedCheerio(el).text()) .toArray() .join(","); + novel.status = loadedCheerio("#editstatus").text().includes("Ongoing") ? "Ongoing" : "Completed"; @@ -137,9 +138,7 @@ class NovelUpdates implements Plugin.PluginBase { 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"); @@ -184,6 +183,12 @@ class NovelUpdates implements Plugin.PluginBase { let isWattpad = chapterUrl.toLowerCase().includes("wattpad"); + let isAnomalously = chapterUrl.toLowerCase().includes('anotivereads'); + + let isBlossomTranslation = chapterUrl + .toLowerCase() + .includes('blossomtranslation'); + let isLightNovelsTls = chapterUrl .toLowerCase() .includes("lightnovelstranslations"); @@ -230,8 +235,10 @@ class NovelUpdates implements Plugin.PluginBase { } else if (isTumblr) { chapterText = loadedCheerio(".post").html()!; } else if (isBlogspot) { - loadedCheerio(".post-share-buttons").remove(); - chapterText = loadedCheerio(".entry-content").html()!; + loadedCheerio('.post-share-buttons').remove(); + chapterText = + loadedCheerio('.entry-content').html() || + loadedCheerio('#post-body').html()!; } else if (isHostedNovel) { chapterText = loadedCheerio(".chapter").html()!; } else if (isScribbleHub) { @@ -241,9 +248,19 @@ class NovelUpdates implements Plugin.PluginBase { } else if (isTravisTranslation) { chapterText = loadedCheerio(".reader-content").html()!; } else if (isLightNovelsTls) { - chapterText = loadedCheerio(".text_story").html()!; + chapterText = loadedCheerio('.text_story').html()!; + } else if (isBlossomTranslation) { + chapterText = loadedCheerio('.manga-child-content').html()!; + } else if (isAnomalously) { + chapterText = loadedCheerio('#comic').html()!; } else if (isiNovelTranslation) { - chapterText = loadedCheerio(".chakra-skeleton").html()!; + const link = 'https://api.' + chapterUrl.slice(8); + const json = await fetchApi(link).then(r => r.json()); + chapterText = json.content.replace(/\n/g, '
'); + if (json.notes) { + chapterText += + '


TL Notes:
' + json.notes.replace(/\n/g, '
'); + } } else if (isWordPress) { /** * Remove wordpress bloat tags @@ -308,8 +325,7 @@ class NovelUpdates implements Plugin.PluginBase { } async searchNovels(searchTerm: string, page: number): Promise { const url =`${this.site}page/${page}/?s=${searchTerm}&post_type=seriesplans`; - const headers = new Headers(); - const result = await fetchApi(url, { headers }); + const result = await fetchApi(url); const body = await result.text(); const loadedCheerio = parseHTML(body); diff --git a/plugins/english/pandaMTL.ts b/plugins/english/pandaMTL.ts index 92232ce..a424d2f 100644 --- a/plugins/english/pandaMTL.ts +++ b/plugins/english/pandaMTL.ts @@ -9,23 +9,22 @@ class PandaMTL implements Plugin.PluginBase { icon = "src/en/wordpress/icon.png"; site = "https://pandamtl.com/"; version = "1.0.0"; - + + async popularNovels( pageNo: number, { filters }: Plugin.PopularNovelsOptions ): Promise { let link = `${this.site}series/?page=${pageNo}`; - if (filters.genres.value.length) { + if (filters.genres.value.length) link += filters.genres.value.map((i) => `&genre[]=${i}`).join(""); - } if (filters.type.value.length) link += filters.type.value.map((i) => `&lang[]=${i}`).join(""); - link += "&status=" + (filters?.status ? filters.status : ""); - - link += "&order=" + (filters?.order ? filters.order : "popular"); + link += "&status=" + filters.status.value; + link += "&order=" + filters.order.value; const headers = new Headers(); const body = await fetchApi(link, { headers }).then((result) => @@ -177,18 +176,13 @@ class PandaMTL implements Plugin.PluginBase { filters = { order: { label: "Sort By", - value: "", + value: "popular", options: [ { label: "Default", value: "" }, - { label: "A-Z", value: "title" }, - { label: "Z-A", value: "titlereverse" }, - { label: "Latest Update", value: "update" }, - { label: "Latest Added", value: "latest" }, - { label: "Popular", value: "popular" }, ], type: FilterTypes.Picker, @@ -198,11 +192,8 @@ class PandaMTL implements Plugin.PluginBase { value: "", options: [ { label: "All", value: "" }, - { label: "Ongoing", value: "ongoing" }, - { label: "Hiatus", value: "hiatus" }, - { label: "Completed", value: "completed" }, ], type: FilterTypes.Picker, @@ -212,7 +203,6 @@ class PandaMTL implements Plugin.PluginBase { label: "Type", options: [ { label: "Light Novel (KR)", value: "light-novel-kr" }, - { label: "Web Novel", value: "web-novel" }, ], type: FilterTypes.CheckboxGroup, @@ -222,41 +212,23 @@ class PandaMTL implements Plugin.PluginBase { value: [], options: [ { label: "Action", value: "action" }, - { label: "Adult", value: "adult" }, - { label: "Adventure", value: "adventure" }, - { label: "Comedy", value: "comedy" }, - { label: "Ecchi", value: "ecchi" }, - { label: "Fantasy", value: "fantasy" }, - { label: "Harem", value: "harem" }, - { label: "Josei", value: "josei" }, - { label: "Martial Arts", value: "martial-arts" }, - { label: "Mature", value: "mature" }, - { label: "Romance", value: "romance" }, - { label: "School Life", value: "school-life" }, - { label: "Sci-fi", value: "sci-fi" }, - { label: "Seinen", value: "seinen" }, - { label: "Slice of Life", value: "slice-of-life" }, - { label: "Smut", value: "smut" }, - { label: "Sports", value: "sports" }, - { label: "Supernatural", value: "supernatural" }, - { label: "Tragedy", value: "tragedy" }, ], type: FilterTypes.CheckboxGroup, diff --git a/plugins/english/scribblehub.ts b/plugins/english/scribblehub.ts new file mode 100644 index 0000000..1b1f5b3 --- /dev/null +++ b/plugins/english/scribblehub.ts @@ -0,0 +1,311 @@ +import { CheerioAPI, load as parseHTML } from "cheerio"; +import { fetchApi, fetchFile } from "@libs/fetch"; +import { FilterTypes, Filters } from "@libs/filterInputs"; +import { Plugin } from "@typings/plugin"; +import dayjs from "dayjs"; + +class ScribbleHubPlugin implements Plugin.PluginBase { + id = "scribblehub"; + name = "ScribbleHub"; + icon = "src/en/scribblehub/icon.png"; + site = "https://www.scribblehub.com/"; + version = "1.0.0"; + parseNovels(loadedCheerio: CheerioAPI){ + const novels: Plugin.NovelItem[] = []; + + loadedCheerio('.search_main_box').each(function () { + const novelName = loadedCheerio(this).find('.search_title > a').text(); + const novelCover = loadedCheerio(this).find('.search_img > img').attr('src'); + 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; + } + + async popularNovels( + page: number, + { showLatestNovels, filters }: Plugin.PopularNovelsOptions + ): Promise { + let link = `${this.site}`; + if (showLatestNovels) + link += "latest-series/" + else // if (filters.genres.value.include?.length || + // filters.genres.value.exclude?.length) + link += "series-finder/?sf=1" + // else + // link += "series-ranking/"; + // TODO, series-ranking filters when hideOnSelect come out + + if (filters.genres.value.include?.length) + link += '&gi=' + filters.genres.value.include.join(','); + + if (filters.genres.value.exclude?.length) + link += '&ge=' + filters.genres.value.exclude.join(','); + + if (filters.genres.value.include?.length || filters.genres.value.exclude?.length) + link += '&mgi=' + filters.genre_operator.value; + + if (filters.content_warning.value.include?.length) + link += '&cti=' + filters.content_warning.value.include.join(','); + + if (filters.content_warning.value.exclude?.length) + link += '&cte=' + filters.content_warning.value.exclude.join(','); + + if (filters.content_warning.value.include?.length || + filters.content_warning.value.exclude?.length) + link += '&mct' + filters.content_warning_operator.value; + + link += '&sort=' + filters.sort.value; + link += '&order=' + filters.order.value; + + const body = await fetchApi(link).then((result) => + result.text() + ); + + const loadedCheerio = parseHTML(body); + return this.parseNovels(loadedCheerio); + } + + async parseNovelAndChapters(novelUrl: string): Promise { + 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('.fic_title').text(); + + novel.cover = loadedCheerio('.fic_image > img').attr('src'); + + novel.summary = loadedCheerio('.wi_fic_desc').text(); + + novel.genres = loadedCheerio('.fic_genre') + .map((i, el) => loadedCheerio(el).text()) + .toArray() + .join(","); + + novel.status = loadedCheerio('.rnd_stats') + .next() + .text() + .includes('Ongoing') + ? 'Ongoing' + : 'Completed'; + + novel.author = loadedCheerio('.auth_name_fic').text(); + + let formData = new FormData(); + formData.append('action', 'wi_getreleases_pagination'); + formData.append('pagenum', '-1'); + formData.append('mypostid', novelUrl.split('/')[4]); + + const data = await fetchApi( + `${this.site}wp-admin/admin-ajax.php`, + { + method: 'POST', + body: formData, + }, + ); + const text = await data.text(); + + loadedCheerio = parseHTML(text); + + const chapter: Plugin.ChapterItem[] = []; + + const parseISODate = (date: string) => { + if (date.includes("ago")) { + const dayJSDate = dayjs(new Date()); // today + const timeAgo = date.match(/\d+/)?.[0] || ""; + const timeAgoInt = parseInt(timeAgo, 10); + + if (!timeAgo) return date; // there is no number! + + if (date.includes("hours ago") || date.includes("hour ago")) { + dayJSDate.subtract(timeAgoInt, "hours"); // go back N hours + } + + if (date.includes("days ago") || date.includes("day ago")) { + dayJSDate.subtract(timeAgoInt, "days"); // go back N days + } + + if (date.includes("months ago") || date.includes("month ago")) { + dayJSDate.subtract(timeAgoInt, "months"); // go back N months + } + + return dayJSDate.toISOString(); + } + return null; + }; + + loadedCheerio('.toc_w').each(function () { + const chapterName = loadedCheerio(this).find('.toc_a').text(); + const releaseDate = loadedCheerio(this).find('.fic_date_pub').text(); + const chapterUrl = loadedCheerio(this).find('a').attr('href'); + + if (!chapterUrl) return; + chapter.push({ + name: chapterName, + releaseTime: parseISODate(releaseDate), + url: chapterUrl, + }); + }); + + novel.chapters = chapter.reverse(); + + return novel; + } + + async parseChapter(chapterUrl: string): Promise { + const result = await fetchApi(chapterUrl); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + + let chapterText = loadedCheerio('div.chp_raw').html() || ''; + return chapterText; + } + + async searchNovels( + searchTerm: string, + pageNo: number + ): Promise { + const url =`${this.site}?s=${searchTerm}&post_type=fictionposts`; + const result = await fetchApi(url); + const body = await result.text(); + + const loadedCheerio = parseHTML(body); + return this.parseNovels(loadedCheerio); + } + + async fetchImage(url: string): Promise { + return await fetchFile(url); + } + + filters = { + sort: { + label: "Sort Results By", + value: "ratings", + options: [ + { label: "Chapters", value: "chapters" }, + { label: "Chapters per week", value: "frequency" }, + { label: "Date Added", value: "dateadded" }, + { label: "Favorites", value: "favorites" }, + { label: "Last Updated", value: "lastchdate" }, + { label: "Number of Ratings", value: "numofrate" }, + { label: "Pages", value: "pages" }, + { label: "Pageviews", value: "pageviews" }, + { label: "Ratings", value: "ratings" }, + { label: "Readers", value: "readers" }, + { label: "Reviews", value: "reviews" }, + { label: "Total Words", value: "totalwords" }, + ], + type: FilterTypes.Picker, + }, + order: { + label: "Order by", + value: "desc", + options: [ + { label: "Descending", value: "desc" }, + { label: "Ascending", value: "asc" }, + ], + type: FilterTypes.Picker, + }, + storyStatus: { + label: "Story Status (Translation)", + value: "", + options: [ + { label: "All", value: "all" }, + { label: "Completed", value: "completed" }, + { label: "Ongoing", value: "ongoing" }, + { label: "Hiatus", value: "hiatus" }, + ], + type: FilterTypes.Picker, + }, + genres: { + label: 'Genres', + value: { + include: [], + exclude: [], + }, + options: [ + { label: 'Action', value: '9' }, + { label: 'Adult', value: '902' }, + { label: 'Adventure', value: '8' }, + { label: 'Boys Love', value: '891' }, + { label: 'Comedy', value: '7' }, + { label: 'Drama', value: '903' }, + { label: 'Ecchi', value: '904' }, + { label: 'Fanfiction', value: '38' }, + { label: 'Fantasy', value: '19' }, + { label: 'Gender Bender', value: '905' }, + { label: 'Girls Love', value: '892' }, + { label: 'Harem', value: '1015' }, + { label: 'Historical', value: '21' }, + { label: 'Horror', value: '22' }, + { label: 'Isekai', value: '37' }, + { label: 'Josei', value: '906' }, + { label: 'LitRPG', value: '1180' }, + { label: 'Martial Arts', value: '907' }, + { label: 'Mature', value: '20' }, + { label: 'Mecha', value: '908' }, + { label: 'Mystery', value: '909' }, + { label: 'Psychological', value: '910' }, + { label: 'Romance', value: '6' }, + { label: 'School Life', value: '911' }, + { label: 'Sci-fi', value: '912' }, + { label: 'Seinen', value: '913' }, + { label: 'Slice of Life', value: '914' }, + { label: 'Smut', value: '915' }, + { label: 'Sports', value: '916' }, + { label: 'Supernatural', value: '5' }, + { label: 'Tragedy', value: '901' }, + ], + type: FilterTypes.ExcludableCheckboxGroup, + }, + genre_operator : { + value: 'and', + label: 'Genre And/Or', + options: [ + { label: 'OR', value: 'or' }, + { label: 'AND', value: 'and' }, + ], + type: FilterTypes.Picker, + }, + content_warning: { + value: { + include: [], + exclude: [], + }, + label: "Mature Content", + options: [ + { label: 'Gore', value: '48' }, + { label: 'Sexual Content', value: '50' }, + { label: 'Strong Language', value: '49' }, + ], + type: FilterTypes.ExcludableCheckboxGroup + }, + content_warning_operator: { + value: 'and', + label: 'Mature Content And/Or', + options: [ + { label: 'OR', value: 'or' }, + { label: 'AND', value: 'and' }, + ], + type: FilterTypes.Picker, + }, + } satisfies Filters; +} + +export default new ScribbleHubPlugin();