fix: Quanben and Add Filters (#1877)
* fix quanben +added filters * remove https://69shu.biz
This commit is contained in:
@@ -1,201 +0,0 @@
|
||||
import { load as parseHTML } from 'cheerio';
|
||||
import { fetchText } from '@/lib/fetch';
|
||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||
import { Plugin } from '@/types/plugin';
|
||||
import { encode } from 'urlencode';
|
||||
import { NovelStatus } from '@libs/novelStatus';
|
||||
|
||||
class XinShu69 implements Plugin.PluginBase {
|
||||
id = '69xinshu';
|
||||
name = '69书吧';
|
||||
icon = 'src/cn/69xinshu/icon.png';
|
||||
site = 'https://69shu.biz';
|
||||
version = '0.1.2';
|
||||
|
||||
async popularNovels(
|
||||
pageNo: number,
|
||||
{ filters }: Plugin.PopularNovelsOptions<typeof this.filters>,
|
||||
): Promise<Plugin.NovelItem[]> {
|
||||
let url: string;
|
||||
if (pageNo === 1) {
|
||||
url = `${this.site}/novels/class/${filters.class.value}.htm`;
|
||||
} else {
|
||||
url = `${this.site}/ajax_novels/class/${filters.class.value}/${pageNo}.htm`;
|
||||
}
|
||||
|
||||
const body = await fetchText(url, {}, 'gbk');
|
||||
if (body === '') throw Error('无法获取小说列表,请检查网络');
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
const novels: Plugin.NovelItem[] = [];
|
||||
|
||||
const novelsList =
|
||||
pageNo === 1
|
||||
? loadedCheerio('.newlistbox > ul > li')
|
||||
: loadedCheerio('li');
|
||||
|
||||
novelsList.each((i, e) => {
|
||||
const novelUrl = loadedCheerio(e).find('div > h3 > a').attr('href');
|
||||
if (novelUrl) {
|
||||
const novelName = loadedCheerio(e).find('div > h3 > a').text();
|
||||
const novelCover = loadedCheerio(e).find('a > img').attr('data-src');
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
cover: novelCover,
|
||||
path: novelUrl.replace(this.site, ''),
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
}
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
|
||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||
const url = this.site + novelPath;
|
||||
|
||||
const body = await fetchText(url, {}, 'gbk');
|
||||
if (body === '') throw Error('无法获取小说内容,请检查网络');
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
const novel: Plugin.SourceNovel = {
|
||||
path: novelPath,
|
||||
chapters: [],
|
||||
name: loadedCheerio('h1 > a').text(),
|
||||
};
|
||||
|
||||
novel.cover = loadedCheerio('div.bookimg2 > img').attr('src');
|
||||
|
||||
novel.summary = loadedCheerio('div.navtxt').text().trim();
|
||||
|
||||
const bookInfo = loadedCheerio('div.booknav2').text();
|
||||
|
||||
const authorMatch = bookInfo.match(/作者:(.*)/);
|
||||
if (authorMatch) {
|
||||
novel.author = authorMatch[1];
|
||||
}
|
||||
|
||||
novel.artist = undefined;
|
||||
|
||||
novel.status = bookInfo.includes('连载')
|
||||
? NovelStatus.Ongoing
|
||||
: NovelStatus.Completed;
|
||||
|
||||
const genresMatch = bookInfo.match(/分类:(.*)/);
|
||||
if (genresMatch) {
|
||||
novel.genres = genresMatch[1];
|
||||
}
|
||||
|
||||
// Table of Content is on a different page than the summary page
|
||||
const chapters: Plugin.ChapterItem[] = [];
|
||||
|
||||
const chaptersUrl = loadedCheerio('a.more-btn').attr('href');
|
||||
if (chaptersUrl) {
|
||||
const chaptersBody = await fetchText(chaptersUrl, {}, 'gbk');
|
||||
|
||||
const chaptersLoadedCheerio = parseHTML(chaptersBody);
|
||||
|
||||
chaptersLoadedCheerio('li').each((i, e) => {
|
||||
const chapterUrl = chaptersLoadedCheerio(e).find('a').attr('href');
|
||||
|
||||
if (chapterUrl?.startsWith('https://')) {
|
||||
const chapterName = chaptersLoadedCheerio(e).find('a').text().trim();
|
||||
|
||||
chapters.push({
|
||||
name: chapterName,
|
||||
path: chapterUrl.replace(this.site, ''),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
novel.chapters = chapters;
|
||||
|
||||
return novel;
|
||||
}
|
||||
|
||||
async parseChapter(chapterPath: string): Promise<string> {
|
||||
const body = await fetchText(this.site + chapterPath, {}, 'gbk');
|
||||
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
const chapterText = (loadedCheerio('div.txtnav').prop('innerText') ?? '')
|
||||
.split('\n')
|
||||
// remove empty lines
|
||||
.map((line: string) => line.trim())
|
||||
.filter((line: string) => line !== '')
|
||||
// remove the first two lines which are the chapter name and author name
|
||||
.slice(2)
|
||||
.map((line: string) => `<p>${line}</p>`)
|
||||
.join('\n');
|
||||
|
||||
return chapterText;
|
||||
}
|
||||
|
||||
async searchNovels(
|
||||
searchTerm: string,
|
||||
pageNo: number,
|
||||
): Promise<Plugin.NovelItem[]> {
|
||||
if (pageNo > 1) return [];
|
||||
|
||||
const searchUrl = `${this.site}/modules/article/search.php`;
|
||||
const formData = new FormData();
|
||||
formData.append('searchkey', encode(searchTerm, 'gbk'));
|
||||
formData.append('searchtype', 'all');
|
||||
|
||||
const body = await fetchText(
|
||||
searchUrl,
|
||||
{ method: 'post', body: formData },
|
||||
'gbk',
|
||||
);
|
||||
if (body === '') throw Error('无法获取搜索结果,请检查网络');
|
||||
const loadedCheerio = parseHTML(body);
|
||||
|
||||
const novels: Plugin.NovelItem[] = [];
|
||||
|
||||
loadedCheerio('.newbox > ul > li').each((i, e) => {
|
||||
const novelUrl = loadedCheerio(e).find('a').attr('href')!;
|
||||
const novelName = loadedCheerio(e).find('div.newnav > h3').text();
|
||||
const novelCover = loadedCheerio(e).find('img').attr('data-src');
|
||||
|
||||
const novel = {
|
||||
name: novelName,
|
||||
path: novelUrl.replace(this.site, ''),
|
||||
cover: novelCover,
|
||||
};
|
||||
|
||||
novels.push(novel);
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
|
||||
filters = {
|
||||
class: {
|
||||
label: '分类',
|
||||
value: '0',
|
||||
options: [
|
||||
{ label: '全部分类', value: '0' },
|
||||
{ label: '言情小说', value: '3' },
|
||||
{ label: '玄幻魔法', value: '1' },
|
||||
{ label: '修真武侠', value: '2' },
|
||||
{ label: '穿越时空', value: '11' },
|
||||
{ label: '都市小说', value: '9' },
|
||||
{ label: '历史军事', value: '4' },
|
||||
{ label: '游戏竞技', value: '5' },
|
||||
{ label: '科幻空间', value: '6' },
|
||||
{ label: '悬疑惊悚', value: '7' },
|
||||
{ label: '同人小说', value: '8' },
|
||||
{ label: '官场职场', value: '10' },
|
||||
{ label: '青春校园', value: '12' },
|
||||
],
|
||||
type: FilterTypes.Picker,
|
||||
},
|
||||
} satisfies Filters;
|
||||
}
|
||||
|
||||
export default new XinShu69();
|
||||
+173
-309
@@ -1,53 +1,44 @@
|
||||
import { Plugin } from '@/types/plugin';
|
||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||
import { fetchApi, fetchFile } from '@/lib/fetch';
|
||||
import { fetchApi } from '@/lib/fetch';
|
||||
import { NovelStatus } from '@libs/novelStatus';
|
||||
import { load as parseHTML } from 'cheerio';
|
||||
import { defaultCover } from '@libs/defaultCover';
|
||||
|
||||
const getStandardNovelPath = (url: string | undefined): string | undefined => {
|
||||
const parseUrl = (url?: string): URL | undefined => {
|
||||
if (!url) return undefined;
|
||||
try {
|
||||
const parsedUrl = new URL(url);
|
||||
const match = parsedUrl.pathname.match(/^(\/amp)?(\/n\/[^\/]+\/)/);
|
||||
return match?.[2];
|
||||
} catch (error) {
|
||||
return new URL(url, 'https://www.quanben.io');
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const getChapterFileName = (url: string | undefined): string | undefined => {
|
||||
if (!url) return undefined;
|
||||
try {
|
||||
const parsedUrl = new URL(url);
|
||||
const pathParts = parsedUrl.pathname.split('/');
|
||||
const fileName = pathParts[pathParts.length - 1];
|
||||
if (fileName && /^\d+\.html$/.test(fileName)) {
|
||||
return fileName;
|
||||
}
|
||||
return undefined;
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
const getStandardNovelPath = (url?: string): string | undefined => {
|
||||
const parsedUrl = parseUrl(url);
|
||||
if (!parsedUrl) return undefined;
|
||||
const match = parsedUrl.pathname.match(/^(\/amp)?(\/n\/[^\/]+\/)/);
|
||||
return match?.[2];
|
||||
};
|
||||
|
||||
const getChapterFileName = (url?: string): string | undefined => {
|
||||
const parsedUrl = parseUrl(url);
|
||||
if (!parsedUrl) return undefined;
|
||||
const fileName = parsedUrl.pathname.split('/').pop();
|
||||
if (fileName && /^\d+\.html$/.test(fileName)) return fileName;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const makeAbsolute = (
|
||||
relativeUrl: string | undefined,
|
||||
baseUrl: string,
|
||||
relativeUrl?: string,
|
||||
baseUrl?: string,
|
||||
): string | undefined => {
|
||||
if (!relativeUrl) return undefined;
|
||||
if (!relativeUrl || !baseUrl) return undefined;
|
||||
try {
|
||||
if (relativeUrl.startsWith('//')) {
|
||||
return 'https:' + relativeUrl;
|
||||
}
|
||||
if (
|
||||
relativeUrl.startsWith('http://') ||
|
||||
relativeUrl.startsWith('https://')
|
||||
) {
|
||||
return relativeUrl;
|
||||
}
|
||||
if (relativeUrl.startsWith('//')) return 'https:' + relativeUrl;
|
||||
if (/^https?:\/\//.test(relativeUrl)) return relativeUrl;
|
||||
return new URL(relativeUrl, baseUrl).href;
|
||||
} catch (e) {
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
@@ -56,357 +47,230 @@ class QuanbenPlugin implements Plugin.PluginBase {
|
||||
id = 'quanben';
|
||||
name = 'Quanben';
|
||||
site = 'https://www.quanben.io/';
|
||||
version = '1.0.0';
|
||||
version = '1.0.1';
|
||||
icon = 'src/cn/quanben/icon.png';
|
||||
defaultCover = defaultCover;
|
||||
|
||||
filters = {} satisfies Filters;
|
||||
// filters
|
||||
filters = {
|
||||
genre: {
|
||||
label: '分类',
|
||||
value: 'all',
|
||||
options: [
|
||||
{ label: '全部', value: 'all' },
|
||||
{ label: '玄幻', value: 'xuanhuan' },
|
||||
{ label: '都市', value: 'dushi' },
|
||||
{ label: '言情', value: 'yanqing' },
|
||||
{ label: '穿越', value: 'chuanyue' },
|
||||
{ label: '青春', value: 'qingchun' },
|
||||
{ label: '仙侠', value: 'xianxia' },
|
||||
{ label: '灵异', value: 'lingyi' },
|
||||
{ label: '悬疑', value: 'xuanyi' },
|
||||
{ label: '历史', value: 'lishi' },
|
||||
{ label: '军事', value: 'junshi' },
|
||||
{ label: '游戏', value: 'youxi' },
|
||||
{ label: '竞技', value: 'jingji' },
|
||||
{ label: '科幻', value: 'kehuan' },
|
||||
{ label: '职场', value: 'zhichang' },
|
||||
{ label: '官场', value: 'guanchang' },
|
||||
{ label: '现言', value: 'xianyan' },
|
||||
{ label: '耽美', value: 'danmei' },
|
||||
{ label: '其它', value: 'qita' },
|
||||
],
|
||||
type: FilterTypes.Picker,
|
||||
},
|
||||
} satisfies Filters;
|
||||
|
||||
async popularNovels(_pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||
const url = this.site + 'amp/';
|
||||
const result = await fetchApi(url);
|
||||
if (!result.ok) {
|
||||
throw new Error(
|
||||
`[Quanben] Failed to fetch AMP popular novels page: ${url} - Status: ${result.status}`,
|
||||
);
|
||||
}
|
||||
const body = await result.text();
|
||||
const $ = parseHTML(body);
|
||||
// homepage, when you first open the extension (with the applied filters if any)
|
||||
async popularNovels(
|
||||
_pageNo: number,
|
||||
{ filters }: Plugin.PopularNovelsOptions<typeof this.filters>,
|
||||
): Promise<Plugin.NovelItem[]> {
|
||||
const url =
|
||||
filters.genre.value === 'all'
|
||||
? this.site
|
||||
: `${this.site}c/${filters.genre.value}.html`;
|
||||
|
||||
const res = await fetchApi(url);
|
||||
if (!res.ok)
|
||||
throw new Error(`[Quanben] Failed to fetch: ${url} - ${res.status}`);
|
||||
|
||||
const $ = parseHTML(await res.text());
|
||||
const novels: Plugin.NovelItem[] = [];
|
||||
const processedAmpPaths = new Set<string>();
|
||||
|
||||
$('div.box').each((_i, box) => {
|
||||
const $box = $(box);
|
||||
$('div.list2').each((_i, list2) => {
|
||||
const $list2 = $(list2);
|
||||
const $link = $list2.find('h3 > a').first();
|
||||
const href = $link.attr('href')?.trim();
|
||||
const name = $link.text().trim();
|
||||
const rawCover =
|
||||
$list2.find('img').attr('src')?.trim() ||
|
||||
$list2.find('img').attr('data-src')?.trim();
|
||||
const cover = makeAbsolute(rawCover, this.site) || this.defaultCover;
|
||||
|
||||
// 1. Process the featured novel (div.list2) if it exists
|
||||
const $featured = $box.find('div.list2');
|
||||
if ($featured.length > 0) {
|
||||
const $link = $featured.find('h3 > a');
|
||||
const ampPath = $link.attr('href')?.trim();
|
||||
const name = $link.text().trim();
|
||||
const rawCoverSrc = $featured.find('amp-img').attr('src')?.trim();
|
||||
const cover = makeAbsolute(rawCoverSrc, this.site) || this.defaultCover;
|
||||
|
||||
if (ampPath && name && !processedAmpPaths.has(ampPath)) {
|
||||
novels.push({ name, path: ampPath, cover });
|
||||
processedAmpPaths.add(ampPath);
|
||||
}
|
||||
if (href && name) {
|
||||
const path = getStandardNovelPath(href);
|
||||
if (path) novels.push({ name, path, cover });
|
||||
}
|
||||
});
|
||||
|
||||
// 2. Process novels in the list (ul.list)
|
||||
$box.find('ul.list li').each((_j, listItem) => {
|
||||
const $listItem = $(listItem);
|
||||
const $link = $listItem.find('a');
|
||||
const ampPath = $link.attr('href')?.trim();
|
||||
const name = $link.find('span').first().text().trim(); // Name is inside a span within the link
|
||||
// only first entry bcs the others dont have an image
|
||||
$('ul.list').each((_i, ul) => {
|
||||
const $firstLi = $(ul).find('li').first();
|
||||
const $a = $firstLi.find('a').first();
|
||||
const href = $a.attr('href')?.trim();
|
||||
const name =
|
||||
$a.text().trim() || $firstLi.find('span.author').text().trim();
|
||||
const cover = this.defaultCover;
|
||||
|
||||
if (ampPath && name && !processedAmpPaths.has(ampPath)) {
|
||||
novels.push({ name, path: ampPath, cover: this.defaultCover }); // Use default cover for list items
|
||||
processedAmpPaths.add(ampPath);
|
||||
}
|
||||
});
|
||||
if (href && name) {
|
||||
const path = getStandardNovelPath(href);
|
||||
if (path) novels.push({ name, path, cover });
|
||||
}
|
||||
});
|
||||
|
||||
return novels;
|
||||
}
|
||||
|
||||
// novel details and metadata
|
||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||
// ** Expects AMP path: /amp/n/novel-name/ **
|
||||
if (
|
||||
!novelPath ||
|
||||
!novelPath.startsWith('/amp/n/') ||
|
||||
!novelPath.endsWith('/')
|
||||
) {
|
||||
throw new Error(
|
||||
`[Quanben parseNovel] Invalid novelPath received: "${novelPath}". Expected AMP format: "/amp/n/novel-name/"`,
|
||||
);
|
||||
}
|
||||
const standardPath = novelPath.replace(/^\/amp/, '');
|
||||
if (!standardPath.startsWith('/n/') || !standardPath.endsWith('/'))
|
||||
throw new Error(`[Quanben parseNovel] Invalid path: ${novelPath}`);
|
||||
|
||||
// Construct full AMP novel URL
|
||||
const fullNovelUrl = makeAbsolute(novelPath, this.site);
|
||||
if (!fullNovelUrl) {
|
||||
throw new Error(
|
||||
`[Quanben parseNovel] Could not construct full AMP novel URL from path: ${novelPath}`,
|
||||
);
|
||||
}
|
||||
const fullUrl = makeAbsolute(standardPath, this.site);
|
||||
if (!fullUrl)
|
||||
throw new Error(`[Quanben parseNovel] Could not construct full URL`);
|
||||
|
||||
// 1. Fetch AMP Novel Page HTML
|
||||
const novelPageResult = await fetchApi(fullNovelUrl);
|
||||
if (!novelPageResult.ok) {
|
||||
throw new Error(
|
||||
`[Quanben parseNovel] Failed to fetch AMP novel page: ${fullNovelUrl} - Status: ${novelPageResult.status}`,
|
||||
);
|
||||
}
|
||||
const novelPageHtml = await novelPageResult.text();
|
||||
const $ = parseHTML(novelPageHtml);
|
||||
const res = await fetchApi(fullUrl);
|
||||
if (!res.ok)
|
||||
throw new Error(`[Quanben parseNovel] Failed to fetch: ${fullUrl}`);
|
||||
|
||||
// 2. Parse Novel Details from AMP page - **Refined Selectors**
|
||||
const $infoBox = $('div.list2'); // Base element for many details
|
||||
const $descriptionBox = $('div.description'); // Separate element for summary
|
||||
const $ = parseHTML(await res.text());
|
||||
const $info = $('div.list2').first();
|
||||
const $desc = $('div.description').first();
|
||||
|
||||
const novel: Plugin.SourceNovel = {
|
||||
path: novelPath, // Use the input AMP path
|
||||
name:
|
||||
$infoBox.find('h3').text().trim() || // **Use h3 inside list2**
|
||||
$('h1[itemprop="name headline"]').text().trim() || // Fallback H1
|
||||
'Unknown Novel Name',
|
||||
path: standardPath,
|
||||
name: $info.find('h3').text().trim() || 'Unknown Novel',
|
||||
cover:
|
||||
makeAbsolute($infoBox.find('amp-img').attr('src'), this.site) || // **amp-img inside list2**
|
||||
makeAbsolute($info.find('img').attr('src'), this.site) ||
|
||||
this.defaultCover,
|
||||
summary:
|
||||
$descriptionBox.find('p').text().trim() || // **Use p inside div.description**
|
||||
$descriptionBox.text().trim() || // Fallback to div.description text
|
||||
undefined,
|
||||
// Use :contains() for more robust selection within the info box
|
||||
author:
|
||||
$infoBox.find("p:contains('作者:') span").text().trim() || undefined,
|
||||
status: NovelStatus.Unknown, // Parsed below
|
||||
genres:
|
||||
$infoBox.find("p:contains('类别:') span").text().trim() || undefined,
|
||||
chapters: [], // Parsed below
|
||||
$desc.find('p').text().trim() || $desc.text().trim() || undefined,
|
||||
author: $info.find("p:contains('作者:') span").text().trim() || undefined,
|
||||
status: NovelStatus.Unknown,
|
||||
genres: $info.find("p:contains('类别:') span").text().trim() || undefined,
|
||||
chapters: await this.parseChapterList(standardPath),
|
||||
};
|
||||
|
||||
// Parse Status from AMP page
|
||||
const statusText =
|
||||
$infoBox.find("p:contains('状态:') span").text().trim() || // Try specific span first
|
||||
$infoBox.text(); // Fallback to full text
|
||||
if (statusText.includes('完结') || statusText.includes('已完成')) {
|
||||
novel.status = NovelStatus.Completed;
|
||||
} else if (statusText.includes('连载中') || statusText.includes('进行中')) {
|
||||
novel.status = NovelStatus.Ongoing;
|
||||
}
|
||||
|
||||
// 3. Fetch and Parse Chapter List
|
||||
novel.chapters = await this.parseChapterList(novelPath);
|
||||
novel.status = NovelStatus.Completed;
|
||||
|
||||
return novel;
|
||||
}
|
||||
|
||||
// Separate function to handle chapter list parsing - **Using AMP**
|
||||
async parseChapterList(novelPath: string): Promise<Plugin.ChapterItem[]> {
|
||||
// ** Expects AMP path: /amp/n/novel-name/ **
|
||||
if (
|
||||
!novelPath ||
|
||||
!novelPath.startsWith('/amp/n/') ||
|
||||
!novelPath.endsWith('/')
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
if (!novelPath.startsWith('/n/') || !novelPath.endsWith('/')) return [];
|
||||
|
||||
// Construct the AMP chapter list URL
|
||||
const ampChapterListUrl = makeAbsolute(novelPath + 'list.html', this.site);
|
||||
if (!ampChapterListUrl) {
|
||||
return [];
|
||||
}
|
||||
const url = makeAbsolute(novelPath + 'list.html', this.site);
|
||||
if (!url) return [];
|
||||
|
||||
const chapterListResult = await fetchApi(ampChapterListUrl);
|
||||
if (!chapterListResult.ok) {
|
||||
return [];
|
||||
}
|
||||
const chapterListHtml = await chapterListResult.text();
|
||||
const $ = parseHTML(chapterListHtml);
|
||||
const res = await fetchApi(url);
|
||||
if (!res.ok) return [];
|
||||
|
||||
const $ = parseHTML(await res.text());
|
||||
const chapters: Plugin.ChapterItem[] = [];
|
||||
// Extract standard novel name from AMP path for chapter path storage
|
||||
const standardNovelPathMatch = novelPath.match(/(\/n\/[^\/]+\/)/);
|
||||
if (!standardNovelPathMatch || !standardNovelPathMatch[1]) {
|
||||
return [];
|
||||
}
|
||||
const novelNameOnly = standardNovelPathMatch[1].replace(/^\/n\/|\/$/g, '');
|
||||
const novelName = novelPath.match(/\/n\/([^\/]+)\//)?.[1];
|
||||
if (!novelName) return [];
|
||||
|
||||
// 1. Parse chapters from ALL list3 uls in the AMP HTML
|
||||
$('ul.list3 li a').each((_i, el) => {
|
||||
const $el = $(el);
|
||||
const chapterName = $el.text().trim();
|
||||
const chapterHref = $el.attr('href');
|
||||
if (chapterName && chapterHref) {
|
||||
// URLs in AMP page usually point to the standard chapter URLs
|
||||
const absoluteUrl = makeAbsolute(chapterHref, this.site); // Use base site URL
|
||||
const chapterFileName = getChapterFileName(absoluteUrl);
|
||||
if (chapterFileName) {
|
||||
// ** Store chapter path in standard format for parseChapter **
|
||||
const chapterPathForStorage = novelNameOnly + '/' + chapterFileName;
|
||||
chapters.push({
|
||||
name: chapterName,
|
||||
path: chapterPathForStorage,
|
||||
// chapterNumber assigned later
|
||||
});
|
||||
}
|
||||
}
|
||||
const name = $el.text().trim();
|
||||
const href = $el.attr('href');
|
||||
if (!name || !href) return;
|
||||
|
||||
const fileName = getChapterFileName(makeAbsolute(href, this.site));
|
||||
if (!fileName) return;
|
||||
|
||||
chapters.push({
|
||||
name,
|
||||
path: `${novelName}/${fileName}`,
|
||||
});
|
||||
});
|
||||
|
||||
// 2. Deduplicate chapters based on path
|
||||
const chapterMap = new Map<string, Plugin.ChapterItem>();
|
||||
chapters.forEach(chapter => {
|
||||
if (!chapterMap.has(chapter.path)) {
|
||||
chapterMap.set(chapter.path, chapter);
|
||||
}
|
||||
});
|
||||
const uniqueChapters = Array.from(chapterMap.values());
|
||||
|
||||
// 3. Sort chapters numerically based on filename
|
||||
const uniqueChapters = Array.from(
|
||||
new Map(chapters.map(c => [c.path, c])).values(),
|
||||
);
|
||||
uniqueChapters.sort((a, b) => {
|
||||
const numA = parseInt(a.path.match(/(\d+)\.html$/)?.[1] || '0', 10);
|
||||
const numB = parseInt(b.path.match(/(\d+)\.html$/)?.[1] || '0', 10);
|
||||
return numA - numB;
|
||||
});
|
||||
|
||||
// 4. Assign chapter numbers
|
||||
return uniqueChapters.map((chapter, index) => ({
|
||||
...chapter,
|
||||
chapterNumber: index + 1,
|
||||
}));
|
||||
return uniqueChapters.map((c, idx) => ({ ...c, chapterNumber: idx + 1 }));
|
||||
}
|
||||
|
||||
async parseChapter(chapterPath: string): Promise<string> {
|
||||
// ** Expects standard path: novel-name/chapterFileName.html **
|
||||
if (
|
||||
!chapterPath ||
|
||||
!chapterPath.includes('/') ||
|
||||
chapterPath.endsWith('/')
|
||||
) {
|
||||
throw new Error(
|
||||
`[Quanben] Invalid chapterPath format received in parseChapter: "${chapterPath}". Expected format: "novel-name/chapterFileName.html"`,
|
||||
);
|
||||
}
|
||||
if (!chapterPath.includes('/') || chapterPath.endsWith('/'))
|
||||
throw new Error(`[Quanben] Invalid chapter path: "${chapterPath}"`);
|
||||
|
||||
// Construct the standard chapter URL using /n/ prefix
|
||||
const chapterUrl = `${this.site}n/${chapterPath}`;
|
||||
const url = `${this.site}n/${chapterPath}`;
|
||||
const res = await fetchApi(url);
|
||||
if (!res.ok) throw new Error(`[Quanben] Failed to fetch chapter: ${url}`);
|
||||
|
||||
const result = await fetchApi(chapterUrl);
|
||||
if (!result.ok) {
|
||||
// Handle potential redirects (e.g., chapter moved) - check Location header if status is 3xx
|
||||
if (result.status >= 300 && result.status < 400) {
|
||||
const redirectUrl = result.headers.get('Location');
|
||||
if (redirectUrl) {
|
||||
const absoluteRedirectUrl = makeAbsolute(redirectUrl, chapterUrl);
|
||||
if (!absoluteRedirectUrl) {
|
||||
throw new Error(
|
||||
`[Quanben] Failed to make redirected URL absolute: ${redirectUrl}`,
|
||||
);
|
||||
}
|
||||
const redirectResult = await fetchApi(absoluteRedirectUrl);
|
||||
if (!redirectResult.ok) {
|
||||
throw new Error(
|
||||
`[Quanben] Failed to fetch redirected chapter content: ${absoluteRedirectUrl} - Status: ${redirectResult.status}`,
|
||||
);
|
||||
}
|
||||
return this.extractChapterContent(
|
||||
await redirectResult.text(),
|
||||
absoluteRedirectUrl,
|
||||
);
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
`[Quanben] Failed to fetch chapter content: ${chapterUrl} - Status: ${result.status}`,
|
||||
);
|
||||
}
|
||||
const body = await result.text();
|
||||
return this.extractChapterContent(body, chapterUrl);
|
||||
return this.extractChapterContent(await res.text());
|
||||
}
|
||||
|
||||
// Helper function to extract and clean chapter content from HTML body
|
||||
private extractChapterContent(body: string, urlForLog: string): string {
|
||||
private extractChapterContent(body: string): string {
|
||||
const $ = parseHTML(body);
|
||||
let $content = $('#contentbody');
|
||||
if (!$content.length) {
|
||||
$content = $('#content');
|
||||
}
|
||||
if (!$content.length) {
|
||||
$content = $('.content');
|
||||
}
|
||||
if (!$content.length) {
|
||||
return 'Error: Could not find chapter content container.';
|
||||
}
|
||||
let $content = $('#contentbody, #content, .content').first();
|
||||
if (!$content.length) return 'Error: Chapter content not found.';
|
||||
|
||||
$content
|
||||
.find(
|
||||
'script, style, ins, iframe, [class*="ads"], [id*="ads"], [class*="google"], [id*="google"], [class*="recommend"], div[align="center"]',
|
||||
)
|
||||
.remove();
|
||||
$content.find('p').each((_i, el) => {
|
||||
const $p = $(el);
|
||||
const pText = $p.text().trim();
|
||||
if (
|
||||
pText.includes('请记住本书首发域名') ||
|
||||
pText.includes('手机版阅读网址') ||
|
||||
pText.includes('quanben') ||
|
||||
pText.includes('最新网址') ||
|
||||
pText.includes('章节报错') ||
|
||||
pText.match(/app|APP|下载|客户端/) ||
|
||||
pText.length === 0 ||
|
||||
($p
|
||||
.html()
|
||||
?.replace(/ /g, '')
|
||||
.trim() === '' &&
|
||||
$p.find('img').length === 0)
|
||||
) {
|
||||
$p.remove();
|
||||
}
|
||||
});
|
||||
$content
|
||||
.contents()
|
||||
.filter(function () {
|
||||
return this.type === 'comment';
|
||||
})
|
||||
.remove();
|
||||
let chapterText = $content.html();
|
||||
if (!chapterText) {
|
||||
return 'Error: Chapter content was empty after cleaning.';
|
||||
}
|
||||
chapterText = chapterText.replace(/<\s*p[^>]*>/gi, '\n\n');
|
||||
chapterText = chapterText.replace(/<\s*br[^>]*>/gi, '\n');
|
||||
chapterText = chapterText.replace(/<[^>]+>/g, '');
|
||||
chapterText = parseHTML(`<div>${chapterText}</div>`).text();
|
||||
chapterText = chapterText.replace(/[\t ]+/g, ' ');
|
||||
chapterText = chapterText.replace(/\n{3,}/g, '\n\n');
|
||||
chapterText = chapterText.trim();
|
||||
return chapterText;
|
||||
|
||||
return (
|
||||
($content.html() || '').replace(/[\t ]+/g, ' ').trim() ||
|
||||
'Error: Chapter content empty.'
|
||||
);
|
||||
}
|
||||
|
||||
async searchNovels(
|
||||
searchTerm: string,
|
||||
_pageNo: number,
|
||||
): Promise<Plugin.NovelItem[]> {
|
||||
const searchUrl = `${this.site}index.php?c=book&a=search&keywords=${encodeURIComponent(searchTerm)}`;
|
||||
const result = await fetchApi(searchUrl);
|
||||
if (!result.ok) {
|
||||
return [];
|
||||
}
|
||||
const body = await result.text();
|
||||
const $ = parseHTML(body);
|
||||
// add search
|
||||
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||
const url = `${this.site}index.php?c=book&a=search&keywords=${encodeURIComponent(searchTerm)}`;
|
||||
const res = await fetchApi(url);
|
||||
if (!res.ok) return [];
|
||||
|
||||
const $ = parseHTML(await res.text());
|
||||
const novels: Plugin.NovelItem[] = [];
|
||||
|
||||
$('div.list2').each((_i, element) => {
|
||||
const $el = $(element);
|
||||
const nameLink = $el.find('h3 > a').first();
|
||||
const img = $el.find('img').first();
|
||||
$('div.list2').each((_i, el) => {
|
||||
const $el = $(el);
|
||||
const $link = $el.find('h3 > a').first();
|
||||
const href = $link.attr('href');
|
||||
const name = $link.text().trim();
|
||||
const cover = makeAbsolute(
|
||||
$el.find('img').attr('src') || $el.find('img').attr('data-src'),
|
||||
this.site,
|
||||
);
|
||||
|
||||
const novelName = nameLink.text().trim();
|
||||
const novelHref = nameLink.attr('href');
|
||||
const novelCover = img.attr('src') || img.attr('data-src');
|
||||
|
||||
if (novelHref && novelName) {
|
||||
const absoluteUrl = makeAbsolute(novelHref, this.site);
|
||||
const standardPath = getStandardNovelPath(absoluteUrl);
|
||||
|
||||
if (standardPath) {
|
||||
// **Construct AMP Path for storage**
|
||||
const ampPath = '/amp' + standardPath;
|
||||
const absoluteCover = makeAbsolute(novelCover, this.site);
|
||||
if (href && name) {
|
||||
const path = getStandardNovelPath(makeAbsolute(href, this.site));
|
||||
if (path)
|
||||
novels.push({
|
||||
name: novelName,
|
||||
path: ampPath, // **Store the AMP path**
|
||||
cover: absoluteCover || this.defaultCover,
|
||||
name,
|
||||
path: '/amp' + path,
|
||||
cover: cover || this.defaultCover,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return novels;
|
||||
}
|
||||
|
||||
// Use fetchApi for fetchImage as it handles potential errors and returns Response
|
||||
async fetchImage(url: string): Promise<Response> {
|
||||
return fetchApi(url);
|
||||
}
|
||||
|
||||
+234
-236
@@ -11,241 +11,240 @@ import p_8 from '@plugins/arabic/dilartube';
|
||||
import p_9 from '@plugins/arabic/rewayatclub';
|
||||
import p_10 from '@plugins/arabic/sunovels';
|
||||
import p_11 from '@plugins/chinese/69shu';
|
||||
import p_12 from '@plugins/chinese/69xinshu';
|
||||
import p_13 from '@plugins/chinese/Quanben';
|
||||
import p_14 from '@plugins/chinese/ixdzs8';
|
||||
import p_15 from '@plugins/chinese/linovel';
|
||||
import p_16 from '@plugins/chinese/linovelib';
|
||||
import p_17 from '@plugins/chinese/linovelib_tw';
|
||||
import p_18 from '@plugins/chinese/novel543';
|
||||
import p_19 from '@plugins/english/AllNovelFull[readnovelfull]';
|
||||
import p_20 from '@plugins/english/AllNovel[readnovelfull]';
|
||||
import p_21 from '@plugins/english/ArcaneTranslations[lightnovelwp]';
|
||||
import p_22 from '@plugins/english/BelleReservoir[madara]';
|
||||
import p_23 from '@plugins/english/BoxNovel[readnovelfull]';
|
||||
import p_24 from '@plugins/english/CPUnovel[lightnovelwp]';
|
||||
import p_25 from '@plugins/english/CitrusAurora[madara]';
|
||||
import p_26 from '@plugins/english/CoralBoutique[madara]';
|
||||
import p_27 from '@plugins/english/DaoNovel[madara]';
|
||||
import p_28 from '@plugins/english/DaoTranslate[lightnovelwp]';
|
||||
import p_29 from '@plugins/english/DaoistQuest[fictioneer]';
|
||||
import p_30 from '@plugins/english/DearestRosalie[fictioneer]';
|
||||
import p_31 from '@plugins/english/DragonTea[madara]';
|
||||
import p_32 from '@plugins/english/Dragonholic[madara]';
|
||||
import p_33 from '@plugins/english/DuskBlossoms[madara]';
|
||||
import p_34 from '@plugins/english/ElloTL[lightnovelwp]';
|
||||
import p_35 from '@plugins/english/Eternalune[madara]';
|
||||
import p_36 from '@plugins/english/EtudeTranslations[madara]';
|
||||
import p_37 from '@plugins/english/FanNovel[readwn]';
|
||||
import p_38 from '@plugins/english/FansMTL[readwn]';
|
||||
import p_39 from '@plugins/english/FansTranslations[madara]';
|
||||
import p_40 from '@plugins/english/FirstKissNovel[madara]';
|
||||
import p_41 from '@plugins/english/Foxaholic[madara]';
|
||||
import p_42 from '@plugins/english/FreeWebNovel[readnovelfull]';
|
||||
import p_43 from '@plugins/english/GalaxyTranslations[madara]';
|
||||
import p_44 from '@plugins/english/Guavaread[madara]';
|
||||
import p_45 from '@plugins/english/HiraethTranslation[madara]';
|
||||
import p_46 from '@plugins/english/HotNovelPub[hotnovelpub]';
|
||||
import p_47 from '@plugins/english/Ippotranslations[lightnovelwp]';
|
||||
import p_48 from '@plugins/english/KeopiTranslations[lightnovelwp]';
|
||||
import p_49 from '@plugins/english/KnoxT[lightnovelwp]';
|
||||
import p_50 from '@plugins/english/LazyGirlTranslations[lightnovelwp]';
|
||||
import p_51 from '@plugins/english/LibRead[readnovelfull]';
|
||||
import p_52 from '@plugins/english/LightNovelCave[lightnovelworld]';
|
||||
import p_53 from '@plugins/english/LightNovelHeaven[madara]';
|
||||
import p_54 from '@plugins/english/LightNovelPlus[readnovelfull]';
|
||||
import p_55 from '@plugins/english/LightNovelPubVip[lightnovelworld]';
|
||||
import p_56 from '@plugins/english/LightNovelUpdates[madara]';
|
||||
import p_57 from '@plugins/english/LightNovelWord[lightnovelworld]';
|
||||
import p_58 from '@plugins/english/LilyontheValley[fictioneer]';
|
||||
import p_59 from '@plugins/english/Ltnovel[readwn]';
|
||||
import p_60 from '@plugins/english/LunarLetters[madara]';
|
||||
import p_61 from '@plugins/english/MTLNovel[madara]';
|
||||
import p_62 from '@plugins/english/MTLNovel[mtlnovel]';
|
||||
import p_63 from '@plugins/english/Meownovel[madara]';
|
||||
import p_64 from '@plugins/english/MoonlightNovels[lightnovelwp]';
|
||||
import p_65 from '@plugins/english/MostNovel[madara]';
|
||||
import p_66 from '@plugins/english/MysticalSeries[madara]';
|
||||
import p_67 from '@plugins/english/NeoSekaiTranslations[madara]';
|
||||
import p_68 from '@plugins/english/NitroManga[madara]';
|
||||
import p_69 from '@plugins/english/NobleMTL[lightnovelwp]';
|
||||
import p_70 from '@plugins/english/NoiceTranslations[madara]';
|
||||
import p_71 from '@plugins/english/NovelBin[readnovelfull]';
|
||||
import p_72 from '@plugins/english/NovelCool[novelcool]';
|
||||
import p_73 from '@plugins/english/NovelFull[readnovelfull]';
|
||||
import p_74 from '@plugins/english/NovelLib[fictioneer]';
|
||||
import p_75 from '@plugins/english/NovelMultiverse[madara]';
|
||||
import p_76 from '@plugins/english/NovelOnline';
|
||||
import p_77 from '@plugins/english/NovelTranslate[madara]';
|
||||
import p_78 from '@plugins/english/NovelsKnight[lightnovelwp]';
|
||||
import p_79 from '@plugins/english/PandaMachineTranslations[lightnovelwp]';
|
||||
import p_80 from '@plugins/english/PastelTales[madara]';
|
||||
import p_81 from '@plugins/english/PenguinSquad[fictioneer]';
|
||||
import p_82 from '@plugins/english/Prizma[fictioneer]';
|
||||
import p_83 from '@plugins/english/Ranobes[ranobes]';
|
||||
import p_84 from '@plugins/english/Ranovel[madara]';
|
||||
import p_85 from '@plugins/english/ReadFanfic[madara]';
|
||||
import p_86 from '@plugins/english/ReadNovelFull[readnovelfull]';
|
||||
import p_87 from '@plugins/english/RequiemTranslations[lightnovelwp]';
|
||||
import p_88 from '@plugins/english/SalmonLatte[madara]';
|
||||
import p_89 from '@plugins/english/SleepyTranslations[madara]';
|
||||
import p_90 from '@plugins/english/SonicMTL[madara]';
|
||||
import p_91 from '@plugins/english/SrankManga[madara]';
|
||||
import p_92 from '@plugins/english/StorySeedling';
|
||||
import p_93 from '@plugins/english/SweetEscape[madara]';
|
||||
import p_94 from '@plugins/english/SystemTranslation[lightnovelwp]';
|
||||
import p_95 from '@plugins/english/TranslatinOtaku[madara]';
|
||||
import p_96 from '@plugins/english/TranslationWeaver[lightnovelwp]';
|
||||
import p_97 from '@plugins/english/UniversalNovel[lightnovelwp]';
|
||||
import p_98 from '@plugins/english/VandyTranslate[lightnovelwp]';
|
||||
import p_99 from '@plugins/english/VioletLily[madara]';
|
||||
import p_100 from '@plugins/english/WebNovelLover[madara]';
|
||||
import p_101 from '@plugins/english/WebNovelPub[lightnovelworld]';
|
||||
import p_102 from '@plugins/english/WebNovelTranslation[madara]';
|
||||
import p_103 from '@plugins/english/WhiteMoonlightNovels[lightnovelwp]';
|
||||
import p_104 from '@plugins/english/WooksTeahouse[madara]';
|
||||
import p_105 from '@plugins/english/WordExcerpt[madara]';
|
||||
import p_106 from '@plugins/english/WuxiaSpace[readwn]';
|
||||
import p_107 from '@plugins/english/WuxiaV[readwn]';
|
||||
import p_108 from '@plugins/english/WuxiaWorldSite[madara]';
|
||||
import p_109 from '@plugins/english/Wuxiabox[readwn]';
|
||||
import p_110 from '@plugins/english/Wuxiafox[readwn]';
|
||||
import p_111 from '@plugins/english/ZetroTranslation[madara]';
|
||||
import p_112 from '@plugins/english/ao3';
|
||||
import p_113 from '@plugins/english/bestlightnovel';
|
||||
import p_114 from '@plugins/english/chrysanthemumgarden';
|
||||
import p_115 from '@plugins/english/divinedaolibrary';
|
||||
import p_116 from '@plugins/english/dreambigtl';
|
||||
import p_117 from '@plugins/english/earlynovel';
|
||||
import p_118 from '@plugins/english/faqwikius';
|
||||
import p_119 from '@plugins/english/fenrirrealm';
|
||||
import p_120 from '@plugins/english/fictionzone';
|
||||
import p_121 from '@plugins/english/foxteller';
|
||||
import p_122 from '@plugins/english/genesis';
|
||||
import p_123 from '@plugins/english/inkitt';
|
||||
import p_124 from '@plugins/english/kdtnovels';
|
||||
import p_125 from '@plugins/english/lightnovelpub';
|
||||
import p_126 from '@plugins/english/lightnoveltranslation';
|
||||
import p_127 from '@plugins/english/lnmtl';
|
||||
import p_128 from '@plugins/english/mtlreader';
|
||||
import p_129 from '@plugins/english/mvlempyr';
|
||||
import p_130 from '@plugins/english/novelbuddy';
|
||||
import p_131 from '@plugins/english/novelfire';
|
||||
import p_132 from '@plugins/english/novelhall';
|
||||
import p_133 from '@plugins/english/novelight';
|
||||
import p_134 from '@plugins/english/novelupdates';
|
||||
import p_135 from '@plugins/english/pawread';
|
||||
import p_136 from '@plugins/english/rainofsnow';
|
||||
import p_137 from '@plugins/english/readfrom';
|
||||
import p_138 from '@plugins/english/readlitenovel';
|
||||
import p_139 from '@plugins/english/reaperscans';
|
||||
import p_140 from '@plugins/english/relibrary';
|
||||
import p_141 from '@plugins/english/royalroad';
|
||||
import p_142 from '@plugins/english/scribblehub';
|
||||
import p_143 from '@plugins/english/vynovel';
|
||||
import p_144 from '@plugins/english/webnovel';
|
||||
import p_145 from '@plugins/english/wtrlab';
|
||||
import p_146 from '@plugins/english/wuxiaworld';
|
||||
import p_147 from '@plugins/french/LighNovelFR[lightnovelwp]';
|
||||
import p_148 from '@plugins/french/MTLNovel(FR)[mtlnovel]';
|
||||
import p_149 from '@plugins/french/MassNovel[madara]';
|
||||
import p_150 from '@plugins/french/WorldNovel[madara]';
|
||||
import p_151 from '@plugins/french/chireads';
|
||||
import p_152 from '@plugins/french/harkeneliwood';
|
||||
import p_153 from '@plugins/french/kisswood';
|
||||
import p_154 from '@plugins/french/noveldeglace';
|
||||
import p_155 from '@plugins/french/novhell';
|
||||
import p_156 from '@plugins/french/phenixscans';
|
||||
import p_157 from '@plugins/french/warriorlegendtrad';
|
||||
import p_158 from '@plugins/french/wuxialnscantrad';
|
||||
import p_159 from '@plugins/french/xiaowaz';
|
||||
import p_160 from '@plugins/indonesian/BacaLightNovel[lightnovelwp]';
|
||||
import p_161 from '@plugins/indonesian/MTLNovel(ID)[mtlnovel]';
|
||||
import p_162 from '@plugins/indonesian/MeioNovel[madara]';
|
||||
import p_163 from '@plugins/indonesian/NovelBookID[madara]';
|
||||
import p_164 from '@plugins/indonesian/Risenovel[madara]';
|
||||
import p_165 from '@plugins/indonesian/SekteNovel[lightnovelwp]';
|
||||
import p_166 from '@plugins/indonesian/WBNovel[madara]';
|
||||
import p_167 from '@plugins/indonesian/indowebnovel';
|
||||
import p_168 from '@plugins/indonesian/novelringan';
|
||||
import p_169 from '@plugins/indonesian/sakuranovel';
|
||||
import p_170 from '@plugins/japanese/Syosetu';
|
||||
import p_171 from '@plugins/japanese/kakuyomu';
|
||||
import p_172 from '@plugins/korean/Agitoon';
|
||||
import p_173 from '@plugins/korean/FortuneEternal[madara]';
|
||||
import p_174 from '@plugins/multi/komga';
|
||||
import p_175 from '@plugins/polish/novelki';
|
||||
import p_176 from '@plugins/portuguese/BetterNovels[lightnovelwp]';
|
||||
import p_177 from '@plugins/portuguese/CentralNovel[lightnovelwp]';
|
||||
import p_178 from '@plugins/portuguese/Kiniga[madara]';
|
||||
import p_179 from '@plugins/portuguese/LaNovels[hotnovelpub]';
|
||||
import p_180 from '@plugins/portuguese/LightNovelBrasil[lightnovelwp]';
|
||||
import p_181 from '@plugins/portuguese/MTLNovel(PT)[mtlnovel]';
|
||||
import p_182 from '@plugins/portuguese/blogdoamonnovels';
|
||||
import p_183 from '@plugins/portuguese/novelmania';
|
||||
import p_184 from '@plugins/portuguese/tsundoku';
|
||||
import p_185 from '@plugins/russian/Bookhamster[ifreedom]';
|
||||
import p_186 from '@plugins/russian/Erolate[rulate]';
|
||||
import p_187 from '@plugins/russian/EzNovels[hotnovelpub]';
|
||||
import p_188 from '@plugins/russian/LitSpace';
|
||||
import p_189 from '@plugins/russian/MTLNovel(RU)[mtlnovel]';
|
||||
import p_190 from '@plugins/russian/NovelCool(RU)[novelcool]';
|
||||
import p_191 from '@plugins/russian/Ranobes(RU)[ranobes]';
|
||||
import p_192 from '@plugins/russian/Rulate[rulate]';
|
||||
import p_193 from '@plugins/russian/authortoday';
|
||||
import p_194 from '@plugins/russian/bookriver';
|
||||
import p_195 from '@plugins/russian/ficbook';
|
||||
import p_196 from '@plugins/russian/jaomix';
|
||||
import p_197 from '@plugins/russian/neobook';
|
||||
import p_198 from '@plugins/russian/novelOvh';
|
||||
import p_199 from '@plugins/russian/novelTL';
|
||||
import p_200 from '@plugins/russian/ranobehub';
|
||||
import p_201 from '@plugins/russian/ranobelib';
|
||||
import p_202 from '@plugins/russian/ranoberf';
|
||||
import p_203 from '@plugins/russian/renovels';
|
||||
import p_204 from '@plugins/russian/ruvers';
|
||||
import p_205 from '@plugins/russian/topliba';
|
||||
import p_206 from '@plugins/russian/zelluloza';
|
||||
import p_207 from '@plugins/russian/СвободныйМирРанобэ[ifreedom]';
|
||||
import p_208 from '@plugins/spanish/AllNovelRead[lightnovelwp]';
|
||||
import p_209 from '@plugins/spanish/AnimesHoy12[madara]';
|
||||
import p_210 from '@plugins/spanish/LightNovelDaily[hotnovelpub]';
|
||||
import p_211 from '@plugins/spanish/MTLNovel(ES)[mtlnovel]';
|
||||
import p_212 from '@plugins/spanish/PanchoTranslations[madara]';
|
||||
import p_213 from '@plugins/spanish/TC&Sega[lightnovelwp]';
|
||||
import p_214 from '@plugins/spanish/TraduccionesAmistosas[madara]';
|
||||
import p_215 from '@plugins/spanish/hasutl';
|
||||
import p_216 from '@plugins/spanish/novelasligera';
|
||||
import p_217 from '@plugins/spanish/novelawuxia';
|
||||
import p_218 from '@plugins/spanish/oasistranslations';
|
||||
import p_219 from '@plugins/spanish/skynovels';
|
||||
import p_220 from '@plugins/spanish/tunovelaligera';
|
||||
import p_221 from '@plugins/spanish/yukitls';
|
||||
import p_222 from '@plugins/thai/NovelLucky[madara]';
|
||||
import p_223 from '@plugins/thai/NovelPDF[madara]';
|
||||
import p_224 from '@plugins/turkish/ArazNovel[madara]';
|
||||
import p_225 from '@plugins/turkish/EKTAPLAR[madara]';
|
||||
import p_226 from '@plugins/turkish/KodeksLibrary[lightnovelwp]';
|
||||
import p_227 from '@plugins/turkish/MangaTR';
|
||||
import p_228 from '@plugins/turkish/NABSCANS[madara]';
|
||||
import p_229 from '@plugins/turkish/Namevt[lightnovelwp]';
|
||||
import p_230 from '@plugins/turkish/NovelTR[lightnovelwp]';
|
||||
import p_231 from '@plugins/turkish/Noveloku[madara]';
|
||||
import p_232 from '@plugins/turkish/RagnarScans[madara]';
|
||||
import p_233 from '@plugins/turkish/ThNovels[hotnovelpub]';
|
||||
import p_234 from '@plugins/turkish/TurkceLightNovels[madara]';
|
||||
import p_235 from '@plugins/turkish/WebNovelOku[madara]';
|
||||
import p_236 from '@plugins/turkish/epiknovel';
|
||||
import p_237 from '@plugins/turkish/kakikata[madara]';
|
||||
import p_238 from '@plugins/ukrainian/bakainua';
|
||||
import p_239 from '@plugins/ukrainian/smakolykytl';
|
||||
import p_240 from '@plugins/ukrainian/uaranobeclub';
|
||||
import p_241 from '@plugins/vietnamese/LNHako';
|
||||
import p_242 from '@plugins/vietnamese/Truyenconect';
|
||||
import p_243 from '@plugins/vietnamese/lightnovelvn';
|
||||
import p_244 from '@plugins/vietnamese/nettruyen';
|
||||
import p_245 from '@plugins/vietnamese/truyenchu';
|
||||
import p_246 from '@plugins/vietnamese/truyenfull';
|
||||
import p_12 from '@plugins/chinese/Quanben';
|
||||
import p_13 from '@plugins/chinese/ixdzs8';
|
||||
import p_14 from '@plugins/chinese/linovel';
|
||||
import p_15 from '@plugins/chinese/linovelib';
|
||||
import p_16 from '@plugins/chinese/linovelib_tw';
|
||||
import p_17 from '@plugins/chinese/novel543';
|
||||
import p_18 from '@plugins/english/AllNovelFull[readnovelfull]';
|
||||
import p_19 from '@plugins/english/AllNovel[readnovelfull]';
|
||||
import p_20 from '@plugins/english/ArcaneTranslations[lightnovelwp]';
|
||||
import p_21 from '@plugins/english/BelleReservoir[madara]';
|
||||
import p_22 from '@plugins/english/BoxNovel[readnovelfull]';
|
||||
import p_23 from '@plugins/english/CPUnovel[lightnovelwp]';
|
||||
import p_24 from '@plugins/english/CitrusAurora[madara]';
|
||||
import p_25 from '@plugins/english/CoralBoutique[madara]';
|
||||
import p_26 from '@plugins/english/DaoNovel[madara]';
|
||||
import p_27 from '@plugins/english/DaoTranslate[lightnovelwp]';
|
||||
import p_28 from '@plugins/english/DaoistQuest[fictioneer]';
|
||||
import p_29 from '@plugins/english/DearestRosalie[fictioneer]';
|
||||
import p_30 from '@plugins/english/DragonTea[madara]';
|
||||
import p_31 from '@plugins/english/Dragonholic[madara]';
|
||||
import p_32 from '@plugins/english/DuskBlossoms[madara]';
|
||||
import p_33 from '@plugins/english/ElloTL[lightnovelwp]';
|
||||
import p_34 from '@plugins/english/Eternalune[madara]';
|
||||
import p_35 from '@plugins/english/EtudeTranslations[madara]';
|
||||
import p_36 from '@plugins/english/FanNovel[readwn]';
|
||||
import p_37 from '@plugins/english/FansMTL[readwn]';
|
||||
import p_38 from '@plugins/english/FansTranslations[madara]';
|
||||
import p_39 from '@plugins/english/FirstKissNovel[madara]';
|
||||
import p_40 from '@plugins/english/Foxaholic[madara]';
|
||||
import p_41 from '@plugins/english/FreeWebNovel[readnovelfull]';
|
||||
import p_42 from '@plugins/english/GalaxyTranslations[madara]';
|
||||
import p_43 from '@plugins/english/Guavaread[madara]';
|
||||
import p_44 from '@plugins/english/HiraethTranslation[madara]';
|
||||
import p_45 from '@plugins/english/HotNovelPub[hotnovelpub]';
|
||||
import p_46 from '@plugins/english/Ippotranslations[lightnovelwp]';
|
||||
import p_47 from '@plugins/english/KeopiTranslations[lightnovelwp]';
|
||||
import p_48 from '@plugins/english/KnoxT[lightnovelwp]';
|
||||
import p_49 from '@plugins/english/LazyGirlTranslations[lightnovelwp]';
|
||||
import p_50 from '@plugins/english/LibRead[readnovelfull]';
|
||||
import p_51 from '@plugins/english/LightNovelCave[lightnovelworld]';
|
||||
import p_52 from '@plugins/english/LightNovelHeaven[madara]';
|
||||
import p_53 from '@plugins/english/LightNovelPlus[readnovelfull]';
|
||||
import p_54 from '@plugins/english/LightNovelPubVip[lightnovelworld]';
|
||||
import p_55 from '@plugins/english/LightNovelUpdates[madara]';
|
||||
import p_56 from '@plugins/english/LightNovelWord[lightnovelworld]';
|
||||
import p_57 from '@plugins/english/LilyontheValley[fictioneer]';
|
||||
import p_58 from '@plugins/english/Ltnovel[readwn]';
|
||||
import p_59 from '@plugins/english/LunarLetters[madara]';
|
||||
import p_60 from '@plugins/english/MTLNovel[madara]';
|
||||
import p_61 from '@plugins/english/MTLNovel[mtlnovel]';
|
||||
import p_62 from '@plugins/english/Meownovel[madara]';
|
||||
import p_63 from '@plugins/english/MoonlightNovels[lightnovelwp]';
|
||||
import p_64 from '@plugins/english/MostNovel[madara]';
|
||||
import p_65 from '@plugins/english/MysticalSeries[madara]';
|
||||
import p_66 from '@plugins/english/NeoSekaiTranslations[madara]';
|
||||
import p_67 from '@plugins/english/NitroManga[madara]';
|
||||
import p_68 from '@plugins/english/NobleMTL[lightnovelwp]';
|
||||
import p_69 from '@plugins/english/NoiceTranslations[madara]';
|
||||
import p_70 from '@plugins/english/NovelBin[readnovelfull]';
|
||||
import p_71 from '@plugins/english/NovelCool[novelcool]';
|
||||
import p_72 from '@plugins/english/NovelFull[readnovelfull]';
|
||||
import p_73 from '@plugins/english/NovelLib[fictioneer]';
|
||||
import p_74 from '@plugins/english/NovelMultiverse[madara]';
|
||||
import p_75 from '@plugins/english/NovelOnline';
|
||||
import p_76 from '@plugins/english/NovelTranslate[madara]';
|
||||
import p_77 from '@plugins/english/NovelsKnight[lightnovelwp]';
|
||||
import p_78 from '@plugins/english/PandaMachineTranslations[lightnovelwp]';
|
||||
import p_79 from '@plugins/english/PastelTales[madara]';
|
||||
import p_80 from '@plugins/english/PenguinSquad[fictioneer]';
|
||||
import p_81 from '@plugins/english/Prizma[fictioneer]';
|
||||
import p_82 from '@plugins/english/Ranobes[ranobes]';
|
||||
import p_83 from '@plugins/english/Ranovel[madara]';
|
||||
import p_84 from '@plugins/english/ReadFanfic[madara]';
|
||||
import p_85 from '@plugins/english/ReadNovelFull[readnovelfull]';
|
||||
import p_86 from '@plugins/english/RequiemTranslations[lightnovelwp]';
|
||||
import p_87 from '@plugins/english/SalmonLatte[madara]';
|
||||
import p_88 from '@plugins/english/SleepyTranslations[madara]';
|
||||
import p_89 from '@plugins/english/SonicMTL[madara]';
|
||||
import p_90 from '@plugins/english/SrankManga[madara]';
|
||||
import p_91 from '@plugins/english/StorySeedling';
|
||||
import p_92 from '@plugins/english/SweetEscape[madara]';
|
||||
import p_93 from '@plugins/english/SystemTranslation[lightnovelwp]';
|
||||
import p_94 from '@plugins/english/TranslatinOtaku[madara]';
|
||||
import p_95 from '@plugins/english/TranslationWeaver[lightnovelwp]';
|
||||
import p_96 from '@plugins/english/UniversalNovel[lightnovelwp]';
|
||||
import p_97 from '@plugins/english/VandyTranslate[lightnovelwp]';
|
||||
import p_98 from '@plugins/english/VioletLily[madara]';
|
||||
import p_99 from '@plugins/english/WebNovelLover[madara]';
|
||||
import p_100 from '@plugins/english/WebNovelPub[lightnovelworld]';
|
||||
import p_101 from '@plugins/english/WebNovelTranslation[madara]';
|
||||
import p_102 from '@plugins/english/WhiteMoonlightNovels[lightnovelwp]';
|
||||
import p_103 from '@plugins/english/WooksTeahouse[madara]';
|
||||
import p_104 from '@plugins/english/WordExcerpt[madara]';
|
||||
import p_105 from '@plugins/english/WuxiaSpace[readwn]';
|
||||
import p_106 from '@plugins/english/WuxiaV[readwn]';
|
||||
import p_107 from '@plugins/english/WuxiaWorldSite[madara]';
|
||||
import p_108 from '@plugins/english/Wuxiabox[readwn]';
|
||||
import p_109 from '@plugins/english/Wuxiafox[readwn]';
|
||||
import p_110 from '@plugins/english/ZetroTranslation[madara]';
|
||||
import p_111 from '@plugins/english/ao3';
|
||||
import p_112 from '@plugins/english/bestlightnovel';
|
||||
import p_113 from '@plugins/english/chrysanthemumgarden';
|
||||
import p_114 from '@plugins/english/divinedaolibrary';
|
||||
import p_115 from '@plugins/english/dreambigtl';
|
||||
import p_116 from '@plugins/english/earlynovel';
|
||||
import p_117 from '@plugins/english/faqwikius';
|
||||
import p_118 from '@plugins/english/fenrirrealm';
|
||||
import p_119 from '@plugins/english/fictionzone';
|
||||
import p_120 from '@plugins/english/foxteller';
|
||||
import p_121 from '@plugins/english/genesis';
|
||||
import p_122 from '@plugins/english/inkitt';
|
||||
import p_123 from '@plugins/english/kdtnovels';
|
||||
import p_124 from '@plugins/english/lightnovelpub';
|
||||
import p_125 from '@plugins/english/lightnoveltranslation';
|
||||
import p_126 from '@plugins/english/lnmtl';
|
||||
import p_127 from '@plugins/english/mtlreader';
|
||||
import p_128 from '@plugins/english/mvlempyr';
|
||||
import p_129 from '@plugins/english/novelbuddy';
|
||||
import p_130 from '@plugins/english/novelfire';
|
||||
import p_131 from '@plugins/english/novelhall';
|
||||
import p_132 from '@plugins/english/novelight';
|
||||
import p_133 from '@plugins/english/novelupdates';
|
||||
import p_134 from '@plugins/english/pawread';
|
||||
import p_135 from '@plugins/english/rainofsnow';
|
||||
import p_136 from '@plugins/english/readfrom';
|
||||
import p_137 from '@plugins/english/readlitenovel';
|
||||
import p_138 from '@plugins/english/reaperscans';
|
||||
import p_139 from '@plugins/english/relibrary';
|
||||
import p_140 from '@plugins/english/royalroad';
|
||||
import p_141 from '@plugins/english/scribblehub';
|
||||
import p_142 from '@plugins/english/vynovel';
|
||||
import p_143 from '@plugins/english/webnovel';
|
||||
import p_144 from '@plugins/english/wtrlab';
|
||||
import p_145 from '@plugins/english/wuxiaworld';
|
||||
import p_146 from '@plugins/french/LighNovelFR[lightnovelwp]';
|
||||
import p_147 from '@plugins/french/MTLNovel(FR)[mtlnovel]';
|
||||
import p_148 from '@plugins/french/MassNovel[madara]';
|
||||
import p_149 from '@plugins/french/WorldNovel[madara]';
|
||||
import p_150 from '@plugins/french/chireads';
|
||||
import p_151 from '@plugins/french/harkeneliwood';
|
||||
import p_152 from '@plugins/french/kisswood';
|
||||
import p_153 from '@plugins/french/noveldeglace';
|
||||
import p_154 from '@plugins/french/novhell';
|
||||
import p_155 from '@plugins/french/phenixscans';
|
||||
import p_156 from '@plugins/french/warriorlegendtrad';
|
||||
import p_157 from '@plugins/french/wuxialnscantrad';
|
||||
import p_158 from '@plugins/french/xiaowaz';
|
||||
import p_159 from '@plugins/indonesian/BacaLightNovel[lightnovelwp]';
|
||||
import p_160 from '@plugins/indonesian/MTLNovel(ID)[mtlnovel]';
|
||||
import p_161 from '@plugins/indonesian/MeioNovel[madara]';
|
||||
import p_162 from '@plugins/indonesian/NovelBookID[madara]';
|
||||
import p_163 from '@plugins/indonesian/Risenovel[madara]';
|
||||
import p_164 from '@plugins/indonesian/SekteNovel[lightnovelwp]';
|
||||
import p_165 from '@plugins/indonesian/WBNovel[madara]';
|
||||
import p_166 from '@plugins/indonesian/indowebnovel';
|
||||
import p_167 from '@plugins/indonesian/novelringan';
|
||||
import p_168 from '@plugins/indonesian/sakuranovel';
|
||||
import p_169 from '@plugins/japanese/Syosetu';
|
||||
import p_170 from '@plugins/japanese/kakuyomu';
|
||||
import p_171 from '@plugins/korean/Agitoon';
|
||||
import p_172 from '@plugins/korean/FortuneEternal[madara]';
|
||||
import p_173 from '@plugins/multi/komga';
|
||||
import p_174 from '@plugins/polish/novelki';
|
||||
import p_175 from '@plugins/portuguese/BetterNovels[lightnovelwp]';
|
||||
import p_176 from '@plugins/portuguese/CentralNovel[lightnovelwp]';
|
||||
import p_177 from '@plugins/portuguese/Kiniga[madara]';
|
||||
import p_178 from '@plugins/portuguese/LaNovels[hotnovelpub]';
|
||||
import p_179 from '@plugins/portuguese/LightNovelBrasil[lightnovelwp]';
|
||||
import p_180 from '@plugins/portuguese/MTLNovel(PT)[mtlnovel]';
|
||||
import p_181 from '@plugins/portuguese/blogdoamonnovels';
|
||||
import p_182 from '@plugins/portuguese/novelmania';
|
||||
import p_183 from '@plugins/portuguese/tsundoku';
|
||||
import p_184 from '@plugins/russian/Bookhamster[ifreedom]';
|
||||
import p_185 from '@plugins/russian/Erolate[rulate]';
|
||||
import p_186 from '@plugins/russian/EzNovels[hotnovelpub]';
|
||||
import p_187 from '@plugins/russian/LitSpace';
|
||||
import p_188 from '@plugins/russian/MTLNovel(RU)[mtlnovel]';
|
||||
import p_189 from '@plugins/russian/NovelCool(RU)[novelcool]';
|
||||
import p_190 from '@plugins/russian/Ranobes(RU)[ranobes]';
|
||||
import p_191 from '@plugins/russian/Rulate[rulate]';
|
||||
import p_192 from '@plugins/russian/authortoday';
|
||||
import p_193 from '@plugins/russian/bookriver';
|
||||
import p_194 from '@plugins/russian/ficbook';
|
||||
import p_195 from '@plugins/russian/jaomix';
|
||||
import p_196 from '@plugins/russian/neobook';
|
||||
import p_197 from '@plugins/russian/novelOvh';
|
||||
import p_198 from '@plugins/russian/novelTL';
|
||||
import p_199 from '@plugins/russian/ranobehub';
|
||||
import p_200 from '@plugins/russian/ranobelib';
|
||||
import p_201 from '@plugins/russian/ranoberf';
|
||||
import p_202 from '@plugins/russian/renovels';
|
||||
import p_203 from '@plugins/russian/ruvers';
|
||||
import p_204 from '@plugins/russian/topliba';
|
||||
import p_205 from '@plugins/russian/zelluloza';
|
||||
import p_206 from '@plugins/russian/СвободныйМирРанобэ[ifreedom]';
|
||||
import p_207 from '@plugins/spanish/AllNovelRead[lightnovelwp]';
|
||||
import p_208 from '@plugins/spanish/AnimesHoy12[madara]';
|
||||
import p_209 from '@plugins/spanish/LightNovelDaily[hotnovelpub]';
|
||||
import p_210 from '@plugins/spanish/MTLNovel(ES)[mtlnovel]';
|
||||
import p_211 from '@plugins/spanish/PanchoTranslations[madara]';
|
||||
import p_212 from '@plugins/spanish/TC&Sega[lightnovelwp]';
|
||||
import p_213 from '@plugins/spanish/TraduccionesAmistosas[madara]';
|
||||
import p_214 from '@plugins/spanish/hasutl';
|
||||
import p_215 from '@plugins/spanish/novelasligera';
|
||||
import p_216 from '@plugins/spanish/novelawuxia';
|
||||
import p_217 from '@plugins/spanish/oasistranslations';
|
||||
import p_218 from '@plugins/spanish/skynovels';
|
||||
import p_219 from '@plugins/spanish/tunovelaligera';
|
||||
import p_220 from '@plugins/spanish/yukitls';
|
||||
import p_221 from '@plugins/thai/NovelLucky[madara]';
|
||||
import p_222 from '@plugins/thai/NovelPDF[madara]';
|
||||
import p_223 from '@plugins/turkish/ArazNovel[madara]';
|
||||
import p_224 from '@plugins/turkish/EKTAPLAR[madara]';
|
||||
import p_225 from '@plugins/turkish/KodeksLibrary[lightnovelwp]';
|
||||
import p_226 from '@plugins/turkish/MangaTR';
|
||||
import p_227 from '@plugins/turkish/NABSCANS[madara]';
|
||||
import p_228 from '@plugins/turkish/Namevt[lightnovelwp]';
|
||||
import p_229 from '@plugins/turkish/NovelTR[lightnovelwp]';
|
||||
import p_230 from '@plugins/turkish/Noveloku[madara]';
|
||||
import p_231 from '@plugins/turkish/RagnarScans[madara]';
|
||||
import p_232 from '@plugins/turkish/ThNovels[hotnovelpub]';
|
||||
import p_233 from '@plugins/turkish/TurkceLightNovels[madara]';
|
||||
import p_234 from '@plugins/turkish/WebNovelOku[madara]';
|
||||
import p_235 from '@plugins/turkish/epiknovel';
|
||||
import p_236 from '@plugins/turkish/kakikata[madara]';
|
||||
import p_237 from '@plugins/ukrainian/bakainua';
|
||||
import p_238 from '@plugins/ukrainian/smakolykytl';
|
||||
import p_239 from '@plugins/ukrainian/uaranobeclub';
|
||||
import p_240 from '@plugins/vietnamese/LNHako';
|
||||
import p_241 from '@plugins/vietnamese/Truyenconect';
|
||||
import p_242 from '@plugins/vietnamese/lightnovelvn';
|
||||
import p_243 from '@plugins/vietnamese/nettruyen';
|
||||
import p_244 from '@plugins/vietnamese/truyenchu';
|
||||
import p_245 from '@plugins/vietnamese/truyenfull';
|
||||
|
||||
const PLUGINS: Plugin.PluginBase[] = [
|
||||
p_0,
|
||||
@@ -494,6 +493,5 @@ const PLUGINS: Plugin.PluginBase[] = [
|
||||
p_243,
|
||||
p_244,
|
||||
p_245,
|
||||
p_246,
|
||||
];
|
||||
export default PLUGINS;
|
||||
|
||||
Reference in New Issue
Block a user