refactor: Overhaul Plugin Architecture & Playground UI (#1846)

* refactor: Move Plugins Out and Consolidate with Multisrc

* feat: Overhaul Playground UI

* feat: Add Filters to New UI

* feat: Persist Selected Plugin

* feat: Add Direct Parse Novel/Chapter Options and Feedback Toasts

[skip ci]

* feat: UX Improvements

* chore: Update Plugin Path in Publish Workflow

* fix: Add Backward Compatibility

* chore: Update Plugin Publishing Script
This commit is contained in:
Rajarshee Chatterjee
2025-10-20 22:54:08 +05:30
committed by GitHub
parent 252beff0bf
commit 20c2b75b01
363 changed files with 6848 additions and 3007 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ node_modules
config.json
# generated multisrc .ts files
src/plugins/*/*\[*\]*.ts
plugins/*/*\[*\]*.ts
# ignore dist stuffs
.js
+22
View File
@@ -0,0 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"registries": {}
}
+25 -24
View File
@@ -11,7 +11,7 @@
Most of the Plugin/Novel type definitions accessed using the `Plugin` namespace imported via
```ts
import { Plugin } from "@typings/plugin";
import { Plugin } from '@/types/plugin';
```
### PluginBase
@@ -349,23 +349,24 @@ It is an object representing information how to store/access the novel
You can use the default `Cover not available` cover by importing
```ts
import { defaultCover } from "@libs/defaultCover";
import { defaultCover } from '@libs/defaultCover';
```
---
### SourceNovel
| Field | Type | Required | Desciption |
| ----- | ------ | -------- | ---------- |
| url | string | yes | |
| name | string | no | string |
|cover|`string`|no||
|genres|`string`|no||
|summary|`string`|no||
|author|`string`|no||
|artist|`string`|no||
|status|[NovelStatus] or `string`|no||
| Field | Type | Required | Desciption |
| ------- | ------------------------- | -------- | ---------- |
| url | string | yes | |
| name | string | no | string |
| cover | `string` | no | |
| genres | `string` | no | |
| summary | `string` | no | |
| author | `string` | no | |
| artist | `string` | no | |
| status | [NovelStatus] or `string` | no | |
chapters?: ChapterItem[];
---
@@ -379,7 +380,7 @@ import { defaultCover } from "@libs/defaultCover";
`Filters` and `FilterTypes` are not in the `Plugin` namespace and are from `@libs/filterInputs` file:
```ts
import { FilterTypes, Filters } from "@libs/filterInputs";
import { FilterTypes, Filters } from '@libs/filterInputs';
```
There are 2 main objects when using filters:
@@ -418,11 +419,11 @@ options.filters.order
filters = {
genre: {
type: FilterTypes.CheckboxGroup,
label: "Genres",
label: 'Genres',
value: [],
options: [
{ label: "Isekai", value: "isekai" },
{ label: "Romance", value: "romans" },
{ label: 'Isekai', value: 'isekai' },
{ label: 'Romance', value: 'romans' },
],
},
} satisfies Filters;
@@ -445,12 +446,12 @@ Types of filters supported
```ts
options: [
{
label: "default", // in-app label
value: "", // in-code value
label: 'default', // in-app label
value: '', // in-code value
},
{
label: "Value ABC",
value: "abc",
label: 'Value ABC',
value: 'abc',
},
];
```
@@ -460,12 +461,12 @@ options: [
```ts
options: [
{
label: "Value ABC", // in-app label
value: "abc", // in-code value
label: 'Value ABC', // in-app label
value: 'abc', // in-code value
},
{
label: "Value DEF",
value: "def",
label: 'Value DEF',
value: 'def',
},
];
```
+3 -3
View File
@@ -1,11 +1,11 @@
import { fetchApi, fetchProto, fetchText } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { fetchApi, fetchProto, fetchText } from '@/lib/fetch';
import { Plugin } from '@/types/plugin';
import { Filters } from '@libs/filterInputs';
import { load as loadCheerio } from 'cheerio';
import { defaultCover } from '@libs/defaultCover';
import { NovelStatus } from '@libs/novelStatus';
// import { isUrlAbsolute } from '@libs/isAbsoluteUrl';
// import { storage, localStorage, sessionStorage } from '@libs/storage';
// import { storage, localStorage, sessionStorage } from '@/lib/storage';
// import { encode, decode } from 'urlencode';
// import dayjs from 'dayjs';
// import { Parser } from 'htmlparser2';
+8 -3
View File
@@ -129,10 +129,15 @@ export default tseslint.config(
...tseslint.configs.stylistic,
prettierConfig,
{
ignores: ['.js', 'docs', 'proxy_server.js'],
ignores: [
'.js',
'docs',
'proxy_server.js',
'plugins/*/*\\[*\\]*.ts', // Files with square brackets in their names
],
},
{
files: ['./src/plugins/*/*.ts', './scripts/multisrc/*/template.ts'],
files: ['./plugins/*/*.ts', './plugins/multisrc/*/template.ts'],
rules: {
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'warn',
@@ -150,7 +155,7 @@ export default tseslint.config(
},
{
files: ['**/*.{ts,tsx,mts,cts,js}'],
ignores: ['./src/plugins/*/*.ts', './scripts/multisrc/*/template.ts'],
ignores: ['./plugins/*/*.ts', './plugins/multisrc/*/template.ts'],
rules: {
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'warn',
+2 -1
View File
@@ -2,9 +2,10 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/ico.png" />
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Plugin testing</title>
<link href="/src/index.css" rel="stylesheet" />
</head>
<body>
<div id="root"></div>
+2515 -193
View File
File diff suppressed because it is too large Load Diff
+21 -2
View File
@@ -8,8 +8,8 @@
"clean:multisrc": "find . -type f -wholename \"*plugins*\\[*\\]*.[t,j]s\" -delete",
"clean:multisrc:windows": "powershell -Command \"Get-ChildItem -Recurse -Include \\\"*].ts\\\",\\\"*].js\\\" | Where-Object { $_.FullName -like \\\"*plugins*\\\" } | Remove-Item\"",
"dev": "vite",
"dev:start": "node scripts/generate-plugin-index.js && vite",
"build:multisrc": "node ./scripts/multisrc/generate-multisrc-plugins.js",
"dev:start": "node ./plugins/multisrc/generate-multisrc-plugins.js && node scripts/generate-plugin-index.js && vite",
"build:multisrc": "node ./plugins/multisrc/generate-multisrc-plugins.js",
"build:compile": "npx tsc --project tsconfig.production.json",
"build:manifest": "node ./scripts/build-plugin-manifest.js",
"build:manifest:dev": "node --env-file=.env ./scripts/build-plugin-manifest.js",
@@ -27,6 +27,23 @@
"author": "LNReader",
"license": "MIT",
"dependencies": {
"@fontsource/geist-mono": "^5.2.7",
"@fontsource/geist-sans": "^5.2.5",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dialog": "^1.1.5",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.1.4",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
"@tailwindcss/vite": "^4.1.14",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.546.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.14",
"terser": "^5.36.0",
"typescript": "^5.7.2"
},
@@ -41,6 +58,7 @@
"@types/eslint__js": "^8.42.3",
"@types/eslint-config-prettier": "^6.11.3",
"@types/http-proxy": "^1.17.16",
"@types/node": "^24.8.1",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@types/react-window": "^1.8.8",
@@ -64,6 +82,7 @@
"react-window": "^1.8.10",
"sanitize-html": "^2.13.1",
"serve": "^14.2.4",
"tw-animate-css": "^1.4.0",
"typescript-eslint": "^7.14.1",
"urlencode": "^2.0.0",
"vite": "^6.3.2",
@@ -1,6 +1,6 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { defaultCover } from '@libs/defaultCover';
@@ -80,8 +80,8 @@ class dilartube implements Plugin.PluginBase {
async parseNovel(novelUrl: string): Promise<Plugin.SourceNovel> {
const chapterItems: Plugin.ChapterItem[] = [];
let fullUrl = this.site + 'api/' + novelUrl;
let chapterUrl = this.site + 'api/' + novelUrl + '/releases';
const fullUrl = this.site + 'api/' + novelUrl;
const chapterUrl = this.site + 'api/' + novelUrl + '/releases';
const manga = await fetchApi(fullUrl).then(r => r.json());
const chapters = await fetchApi(chapterUrl).then(r => r.json());
const mangaData = manga.mangaData;
@@ -199,21 +199,21 @@ class dilartube implements Plugin.PluginBase {
export default new dilartube();
interface Category {
type Category = {
id: number;
name: string;
icon: string | null;
manga_id: number;
}
};
interface Type {
type Type = {
id: number;
name: string;
reading_direction: string;
title: string;
}
};
interface Manga {
type Manga = {
id: number;
title: string;
summary: string;
@@ -249,8 +249,8 @@ interface Manga {
artists: any[];
categories: Category[];
type: Type;
}
interface Release {
};
type Release = {
id: number;
manga_id: number;
created_at: string;
@@ -272,11 +272,11 @@ interface Release {
team_paypal: string | null;
has_rev_link: boolean;
manga: Manga;
}
interface ApiResponse {
};
type ApiResponse = {
releases: Release[];
data: searchManga[];
}
};
type MangaCategory = {
id: number;
name: string;
@@ -384,7 +384,7 @@ type ChapterRelease = {
team_name: string;
has_rev_link: boolean;
};
interface searchManga {
type searchManga = {
filter: any;
id: number;
title: string;
@@ -421,4 +421,4 @@ interface searchManga {
artists: any[];
categories: Category[];
type: Type;
}
};
@@ -1,6 +1,6 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { defaultCover } from '@libs/defaultCover';
@@ -141,7 +141,7 @@ class RewayatClub implements Plugin.PagePlugin {
};
}
async parseChapter(chapterUrl: string): Promise<string> {
let link = this.site + 'api/chapters/' + chapterUrl.slice(6);
const link = this.site + 'api/chapters/' + chapterUrl.slice(6);
const result = await fetchApi(link).then(r => r.json());
let chapterText = result.content
.flat()
@@ -211,7 +211,7 @@ class RewayatClub implements Plugin.PagePlugin {
export default new RewayatClub();
interface NovelEntry {
type NovelEntry = {
arabic: string;
english: string;
about: string;
@@ -234,14 +234,14 @@ interface NovelEntry {
poster_url: string;
original: boolean;
};
}
interface NovelData {
};
type NovelData = {
count?: number;
next?: string;
previous?: string;
results: NovelEntry[];
}
interface ChapterEntry {
};
type ChapterEntry = {
number: number;
title: string;
date: string;
@@ -254,11 +254,11 @@ interface ChapterEntry {
id: number;
};
read: any[];
}
};
interface ChapterData {
type ChapterData = {
count: number;
next: string;
previous: string;
results: ChapterEntry[];
}
};
@@ -1,6 +1,6 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { defaultCover } from '@libs/defaultCover';
@@ -22,7 +22,7 @@ class Sunovels implements Plugin.PagePlugin {
imageUrlList.push(...imageUrlMatched);
}
});
let counter: number = 0;
let counter = 0;
loadedCheerio('.list-item').each((idx, ele) => {
loadedCheerio(ele)
.find('a')
@@ -150,7 +150,7 @@ class Sunovels implements Plugin.PagePlugin {
const pageUrl = firstUrl + '?activeTab=chapters&page=' + pageCorrected;
const body = await fetchApi(pageUrl).then(r => r.text());
const loadedCheerio = parseHTML(body);
let dataJson: {
const dataJson: {
pages_count: string;
chapters: ChapterEntry[];
} = { pages_count: '', chapters: [] };
@@ -298,9 +298,9 @@ class Sunovels implements Plugin.PagePlugin {
export default new Sunovels();
interface ChapterEntry {
type ChapterEntry = {
chapterName: string;
chapterUrl: string;
releaseTime: string;
chapterNumber: string | number;
}
};
@@ -1,7 +1,7 @@
import { load as parseHTML } from 'cheerio';
import { fetchText } from '@libs/fetch';
import { fetchText } from '@/lib/fetch';
import { FilterTypes, Filters } from '@libs/filterInputs';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { NovelStatus } from '@libs/novelStatus';
class Shu69 implements Plugin.PluginBase {
@@ -1,7 +1,7 @@
import { load as parseHTML } from 'cheerio';
import { fetchText } from '@libs/fetch';
import { fetchText } from '@/lib/fetch';
import { FilterTypes, Filters } from '@libs/filterInputs';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { encode } from 'urlencode';
import { NovelStatus } from '@libs/novelStatus';
@@ -1,6 +1,6 @@
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { FilterTypes, Filters } from '@libs/filterInputs';
import { fetchApi, fetchFile } from '@libs/fetch';
import { fetchApi, fetchFile } from '@/lib/fetch';
import { NovelStatus } from '@libs/novelStatus';
import { load as parseHTML } from 'cheerio';
import { defaultCover } from '@libs/defaultCover';
@@ -207,7 +207,7 @@ class QuanbenPlugin implements Plugin.PluginBase {
const chapterListHtml = await chapterListResult.text();
const $ = parseHTML(chapterListHtml);
let chapters: Plugin.ChapterItem[] = [];
const chapters: Plugin.ChapterItem[] = [];
// Extract standard novel name from AMP path for chapter path storage
const standardNovelPathMatch = novelPath.match(/(\/n\/[^\/]+\/)/);
if (!standardNovelPathMatch || !standardNovelPathMatch[1]) {
@@ -385,7 +385,7 @@ class QuanbenPlugin implements Plugin.PluginBase {
const novelName = nameLink.text().trim();
const novelHref = nameLink.attr('href');
let novelCover = img.attr('src') || img.attr('data-src');
const novelCover = img.attr('src') || img.attr('data-src');
if (novelHref && novelName) {
const absoluteUrl = makeAbsolute(novelHref, this.site);
@@ -1,6 +1,6 @@
import { load as parseHTML } from 'cheerio';
import { fetchApi, fetchFile } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { fetchApi, fetchFile } from '@/lib/fetch';
import { Plugin } from '@/types/plugin';
import { defaultCover } from '@libs/defaultCover';
import { NovelStatus } from '@libs/novelStatus';
@@ -39,7 +39,7 @@ class ixdzs8Plugin implements Plugin.PluginBase {
};
async popularNovels(pageNo: number): Promise<Plugin.NovelItem[]> {
let url = `${this.site}hot/?page=${pageNo}`;
const url = `${this.site}hot/?page=${pageNo}`;
const result = await fetchApi(url);
if (!result.ok) return [];
@@ -146,7 +146,7 @@ class ixdzs8Plugin implements Plugin.PluginBase {
const bookId = String(bid);
// POST request to fetch chapter list JSON
let url = `${this.site}novel/clist/`;
const url = `${this.site}novel/clist/`;
const res = await fetch(url, {
method: 'POST',
headers: {
@@ -1,5 +1,5 @@
import { fetchText } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { fetchText } from '@/lib/fetch';
import { Plugin } from '@/types/plugin';
import { Filters } from '@libs/filterInputs';
import { load as parseHTML } from 'cheerio';
import { NovelStatus } from '@libs/novelStatus';
@@ -1,7 +1,7 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchText } from '@libs/fetch';
import { fetchText } from '@/lib/fetch';
import { FilterTypes, Filters } from '@libs/filterInputs';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { NovelStatus } from '@libs/novelStatus';
class Linovelib implements Plugin.PluginBase {
@@ -1,7 +1,7 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchText } from '@libs/fetch';
import { fetchText } from '@/lib/fetch';
import { FilterTypes, Filters } from '@libs/filterInputs';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { NovelStatus } from '@libs/novelStatus';
class Linovelib_tw implements Plugin.PluginBase {
@@ -1,6 +1,6 @@
import { load as parseHTML } from 'cheerio';
import { fetchApi, fetchFile } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { fetchApi, fetchFile } from '@/lib/fetch';
import { Plugin } from '@/types/plugin';
import { defaultCover } from '@libs/defaultCover';
import { NovelStatus } from '@libs/novelStatus';
@@ -1,6 +1,6 @@
import { load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
class NovelsOnline implements Plugin.PluginBase {
@@ -1,5 +1,5 @@
import { CheerioAPI, load } from 'cheerio';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { fetchApi } from '@libs/fetch';
import { NovelStatus } from '@libs/novelStatus';
import { defaultCover } from '@libs/defaultCover';
@@ -1,6 +1,6 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { defaultCover } from '@libs/defaultCover';
@@ -172,7 +172,7 @@ class ArchiveOfOurOwn implements Plugin.PluginBase {
const releaseTime = releaseTimeText
? new Date(releaseTimeText).toISOString()
: '';
let dateCounter: number = 0;
let dateCounter = 0;
if (loadedCheerio('#chapter_index select').length > 0) {
loadedCheerio('#chapter_index select').each((i, selectEl) => {
loadedCheerio(selectEl)
@@ -1,5 +1,5 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { defaultCover } from '@libs/defaultCover';
import { fetchApi } from '@libs/fetch';
import { NovelStatus } from '@libs/novelStatus';
@@ -1,5 +1,5 @@
import { fetchApi, fetchProto, fetchText } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { fetchApi, fetchProto, fetchText } from '@/lib/fetch';
import { Plugin } from '@/types/plugin';
import { Filters } from '@libs/filterInputs';
import { load as loadCheerio } from 'cheerio';
import { defaultCover } from '@libs/defaultCover';
@@ -144,13 +144,13 @@ class Chrysanthemumgarden implements Plugin.PluginBase {
export default new Chrysanthemumgarden();
interface ChrysanthemumGardenNovel {
type ChrysanthemumGardenNovel = {
name: string;
link: string;
}
};
interface ChrysanthemumGardenNovelItem {
type ChrysanthemumGardenNovelItem = {
name: string;
path: string;
cover: string;
}
};
@@ -1,7 +1,7 @@
import { defaultCover } from '@libs/defaultCover';
import { fetchApi } from '@libs/fetch';
import { Filters, FilterTypes, FilterValueWithType } from '@libs/filterInputs';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { load as parseHTML } from 'cheerio';
//TODO: this looks similar to fictioneer source? maybe use multisrc someday
@@ -1,6 +1,6 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
class DreamBigTL implements Plugin.PluginBase {
id = 'dreambigtl';
@@ -1,7 +1,7 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { FilterTypes, Filters } from '@libs/filterInputs';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
class EarlyNovelPlugin implements Plugin.PagePlugin {
id = 'earlynovel';
@@ -1,4 +1,4 @@
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { fetchApi } from '@libs/fetch';
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { NovelStatus } from '@libs/novelStatus';
@@ -1,8 +1,8 @@
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { load as loadCheerio } from 'cheerio';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { storage } from '@libs/storage';
import { storage } from '@/lib/storage';
import { defaultCover } from '@libs/defaultCover';
class FenrirRealmPlugin implements Plugin.PluginBase {
@@ -1,5 +1,5 @@
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters } from '@libs/filterInputs';
import { load as loadCheerio } from 'cheerio';
import { NovelStatus } from '@libs/novelStatus';
@@ -17,7 +17,7 @@ class FictionZonePlugin implements Plugin.PagePlugin {
//flag indicates whether access to LocalStorage, SesesionStorage is required.
webStorageUtilized?: boolean;
cachedNovelIds: Map<string, string> = new Map();
cachedNovelIds = new Map<string, string>();
async popularNovels(
pageNo: number,
@@ -79,10 +79,10 @@ class FictionZonePlugin implements Plugin.PagePlugin {
if (status === 'Ongoing') novel.status = NovelStatus.Ongoing;
novel.summary = loadedCheerio('#synopsis > div.content').text();
let nuxtData = loadedCheerio('script#__NUXT_DATA__').html();
let parsed = JSON.parse(nuxtData!);
const nuxtData = loadedCheerio('script#__NUXT_DATA__').html();
const parsed = JSON.parse(nuxtData!);
let last = null;
for (let a of parsed) {
for (const a of parsed) {
if (typeof a === 'string' && a.startsWith('novel_covers/')) break;
last = a;
}
@@ -1,4 +1,4 @@
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Parser } from 'htmlparser2';
import { FilterTypes, Filters } from '@libs/filterInputs';
import { defaultCover } from '@libs/defaultCover';
@@ -1,6 +1,6 @@
import { fetchApi } from '@libs/fetch';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
/**
* Example for novel API:
@@ -1,5 +1,5 @@
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { load as loadCheerio } from 'cheerio';
import { NovelStatus } from '@libs/novelStatus';
@@ -57,9 +57,9 @@ class InkittPlugin implements Plugin.PluginBase {
}
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
let req = await fetchApi(this.site + `/stories/${novelPath}`);
let text = await req.text();
let loadedCheerio = loadCheerio(text);
const req = await fetchApi(this.site + `/stories/${novelPath}`);
const text = await req.text();
const loadedCheerio = loadCheerio(text);
const novel: Plugin.SourceNovel = {
path: novelPath,
@@ -1,6 +1,6 @@
import { load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { defaultCover } from '@libs/defaultCover';
import { NovelStatus } from '@libs/novelStatus';
@@ -1,6 +1,6 @@
import { Parser } from 'htmlparser2';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
import dayjs from 'dayjs';
@@ -1,11 +1,11 @@
import { fetchApi, fetchProto, fetchText } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { fetchApi, fetchProto, fetchText } from '@/lib/fetch';
import { Plugin } from '@/types/plugin';
import { Filters } from '@libs/filterInputs';
import { load as loadCheerio } from 'cheerio';
import { defaultCover } from '@libs/defaultCover';
import { NovelStatus } from '@libs/novelStatus';
// import { isUrlAbsolute } from '@libs/isAbsoluteUrl';
// import { storage, localStorage, sessionStorage } from '@libs/storage';
// import { storage, localStorage, sessionStorage } from '@/lib/storage';
// import { encode, decode } from 'urlencode';
// import dayjs from 'dayjs';
import { Parser } from 'htmlparser2';
@@ -41,8 +41,8 @@ class LNTPlugin implements Plugin.PluginBase {
const baseUrl = this.site;
const novels: Plugin.NovelItem[] = [];
loadedCheerio('div.read_list-story-item').each((i, el) => {
let tempNovel = {} as Plugin.NovelItem;
let img = loadedCheerio(el)
const tempNovel = {} as Plugin.NovelItem;
const img = loadedCheerio(el)
.find('.item_thumb')
.find('img')
.first()
@@ -53,7 +53,7 @@ class LNTPlugin implements Plugin.PluginBase {
.first()
.attr('href');
path = path ? path.slice(baseUrl.length) : '';
let title = loadedCheerio(el)
const title = loadedCheerio(el)
.find('.item_thumb')
.find('a')
.first()
@@ -67,7 +67,7 @@ class LNTPlugin implements Plugin.PluginBase {
return novels;
}
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
let url = this.site + novelPath;
const url = this.site + novelPath;
const body = await fetchApi(url);
const html = await body.text().then(r => r.replace(/>\s+</g, '><'));
@@ -161,7 +161,7 @@ class LNTPlugin implements Plugin.PluginBase {
const formData = new FormData();
formData.append('field-search', searchTerm);
let results = await fetchApi(searchUrl, {
const results = await fetchApi(searchUrl, {
method: 'POST',
body: formData,
});
@@ -170,8 +170,8 @@ class LNTPlugin implements Plugin.PluginBase {
const loadedCheerio = loadCheerio(body);
const novels: Plugin.NovelItem[] = [];
loadedCheerio('div.read_list-story-item').each((i, el) => {
let tempNovel = {} as Plugin.NovelItem;
let img = loadedCheerio(el)
const tempNovel = {} as Plugin.NovelItem;
const img = loadedCheerio(el)
.find('.item_thumb')
.find('img')
.first()
@@ -182,7 +182,7 @@ class LNTPlugin implements Plugin.PluginBase {
.first()
.attr('href');
path = path ? path.slice(this.site.length) : '';
let title = loadedCheerio(el)
const title = loadedCheerio(el)
.find('.item_thumb')
.find('a')
.first()
@@ -1,7 +1,7 @@
import { Parser } from 'htmlparser2';
import { fetchApi } from '@libs/fetch';
import { FilterTypes, Filters } from '@libs/filterInputs';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
class LnMTLPlugin implements Plugin.PagePlugin {
id = 'lnmtl';
@@ -1,6 +1,6 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
class MTLReader implements Plugin.PluginBase {
id = 'mtlreader';
@@ -1,6 +1,6 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { Parser } from 'htmlparser2';
@@ -87,7 +87,7 @@ class MVLEMPYRPlugin implements Plugin.PluginBase {
}
convertNovelId(e: bigint) {
let t = 1999999997n;
const t = 1999999997n;
let u = 1n,
c = 7n % t,
d = e;
@@ -1,6 +1,6 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
class NovelBuddy implements Plugin.PluginBase {
@@ -1,6 +1,6 @@
import { CheerioAPI, load } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { NovelStatus } from '@libs/novelStatus';
import { Filters, FilterTypes } from '@libs/filterInputs';
@@ -1,6 +1,6 @@
import { load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { defaultCover } from '@libs/defaultCover';
class NovelHall implements Plugin.PluginBase {
@@ -1,9 +1,9 @@
import { load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { defaultCover } from '@libs/defaultCover';
import dayjs from 'dayjs';
import { storage } from '@libs/storage';
import { storage } from '@/lib/storage';
import { NovelStatus } from '@libs/novelStatus';
class Novelight implements Plugin.PagePlugin {
@@ -1,7 +1,7 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
class NovelUpdates implements Plugin.PluginBase {
id = 'novelupdates';
@@ -273,7 +273,7 @@ class NovelUpdates implements Plugin.PluginBase {
);
}
let chapterTitle = pageData.props.chapter.title;
const chapterTitle = pageData.props.chapter.title;
let chapterContent = pageData.props.chapter.content;
// Clean up content (remove inline styles/scripts if needed)
@@ -1,6 +1,6 @@
import { Parser } from 'htmlparser2';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
class PawRead implements Plugin.PluginBase {
@@ -1,5 +1,5 @@
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { NovelStatus } from '@libs/novelStatus';
@@ -1,5 +1,5 @@
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters } from '@libs/filterInputs';
import { CheerioAPI, load as loadCheerio } from 'cheerio';
import { defaultCover } from '@libs/defaultCover';
@@ -30,11 +30,11 @@ class ReadFromPlugin implements Plugin.PluginBase {
genres: string;
author: string;
})[] {
let ret = loadedCheerio(
const ret = loadedCheerio(
(isSearch ? 'div.text' : '#dle-content') + ' > article.box',
)
.map((i, el) => {
let summary = loadedCheerio(el).find(
const summary = loadedCheerio(el).find(
isSearch ? 'div.text5' : 'div.text3',
)[0];
loadedCheerio(summary).find('.coll-ellipsis').remove();
@@ -83,18 +83,18 @@ class ReadFromPlugin implements Plugin.PluginBase {
filters,
}: Plugin.PopularNovelsOptions<typeof this.filters>,
) {
let type = showLatestNovels ? 'last_added_books' : 'allbooks';
let res = await fetchApi(
const type = showLatestNovels ? 'last_added_books' : 'allbooks';
const res = await fetchApi(
'https://readfrom.net/' + type + '/page/' + pageNo + '/',
);
let text = await res.text();
const text = await res.text();
return this.parseNovels(loadCheerio(text));
}
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
let data = await fetchApi('https://readfrom.net/' + novelPath);
let text = await data.text();
let loadedCheerio = loadCheerio(text);
const data = await fetchApi('https://readfrom.net/' + novelPath);
const text = await data.text();
const loadedCheerio = loadCheerio(text);
const novel: Plugin.SourceNovel = {
path: novelPath,
@@ -142,12 +142,12 @@ class ReadFromPlugin implements Plugin.PluginBase {
novel.author = moreNovelInfo.author;
}
let seriesElm = loadedCheerio('center > b:has(a)').filter((i, el) =>
const seriesElm = loadedCheerio('center > b:has(a)').filter((i, el) =>
loadedCheerio(el).find('a').attr('href')!.startsWith('/series.html'),
)[0];
if (seriesElm) {
let seriesText = loadedCheerio(seriesElm).text().trim();
const seriesText = loadedCheerio(seriesElm).text().trim();
novel.summary = seriesText + '\n\n' + novel.summary;
}
@@ -198,11 +198,11 @@ class ReadFromPlugin implements Plugin.PluginBase {
async searchNovels(searchTerm: string, pageNo: number) {
if (pageNo !== 1) return [];
let res = await fetchApi(
const res = await fetchApi(
'https://readfrom.net/build_in_search/?q=' +
encodeURIComponent(searchTerm),
);
let text = await res.text();
const text = await res.text();
return this.parseNovels(loadCheerio(text), true);
}
@@ -1,7 +1,7 @@
import { load as parseHTML } from 'cheerio';
import { isUrlAbsolute } from '@libs/isAbsoluteUrl';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
import dayjs from 'dayjs';
@@ -1,10 +1,10 @@
import { fetchApi } from '@libs/fetch';
import type { Plugin } from '@typings/plugin';
import type { Plugin } from '@/types/plugin';
const API_BASE = 'https://api.reaperscans.com';
const MEDIA_BASE = 'https://media.reaperscans.com/file/4SRBHm/';
interface ApiResponse<T> {
type ApiResponse<T> = {
data: T;
meta: {
total: number;
@@ -12,13 +12,13 @@ interface ApiResponse<T> {
current_page: number;
last_page: number;
};
}
};
interface ReaperNovel {
type ReaperNovel = {
title: string;
thumbnail: string;
series_slug: string;
}
};
class ReaperScans implements Plugin.PluginBase {
id = 'reaperscans.com';
@@ -1,5 +1,5 @@
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters } from '@libs/filterInputs';
import { load as loadCheerio } from 'cheerio';
import { defaultCover } from '@libs/defaultCover';
@@ -290,7 +290,7 @@ class ReLibraryPlugin implements Plugin.PluginBase {
.startsWith('status')
) {
loadedCheerio(el).find('strong').remove();
let status = loadedCheerio(el).text().toLowerCase().trim();
const status = loadedCheerio(el).text().toLowerCase().trim();
if (status.includes('on-going')) {
novel.status = NovelStatus.Ongoing;
} else if (status.includes('completed')) {
@@ -1,6 +1,6 @@
import { Parser } from 'htmlparser2';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { NovelStatus } from '@libs/novelStatus';
import { isUrlAbsolute } from '@libs/isAbsoluteUrl';
@@ -1,7 +1,7 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { FilterTypes, Filters } from '@libs/filterInputs';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import dayjs from 'dayjs';
class ScribbleHubPlugin implements Plugin.PluginBase {
@@ -1,4 +1,4 @@
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { FilterTypes, Filters } from '@libs/filterInputs';
import { defaultCover } from '@libs/defaultCover';
import { fetchApi } from '@libs/fetch';
@@ -1,8 +1,8 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { Plugin } from '@typings/plugin';
import { storage } from '@libs/storage';
import { Plugin } from '@/types/plugin';
import { storage } from '@/lib/storage';
class Webnovel implements Plugin.PluginBase {
id = 'webnovel';
@@ -138,9 +138,9 @@ class Webnovel implements Plugin.PluginBase {
const chapters: Plugin.ChapterItem[] = [];
loadedCheerio('.volume-item').each((i_v_, ele_v) => {
let originalVolumeName = loadedCheerio(ele_v).first().text().trim();
let volumeNameMatch = originalVolumeName.match(/Volume\s(\d+)/);
let volumeName = volumeNameMatch
const originalVolumeName = loadedCheerio(ele_v).first().text().trim();
const volumeNameMatch = originalVolumeName.match(/Volume\s(\d+)/);
const volumeName = volumeNameMatch
? `Volume ${volumeNameMatch[1]}`
: 'Unknown Volume';
@@ -1,4 +1,4 @@
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { fetchApi } from '@libs/fetch';
import { FilterTypes, Filters } from '@libs/filterInputs';
import { load as parseHTML } from 'cheerio';
@@ -1,6 +1,6 @@
import { load as parseHTML } from 'cheerio';
import { fetchApi, fetchProto } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { fetchApi, fetchProto } from '@/lib/fetch';
import { Plugin } from '@/types/plugin';
import { Filters } from '@libs/filterInputs';
import { NovelStatus } from '@libs/novelStatus';
@@ -1,6 +1,6 @@
import { CheerioAPI, load } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
import dayjs from 'dayjs';
import { defaultCover } from '@libs/defaultCover';
@@ -1,6 +1,6 @@
import { CheerioAPI, load } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { defaultCover } from '@libs/defaultCover';
import { NovelStatus } from '@libs/novelStatus';
import dayjs from 'dayjs';
@@ -1,6 +1,6 @@
import { CheerioAPI, load } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { defaultCover } from '@libs/defaultCover';
import { NovelStatus } from '@libs/novelStatus';
@@ -1,6 +1,6 @@
import { load, CheerioAPI } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { NovelStatus } from '@libs/novelStatus';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { defaultCover } from '@libs/defaultCover';
@@ -1,6 +1,6 @@
import { CheerioAPI, load } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { defaultCover } from '@libs/defaultCover';
import { NovelStatus } from '@libs/novelStatus';
@@ -1,6 +1,6 @@
import { CheerioAPI, load } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { defaultCover } from '@libs/defaultCover';
import { NovelStatus } from '@libs/novelStatus';
@@ -1,6 +1,6 @@
import { CheerioAPI, load } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { defaultCover } from '@libs/defaultCover';
import { NovelStatus } from '@libs/novelStatus';
import dayjs from 'dayjs';
@@ -1,6 +1,6 @@
import { CheerioAPI, load } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { defaultCover } from '@libs/defaultCover';
import { NovelStatus } from '@libs/novelStatus';
import dayjs from 'dayjs';
@@ -1,6 +1,6 @@
import { Cheerio, CheerioAPI, load, Element } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { defaultCover } from '@libs/defaultCover';
import { NovelStatus } from '@libs/novelStatus';
+497
View File
@@ -0,0 +1,497 @@
import { Plugin } from '@/types/plugin';
import p_0 from '@plugins/arabic/ArNovel[madara]';
import p_1 from '@plugins/arabic/Azora[madara]';
import p_2 from '@plugins/arabic/HizoManga[madara]';
import p_3 from '@plugins/arabic/KolNovel[lightnovelwp]';
import p_4 from '@plugins/arabic/Novel4Up[madara]';
import p_5 from '@plugins/arabic/NovelsParadise[lightnovelwp]';
import p_6 from '@plugins/arabic/Olaoecyou[madara]';
import p_7 from '@plugins/arabic/Riwyat[madara]';
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/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,
p_1,
p_2,
p_3,
p_4,
p_5,
p_6,
p_7,
p_8,
p_9,
p_10,
p_11,
p_12,
p_13,
p_14,
p_15,
p_16,
p_17,
p_18,
p_19,
p_20,
p_21,
p_22,
p_23,
p_24,
p_25,
p_26,
p_27,
p_28,
p_29,
p_30,
p_31,
p_32,
p_33,
p_34,
p_35,
p_36,
p_37,
p_38,
p_39,
p_40,
p_41,
p_42,
p_43,
p_44,
p_45,
p_46,
p_47,
p_48,
p_49,
p_50,
p_51,
p_52,
p_53,
p_54,
p_55,
p_56,
p_57,
p_58,
p_59,
p_60,
p_61,
p_62,
p_63,
p_64,
p_65,
p_66,
p_67,
p_68,
p_69,
p_70,
p_71,
p_72,
p_73,
p_74,
p_75,
p_76,
p_77,
p_78,
p_79,
p_80,
p_81,
p_82,
p_83,
p_84,
p_85,
p_86,
p_87,
p_88,
p_89,
p_90,
p_91,
p_92,
p_93,
p_94,
p_95,
p_96,
p_97,
p_98,
p_99,
p_100,
p_101,
p_102,
p_103,
p_104,
p_105,
p_106,
p_107,
p_108,
p_109,
p_110,
p_111,
p_112,
p_113,
p_114,
p_115,
p_116,
p_117,
p_118,
p_119,
p_120,
p_121,
p_122,
p_123,
p_124,
p_125,
p_126,
p_127,
p_128,
p_129,
p_130,
p_131,
p_132,
p_133,
p_134,
p_135,
p_136,
p_137,
p_138,
p_139,
p_140,
p_141,
p_142,
p_143,
p_144,
p_145,
p_146,
p_147,
p_148,
p_149,
p_150,
p_151,
p_152,
p_153,
p_154,
p_155,
p_156,
p_157,
p_158,
p_159,
p_160,
p_161,
p_162,
p_163,
p_164,
p_165,
p_166,
p_167,
p_168,
p_169,
p_170,
p_171,
p_172,
p_173,
p_174,
p_175,
p_176,
p_177,
p_178,
p_179,
p_180,
p_181,
p_182,
p_183,
p_184,
p_185,
p_186,
p_187,
p_188,
p_189,
p_190,
p_191,
p_192,
p_193,
p_194,
p_195,
p_196,
p_197,
p_198,
p_199,
p_200,
p_201,
p_202,
p_203,
p_204,
p_205,
p_206,
p_207,
p_208,
p_209,
p_210,
p_211,
p_212,
p_213,
p_214,
p_215,
p_216,
p_217,
p_218,
p_219,
p_220,
p_221,
p_222,
p_223,
p_224,
p_225,
p_226,
p_227,
p_228,
p_229,
p_230,
p_231,
p_232,
p_233,
p_234,
p_235,
p_236,
p_237,
p_238,
p_239,
p_240,
p_241,
p_242,
p_243,
p_244,
p_245,
];
export default PLUGINS;
@@ -1,6 +1,6 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { NovelStatus } from '@libs/novelStatus';
import { Filters, FilterTypes } from '@libs/filterInputs';
@@ -1,6 +1,6 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
class NovelRingan implements Plugin.PluginBase {
@@ -1,6 +1,6 @@
import { CheerioAPI, load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
class SakuraNovel implements Plugin.PluginBase {
@@ -1,11 +1,11 @@
import { load as loadCheerio } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { defaultCover } from '@libs/defaultCover';
import { FilterTypes, Filters } from '@libs/filterInputs';
import { NovelStatus } from '@libs/novelStatus';
// const novelStatus = require('@libs/novelStatus');
// const isUrlAbsolute = require('@libs/isAbsoluteUrl');
// const novelStatus = require('@/types/constants');
// const isUrlAbsolute = require('@/lib/utils');
// const parseDate = require('@libs/parseDate');
class Syosetu implements Plugin.PluginBase {
@@ -1,5 +1,5 @@
import { fetchText } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { fetchText } from '@/lib/fetch';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { load as loadCheerio } from 'cheerio';
import { defaultCover } from '@libs/defaultCover';
@@ -1,5 +1,5 @@
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { load as parseHTML } from 'cheerio';
class Agitoon implements Plugin.PluginBase {
@@ -1,9 +1,9 @@
import { fetchApi } from '@libs/fetch';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { NovelStatus } from '@libs/novelStatus';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { load as parseHTML } from 'cheerio';
import { storage } from '@libs/storage';
import { storage } from '@/lib/storage';
class KomgaPlugin implements Plugin.PluginBase {
id = 'komga';
@@ -26,10 +26,10 @@ class KomgaPlugin implements Plugin.PluginBase {
}).then(res => res.text());
}
btoa(input: string = '') {
btoa(input = '') {
const chars =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
let str = input;
const str = input;
let output = '';
for (
@@ -67,11 +67,11 @@ class KomgaPlugin implements Plugin.PluginBase {
async getSeries(url: string): Promise<Plugin.NovelItem[]> {
const novels: Plugin.NovelItem[] = [];
var response = await this.makeRequest(url);
const response = await this.makeRequest(url);
const series = JSON.parse(response).content;
for (let s of series) {
for (const s of series) {
novels.push({
name: s.name,
path: 'api/v1/series/' + s.id,
@@ -110,7 +110,7 @@ class KomgaPlugin implements Plugin.PluginBase {
const url = this.site + novelPath;
var response = await this.makeRequest(url);
const response = await this.makeRequest(url);
const series = JSON.parse(response);
@@ -152,7 +152,7 @@ class KomgaPlugin implements Plugin.PluginBase {
const booksData = JSON.parse(booksResponse).content;
for (let book of booksData) {
for (const book of booksData) {
const bookManifestResponse = await this.makeRequest(
this.site + `opds/v2/books/${book.id}/manifest`,
);
@@ -162,7 +162,7 @@ class KomgaPlugin implements Plugin.PluginBase {
const toc = this.flattenArray(bookManifest.toc);
let i = 1;
for (let page of bookManifest.readingOrder) {
for (const page of bookManifest.readingOrder) {
const tocItem = toc.find(
(v: any) => v.href?.split('#')[0] === page.href,
);
@@ -1,8 +1,8 @@
import { CheerioAPI, load as loadCheerio, load } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { NovelStatus } from '@libs/novelStatus';
import { Filters } from '../../../src/libs/filterInputs';
import { Filters } from '@libs/filterInputs';
type FictioneerOptions = {
browsePage: string;
@@ -24,7 +24,7 @@ const generate = async name => {
console.warn(name, ': lang, filename, pluginScript are required!');
continue;
}
const pluginsDir = './src/plugins';
const pluginsDir = './plugins';
const filePath = path.join(
pluginsDir,
lang.toLowerCase(),
@@ -39,7 +39,7 @@ const generate = async name => {
}
};
const MULTISRC_DIR = './scripts/multisrc';
const MULTISRC_DIR = './plugins/multisrc';
const run = async () => {
const sources = fs
@@ -1,6 +1,6 @@
import { fetchApi } from '@libs/fetch';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { NovelStatus } from '@libs/novelStatus';
export type HotNovelPubMetadata = {
@@ -1,6 +1,6 @@
import { fetchApi } from '@libs/fetch';
import { Filters, FilterTypes } from '@libs/filterInputs';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { NovelStatus } from '@libs/novelStatus';
import { load as parseHTML } from 'cheerio';
import dayjs from 'dayjs';
@@ -1,6 +1,6 @@
import { load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { Plugin } from '@/types/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
import dayjs from 'dayjs';
@@ -44,6 +44,6 @@ and add it to the folder "public/static/multisrc/lightnovelwp/{sourceID}/icon.pn
### filters
To add filters to a source you need to run the script "get_filters.js" \
(`npx node scripts/multisrc/lightnovelwp/get_filters.js`
(`npx node plugins/multisrc/lightnovelwp/get_filters.js`
(if you are at the root of the project) (and you have ran "npm install" before))
and follow the instructions (url is easier and faster but sometimes it doesn't work)

Some files were not shown because too many files have changed in this diff Show More