Files
lnreader-plugins/plugins/chinese/linovelib.broken.ts
T
nyagami d818d1758f Beta phase (#801)
* beta host

* Refactor: url -> path (#757)

* refactor: url -> path

* clean up

* fix: json branch

* PagePlugin (#769)

* paginated novel support

* fix: truyenfull

* fix: tunovel, remove pageList

* parsePage support for webtest (#773)

* add parsePage api

* add parsePage UI

* add totalPages

* (FR src) Beta plugin & Added filters (#775)

* noveldeglace:
 - added filters
 - added latest
 - defaultCover fallback
 - change id to noveldeglace.com
 - added complementary infos in summary
 - added cover artist
 - chapters are now parts
 - show volume number for multi volume novels
 - better search
 - fix search page number
 - refactored for plugin beta
 - releaseDate as Date.toString()
 - separted parseNovels
 - separated getCheerio
 - loadedCheerio => $ for shorter lines & simpler test on web

* chireads:
 - fix different parsing for mobile version
 - added filters
 - added author
 - added status
 - added released date
 - refactored for plugin beta
 - separted parseNovels
 - separated getCheerio
 - loadedCheerio => $ for shorter lines & simpler test on web

* wpmangastream => lightnovelwp rework & new sources (#776)

* Added sources:
 - Knoxt (EN)
 - Baca Light Novel (ID)
 - AllNovelRead (ES)
 - DaoTranslate (EN)
 - جنة الروايات (AR)
 - Universal Novel (EN)
 - NovelTR (TR)
old sources auto-generated:
 - GenesisTls (EN)
 - NobleMTL (EN)
wpmangastream => lightnovelwp:
 - added doc
 - added filters in a separte file
 - semi-automatic filter generator
 - made it compatible with version 1.0.X and 1.1.X versions of the theme
 - loadedCheerio => $
 - icons in "/icons/lightnovel/id.png"
 - code cleanup

* removed auto-implemented sources

* Added README.md to lightnovelwp

* removed ai generated text for kolnovel

* added novelsknight

* added centralNovel

* pandamtl accual name

* adding folder plugin/arabic

* Fix: Vietnamese plugins (#777)

* fix: lightnovelvn

* add nettruyen

* add truyenchu

* fix: truyenconect

* Fix: Migrate plugins (#771)

* Add novel.ovh (#756)

* fix

* test

* fix

* date fix + master merge (#761)

* date fix

* Added Brazilian Portuguese E-Novel (#758)

* fixed single digit dates (#767)

Co-authored-by: NaddH1 <naderhorani@hotmail.com>

* fix

* test

* fix order

* Update renovels.ts

* require_chapters

* Delete plugins/english/genesistl.broken.ts

* Update novelTL.ts

---------

Co-authored-by: NaddH1 <naderhorani@hotmail.com>

* (src update) ENG (#772)

* allnovelfull

* bestlightnovel

* divinedaolibrary

* earlynovel

* lightnovelpub

* L

* M

* novelbin

* novelfull

* novelbuddy

* N S

* novel updates

* async sleep

* rename file

* pagePlugin

* Feat: minify js dir (#781)

* Multisrc: HotNovelPub (#784)

* HotNovelPub

* fix search

* Squashed commit of the following:

commit 3b759c68eddc06c0adfb959f775de069a85adca7
Author: nyagami <hoangquan05112002@gmail.com>
Date:   Sat Feb 17 20:33:47 2024 +0700

    Feat: minify js dir (#781)

* jsmin changed to terser

* test

* why I did it.

* test

* Update ranobelib.ts

* fix page

* Update ranobelib.ts

* updated modules

* Update terser.json

Co-authored-by: nyagami <hoangquan05112002@gmail.com>

* Update scripts/multisrc/hotnovelpub/template.ts

Co-authored-by: nyagami <hoangquan05112002@gmail.com>

* terser api

---------

Co-authored-by: nyagami <hoangquan05112002@gmail.com>

* Added sources & fix sources (#787)

* lightnovelwp:
 - fix author name
 - added more localisation of strings
 - README typo
 - error on !r.ok new message

* noveldeglace:
 - fix chapterNumber for parts
 - better dates
 - better search

* Added sources:
 - White Moonlight Novels (EN)
 - System Translation (EN)

* Resized centralnovel.png

* Add Faqwiki.us source (#792)

* Add Faqwiki.us source (beta branch)

* Fix Path, Fix Chapter Parsing, Fix Novel Title

* search novel path fix

* fix chapter parsing. some chapter in this site store their whole text in 1-4 <p>,

* Add WTR-LAB Source (#797)

* Add WTR-LAB Source (Beta Branch)

* Add other language from WTR-LAB source

* change lang to sourceLang

* remove id es tr lang from WTR-LAB

* Update plugins/english/wtrlab.ts

agree

Co-authored-by: nyagami <hoangquan05112002@gmail.com>

* Update plugins/english/wtrlab.ts

agree

Co-authored-by: nyagami <hoangquan05112002@gmail.com>

---------

Co-authored-by: nyagami <hoangquan05112002@gmail.com>

* revert: scripts to master

---------

Co-authored-by: Justin COLLON <48163201+error7404@users.noreply.github.com>
Co-authored-by: Rider21 <58046032+Rider21@users.noreply.github.com>
Co-authored-by: NaddH1 <naderhorani@hotmail.com>
Co-authored-by: K1ngfish3r <trulyihu@gmail.com>
Co-authored-by: bregassa <bregas.sat1@gmail.com>
2024-02-25 14:52:37 +07:00

566 lines
18 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { CheerioAPI, load as parseHTML } from "cheerio";
import { fetchText, fetchFile } from "@libs/fetch";
import { FilterTypes, Filters } from "@libs/filterInputs";
import { Plugin } from "@typings/plugin";
import { NovelStatus } from "@libs/novelStatus";
class Linovelib implements Plugin.PluginBase {
id = "linovelib";
name = "Linovelib";
icon = "src/cn/linovelib/icon.png";
site = "https://www.bilinovel.com";
version = "1.1.1";
async popularNovels(
pageNo: number,
{
showLatestNovels,
filters,
}: Plugin.PopularNovelsOptions<typeof this.filters>
): Promise<Plugin.NovelItem[]> {
const rank = showLatestNovels ? 'lastupdate' : filters.rank.value;
const url = `${this.site}/top/${rank}/${pageNo}.html`;
const body = await fetchText(url);
if (body === '') throw Error('无法获取小说列表,请检查网络');
const loadedCheerio = parseHTML(body);
const novels: Plugin.NovelItem[] = [];
loadedCheerio(".module-rank-booklist .book-layout").each((i, el) => {
let url = loadedCheerio(el).attr("href");
const novelName = loadedCheerio(el).find(".book-title").text();
const novelCover = loadedCheerio(el)
.find("div.book-cover > img")
.attr("data-src");
const novelUrl = this.site + url;
if (!url) 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 fetchText(url);
if (body === '') throw Error('无法获取小说内容,请检查网络');
let loadedCheerio = parseHTML(body);
const novel: Plugin.SourceNovel = {
url,
chapters: [],
};
novel.name = loadedCheerio("#bookDetailWrapper .book-title").text();
novel.cover = loadedCheerio("#bookDetailWrapper img.book-cover").attr(
"src"
);
novel.summary = loadedCheerio("#bookSummary content").text();
novel.author = loadedCheerio(
"#bookDetailWrapper .book-rand-a a"
).text();
const meta = loadedCheerio("#bookDetailWrapper .book-meta").text()
novel.status = meta.includes("完结") ? NovelStatus.Completed : NovelStatus.Ongoing;
novel.genres = loadedCheerio(".tag-small.red")
.children("a")
.map((i, el) => loadedCheerio(el).text())
.toArray()
.join(",");
// Table of Content is on a different page than the summary page
let chapter: Plugin.ChapterItem[] = [];
const idPattern = /\/(\d+)\.html/;
const novelId = url.match(idPattern)?.[1];
const chaptersUrl =
this.site + loadedCheerio("#btnReadBook").attr("href");
const chaptersBody = await fetchText(chaptersUrl);
const chaptersLoadedCheerio = parseHTML(chaptersBody);
let volumeName: string, chapterId: number;
chaptersLoadedCheerio("#volumes .chapter-li:not(.volume-cover)").each((i, el) => {
if (chaptersLoadedCheerio(el).hasClass("chapter-bar")) {
volumeName = chaptersLoadedCheerio(el).text();
return;
} else {
const urlPart = chaptersLoadedCheerio(el)
.find(".chapter-li-a")
.attr("href");
const chapterIdMatch = urlPart?.match(idPattern);
// Sometimes the href attribute does not contain the url, but javascript:cid(0).
// Increment the previous chapter ID should result in the right URL
if (chapterIdMatch) {
chapterId = +chapterIdMatch[1];
} else {
chapterId++;
}
}
const chapterUrl = `${this.site}/novel/${novelId}/${chapterId}.html`;
const chapterName =
volumeName +
" — " +
chaptersLoadedCheerio(el).find(".chapter-index").text().trim();
const releaseDate = null;
if (!chapterId) return;
chapter.push({
name: chapterName,
releaseTime: releaseDate,
url: chapterUrl,
});
});
novel.chapters = chapter;
return novel;
}
async parseChapter(chapterUrl: string): Promise<string> {
let chapterName,
chapterText = "",
hasNextPage,
pageHasNextPage,
pageText = "";
let pageNumber = 1;
/*
* TODO: Maybe there are other ways to get the translation table
* It is embed and encrypted inside readtool.js
* UPDATE: Decrypted, see skillgg
*/
// const mapping_dict = {
// '“': '「',
// '’': '』',
// '': '是',
// '': '不',
// '': '好',
// '': '个',
// '': '开',
// '': '样',
// '': '想',
// '': '说',
// '': '年',
// '': '那',
// '': '她',
// '': '美',
// '': '自',
// '': '家',
// '': '而',
// '': '去',
// '': '都',
// '': '于',
// '': '舔',
// '': '他',
// '': '只',
// '': '看',
// '': '来',
// '': '用',
// '': '道',
// '': '得',
// '': '乳',
// '': '茎',
// '': '肉',
// '': '胸',
// '': '淫',
// '': '性',
// '': '骚',
// '”': '」',
// '': '的',
// '': '当',
// '': '人',
// '': '有',
// '': '上',
// '': '到',
// '': '地',
// '': '中',
// '': '生',
// '': '着',
// '': '和',
// '': '起',
// '': '交',
// '': '以',
// '': '可',
// '': '过',
// '': '能',
// '': '多',
// '': '心',
// '': '小',
// '': '成',
// '': '了',
// '': '把',
// '': '发',
// '': '第',
// '': '子',
// '': '事',
// '': '阴',
// '': '欲',
// '': '里',
// '': '私',
// '': '臀',
// '': '脱',
// '': '唇',
// '‘': '『',
// '': '一',
// '': '我',
// '': '在',
// '': '这',
// '': '们',
// '': '时',
// '': '为',
// '': '你',
// '': '国',
// '': '就',
// '': '要',
// '': '也',
// '': '后',
// '': '没',
// '': '下',
// '': '天',
// '': '对',
// '': '然',
// '': '学',
// '': '之',
// '': '出',
// '': '没',
// '': '如',
// '': '还',
// '': '大',
// '': '作',
// '': '种',
// '': '液',
// '': '呻',
// '': '射',
// '': '穴',
// '': '么',
// '': '裸',
// };
const skillgg: Record<string, string> = {
"\u201c": "\u300c",
"\u201d": "\u300d",
"\u2018": "\u300e",
"\u2019": "\u300f",
"\ue82c": "\u7684",
"\ue852": "\u4e00",
"\ue82d": "\u662f",
"\ue819": "\u4e86",
"\ue856": "\u6211",
"\ue857": "\u4e0d",
"\ue816": "\u4eba",
"\ue83c": "\u5728",
"\ue830": "\u4ed6",
"\ue82e": "\u6709",
"\ue836": "\u8fd9",
"\ue859": "\u4e2a",
"\ue80a": "\u4e0a",
"\ue855": "\u4eec",
"\ue842": "\u6765",
"\ue858": "\u5230",
"\ue80b": "\u65f6",
"\ue81f": "\u5927",
"\ue84a": "\u5730",
"\ue853": "\u4e3a",
"\ue81e": "\u5b50",
"\ue822": "\u4e2d",
"\ue813": "\u4f60",
"\ue85b": "\u8bf4",
"\ue807": "\u751f",
"\ue818": "\u56fd",
"\ue810": "\u5e74",
"\ue812": "\u7740",
"\ue851": "\u5c31",
"\ue801": "\u90a3",
"\ue80c": "\u548c",
"\ue815": "\u8981",
"\ue84c": "\u5979",
"\ue840": "\u51fa",
"\ue848": "\u4e5f",
"\ue835": "\u5f97",
"\ue800": "\u91cc",
"\ue826": "\u540e",
"\ue863": "\u81ea",
"\ue861": "\u4ee5",
"\ue854": "\u4f1a",
"\ue827": "\u5bb6",
"\ue83b": "\u53ef",
"\ue85d": "\u4e0b",
"\ue84d": "\u800c",
"\ue862": "\u8fc7",
"\ue81c": "\u5929",
"\ue81d": "\u53bb",
"\ue860": "\u80fd",
"\ue843": "\u5bf9",
"\ue82f": "\u5c0f",
"\ue802": "\u591a",
"\ue831": "\u7136",
"\ue84b": "\u4e8e",
"\ue837": "\u5fc3",
"\ue829": "\u5b66",
"\ue85e": "\u4e48",
"\ue83a": "\u4e4b",
"\ue832": "\u90fd",
"\ue808": "\u597d",
"\ue841": "\u770b",
"\ue821": "\u8d77",
"\ue845": "\u53d1",
"\ue803": "\u5f53",
"\ue828": "\u6ca1",
"\ue81b": "\u6210",
"\ue83e": "\u53ea",
"\ue820": "\u5982",
"\ue84e": "\u4e8b",
"\ue85a": "\u628a",
"\ue806": "\u8fd8",
"\ue83f": "\u7528",
"\ue833": "\u7b2c",
"\ue811": "\u6837",
"\ue804": "\u9053",
"\ue814": "\u60f3",
"\ue80f": "\u4f5c",
"\ue84f": "\u79cd",
"\ue80e": "\u5f00",
"\ue823": "\u7f8e",
"\ue849": "\u4e73",
"\ue805": "\u9634",
"\ue809": "\u6db2",
"\ue81a": "\u830e",
"\ue844": "\u6b32",
"\ue847": "\u547b",
"\ue850": "\u8089",
"\ue824": "\u4ea4",
"\ue85f": "\u6027",
"\ue817": "\u80f8",
"\ue85c": "\u79c1",
"\ue838": "\u7a74",
"\ue82a": "\u6deb",
"\ue83d": "\u81c0",
"\ue82b": "\u8214",
"\ue80d": "\u5c04",
"\ue839": "\u8131",
"\ue834": "\u88f8",
"\ue846": "\u9a9a",
"\ue825": "\u5507",
};
const addPage = async (pageCheerio: CheerioAPI) => {
const formatPage = async () => {
// Remove JS
pageCheerio("#acontentz .adsbygoogle").remove();
pageCheerio("#acontentz script").remove();
// Load lazyloaded images
pageCheerio("#acontentz img.imagecontent").each((i, el) => {
// Sometimes images are either in data-src or src
const imgSrc =
pageCheerio(el).attr("data-src") ||
pageCheerio(el).attr("src");
if (imgSrc) {
// Clean up img element
pageCheerio(el)
.attr("src", imgSrc)
.removeAttr("data-src")
.removeClass("lazyload");
}
});
// Recover the original character
pageText = pageCheerio("#acontentz").html() || "";
pageText = pageText.replace(
/./g,
(char) => skillgg[char] || char
);
return Promise.resolve();
};
await formatPage();
chapterName =
pageCheerio("#atitle + h3").text() +
" — " +
pageCheerio("#atitle").text();
if (chapterText === "") {
chapterText = "<h2>" + chapterName + "</h2>";
}
chapterText += pageText;
};
const loadPage = async (url: string) => {
const headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Language": "zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en;q=0.6,en-GB;q=0.5,en-US;q=0.4",
"Cache-Control": "no-cache",
}
const body = await fetchText(url, { headers });
const pageCheerio = parseHTML(body);
await addPage(pageCheerio);
pageHasNextPage =
pageCheerio("#footlink a:last").text() === "下一页"
? true
: false;
return { pageCheerio, pageHasNextPage };
};
let url = chapterUrl;
do {
const page = await loadPage(url);
hasNextPage = page.pageHasNextPage;
if (hasNextPage === true) {
pageNumber++;
url = chapterUrl.replace(
/\.html/gi,
`_${pageNumber}` + ".html"
);
}
} while (hasNextPage === true);
return chapterText;
}
async searchNovels(
searchTerm: string,
pageNo: number
): Promise<Plugin.NovelItem[]> {
const url = `${this.site}/search/${encodeURI(searchTerm)}_${pageNo}.html`;
const body = await fetchText(url);
if (body === '') throw Error('无法获取搜索结果,请检查网络');
const pageCheerio = parseHTML(body);
const novels: Plugin.NovelItem[] = [];
// const addPage = async (pageCheerio: CheerioAPI, redirect: string) => {
// const loadSearchResults = () => {
// pageCheerio(".book-ol .book-layout").each((i, el) => {
// let nUrl = pageCheerio(el).attr("href");
// const novelName = pageCheerio(el)
// .find(".book-title")
// .text();
// const novelCover = pageCheerio(el)
// .find("div.book-cover > img")
// .attr("data-src");
// const novelUrl = this.site + nUrl;
// if (!nUrl) return;
// novels.push({
// name: novelName,
// url: novelUrl,
// cover: novelCover,
// });
// });
// };
// const novelResults = pageCheerio(".book-ol a.book-layout");
// if (novelResults.length === 0) {
// } else {
// loadSearchResults();
// }
// if (redirect.length) {
// novels.length = 0;
// const novelName = pageCheerio(
// "#bookDetailWrapper .book-title"
// ).text();
// const novelCover = pageCheerio(
// "#bookDetailWrapper div.book-cover > img"
// ).attr("src");
// const novelUrl =
// this.site +
// pageCheerio("#btnReadBook").attr("href")?.slice(0, -8) +
// ".html";
// novels.push({
// name: novelName,
// url: novelUrl,
// cover: novelCover,
// });
// }
// };
// NOTE: don't know redirect is for what, comment out for now
// const redirect = pageCheerio("div.book-layout").text();
// await addPage(pageCheerio, redirect);
pageCheerio(".book-ol .book-layout").each((i, el) => {
let nUrl = pageCheerio(el).attr("href");
const novelName = pageCheerio(el)
.find(".book-title")
.text();
const novelCover = pageCheerio(el)
.find("div.book-cover > img")
.attr("data-src");
const novelUrl = this.site + nUrl;
if (!nUrl) return;
novels.push({
name: novelName,
url: novelUrl,
cover: novelCover,
});
});
return novels;
}
async fetchImage(url: string): Promise<string | undefined> {
const headers = new Headers(
{
"Referer": `${this.site}/`,
}
)
return await fetchFile(url, { headers });
}
filters = {
rank: {
label: "排行榜",
value: "monthvisit",
options: [
{ label: "月点击榜", value: "monthvisit" },
{ label: "周点击榜", value: "weekvisit" },
{ label: "月推荐榜", value: "monthvote" },
{ label: "周推荐榜", value: "weekvote" },
{ label: "月鲜花榜", value: "monthflower" },
{ label: "周鲜花榜", value: "weekflower" },
{ label: "月鸡蛋榜", value: "monthegg" },
{ label: "周鸡蛋榜", value: "weekegg" },
{ label: "最近更新", value: "lastupdate" },
{ label: "最新入库", value: "postdate" },
{ label: "收藏榜", value: "goodnum" },
{ label: "新书榜", value: "newhot" },
],
type: FilterTypes.Picker,
},
} satisfies Filters;
}
export default new Linovelib();