Add TopLiba (#1003)
* test * cleanup deleted unused `fetchFile` import replaced fetch with `fetchApi` * eslint * fix * fix multi * Hmm, what to do next. * test URLSearchParams * fix
This commit is contained in:
+12
-10
@@ -1,11 +1,10 @@
|
|||||||
import { fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters } from '@libs/filterInputs';
|
import { Filters } from '@libs/filterInputs';
|
||||||
import { load as loadCheerio } from 'cheerio';
|
import { load as loadCheerio } from 'cheerio';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
// import { isUrlAbsolute } from "@libs/isAbsoluteUrl";
|
// import { isUrlAbsolute } from '@libs/isAbsoluteUrl';
|
||||||
// import { parseMadaraDate } from "@libs/parseMadaraDate";
|
|
||||||
|
|
||||||
class TemplatePlugin implements Plugin.PluginBase {
|
class TemplatePlugin implements Plugin.PluginBase {
|
||||||
id = '';
|
id = '';
|
||||||
@@ -42,15 +41,15 @@ class TemplatePlugin implements Plugin.PluginBase {
|
|||||||
// TODO: get here data from the site and
|
// TODO: get here data from the site and
|
||||||
// un-comment and fill-in the relevant fields
|
// un-comment and fill-in the relevant fields
|
||||||
|
|
||||||
// novel.name = "";
|
// novel.name = '';
|
||||||
// novel.artist = "";
|
// novel.artist = '';
|
||||||
// novel.author = "";
|
// novel.author = '';
|
||||||
novel.cover = defaultCover;
|
novel.cover = defaultCover;
|
||||||
// novel.genres = "";
|
// novel.genres = '';
|
||||||
// novel.status = NovelStatus.Completed;
|
// novel.status = NovelStatus.Completed;
|
||||||
// novel.summary = "";
|
// novel.summary = '';
|
||||||
|
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
// TODO: here parse the chapter list
|
// TODO: here parse the chapter list
|
||||||
|
|
||||||
@@ -75,12 +74,15 @@ class TemplatePlugin implements Plugin.PluginBase {
|
|||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
pageNo: number,
|
pageNo: number,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
let novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
// get novels using the search term
|
// get novels using the search term
|
||||||
|
|
||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolveUrl = (path: string, isNovel?: boolean) =>
|
||||||
|
this.site + (isNovel ? '/book/' : '/chapter/') + path;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new TemplatePlugin();
|
export default new TemplatePlugin();
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
// @ts-check
|
||||||
|
|
||||||
|
import eslint from '@eslint/js';
|
||||||
|
import prettierConfig from 'eslint-config-prettier';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
import globals from 'globals';
|
||||||
|
|
||||||
|
const globalsHermes = [
|
||||||
|
//hermes@0.72
|
||||||
|
'AggregateError',
|
||||||
|
'Array',
|
||||||
|
'ArrayBuffer',
|
||||||
|
'BigInt',
|
||||||
|
'BigInt64Array',
|
||||||
|
'BigUint64Array',
|
||||||
|
'Boolean',
|
||||||
|
'DataView',
|
||||||
|
'Date',
|
||||||
|
'DebuggerInternal',
|
||||||
|
'Error',
|
||||||
|
'EvalError',
|
||||||
|
'Float32Array',
|
||||||
|
'Float64Array',
|
||||||
|
'Function',
|
||||||
|
'HermesInternal',
|
||||||
|
'Infinity',
|
||||||
|
'Int16Array',
|
||||||
|
'Int32Array',
|
||||||
|
'Int8Array',
|
||||||
|
'JSON',
|
||||||
|
'Map',
|
||||||
|
'Math',
|
||||||
|
'NaN',
|
||||||
|
'Number',
|
||||||
|
'Object',
|
||||||
|
'Promise',
|
||||||
|
'Proxy',
|
||||||
|
'QuitError',
|
||||||
|
'RangeError',
|
||||||
|
'ReferenceError',
|
||||||
|
'Reflect',
|
||||||
|
'RegExp',
|
||||||
|
'Set',
|
||||||
|
'String',
|
||||||
|
'Symbol',
|
||||||
|
'SyntaxError',
|
||||||
|
'TimeoutError',
|
||||||
|
'TypeError',
|
||||||
|
'URIError',
|
||||||
|
'Uint16Array',
|
||||||
|
'Uint32Array',
|
||||||
|
'Uint8Array',
|
||||||
|
'Uint8ClampedArray',
|
||||||
|
'WeakMap',
|
||||||
|
'WeakSet',
|
||||||
|
'__defineGetter__',
|
||||||
|
'__defineSetter__',
|
||||||
|
'__lookupGetter__',
|
||||||
|
'__lookupSetter__',
|
||||||
|
'__proto__',
|
||||||
|
'clearTimeout',
|
||||||
|
'constructor',
|
||||||
|
'createHeapSnapshot',
|
||||||
|
'decodeURI',
|
||||||
|
'decodeURIComponent',
|
||||||
|
'encodeURI',
|
||||||
|
'encodeURIComponent',
|
||||||
|
'escape',
|
||||||
|
'gc',
|
||||||
|
'globalThis',
|
||||||
|
'hasOwnProperty',
|
||||||
|
'isFinite',
|
||||||
|
'isNaN',
|
||||||
|
'isPrototypeOf',
|
||||||
|
'loadSegment',
|
||||||
|
'parseFloat',
|
||||||
|
'parseInt',
|
||||||
|
'print',
|
||||||
|
'propertyIsEnumerable',
|
||||||
|
'setImmediate',
|
||||||
|
'setTimeout',
|
||||||
|
'toLocaleString',
|
||||||
|
'toString',
|
||||||
|
'undefined',
|
||||||
|
'unescape',
|
||||||
|
'valueOf',
|
||||||
|
//react-native
|
||||||
|
'AbortController',
|
||||||
|
'Blob',
|
||||||
|
'ErrorUtils',
|
||||||
|
'Event',
|
||||||
|
'EventTarget',
|
||||||
|
'File',
|
||||||
|
'FileReader',
|
||||||
|
'FormData',
|
||||||
|
'Headers',
|
||||||
|
'Intl',
|
||||||
|
'URL',
|
||||||
|
'URLSearchParams',
|
||||||
|
'WebSocket',
|
||||||
|
'XMLHttpRequest',
|
||||||
|
'__DEV__',
|
||||||
|
'__dirname',
|
||||||
|
'__fbBatchedBridgeConfig',
|
||||||
|
'alert',
|
||||||
|
'cancelAnimationFrame',
|
||||||
|
'cancelIdleCallback',
|
||||||
|
'clearImmediate',
|
||||||
|
'clearInterval',
|
||||||
|
'console',
|
||||||
|
'document',
|
||||||
|
'exports',
|
||||||
|
'fetch',
|
||||||
|
'global',
|
||||||
|
'module',
|
||||||
|
'navigator',
|
||||||
|
'process',
|
||||||
|
'queueMicrotask',
|
||||||
|
'requestAnimationFrame',
|
||||||
|
'requestIdleCallback',
|
||||||
|
'require',
|
||||||
|
'setInterval',
|
||||||
|
'window',
|
||||||
|
].map(key => ({ [key]: 'readonly' }));
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
eslint.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
...tseslint.configs.stylistic,
|
||||||
|
prettierConfig,
|
||||||
|
{
|
||||||
|
ignores: ['.js', 'docs', 'proxy_server.js'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['./src/plugins/*/*.ts', './scripts/multisrc/*/template.ts'],
|
||||||
|
rules: {
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': 'warn',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'warn',
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
|
'no-case-declarations': 'warn',
|
||||||
|
'no-undef': 'error',
|
||||||
|
},
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 5,
|
||||||
|
sourceType: 'module',
|
||||||
|
globals: Object.assign(...globalsHermes),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.{ts,tsx,mts,cts,js}'],
|
||||||
|
ignores: ['./src/plugins/*/*.ts', './scripts/multisrc/*/template.ts'],
|
||||||
|
rules: {
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': 'warn',
|
||||||
|
'@typescript-eslint/no-namespace': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'warn',
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
|
'no-undef': 'error',
|
||||||
|
},
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.serviceworker,
|
||||||
|
...globals.browser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import path from 'path';
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
let content = `import { Plugin } from '@typings/plugin';\n`;
|
let content = `import { Plugin } from '@typings/plugin';\n`;
|
||||||
|
|||||||
Generated
+9352
-6980
File diff suppressed because it is too large
Load Diff
+69
-61
@@ -1,62 +1,70 @@
|
|||||||
{
|
{
|
||||||
"name": "lnreader-plugins",
|
"name": "lnreader-plugins",
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"description": "Plugins repo for LNReader",
|
"description": "Plugins repo for LNReader",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node importPlugins.js && concurrently \"npm run dev\" \"npm run proxy\"",
|
"clearMultisrc": "find . -type f -wholename \"*plugins*\\[*\\]*.[t,j]s\" -delete",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"proxy": "node proxy_server.js",
|
"generate": "node ./scripts/multisrc/generate.js",
|
||||||
"test": "exit 0",
|
"host-linux": "chmod +x ./host.sh && ./host.sh",
|
||||||
"json": "node ./scripts/json_plugins.js",
|
"host-windows": "powershell ./host.ps1",
|
||||||
"generate": "node ./scripts/multisrc/generate.js",
|
"icons": "npm run clearMultisrc && npm run generate && npx tsc -m node16 && npm run json && node ./scripts/icon_reloading.js",
|
||||||
"clearMultisrc": "find . -type f -wholename \"*plugins*\\[*\\]*.[t,j]s\" -delete",
|
"json": " node ./scripts/json_plugins.js",
|
||||||
"host-linux": "chmod +x ./host.sh && ./host.sh",
|
"lint": "npx eslint .",
|
||||||
"host-windows": "powershell ./host.ps1",
|
"prepare": "husky install",
|
||||||
"prettier": "prettier --write \"./**/*.{js,ts}\"",
|
"prettier": "prettier --write \"./**/*.{js,ts}\"",
|
||||||
"prepare": "husky install",
|
"proxy": "node proxy_server.js",
|
||||||
"icons": "npm run clearMultisrc && npm run generate && npx tsc -m node16 && npm run json && node ./scripts/icon_reloading.js"
|
"start": "node importPlugins.js && concurrently \"npm run dev\" \"npm run proxy\"",
|
||||||
},
|
"test": "exit 0"
|
||||||
"author": "LNReader",
|
},
|
||||||
"license": "MIT",
|
"author": "LNReader",
|
||||||
"dependencies": {
|
"license": "MIT",
|
||||||
"@emotion/react": "^11.11.4",
|
"dependencies": {
|
||||||
"@emotion/styled": "^11.11.5",
|
"@emotion/react": "^11.11.4",
|
||||||
"@fontsource/roboto": "^5.0.13",
|
"@emotion/styled": "^11.11.5",
|
||||||
"@mui/icons-material": "^5.15.20",
|
"@fontsource/roboto": "^5.0.13",
|
||||||
"@mui/material": "^5.15.20",
|
"@mui/icons-material": "^5.15.20",
|
||||||
"@reduxjs/toolkit": "^2.2.5",
|
"@mui/material": "^5.15.20",
|
||||||
"cheerio": "^1.0.0-rc.12",
|
"@reduxjs/toolkit": "^2.2.5",
|
||||||
"dayjs": "^1.11.10",
|
"cheerio": "^1.0.0-rc.12",
|
||||||
"htmlparser2": "^9.1.0",
|
"dayjs": "^1.11.10",
|
||||||
"protobufjs": "^7.2.6",
|
"htmlparser2": "^9.1.0",
|
||||||
"qs": "^6.11.2",
|
"protobufjs": "^7.2.6",
|
||||||
"react": "^18.2.0",
|
"qs": "^6.11.2",
|
||||||
"react-dom": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-redux": "^9.1.2",
|
"react-dom": "^18.2.0",
|
||||||
"react-virtuoso": "^4.7.11",
|
"react-redux": "^9.1.2",
|
||||||
"react-window": "^1.8.10",
|
"react-virtuoso": "^4.7.11",
|
||||||
"sanitize-html": "^2.12.1",
|
"react-window": "^1.8.10",
|
||||||
"urlencode": "^2.0.0"
|
"sanitize-html": "^2.12.1",
|
||||||
},
|
"urlencode": "^2.0.0"
|
||||||
"devDependencies": {
|
},
|
||||||
"@types/qs": "^6.9.15",
|
"devDependencies": {
|
||||||
"@types/react": "^18.2.66",
|
"@eslint/js": "^9.5.0",
|
||||||
"@types/react-dom": "^18.2.22",
|
"@types/eslint__js": "^8.42.3",
|
||||||
"@types/react-window": "^1.8.8",
|
"@types/qs": "^6.9.15",
|
||||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
"@types/react": "^18.2.66",
|
||||||
"concurrently": "^8.2.2",
|
"@types/react-dom": "^18.2.22",
|
||||||
"http-proxy": "^1.18.1",
|
"@types/react-window": "^1.8.8",
|
||||||
"husky": "^9.0.11",
|
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||||
"image-size": "^1.1.1",
|
"concurrently": "^8.2.2",
|
||||||
"lint-staged": "^15.2.2",
|
"eslint": "^8.57.0",
|
||||||
"prettier": "3.2.5",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"terser": "^5.31.1",
|
"globals": "^15.6.0",
|
||||||
"ts-node": "^10.9.2",
|
"http-proxy": "^1.18.1",
|
||||||
"vite": "^5.2.0"
|
"husky": "^9.0.11",
|
||||||
},
|
"image-size": "^1.1.1",
|
||||||
"lint-staged": {
|
"lint-staged": "^15.2.2",
|
||||||
"**/*.{js,ts,jsx,tsx}": "prettier --write"
|
"prettier": "^3.2.5",
|
||||||
}
|
"terser": "^5.31.1",
|
||||||
}
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.5.2",
|
||||||
|
"typescript-eslint": "^7.14.1",
|
||||||
|
"vite": "^5.2.0"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"**/*.{js,ts,jsx,tsx}": "prettier --write"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import http from 'http';
|
import http from 'http';
|
||||||
import httpProxy from 'http-proxy';
|
import httpProxy from 'http-proxy';
|
||||||
import url from 'url';
|
|
||||||
import fs from 'fs';
|
|
||||||
|
|
||||||
const CLIENT_HOST = 'http://localhost:3000';
|
const CLIENT_HOST = 'http://localhost:3000';
|
||||||
const proxy = httpProxy.createProxyServer({});
|
const proxy = httpProxy.createProxyServer({});
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 870 B After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 466 B After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 494 B |
@@ -1,3 +1,4 @@
|
|||||||
|
/* global Buffer */
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import sizeOf from 'image-size';
|
import sizeOf from 'image-size';
|
||||||
@@ -83,7 +84,7 @@ const notAvailableImage = fs.readFileSync(
|
|||||||
const exist = fs.existsSync(icon);
|
const exist = fs.existsSync(icon);
|
||||||
|
|
||||||
if (!exist) {
|
if (!exist) {
|
||||||
const dir = icon.match(/^.*[\\\/]/)[0];
|
const dir = path.dirname(icon);
|
||||||
fs.mkdirSync(dir, { recursive: true });
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { exit } from 'process';
|
|
||||||
import languages from './languages.js';
|
import languages from './languages.js';
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
import { minify } from './terser.js';
|
import { minify } from './terser.js';
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const generate = async name => {
|
|||||||
const filePath = path.join(
|
const filePath = path.join(
|
||||||
pluginsDir,
|
pluginsDir,
|
||||||
lang.toLowerCase(),
|
lang.toLowerCase(),
|
||||||
filename.replace(/[\s-\.]+/g, '') + `[${name}].ts`,
|
filename.replace(/[\s-.]+/g, '') + `[${name}].ts`,
|
||||||
);
|
);
|
||||||
fs.writeFileSync(filePath, pluginScript, { encoding: 'utf-8' });
|
fs.writeFileSync(filePath, pluginScript, { encoding: 'utf-8' });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import list from './sources.json' with { type: 'json' };
|
import list from './sources.json' with { type: 'json' };
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import path from 'path';
|
import { fileURLToPath } from 'url';
|
||||||
|
import { dirname, join } from 'path';
|
||||||
|
|
||||||
|
const folder = dirname(fileURLToPath(import.meta.url));
|
||||||
const FilterTypes = {
|
const FilterTypes = {
|
||||||
TextInput: 'Text',
|
TextInput: 'Text',
|
||||||
Picker: 'Picker',
|
Picker: 'Picker',
|
||||||
@@ -40,12 +42,9 @@ export const generateAll = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const generator = function generator(metadata) {
|
const generator = function generator(metadata) {
|
||||||
const HotNovelPubTemplate = readFileSync(
|
const HotNovelPubTemplate = readFileSync(join(folder, 'template.ts'), {
|
||||||
'./scripts/multisrc/hotnovelpub/template.ts',
|
encoding: 'utf-8',
|
||||||
{
|
});
|
||||||
encoding: 'utf-8',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const pluginScript = `
|
const pluginScript = `
|
||||||
${HotNovelPubTemplate}
|
${HotNovelPubTemplate}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { fetchApi } from '@libs/fetch';
|
|||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import dayjs from 'dayjs';
|
|
||||||
|
|
||||||
export interface HotNovelPubMetadata {
|
export interface HotNovelPubMetadata {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -135,10 +134,7 @@ class HotNovelPubPlugin implements Plugin.PluginBase {
|
|||||||
return chapterText.replace(/\.copy right hot novel pub/g, '');
|
return chapterText.replace(/\.copy right hot novel pub/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
searchTerm: string,
|
|
||||||
pageNo: number,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const result: responseSearch = await fetchApi(this.apiSite + '/search', {
|
const result: responseSearch = await fetchApi(this.apiSite + '/search', {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json;charset=utf-8',
|
'Content-Type': 'application/json;charset=utf-8',
|
||||||
@@ -162,7 +158,7 @@ class HotNovelPubPlugin implements Plugin.PluginBase {
|
|||||||
|
|
||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
resolveUrl = (path: string, isNovel?: boolean) => this.site + '/' + path;
|
resolveUrl = (path: string) => this.site + '/' + path;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface responseNovels {
|
interface responseNovels {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import list from './sources.json' with { type: 'json' };
|
import list from './sources.json' with { type: 'json' };
|
||||||
import defaultSettings from './settings.json' with { type: 'json' };
|
import defaultSettings from './settings.json' with { type: 'json' };
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import path from 'path';
|
import { fileURLToPath } from 'url';
|
||||||
|
import { dirname, join } from 'path';
|
||||||
|
|
||||||
|
const folder = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
export const generateAll = function () {
|
export const generateAll = function () {
|
||||||
return list.map(metadata => {
|
return list.map(metadata => {
|
||||||
@@ -12,12 +15,9 @@ export const generateAll = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const generator = function generator(metadata) {
|
const generator = function generator(metadata) {
|
||||||
const IfreedomTemplate = readFileSync(
|
const IfreedomTemplate = readFileSync(join(folder, 'template.ts'), {
|
||||||
'./scripts/multisrc/ifreedom/template.ts',
|
encoding: 'utf-8',
|
||||||
{
|
});
|
||||||
encoding: 'utf-8',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const pluginScript = `
|
const pluginScript = `
|
||||||
${IfreedomTemplate}
|
${IfreedomTemplate}
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ class IfreedomPlugin implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseDate = (dateString: string | undefined = '') => {
|
parseDate = (dateString: string | undefined = '') => {
|
||||||
const months: { [key: string]: number } = {
|
const months: Record<string, number> = {
|
||||||
января: 1,
|
января: 1,
|
||||||
февраля: 2,
|
февраля: 2,
|
||||||
марта: 3,
|
марта: 3,
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
import list from './sources.json' with { type: 'json' };
|
import list from './sources.json' with { type: 'json' };
|
||||||
import { readFileSync } from 'fs';
|
import { existsSync, readFileSync } from 'fs';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import { dirname, join } from 'path';
|
||||||
|
|
||||||
|
const folder = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
export const generateAll = function () {
|
export const generateAll = function () {
|
||||||
return list.map(source => {
|
return list.map(source => {
|
||||||
let filters = {};
|
const exist = existsSync(join(folder, 'filters', source.id + '.json'));
|
||||||
try {
|
if (exist) {
|
||||||
filters = require(`./filters/${source.id}`);
|
const filters = readFileSync(
|
||||||
source.filters = filters.filters;
|
join(folder, 'filters', source.id + '.json'),
|
||||||
} catch (e) {}
|
);
|
||||||
|
source.filters = JSON.parse(filters);
|
||||||
|
}
|
||||||
console.log(
|
console.log(
|
||||||
`[lightnovelworld] Generating: ${source.id}${' '.repeat(20 - source.id.length)} ${source.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
|
`[lightnovelworld] Generating: ${source.id}${' '.repeat(20 - source.id.length)} ${source.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
|
||||||
);
|
);
|
||||||
@@ -16,12 +22,9 @@ export const generateAll = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const generator = function generator(source) {
|
const generator = function generator(source) {
|
||||||
const LightNovelWPTemplate = readFileSync(
|
const LightNovelWPTemplate = readFileSync(join(folder, 'template.ts'), {
|
||||||
'./scripts/multisrc/lightnovelworld/template.ts',
|
encoding: 'utf-8',
|
||||||
{
|
});
|
||||||
encoding: 'utf-8',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const pluginScript = `
|
const pluginScript = `
|
||||||
${LightNovelWPTemplate}
|
${LightNovelWPTemplate}
|
||||||
|
|||||||
@@ -148,22 +148,19 @@ class LightNovelWorld implements Plugin.PagePlugin {
|
|||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
let chapterText = loadedCheerio('#chapter-container').html() || '';
|
const chapterText = loadedCheerio('#chapter-container').html() || '';
|
||||||
|
|
||||||
return chapterText;
|
return chapterText;
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
searchTerm: string,
|
|
||||||
pageNo: number,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const url = `${this.site}lnsearchlive`;
|
const url = `${this.site}lnsearchlive`;
|
||||||
const link = `${this.site}search`;
|
const link = `${this.site}search`;
|
||||||
const response = await fetchApi(link).then(r => r.text());
|
const response = await fetchApi(link).then(r => r.text());
|
||||||
const token = parseHTML(response);
|
const token = parseHTML(response);
|
||||||
let verifytoken = token('#novelSearchForm > input').attr('value');
|
const verifytoken = token('#novelSearchForm > input').attr('value');
|
||||||
|
|
||||||
let formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('inputContent', searchTerm);
|
formData.append('inputContent', searchTerm);
|
||||||
|
|
||||||
const results = await fetchApi(url, {
|
const results = await fetchApi(url, {
|
||||||
@@ -172,7 +169,7 @@ class LightNovelWorld implements Plugin.PagePlugin {
|
|||||||
body: formData,
|
body: formData,
|
||||||
}).then(r => r.json());
|
}).then(r => r.json());
|
||||||
|
|
||||||
let novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(results.resultview);
|
const loadedCheerio = parseHTML(results.resultview);
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
import list from './sources.json' with { type: 'json' };
|
import list from './sources.json' with { type: 'json' };
|
||||||
import { readFileSync } from 'fs';
|
import { existsSync, readFileSync } from 'fs';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import { dirname, join } from 'path';
|
||||||
|
|
||||||
|
const folder = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
export const generateAll = function () {
|
export const generateAll = function () {
|
||||||
return list.map(source => {
|
return list.map(source => {
|
||||||
let filters = {};
|
const exist = existsSync(join(folder, 'filters', source.id + '.json'));
|
||||||
try {
|
if (exist) {
|
||||||
filters = require(`./filters/${source.id}`);
|
const filters = readFileSync(
|
||||||
source.filters = filters.filters;
|
join(folder, 'filters', source.id + '.json'),
|
||||||
} catch (e) {}
|
);
|
||||||
|
source.filters = JSON.parse(filters);
|
||||||
|
}
|
||||||
console.log(
|
console.log(
|
||||||
`[lightnovelwp] Generating: ${source.id}${' '.repeat(20 - source.id.length)} ${source.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
|
`[lightnovelwp] Generating: ${source.id}${' '.repeat(20 - source.id.length)} ${source.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
|
||||||
);
|
);
|
||||||
@@ -16,12 +22,9 @@ export const generateAll = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const generator = function generator(source) {
|
const generator = function generator(source) {
|
||||||
const LightNovelWPTemplate = readFileSync(
|
const LightNovelWPTemplate = readFileSync(join(folder, 'template.ts'), {
|
||||||
'./scripts/multisrc/lightnovelwp/template.ts',
|
encoding: 'utf-8',
|
||||||
{
|
});
|
||||||
encoding: 'utf-8',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const pluginScript = `
|
const pluginScript = `
|
||||||
${LightNovelWPTemplate.replace(
|
${LightNovelWPTemplate.replace(
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class LightNovelWPPlugin implements Plugin.PluginBase {
|
|||||||
|
|
||||||
getHostname(url: string): string {
|
getHostname(url: string): string {
|
||||||
url = url.split('/')[2];
|
url = url.split('/')[2];
|
||||||
let url_parts = url.split('.');
|
const url_parts = url.split('.');
|
||||||
url_parts.pop(); // remove TLD
|
url_parts.pop(); // remove TLD
|
||||||
return url_parts.join('.');
|
return url_parts.join('.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,39 @@
|
|||||||
import list from './sources.json' with { type: 'json' };
|
import list from './sources.json' with { type: 'json' };
|
||||||
import { readFileSync } from 'fs';
|
import { existsSync, readFileSync } from 'fs';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import { dirname, join } from 'path';
|
||||||
|
|
||||||
|
const folder = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
export const generateAll = function () {
|
export const generateAll = function () {
|
||||||
return list.map(metadata => {
|
return list.map(source => {
|
||||||
let filters = {};
|
const exist = existsSync(join(folder, 'filters', source.id + '.json'));
|
||||||
try {
|
if (exist) {
|
||||||
filters = require(`./filters/${metadata.id}`);
|
const filters = readFileSync(
|
||||||
metadata.filters = filters.filters;
|
join(folder, 'filters', source.id + '.json'),
|
||||||
} catch (e) {}
|
);
|
||||||
|
source.filters = JSON.parse(filters);
|
||||||
|
}
|
||||||
console.log(
|
console.log(
|
||||||
`[madara] Generating: ${metadata.id}${' '.repeat(20 - metadata.id.length)} ${metadata.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
|
`[madara] Generating: ${source.id}${' '.repeat(20 - source.id.length)} ${source.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
|
||||||
);
|
);
|
||||||
return generator(metadata);
|
return generator(source);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const generator = function generator(metadata) {
|
const generator = function generator(source) {
|
||||||
const madaraTemplate = readFileSync('./scripts/multisrc/madara/template.ts', {
|
const madaraTemplate = readFileSync(join(folder, 'template.ts'), {
|
||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
});
|
});
|
||||||
|
|
||||||
const pluginScript = `
|
const pluginScript = `
|
||||||
${madaraTemplate}
|
${madaraTemplate}
|
||||||
const plugin = new MadaraPlugin(${JSON.stringify(metadata)});
|
const plugin = new MadaraPlugin(${JSON.stringify(source)});
|
||||||
export default plugin;
|
export default plugin;
|
||||||
`.trim();
|
`.trim();
|
||||||
return {
|
return {
|
||||||
lang: metadata.options?.lang || 'English',
|
lang: source.options?.lang || 'English',
|
||||||
filename: metadata.sourceName,
|
filename: source.sourceName,
|
||||||
pluginScript,
|
pluginScript,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { fetchApi } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters } from '@libs/filterInputs';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Cheerio, AnyNode, CheerioAPI, load as parseHTML } from 'cheerio';
|
import { Cheerio, AnyNode, CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
@@ -82,7 +82,7 @@ class MadaraPlugin implements Plugin.PluginBase {
|
|||||||
|
|
||||||
getHostname(url: string): string {
|
getHostname(url: string): string {
|
||||||
url = url.split('/')[2];
|
url = url.split('/')[2];
|
||||||
let url_parts = url.split('.');
|
const url_parts = url.split('.');
|
||||||
url_parts.pop(); // remove TLD
|
url_parts.pop(); // remove TLD
|
||||||
return url_parts.join('.');
|
return url_parts.join('.');
|
||||||
}
|
}
|
||||||
@@ -203,7 +203,7 @@ class MadaraPlugin implements Plugin.PluginBase {
|
|||||||
});
|
});
|
||||||
|
|
||||||
loadedCheerio('div.summary__content .code-block,script').remove();
|
loadedCheerio('div.summary__content .code-block,script').remove();
|
||||||
let summary =
|
const summary =
|
||||||
loadedCheerio('div.summary__content') ||
|
loadedCheerio('div.summary__content') ||
|
||||||
loadedCheerio('#tab-manga-about') ||
|
loadedCheerio('#tab-manga-about') ||
|
||||||
loadedCheerio('.post-content_item h5:contains("Summary")').next();
|
loadedCheerio('.post-content_item h5:contains("Summary")').next();
|
||||||
@@ -250,7 +250,7 @@ class MadaraPlugin implements Plugin.PluginBase {
|
|||||||
releaseDate = dayjs().format('LL');
|
releaseDate = dayjs().format('LL');
|
||||||
}
|
}
|
||||||
|
|
||||||
let chapterUrl = loadedCheerio(element).find('a').attr('href') || '';
|
const chapterUrl = loadedCheerio(element).find('a').attr('href') || '';
|
||||||
|
|
||||||
chapters.push({
|
chapters.push({
|
||||||
name: chapterName,
|
name: chapterName,
|
||||||
|
|||||||
@@ -1,31 +1,33 @@
|
|||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import path from 'path';
|
import { fileURLToPath } from 'url';
|
||||||
|
import { dirname, join } from 'path';
|
||||||
|
|
||||||
|
const folder = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
import list from './sources.json' with { type: 'json' };
|
import list from './sources.json' with { type: 'json' };
|
||||||
|
|
||||||
export const generateAll = function () {
|
export const generateAll = function () {
|
||||||
return list.map(metadata => {
|
return list.map(source => {
|
||||||
console.log(
|
console.log(
|
||||||
`[readnovelfull] Generating: ${metadata.id}${' '.repeat(20 - metadata.id.length)}`,
|
`[readnovelfull] Generating: ${source.id}${' '.repeat(20 - source.id.length)}`,
|
||||||
);
|
);
|
||||||
return generator(metadata);
|
return generator(source);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const generator = function generator(metadata) {
|
const generator = function generator(source) {
|
||||||
const readNovelFullTemplate = readFileSync(
|
const readNovelFullTemplate = readFileSync(join(folder, 'template.ts'), {
|
||||||
'./scripts/multisrc/readnovelfull/template.ts',
|
encoding: 'utf-8',
|
||||||
{ encoding: 'utf-8' },
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const pluginScript = `
|
const pluginScript = `
|
||||||
${readNovelFullTemplate}
|
${readNovelFullTemplate}
|
||||||
const plugin = new ReadNovelFullPlugin(${JSON.stringify(metadata)});
|
const plugin = new ReadNovelFullPlugin(${JSON.stringify(source)});
|
||||||
export default plugin;
|
export default plugin;
|
||||||
`.trim();
|
`.trim();
|
||||||
return {
|
return {
|
||||||
lang: metadata.options?.lang || 'English',
|
lang: source.options?.lang || 'English',
|
||||||
filename: metadata.sourceName,
|
filename: source.sourceName,
|
||||||
pluginScript,
|
pluginScript,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,34 +1,40 @@
|
|||||||
import list from './sources.json' with { type: 'json' };
|
import list from './sources.json' with { type: 'json' };
|
||||||
import { readFileSync } from 'fs';
|
import { existsSync, readFileSync } from 'fs';
|
||||||
import path from 'path';
|
import { fileURLToPath } from 'url';
|
||||||
|
import { dirname, join } from 'path';
|
||||||
|
|
||||||
|
const folder = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
export const generateAll = function () {
|
export const generateAll = function () {
|
||||||
return list.map(metadata => {
|
return list.map(source => {
|
||||||
try {
|
const exist = existsSync(join(folder, 'filters', source.id + '.json'));
|
||||||
const { filters } = require(`./filters/${metadata.id}`);
|
if (exist) {
|
||||||
metadata.filters = filters;
|
const filters = readFileSync(
|
||||||
} catch (e) {}
|
join(folder, 'filters', source.id + '.json'),
|
||||||
|
);
|
||||||
|
source.filters = JSON.parse(filters);
|
||||||
|
}
|
||||||
console.log(
|
console.log(
|
||||||
`[readwn] Generating: ${metadata.id}${' '.repeat(20 - metadata.id.length)} ${metadata.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
|
`[readwn] Generating: ${source.id}${' '.repeat(20 - source.id.length)} ${source.filters ? '🔎with filters🔍' : '🚫no filters🚫'}`,
|
||||||
);
|
);
|
||||||
return generator(metadata);
|
return generator(source);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const generator = function generator(metadata) {
|
const generator = function generator(source) {
|
||||||
const readwnTemplate = readFileSync('./scripts/multisrc/readwn/template.ts', {
|
const readwnTemplate = readFileSync(join(folder, 'template.ts'), {
|
||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
});
|
});
|
||||||
|
|
||||||
const pluginScript = `
|
const pluginScript = `
|
||||||
${readwnTemplate}
|
${readwnTemplate}
|
||||||
const plugin = new ReadwnPlugin(${JSON.stringify(metadata)});
|
const plugin = new ReadwnPlugin(${JSON.stringify(source)});
|
||||||
export default plugin;
|
export default plugin;
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
lang: 'english',
|
lang: 'english',
|
||||||
filename: metadata.sourceName,
|
filename: source.sourceName,
|
||||||
pluginScript,
|
pluginScript,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ async function getFilters(name: string, url: string) {
|
|||||||
?.replace('-0.html', ''),
|
?.replace('-0.html', ''),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
let nextPage = $('.pagination > li:last-child > a').attr('href');
|
const nextPage = $('.pagination > li:last-child > a').attr('href');
|
||||||
if (nextPage) {
|
if (nextPage) {
|
||||||
const allpage = parseInt(nextPage.replace(/[^0-9]/g, '') || '0', 10);
|
const allpage = parseInt(nextPage.replace(/[^0-9]/g, '') || '0', 10);
|
||||||
for (let pageNo = 0; pageNo < allpage; pageNo++) {
|
for (let pageNo = 0; pageNo < allpage; pageNo++) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { fetchApi } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters } from '@libs/filterInputs';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import list from './sources.json' with { type: 'json' };
|
import list from './sources.json' with { type: 'json' };
|
||||||
import defaultSettings from './settings.json' with { type: 'json' };
|
import defaultSettings from './settings.json' with { type: 'json' };
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import { dirname, join } from 'path';
|
||||||
|
|
||||||
|
const folder = dirname(fileURLToPath(import.meta.url));
|
||||||
const FilterTypes = {
|
const FilterTypes = {
|
||||||
TextInput: 'Text',
|
TextInput: 'Text',
|
||||||
Picker: 'Picker',
|
Picker: 'Picker',
|
||||||
@@ -31,20 +34,20 @@ export const generateAll = function () {
|
|||||||
}
|
}
|
||||||
return { ...p, filters: d ? undefined : filters };
|
return { ...p, filters: d ? undefined : filters };
|
||||||
})
|
})
|
||||||
.map(metadata => {
|
.map(source => {
|
||||||
console.log(`[rulate]: Generating`, metadata.id);
|
console.log(`[rulate]: Generating`, source.id);
|
||||||
return generator(metadata);
|
return generator(source);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const generator = function generator(metadata) {
|
const generator = function generator(source) {
|
||||||
const rulateTemplate = readFileSync('./scripts/multisrc/rulate/template.ts', {
|
const rulateTemplate = readFileSync(join(folder, 'template.ts'), {
|
||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
});
|
});
|
||||||
|
|
||||||
const pluginScript = `
|
const pluginScript = `
|
||||||
${rulateTemplate}
|
${rulateTemplate}
|
||||||
const plugin = new RulatePlugin(${JSON.stringify(metadata)
|
const plugin = new RulatePlugin(${JSON.stringify(source)
|
||||||
.replace(/"type":"([^"]+)"/g, '"type":FilterTypes.$1')
|
.replace(/"type":"([^"]+)"/g, '"type":FilterTypes.$1')
|
||||||
.replace(/\.XCheckbox/g, '.ExcludableCheckboxGroup') //remember to redo
|
.replace(/\.XCheckbox/g, '.ExcludableCheckboxGroup') //remember to redo
|
||||||
.replace(/\.Checkbox/g, '.CheckboxGroup')});
|
.replace(/\.Checkbox/g, '.CheckboxGroup')});
|
||||||
@@ -53,7 +56,7 @@ export default plugin;
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
lang: 'russian',
|
lang: 'russian',
|
||||||
filename: metadata.sourceName,
|
filename: source.sourceName,
|
||||||
pluginScript,
|
pluginScript,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ class RulatePlugin implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseDate = (dateString: string | undefined = '') => {
|
parseDate = (dateString: string | undefined = '') => {
|
||||||
const months: { [key: string]: number } = {
|
const months: Record<string, number> = {
|
||||||
'янв.': 1,
|
'янв.': 1,
|
||||||
'февр.': 2,
|
'февр.': 2,
|
||||||
'мар.': 3,
|
'мар.': 3,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import {
|
import {
|
||||||
Box,
|
|
||||||
Container,
|
Container,
|
||||||
createTheme,
|
createTheme,
|
||||||
CssBaseline,
|
CssBaseline,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { forwardRef, useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import AppBar from '@mui/material/AppBar';
|
import AppBar from '@mui/material/AppBar';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Toolbar from '@mui/material/Toolbar';
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
@@ -18,8 +18,7 @@ import { Search, Translate } from '@mui/icons-material';
|
|||||||
import { searchPlugins } from '@provider/plugins';
|
import { searchPlugins } from '@provider/plugins';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FixedSizeList, ListChildComponentProps } from 'react-window';
|
import { FixedSizeList, ListChildComponentProps } from 'react-window';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { AppState } from '@redux/store';
|
|
||||||
import { selectPlugin } from '@redux/pluginSlice';
|
import { selectPlugin } from '@redux/pluginSlice';
|
||||||
import usePlugin from '@hooks/usePlugin';
|
import usePlugin from '@hooks/usePlugin';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Box, Button, Tooltip, withStyles } from '@mui/material';
|
import { Box, Button, Tooltip } from '@mui/material';
|
||||||
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import CopiableItem from './CopiableItem';
|
import CopiableItem from './CopiableItem';
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import React from 'react';
|
|
||||||
import { styled, TextareaAutosize } from '@mui/material';
|
import { styled, TextareaAutosize } from '@mui/material';
|
||||||
|
|
||||||
const blue = {
|
const blue = {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import PathItem from '@components/PathItem';
|
import PathItem from '@components/PathItem';
|
||||||
import {
|
import {
|
||||||
Box,
|
|
||||||
Paper,
|
Paper,
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
@@ -8,7 +7,6 @@ import {
|
|||||||
TableContainer,
|
TableContainer,
|
||||||
TableHead,
|
TableHead,
|
||||||
TableRow,
|
TableRow,
|
||||||
Typography,
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Divider,
|
|
||||||
Pagination,
|
Pagination,
|
||||||
Stack,
|
Stack,
|
||||||
TextField,
|
TextField,
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export default function PopularNovels() {
|
|||||||
setNovels([]);
|
setNovels([]);
|
||||||
|
|
||||||
if (plugin?.filters) {
|
if (plugin?.filters) {
|
||||||
let filters = {};
|
const filters = {};
|
||||||
for (const fKey in plugin.filters) {
|
for (const fKey in plugin.filters) {
|
||||||
filters[fKey] = {
|
filters[fKey] = {
|
||||||
type: plugin.filters[fKey].type,
|
type: plugin.filters[fKey].type,
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ import NovelItemCard from '../components/NovelItemCard';
|
|||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from '@redux/store';
|
import { AppState } from '@redux/store';
|
||||||
import { getPlugin } from '@provider/plugins';
|
import { getPlugin } from '@provider/plugins';
|
||||||
import { Filters } from '@libs/filterInputs';
|
|
||||||
|
|
||||||
export default function SearchNovels() {
|
export default function SearchNovels() {
|
||||||
const selectedPluginItem = useSelector(
|
const selectedPluginItem = useSelector(
|
||||||
(state: AppState) => state.plugin.selected,
|
(state: AppState) => state.plugin.selected,
|
||||||
);
|
);
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [page, setPage] = useState(1);
|
const [page,] = useState(1);
|
||||||
const [plugin, setPlugin] = useState<Plugin.PluginBase | undefined>();
|
const [plugin, setPlugin] = useState<Plugin.PluginBase | undefined>();
|
||||||
const [novels, setNovels] = useState<Plugin.NovelItem[]>([]);
|
const [novels, setNovels] = useState<Plugin.NovelItem[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|||||||
+5
-3
@@ -1,6 +1,8 @@
|
|||||||
|
/* global Buffer, RequestInit */
|
||||||
|
|
||||||
import { parse as parseProto } from 'protobufjs';
|
import { parse as parseProto } from 'protobufjs';
|
||||||
|
|
||||||
type FetchInit = {
|
interface FetchInit {
|
||||||
headers?: Record<string, string | undefined> | Headers;
|
headers?: Record<string, string | undefined> | Headers;
|
||||||
method?: string;
|
method?: string;
|
||||||
body?: FormData | string;
|
body?: FormData | string;
|
||||||
@@ -10,10 +12,10 @@ type FetchInit = {
|
|||||||
| undefined
|
| undefined
|
||||||
| FormData
|
| FormData
|
||||||
| Headers;
|
| Headers;
|
||||||
};
|
}
|
||||||
|
|
||||||
const makeInit = async (init?: FetchInit) => {
|
const makeInit = async (init?: FetchInit) => {
|
||||||
let defaultHeaders: Record<string, string> = {
|
const defaultHeaders: Record<string, string> = {
|
||||||
'Connection': 'keep-alive',
|
'Connection': 'keep-alive',
|
||||||
'Accept': '*/*',
|
'Accept': '*/*',
|
||||||
'Accept-Language': '*',
|
'Accept-Language': '*',
|
||||||
|
|||||||
+10
-10
@@ -11,36 +11,36 @@ export enum FilterTypes {
|
|||||||
ExcludableCheckboxGroup = 'XCheckbox',
|
ExcludableCheckboxGroup = 'XCheckbox',
|
||||||
}
|
}
|
||||||
|
|
||||||
type SwitchFilter = {
|
interface SwitchFilter {
|
||||||
type: FilterTypes.Switch;
|
type: FilterTypes.Switch;
|
||||||
/** Default value */
|
/** Default value */
|
||||||
value: boolean;
|
value: boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
type TextFilter = {
|
interface TextFilter {
|
||||||
type: FilterTypes.TextInput;
|
type: FilterTypes.TextInput;
|
||||||
/** Default value */
|
/** Default value */
|
||||||
value: string;
|
value: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
type CheckboxFilter = {
|
interface CheckboxFilter {
|
||||||
type: FilterTypes.CheckboxGroup;
|
type: FilterTypes.CheckboxGroup;
|
||||||
options: readonly FilterOption[];
|
options: readonly FilterOption[];
|
||||||
/** Default value */
|
/** Default value */
|
||||||
value: string[];
|
value: string[];
|
||||||
};
|
}
|
||||||
type PickerFilter = {
|
interface PickerFilter {
|
||||||
type: FilterTypes.Picker;
|
type: FilterTypes.Picker;
|
||||||
options: readonly FilterOption[];
|
options: readonly FilterOption[];
|
||||||
/** Default value */
|
/** Default value */
|
||||||
value: string;
|
value: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
type ExcludableCheckboxFilter = {
|
interface ExcludableCheckboxFilter {
|
||||||
type: FilterTypes.ExcludableCheckboxGroup;
|
type: FilterTypes.ExcludableCheckboxGroup;
|
||||||
options: readonly FilterOption[];
|
options: readonly FilterOption[];
|
||||||
value: { include?: string[]; exclude?: string[] };
|
value: { include?: string[]; exclude?: string[] };
|
||||||
};
|
}
|
||||||
|
|
||||||
export type Filters = Record<
|
export type Filters = Record<
|
||||||
string,
|
string,
|
||||||
|
|||||||
+2
-8
@@ -1,6 +1,3 @@
|
|||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
class Storage {
|
class Storage {
|
||||||
private db: Record<string, { created: Date; value: any; expires?: number }>;
|
private db: Record<string, { created: Date; value: any; expires?: number }>;
|
||||||
|
|
||||||
@@ -54,7 +51,6 @@ class Storage {
|
|||||||
/**
|
/**
|
||||||
* Deletes a key from the storage.
|
* Deletes a key from the storage.
|
||||||
*
|
*
|
||||||
* @param pluginID - The ID of the plugin.
|
|
||||||
* @param key - The key to delete.
|
* @param key - The key to delete.
|
||||||
*/
|
*/
|
||||||
delete(key: string): void {
|
delete(key: string): void {
|
||||||
@@ -64,7 +60,7 @@ class Storage {
|
|||||||
/**
|
/**
|
||||||
* Clears all stored items from storage.
|
* Clears all stored items from storage.
|
||||||
*/
|
*/
|
||||||
clearAll(pluginID: string): void {
|
clearAll(): void {
|
||||||
this.db = {};
|
this.db = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,9 +80,7 @@ https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage
|
|||||||
/**
|
/**
|
||||||
* Represents the structure of a storage object with string keys and values.
|
* Represents the structure of a storage object with string keys and values.
|
||||||
*/
|
*/
|
||||||
interface StorageObject {
|
type StorageObject = Record<string, any>;
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a simplified version of the browser's localStorage.
|
* Represents a simplified version of the browser's localStorage.
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ import 'protobufjs';
|
|||||||
|
|
||||||
const { fetch: originalFetch } = window;
|
const { fetch: originalFetch } = window;
|
||||||
window.fetch = async (...args) => {
|
window.fetch = async (...args) => {
|
||||||
let [resource, config] = args;
|
const [resource, config] = args;
|
||||||
if (resource.toString().includes('localhost')) {
|
if (resource.toString().includes('localhost')) {
|
||||||
const response = await originalFetch(resource, config);
|
const response = await originalFetch(resource, config);
|
||||||
return response;
|
return response;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
import { fetchText, fetchFile } from '@libs/fetch';
|
import { fetchText } from '@libs/fetch';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
@@ -35,7 +35,7 @@ class Shu69 implements Plugin.PluginBase {
|
|||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
loadedCheerio('div.book-coverlist').each((i, el) => {
|
loadedCheerio('div.book-coverlist').each((i, el) => {
|
||||||
let url = loadedCheerio(el).find('a.cover').attr('href');
|
const url = loadedCheerio(el).find('a.cover').attr('href');
|
||||||
|
|
||||||
const novelName = loadedCheerio(el).find('h4.name').text().trim();
|
const novelName = loadedCheerio(el).find('h4.name').text().trim();
|
||||||
const novelCover = loadedCheerio(el).find('a.cover > img').attr('src');
|
const novelCover = loadedCheerio(el).find('a.cover > img').attr('src');
|
||||||
@@ -59,7 +59,7 @@ class Shu69 implements Plugin.PluginBase {
|
|||||||
const body = await fetchText(url);
|
const body = await fetchText(url);
|
||||||
if (body === '') throw Error('无法获取小说内容,请检查网络');
|
if (body === '') throw Error('无法获取小说内容,请检查网络');
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
const novel: Plugin.SourceNovel = {
|
const novel: Plugin.SourceNovel = {
|
||||||
path: novelPath,
|
path: novelPath,
|
||||||
@@ -84,7 +84,7 @@ class Shu69 implements Plugin.PluginBase {
|
|||||||
novel.genres = '';
|
novel.genres = '';
|
||||||
|
|
||||||
// Table of Content is on a different page than the summary page
|
// Table of Content is on a different page than the summary page
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
const allUrl = loadedCheerio('dd.all > a').attr('href');
|
const allUrl = loadedCheerio('dd.all > a').attr('href');
|
||||||
if (allUrl) {
|
if (allUrl) {
|
||||||
@@ -156,12 +156,12 @@ class Shu69 implements Plugin.PluginBase {
|
|||||||
});
|
});
|
||||||
if (body === '') throw Error('无法获取搜索结果,请检查网络');
|
if (body === '') throw Error('无法获取搜索结果,请检查网络');
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
loadedCheerio('div.book-coverlist').each((i, el) => {
|
loadedCheerio('div.book-coverlist').each((i, el) => {
|
||||||
let url = loadedCheerio(el).find('a.cover').attr('href');
|
const url = loadedCheerio(el).find('a.cover').attr('href');
|
||||||
|
|
||||||
const novelName = loadedCheerio(el).find('h4.name').text().trim();
|
const novelName = loadedCheerio(el).find('h4.name').text().trim();
|
||||||
const novelCover = loadedCheerio(el).find('a.cover > img').attr('src');
|
const novelCover = loadedCheerio(el).find('a.cover > img').attr('src');
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
import { fetchText, fetchFile } from '@libs/fetch';
|
import { fetchText } from '@libs/fetch';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
// import { encode } from 'urlencode';
|
// import { encode } from 'urlencode';
|
||||||
@@ -66,7 +66,7 @@ class XinShu69 implements Plugin.PluginBase {
|
|||||||
const body = await fetchText(url, {}, 'gbk');
|
const body = await fetchText(url, {}, 'gbk');
|
||||||
if (body === '') throw Error('无法获取小说内容,请检查网络');
|
if (body === '') throw Error('无法获取小说内容,请检查网络');
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
const novel: Plugin.SourceNovel = {
|
const novel: Plugin.SourceNovel = {
|
||||||
path: novelPath,
|
path: novelPath,
|
||||||
@@ -97,7 +97,7 @@ class XinShu69 implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Table of Content is on a different page than the summary page
|
// Table of Content is on a different page than the summary page
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
const chaptersUrl = loadedCheerio('a.more-btn').attr('href');
|
const chaptersUrl = loadedCheerio('a.more-btn').attr('href');
|
||||||
if (chaptersUrl) {
|
if (chaptersUrl) {
|
||||||
@@ -160,7 +160,7 @@ class XinShu69 implements Plugin.PluginBase {
|
|||||||
);
|
);
|
||||||
if (body === '') throw Error('无法获取搜索结果,请检查网络');
|
if (body === '') throw Error('无法获取搜索结果,请检查网络');
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { fetchText, fetchFile } from '@libs/fetch';
|
import { fetchText } from '@libs/fetch';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
@@ -29,7 +29,7 @@ class Linovelib implements Plugin.PluginBase {
|
|||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
loadedCheerio('.module-rank-booklist .book-layout').each((i, el) => {
|
loadedCheerio('.module-rank-booklist .book-layout').each((i, el) => {
|
||||||
let url = loadedCheerio(el).attr('href');
|
const url = loadedCheerio(el).attr('href');
|
||||||
|
|
||||||
const novelName = loadedCheerio(el).find('.book-title').text();
|
const novelName = loadedCheerio(el).find('.book-title').text();
|
||||||
const novelCover = loadedCheerio(el)
|
const novelCover = loadedCheerio(el)
|
||||||
@@ -55,7 +55,7 @@ class Linovelib implements Plugin.PluginBase {
|
|||||||
const body = await fetchText(url);
|
const body = await fetchText(url);
|
||||||
if (body === '') throw Error('无法获取小说内容,请检查网络');
|
if (body === '') throw Error('无法获取小说内容,请检查网络');
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
const novel: Plugin.SourceNovel = {
|
const novel: Plugin.SourceNovel = {
|
||||||
path: novelPath,
|
path: novelPath,
|
||||||
@@ -83,7 +83,7 @@ class Linovelib implements Plugin.PluginBase {
|
|||||||
.join(',');
|
.join(',');
|
||||||
|
|
||||||
// Table of Content is on a different page than the summary page
|
// Table of Content is on a different page than the summary page
|
||||||
let chapter: Plugin.ChapterItem[] = [];
|
const chapter: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
const idPattern = /\/(\d+)\.html/;
|
const idPattern = /\/(\d+)\.html/;
|
||||||
const novelId = url.match(idPattern)?.[1];
|
const novelId = url.match(idPattern)?.[1];
|
||||||
@@ -421,7 +421,7 @@ class Linovelib implements Plugin.PluginBase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let url = this.site + chapterPath;
|
let url = this.site + chapterPath;
|
||||||
let baseUrl = url;
|
const baseUrl = url;
|
||||||
do {
|
do {
|
||||||
const page = await loadPage(url);
|
const page = await loadPage(url);
|
||||||
hasNextPage = page.pageHasNextPage;
|
hasNextPage = page.pageHasNextPage;
|
||||||
@@ -502,7 +502,7 @@ class Linovelib implements Plugin.PluginBase {
|
|||||||
// await addPage(pageCheerio, redirect);
|
// await addPage(pageCheerio, redirect);
|
||||||
|
|
||||||
pageCheerio('.book-ol .book-layout').each((i, el) => {
|
pageCheerio('.book-ol .book-layout').each((i, el) => {
|
||||||
let nUrl = pageCheerio(el).attr('href');
|
const nUrl = pageCheerio(el).attr('href');
|
||||||
|
|
||||||
const novelName = pageCheerio(el).find('.book-title').text();
|
const novelName = pageCheerio(el).find('.book-title').text();
|
||||||
const novelCover = pageCheerio(el)
|
const novelCover = pageCheerio(el)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import qs from 'qs';
|
import qs from 'qs';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
import qs from 'qs';
|
import qs from 'qs';
|
||||||
@@ -48,7 +48,7 @@ class NovelsOnline implements Plugin.PluginBase {
|
|||||||
const result = await fetchApi(this.site + novelPath).then(res =>
|
const result = await fetchApi(this.site + novelPath).then(res =>
|
||||||
res.text(),
|
res.text(),
|
||||||
);
|
);
|
||||||
let $ = parseHTML(result);
|
const $ = parseHTML(result);
|
||||||
|
|
||||||
const novel: Plugin.SourceNovel = {
|
const novel: Plugin.SourceNovel = {
|
||||||
path: novelPath,
|
path: novelPath,
|
||||||
@@ -58,8 +58,8 @@ class NovelsOnline implements Plugin.PluginBase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$('.novel-detail-item').each((i, el) => {
|
$('.novel-detail-item').each((i, el) => {
|
||||||
let detailName = $(el).find('h6').text();
|
const detailName = $(el).find('h6').text();
|
||||||
let detail = $(el).find('.novel-detail-body');
|
const detail = $(el).find('.novel-detail-body');
|
||||||
|
|
||||||
switch (detailName) {
|
switch (detailName) {
|
||||||
case 'Description':
|
case 'Description':
|
||||||
@@ -111,11 +111,8 @@ class NovelsOnline implements Plugin.PluginBase {
|
|||||||
return chapterText;
|
return chapterText;
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
searchTerm: string,
|
const result = await fetchApi(this.site + '/sResults.php', {
|
||||||
pageNo: number,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const result = await fetchApi('https://novelsonline.net/sResults.php', {
|
|
||||||
headers: {
|
headers: {
|
||||||
Accept: '*/*',
|
Accept: '*/*',
|
||||||
'Accept-Language': 'pl,en-US;q=0.7,en;q=0.3',
|
'Accept-Language': 'pl,en-US;q=0.7,en;q=0.3',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
|
|
||||||
@@ -8,54 +8,49 @@ class StorySeedlingPlugin implements Plugin.PluginBase {
|
|||||||
name = 'StorySeedling';
|
name = 'StorySeedling';
|
||||||
icon = 'src/en/storyseedling/icon.png';
|
icon = 'src/en/storyseedling/icon.png';
|
||||||
site = 'https://storyseedling.com/';
|
site = 'https://storyseedling.com/';
|
||||||
version = '1.0.1';
|
version = '1.0.2';
|
||||||
|
|
||||||
async popularNovels(pageNo: number): Promise<Plugin.NovelItem[]> {
|
async popularNovels(pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
let novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
let url = 'https://storyseedling.com/ajax';
|
const body = await fetchApi(this.site + 'browse').then(r => r.text());
|
||||||
|
|
||||||
const postUrl = `https://storyseedling.com/browse/`;
|
|
||||||
const body = await fetchApi(postUrl).then(r => r.text());
|
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
var postValue = loadedCheerio('div[ax-load][x-data]')
|
const postValue = loadedCheerio('div[ax-load][x-data]')
|
||||||
.attr('x-data')
|
.attr('x-data')
|
||||||
?.replace("browse('", '')
|
?.replace("browse('", '')
|
||||||
.replace("')", '') as string;
|
.replace("')", '') as string;
|
||||||
|
|
||||||
var data = new FormData();
|
const data = new FormData();
|
||||||
data.append('search', '');
|
data.append('search', '');
|
||||||
data.append('orderBy', 'recent');
|
data.append('orderBy', 'recent');
|
||||||
data.append('curpage', pageNo.toString());
|
data.append('curpage', pageNo.toString());
|
||||||
data.append('post', postValue);
|
data.append('post', postValue);
|
||||||
data.append('action', 'fetch_browse');
|
data.append('action', 'fetch_browse');
|
||||||
|
|
||||||
var response = await (
|
const response: any = await fetchApi(this.site + 'ajax', {
|
||||||
await fetchApi(url, { body: data, method: 'POST' })
|
body: data,
|
||||||
).json();
|
method: 'POST',
|
||||||
|
}).then(res => res.json());
|
||||||
|
|
||||||
response.data.posts.forEach((element: any) => {
|
response.data.posts.forEach((element: any) =>
|
||||||
const novel = {
|
novels.push({
|
||||||
name: element.title,
|
name: element.title,
|
||||||
cover: element.thumbnail,
|
cover: element.thumbnail,
|
||||||
path: element.permalink.replace(this.site, ''),
|
path: element.permalink.replace(this.site, ''),
|
||||||
};
|
}),
|
||||||
novels.push(novel);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||||
var site = this.site;
|
const site = this.site;
|
||||||
const novel: Plugin.SourceNovel = {
|
const novel: Plugin.SourceNovel = {
|
||||||
path: novelPath,
|
path: novelPath,
|
||||||
name: '',
|
name: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const body = await fetchApi(this.site + novelPath).then(r => r.text());
|
const body = await fetchApi(this.site + novelPath).then(r => r.text());
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
novel.name = loadedCheerio('h1').text().trim();
|
novel.name = loadedCheerio('h1').text().trim();
|
||||||
@@ -68,7 +63,8 @@ class StorySeedlingPlugin implements Plugin.PluginBase {
|
|||||||
if (!novel.cover) {
|
if (!novel.cover) {
|
||||||
novel.cover = defaultCover;
|
novel.cover = defaultCover;
|
||||||
}
|
}
|
||||||
let genres: string[] = [];
|
|
||||||
|
const genres: string[] = [];
|
||||||
loadedCheerio(
|
loadedCheerio(
|
||||||
'section[x-data="{ tab: location.hash.substr(1) || \'chapters\' }"].relative > div > div > div.flex.flex-wrap > a',
|
'section[x-data="{ tab: location.hash.substr(1) || \'chapters\' }"].relative > div > div > div.flex.flex-wrap > a',
|
||||||
).each(function () {
|
).each(function () {
|
||||||
@@ -81,7 +77,7 @@ class StorySeedlingPlugin implements Plugin.PluginBase {
|
|||||||
.trim()
|
.trim()
|
||||||
.replace(/(\r\n|\n|\r)/gm, '');
|
.replace(/(\r\n|\n|\r)/gm, '');
|
||||||
|
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
loadedCheerio(
|
loadedCheerio(
|
||||||
'div.grid.w-full.grid-cols-1.gap-4.md\\:grid-cols-2 > a',
|
'div.grid.w-full.grid-cols-1.gap-4.md\\:grid-cols-2 > a',
|
||||||
@@ -89,20 +85,20 @@ class StorySeedlingPlugin implements Plugin.PluginBase {
|
|||||||
if (loadedCheerio(this).find('> div').length == 2) {
|
if (loadedCheerio(this).find('> div').length == 2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let name = loadedCheerio(this).find('.truncate').text().trim();
|
const name = loadedCheerio(this).find('.truncate').text().trim();
|
||||||
let url = loadedCheerio(this).attr('href') as string;
|
const url = loadedCheerio(this).attr('href') as string;
|
||||||
let releaseTime = loadedCheerio(this)
|
const releaseTime = loadedCheerio(this)
|
||||||
.find('div > div > small')
|
.find('div > div > small')
|
||||||
.text()
|
.text()
|
||||||
.trim();
|
.trim();
|
||||||
let chapterNumber = name.split('-')[0].trim().split(' ')[1];
|
const chapterNumber = name.split('-')[0].trim().split(' ')[1];
|
||||||
const chapter: Plugin.ChapterItem = {
|
|
||||||
|
chapters.push({
|
||||||
name: name,
|
name: name,
|
||||||
path: url.replace(site, ''),
|
path: url.replace(site, ''),
|
||||||
releaseTime: releaseTime,
|
releaseTime,
|
||||||
chapterNumber: parseInt(chapterNumber),
|
chapterNumber: parseInt(chapterNumber),
|
||||||
};
|
});
|
||||||
chapters.push(chapter);
|
|
||||||
});
|
});
|
||||||
novel.chapters = chapters;
|
novel.chapters = chapters;
|
||||||
return novel;
|
return novel;
|
||||||
@@ -111,8 +107,8 @@ class StorySeedlingPlugin implements Plugin.PluginBase {
|
|||||||
const body = await fetchApi(this.site + chapterPath).then(r => r.text());
|
const body = await fetchApi(this.site + chapterPath).then(r => r.text());
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
let t = loadedCheerio('div.justify-center > div.mb-4');
|
const t = loadedCheerio('div.justify-center > div.mb-4');
|
||||||
let chapterText = t.html() || '';
|
const chapterText = t.html() || '';
|
||||||
|
|
||||||
return chapterText;
|
return chapterText;
|
||||||
}
|
}
|
||||||
@@ -121,38 +117,35 @@ class StorySeedlingPlugin implements Plugin.PluginBase {
|
|||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
pageNo: number,
|
pageNo: number,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
let novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
let url = 'https://storyseedling.com/ajax';
|
|
||||||
|
|
||||||
const postUrl = `https://storyseedling.com/browse/`;
|
|
||||||
const body = await fetchApi(postUrl).then(r => r.text());
|
|
||||||
|
|
||||||
|
const body = await fetchApi(this.site + 'browse').then(r => r.text());
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
var postValue = loadedCheerio('div[ax-load][x-data]')
|
const postValue = loadedCheerio('div[ax-load][x-data]')
|
||||||
.attr('x-data')
|
.attr('x-data')
|
||||||
?.replace("browse('", '')
|
?.replace("browse('", '')
|
||||||
.replace("')", '') as string;
|
.replace("')", '') as string;
|
||||||
|
|
||||||
var data = new FormData();
|
const data = new FormData();
|
||||||
data.append('search', searchTerm);
|
data.append('search', searchTerm);
|
||||||
data.append('orderBy', 'recent');
|
data.append('orderBy', 'recent');
|
||||||
data.append('curpage', pageNo.toString());
|
data.append('curpage', pageNo.toString());
|
||||||
data.append('post', postValue);
|
data.append('post', postValue);
|
||||||
data.append('action', 'fetch_browse');
|
data.append('action', 'fetch_browse');
|
||||||
|
|
||||||
var response = await (
|
const response: any = await fetchApi(this.site + 'ajax', {
|
||||||
await fetchApi(url, { body: data, method: 'POST' })
|
body: data,
|
||||||
).json();
|
method: 'POST',
|
||||||
|
}).then(res => res.json());
|
||||||
|
|
||||||
response.data.posts.forEach((element: any) => {
|
response.data.posts.forEach((element: any) =>
|
||||||
const novel = {
|
novels.push({
|
||||||
name: element.title,
|
name: element.title,
|
||||||
cover: element.thumbnail,
|
cover: element.thumbnail,
|
||||||
path: element.permalink.replace(this.site, ''),
|
path: element.permalink.replace(this.site, ''),
|
||||||
};
|
}),
|
||||||
novels.push(novel);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { isUrlAbsolute } from '@libs/isAbsoluteUrl';
|
import { isUrlAbsolute } from '@libs/isAbsoluteUrl';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ class BLN implements Plugin.PluginBase {
|
|||||||
link += '&state=' + filters.status.value;
|
link += '&state=' + filters.status.value;
|
||||||
link += '&page=' + page;
|
link += '&page=' + page;
|
||||||
|
|
||||||
const result = await fetch(link);
|
const result = await fetchApi(link);
|
||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
console.error(await result.text());
|
console.error(await result.text());
|
||||||
// TODO: Cloudflare protection or other error
|
// TODO: Cloudflare protection or other error
|
||||||
@@ -61,7 +61,7 @@ class BLN implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||||
const result = await fetch(this.site + novelPath);
|
const result = await fetchApi(this.site + novelPath);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
@@ -146,7 +146,7 @@ class BLN implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
const result = await fetch(this.site + chapterPath);
|
const result = await fetchApi(this.site + chapterPath);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
@@ -162,7 +162,7 @@ class BLN implements Plugin.PluginBase {
|
|||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
const url = `${this.site}search_novels/${searchTerm}?page=${page}`;
|
const url = `${this.site}search_novels/${searchTerm}?page=${page}`;
|
||||||
|
|
||||||
const result = await fetch(url);
|
const result = await fetchApi(url);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@@ -52,7 +52,7 @@ class ComradeMaoPlugin implements Plugin.PluginBase {
|
|||||||
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
async parseNovelAndChapters(novelUrl: string): Promise<Plugin.SourceNovel> {
|
||||||
const body = await fetchApi(novelUrl).then(result => result.text());
|
const body = await fetchApi(novelUrl).then(result => result.text());
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
const novel: Plugin.SourceNovel = {
|
const novel: Plugin.SourceNovel = {
|
||||||
url: novelUrl,
|
url: novelUrl,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
|
|
||||||
@@ -9,7 +9,6 @@ class DDLPlugin implements Plugin.PluginBase {
|
|||||||
site = 'https://www.divinedaolibrary.com/';
|
site = 'https://www.divinedaolibrary.com/';
|
||||||
version = '1.0.1';
|
version = '1.0.1';
|
||||||
icon = 'src/en/divinedaolibrary/icon.png';
|
icon = 'src/en/divinedaolibrary/icon.png';
|
||||||
filters?: undefined;
|
|
||||||
|
|
||||||
parseNovels(loadedCheerio: CheerioAPI, searchTerm?: string) {
|
parseNovels(loadedCheerio: CheerioAPI, searchTerm?: string) {
|
||||||
let novels: Plugin.NovelItem[] = [];
|
let novels: Plugin.NovelItem[] = [];
|
||||||
@@ -40,11 +39,8 @@ class DDLPlugin implements Plugin.PluginBase {
|
|||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(): Promise<Plugin.NovelItem[]> {
|
||||||
pageNo: number,
|
const link = this.site + 'novels';
|
||||||
options: Plugin.PopularNovelsOptions,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
let link = this.site + 'novels';
|
|
||||||
|
|
||||||
const body = await fetchApi(link).then(res => res.text());
|
const body = await fetchApi(link).then(res => res.text());
|
||||||
|
|
||||||
@@ -56,7 +52,7 @@ class DDLPlugin implements Plugin.PluginBase {
|
|||||||
const result = await fetchApi(this.site + novelPath);
|
const result = await fetchApi(this.site + novelPath);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
const novel: Plugin.SourceNovel = {
|
const novel: Plugin.SourceNovel = {
|
||||||
path: novelPath,
|
path: novelPath,
|
||||||
@@ -116,11 +112,8 @@ class DDLPlugin implements Plugin.PluginBase {
|
|||||||
return chapterText;
|
return chapterText;
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
searchTerm: string,
|
const url = this.site + 'novels';
|
||||||
pageNo: number,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
let url = this.site + 'novels';
|
|
||||||
|
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ class EarlyNovelPlugin implements Plugin.PagePlugin {
|
|||||||
const result = await fetchApi(this.site + novelPath);
|
const result = await fetchApi(this.site + novelPath);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
let loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
loadedCheerio('.glyphicon-menu-right').closest('li').remove();
|
loadedCheerio('.glyphicon-menu-right').closest('li').remove();
|
||||||
const pagenav = loadedCheerio('.page-nav').prev().find('a');
|
const pagenav = loadedCheerio('.page-nav').prev().find('a');
|
||||||
const lastPageStr = pagenav.attr('title')?.match(/(\d+)/);
|
const lastPageStr = pagenav.attr('title')?.match(/(\d+)/);
|
||||||
@@ -84,8 +84,8 @@ class EarlyNovelPlugin implements Plugin.PagePlugin {
|
|||||||
};
|
};
|
||||||
|
|
||||||
loadedCheerio('.info > div > h3').each(function () {
|
loadedCheerio('.info > div > h3').each(function () {
|
||||||
let detailName = loadedCheerio(this).text();
|
const detailName = loadedCheerio(this).text();
|
||||||
let detail = loadedCheerio(this)
|
const detail = loadedCheerio(this)
|
||||||
.siblings()
|
.siblings()
|
||||||
.map((i, el) => loadedCheerio(el).text())
|
.map((i, el) => loadedCheerio(el).text())
|
||||||
.toArray()
|
.toArray()
|
||||||
@@ -127,10 +127,7 @@ class EarlyNovelPlugin implements Plugin.PagePlugin {
|
|||||||
return chapterText;
|
return chapterText;
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
searchTerm: string,
|
|
||||||
pageNo: number,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const url = `${this.site}search?keyword=${searchTerm}`;
|
const url = `${this.site}search?keyword=${searchTerm}`;
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
|
|
||||||
@@ -43,10 +43,7 @@ class FaqWikiUs implements Plugin.PluginBase {
|
|||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(): Promise<Plugin.NovelItem[]> {
|
||||||
page: number,
|
|
||||||
{ showLatestNovels }: Plugin.PopularNovelsOptions,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const body = await fetchApi(this.site).then(res => res.text());
|
const body = await fetchApi(this.site).then(res => res.text());
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
@@ -79,7 +76,7 @@ class FaqWikiUs implements Plugin.PluginBase {
|
|||||||
|
|
||||||
const div = loadedCheerio('.book-review-block__meta-item-value');
|
const div = loadedCheerio('.book-review-block__meta-item-value');
|
||||||
|
|
||||||
div.html(div.html()?.replace(/(?<=>)([^<]+)(?=<br\s*\/?>)/g, '<p>$1</p>')!);
|
div.html(div.html()?.replace(/(?<=>)([^<]+)(?=<br\s*\/?>)/g, '<p>$1</p>'));
|
||||||
|
|
||||||
loadedCheerio('.book-review-block__meta-item-value strong').each(
|
loadedCheerio('.book-review-block__meta-item-value strong').each(
|
||||||
(i, el) => {
|
(i, el) => {
|
||||||
|
|||||||
@@ -264,8 +264,7 @@ class Foxteller implements Plugin.PluginBase {
|
|||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveUrl = (path: string, isNovel?: boolean) =>
|
resolveUrl = (path: string) => this.site + '/novel/' + path;
|
||||||
this.site + '/novel/' + path;
|
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
order: {
|
order: {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
|
||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import qs from 'qs';
|
import qs from 'qs';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Parser } from 'htmlparser2';
|
import { Parser } from 'htmlparser2';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@@ -85,7 +85,7 @@ class LightNovelPub implements Plugin.PagePlugin {
|
|||||||
let isSummary = false;
|
let isSummary = false;
|
||||||
let isGenres = false;
|
let isGenres = false;
|
||||||
let isTags = false;
|
let isTags = false;
|
||||||
let genreArray: string[] = [];
|
const genreArray: string[] = [];
|
||||||
let isCover = false;
|
let isCover = false;
|
||||||
const parser = new Parser({
|
const parser = new Parser({
|
||||||
onopentag(name, attribs) {
|
onopentag(name, attribs) {
|
||||||
@@ -245,7 +245,7 @@ class LightNovelPub implements Plugin.PagePlugin {
|
|||||||
const parser = new Parser({
|
const parser = new Parser({
|
||||||
onopentag(name, attribs) {
|
onopentag(name, attribs) {
|
||||||
if (isChapter && name === 'div') {
|
if (isChapter && name === 'div') {
|
||||||
let stylediv = attribs['style'];
|
const stylediv = attribs['style'];
|
||||||
if (stylediv) {
|
if (stylediv) {
|
||||||
chapterText += `<div style="${stylediv}">`;
|
chapterText += `<div style="${stylediv}">`;
|
||||||
isStyleText = true;
|
isStyleText = true;
|
||||||
@@ -254,7 +254,7 @@ class LightNovelPub implements Plugin.PagePlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isChapter && name === 'table') {
|
if (isChapter && name === 'table') {
|
||||||
let w = attribs['width'];
|
const w = attribs['width'];
|
||||||
if (w) {
|
if (w) {
|
||||||
chapterText += `<table width="${w}">`;
|
chapterText += `<table width="${w}">`;
|
||||||
} else {
|
} else {
|
||||||
@@ -268,7 +268,7 @@ class LightNovelPub implements Plugin.PagePlugin {
|
|||||||
chapterText += `<tr>`;
|
chapterText += `<tr>`;
|
||||||
}
|
}
|
||||||
if (isChapter && name === 'td') {
|
if (isChapter && name === 'td') {
|
||||||
let w1 = attribs['width'];
|
const w1 = attribs['width'];
|
||||||
if (w1) {
|
if (w1) {
|
||||||
chapterText += `<td width="${w1}">`;
|
chapterText += `<td width="${w1}">`;
|
||||||
} else {
|
} else {
|
||||||
@@ -334,10 +334,7 @@ class LightNovelPub implements Plugin.PagePlugin {
|
|||||||
return chapterText;
|
return chapterText;
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
searchTerm: string,
|
|
||||||
pageNo: number,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const url = `${this.site}lnsearchlive`;
|
const url = `${this.site}lnsearchlive`;
|
||||||
const link = `${this.site}search`;
|
const link = `${this.site}search`;
|
||||||
const response = await fetchApi(link).then(r => r.text());
|
const response = await fetchApi(link).then(r => r.text());
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Parser } from 'htmlparser2';
|
import { Parser } from 'htmlparser2';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ class LnMTLPlugin implements Plugin.PagePlugin {
|
|||||||
let isStatusKey = false;
|
let isStatusKey = false;
|
||||||
let isList = 0;
|
let isList = 0;
|
||||||
let isGenres = false;
|
let isGenres = false;
|
||||||
let genreArray: string[] = [];
|
const genreArray: string[] = [];
|
||||||
const parser = new Parser({
|
const parser = new Parser({
|
||||||
onopentag(name, attribs) {
|
onopentag(name, attribs) {
|
||||||
if (name === 'img' && attribs['class']?.includes('img-rounded')) {
|
if (name === 'img' && attribs['class']?.includes('img-rounded')) {
|
||||||
@@ -272,10 +272,7 @@ class LnMTLPlugin implements Plugin.PagePlugin {
|
|||||||
return chapterText.replace(/„/g, '“');
|
return chapterText.replace(/„/g, '“');
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
searchTerm: string,
|
|
||||||
pageNo: number,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const html = await fetchApi(this.site)
|
const html = await fetchApi(this.site)
|
||||||
.then(b => b.text())
|
.then(b => b.text())
|
||||||
.then(r => r.replace(/>\s+</g, '><'));
|
.then(r => r.replace(/>\s+</g, '><'));
|
||||||
@@ -312,7 +309,7 @@ class LnMTLPlugin implements Plugin.PagePlugin {
|
|||||||
parser.write(html);
|
parser.write(html);
|
||||||
parser.end();
|
parser.end();
|
||||||
|
|
||||||
const search = await fetch(`${this.site}${list}`);
|
const search = await fetchApi(`${this.site}${list}`);
|
||||||
const data = await search.json();
|
const data = await search.json();
|
||||||
|
|
||||||
const nov = data.filter((res: { name: string }) =>
|
const nov = data.filter((res: { name: string }) =>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
|
|
||||||
@@ -21,7 +21,9 @@ class MTLNovel implements Plugin.PluginBase {
|
|||||||
|
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
headers.append('Alt-Used', 'www.mtlnovel.com');
|
headers.append('Alt-Used', 'www.mtlnovel.com');
|
||||||
const body = await fetch(link, { headers }).then(result => result.text());
|
const body = await fetchApi(link, { headers }).then(result =>
|
||||||
|
result.text(),
|
||||||
|
);
|
||||||
|
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
@@ -50,7 +52,7 @@ class MTLNovel implements Plugin.PluginBase {
|
|||||||
headers.append('Referer', `${this.site}novel-list/`);
|
headers.append('Referer', `${this.site}novel-list/`);
|
||||||
headers.append('Alt-Used', 'www.mtlnovel.com');
|
headers.append('Alt-Used', 'www.mtlnovel.com');
|
||||||
|
|
||||||
const body = await fetch(this.site + novelPath, {
|
const body = await fetchApi(this.site + novelPath, {
|
||||||
headers,
|
headers,
|
||||||
}).then(result => result.text());
|
}).then(result => result.text());
|
||||||
|
|
||||||
@@ -70,7 +72,7 @@ class MTLNovel implements Plugin.PluginBase {
|
|||||||
const chapterListUrl = this.site + novelPath + 'chapter-list/';
|
const chapterListUrl = this.site + novelPath + 'chapter-list/';
|
||||||
|
|
||||||
const getChapters = async () => {
|
const getChapters = async () => {
|
||||||
const listResult = await fetch(chapterListUrl, { headers });
|
const listResult = await fetchApi(chapterListUrl, { headers });
|
||||||
const listBody = await listResult.text();
|
const listBody = await listResult.text();
|
||||||
|
|
||||||
loadedCheerio = parseHTML(listBody);
|
loadedCheerio = parseHTML(listBody);
|
||||||
@@ -101,7 +103,7 @@ class MTLNovel implements Plugin.PluginBase {
|
|||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
headers.append('Alt-Used', 'www.mtlnovel.com');
|
headers.append('Alt-Used', 'www.mtlnovel.com');
|
||||||
const body = await fetch(this.site + chapterPath, { headers }).then(r =>
|
const body = await fetchApi(this.site + chapterPath, { headers }).then(r =>
|
||||||
r.text(),
|
r.text(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -112,19 +114,13 @@ class MTLNovel implements Plugin.PluginBase {
|
|||||||
return chapterText;
|
return chapterText;
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
searchTerm: string,
|
|
||||||
pageNo: number,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
headers.append('Alt-Used', 'www.mtlnovel.com');
|
headers.append('Alt-Used', 'www.mtlnovel.com');
|
||||||
const searchUrl =
|
const searchUrl =
|
||||||
this.site +
|
this.site + 'wp-admin/admin-ajax.php?action=autosuggest&q=' + searchTerm;
|
||||||
'wp-admin/admin-ajax.php?action=autosuggest&q=' +
|
|
||||||
searchTerm +
|
|
||||||
'&__amp_source_origin=https%3A%2F%2Fwww.mtlnovel.com';
|
|
||||||
|
|
||||||
const res = await fetch(searchUrl, { headers });
|
const res = await fetchApi(searchUrl, { headers });
|
||||||
const result = await res.json();
|
const result = await res.json();
|
||||||
|
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters } from '@libs/filterInputs';
|
|
||||||
|
|
||||||
class MTLReader implements Plugin.PluginBase {
|
class MTLReader implements Plugin.PluginBase {
|
||||||
id = 'mtlreader';
|
id = 'mtlreader';
|
||||||
@@ -9,6 +8,7 @@ class MTLReader implements Plugin.PluginBase {
|
|||||||
version = '1.0.0';
|
version = '1.0.0';
|
||||||
icon = 'src/en/mtlreader/icon.png';
|
icon = 'src/en/mtlreader/icon.png';
|
||||||
site = 'https://mtlreader.com/';
|
site = 'https://mtlreader.com/';
|
||||||
|
|
||||||
parseNovels(loadedCheerio: CheerioAPI) {
|
parseNovels(loadedCheerio: CheerioAPI) {
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
@@ -31,10 +31,7 @@ class MTLReader implements Plugin.PluginBase {
|
|||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
pageNo: number,
|
|
||||||
options: Plugin.PopularNovelsOptions<Filters>,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const url = `${this.site}novels?page=${pageNo}`;
|
const url = `${this.site}novels?page=${pageNo}`;
|
||||||
|
|
||||||
const body = await fetchApi(url).then(r => r.text());
|
const body = await fetchApi(url).then(r => r.text());
|
||||||
@@ -90,10 +87,7 @@ class MTLReader implements Plugin.PluginBase {
|
|||||||
|
|
||||||
return chapterText;
|
return chapterText;
|
||||||
}
|
}
|
||||||
async searchNovels(
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
searchTerm: string,
|
|
||||||
pageNo: number,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const body = await fetchApi(this.site).then(r => r.text());
|
const body = await fetchApi(this.site).then(r => r.text());
|
||||||
const tokenCheerio = parseHTML(body);
|
const tokenCheerio = parseHTML(body);
|
||||||
const token = tokenCheerio('input[name="_token"]').attr('value');
|
const token = tokenCheerio('input[name="_token"]').attr('value');
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
|
|
||||||
@@ -73,8 +73,8 @@ class NovelBin implements Plugin.PluginBase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
loadedCheerio('ul.info > li > h3').each(function () {
|
loadedCheerio('ul.info > li > h3').each(function () {
|
||||||
let detailName = loadedCheerio(this).text();
|
const detailName = loadedCheerio(this).text();
|
||||||
let detail = loadedCheerio(this)
|
const detail = loadedCheerio(this)
|
||||||
.siblings()
|
.siblings()
|
||||||
.map((i, el) => loadedCheerio(el).text())
|
.map((i, el) => loadedCheerio(el).text())
|
||||||
.toArray()
|
.toArray()
|
||||||
@@ -137,10 +137,7 @@ class NovelBin implements Plugin.PluginBase {
|
|||||||
return chapterText;
|
return chapterText;
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
searchTerm: string,
|
|
||||||
pageNo: number,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const url = `${this.site}search/?keyword=${searchTerm}`;
|
const url = `${this.site}search/?keyword=${searchTerm}`;
|
||||||
const body = await fetchApi(url).then(r => r.text());
|
const body = await fetchApi(url).then(r => r.text());
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters } from '@libs/filterInputs';
|
|
||||||
|
|
||||||
class NovelBuddy implements Plugin.PluginBase {
|
class NovelBuddy implements Plugin.PluginBase {
|
||||||
id = 'novelbuddy';
|
id = 'novelbuddy';
|
||||||
@@ -9,7 +8,6 @@ class NovelBuddy implements Plugin.PluginBase {
|
|||||||
site = 'https://novelbuddy.io/';
|
site = 'https://novelbuddy.io/';
|
||||||
version = '1.0.0';
|
version = '1.0.0';
|
||||||
icon = 'src/en/novelbuddy/icon.png';
|
icon = 'src/en/novelbuddy/icon.png';
|
||||||
filters?: undefined; //TODO: Filters, Filters in Filters required
|
|
||||||
|
|
||||||
parseNovels(loadedCheerio: CheerioAPI) {
|
parseNovels(loadedCheerio: CheerioAPI) {
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
@@ -33,10 +31,7 @@ class NovelBuddy implements Plugin.PluginBase {
|
|||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
pageNo: number,
|
|
||||||
options: Plugin.PopularNovelsOptions,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const url = `${this.site}popular?page=${pageNo}`;
|
const url = `${this.site}popular?page=${pageNo}`;
|
||||||
|
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { load, load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters } from '@libs/filterInputs';
|
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
|
|
||||||
class NovelHall implements Plugin.PluginBase {
|
class NovelHall implements Plugin.PluginBase {
|
||||||
@@ -9,13 +8,9 @@ class NovelHall implements Plugin.PluginBase {
|
|||||||
name = 'Novel Hall';
|
name = 'Novel Hall';
|
||||||
version = '1.0.1';
|
version = '1.0.1';
|
||||||
icon = 'src/en/novelhall/icon.png';
|
icon = 'src/en/novelhall/icon.png';
|
||||||
filters?: Filters | undefined; //TODO: Filters Requires hideOnSelect
|
|
||||||
site = 'https://novelhall.com/';
|
site = 'https://novelhall.com/';
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(page: number): Promise<Plugin.NovelItem[]> {
|
||||||
page: number,
|
|
||||||
options: Plugin.PopularNovelsOptions<Filters>,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const url = `${this.site}all2022-${page}.html`;
|
const url = `${this.site}all2022-${page}.html`;
|
||||||
|
|
||||||
const body = await fetchApi(url).then(r => r.text());
|
const body = await fetchApi(url).then(r => r.text());
|
||||||
@@ -94,10 +89,7 @@ class NovelHall implements Plugin.PluginBase {
|
|||||||
return chapterText;
|
return chapterText;
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
searchTerm: string,
|
|
||||||
pageNo: number,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const url = `${this.site}index.php?s=so&module=book&keyword=${searchTerm}`;
|
const url = `${this.site}index.php?s=so&module=book&keyword=${searchTerm}`;
|
||||||
const body = await fetchApi(url).then(r => r.text());
|
const body = await fetchApi(url).then(r => r.text());
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable no-case-declarations */
|
||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
@@ -142,7 +143,7 @@ class NovelUpdates implements Plugin.PluginBase {
|
|||||||
|
|
||||||
loadedCheerio = parseHTML(text);
|
loadedCheerio = parseHTML(text);
|
||||||
|
|
||||||
const nameReplacements: { [key: string]: string } = {
|
const nameReplacements: Record<string, string> = {
|
||||||
'v': 'volume ',
|
'v': 'volume ',
|
||||||
'c': ' chapter ',
|
'c': ' chapter ',
|
||||||
'part': 'part ',
|
'part': 'part ',
|
||||||
@@ -151,7 +152,7 @@ class NovelUpdates implements Plugin.PluginBase {
|
|||||||
|
|
||||||
loadedCheerio('li.sp_li_chp').each((i, el) => {
|
loadedCheerio('li.sp_li_chp').each((i, el) => {
|
||||||
let chapterName = loadedCheerio(el).text();
|
let chapterName = loadedCheerio(el).text();
|
||||||
for (let name in nameReplacements) {
|
for (const name in nameReplacements) {
|
||||||
chapterName = chapterName.replace(name, nameReplacements[name]);
|
chapterName = chapterName.replace(name, nameReplacements[name]);
|
||||||
}
|
}
|
||||||
chapterName = chapterName.replace(/\b\w/g, l => l.toUpperCase()).trim();
|
chapterName = chapterName.replace(/\b\w/g, l => l.toUpperCase()).trim();
|
||||||
@@ -170,7 +171,7 @@ class NovelUpdates implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getLocation(href: string) {
|
getLocation(href: string) {
|
||||||
var match = href.match(
|
const match = href.match(
|
||||||
/^(https?:)\/\/(([^:/?#]*)(?::([0-9]+))?)([/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/,
|
/^(https?:)\/\/(([^:/?#]*)(?::([0-9]+))?)([/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/,
|
||||||
);
|
);
|
||||||
return match && `${match[1]}//${match[3]}`;
|
return match && `${match[1]}//${match[3]}`;
|
||||||
@@ -198,7 +199,7 @@ class NovelUpdates implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'asuratls':
|
case 'asuratls':
|
||||||
let titleElementAsura = loadedCheerio('.post-body div b').first();
|
const titleElementAsura = loadedCheerio('.post-body div b').first();
|
||||||
chapterTitle = titleElementAsura.text()!;
|
chapterTitle = titleElementAsura.text()!;
|
||||||
titleElementAsura.remove();
|
titleElementAsura.remove();
|
||||||
chapterContent = loadedCheerio('.post-body').html()!;
|
chapterContent = loadedCheerio('.post-body').html()!;
|
||||||
@@ -235,7 +236,7 @@ class NovelUpdates implements Plugin.PluginBase {
|
|||||||
loadedCheerio('#readerarea.rdminimal')
|
loadedCheerio('#readerarea.rdminimal')
|
||||||
.children()
|
.children()
|
||||||
.each((idx, ele) => {
|
.each((idx, ele) => {
|
||||||
let elementText = loadedCheerio(ele).text();
|
const elementText = loadedCheerio(ele).text();
|
||||||
if (elementText.includes(chapterStringHelScans)) {
|
if (elementText.includes(chapterStringHelScans)) {
|
||||||
chapterTitle = elementText;
|
chapterTitle = elementText;
|
||||||
loadedCheerio(ele).remove();
|
loadedCheerio(ele).remove();
|
||||||
@@ -295,12 +296,12 @@ class NovelUpdates implements Plugin.PluginBase {
|
|||||||
case 'ko-fi':
|
case 'ko-fi':
|
||||||
chapterText = loadedCheerio('script:contains("shadowDom.innerHTML")')
|
chapterText = loadedCheerio('script:contains("shadowDom.innerHTML")')
|
||||||
.html()
|
.html()
|
||||||
?.match(/shadowDom\.innerHTML \+= '(<div.*?)';/)![1]!;
|
?.match(/shadowDom\.innerHTML \+= '(<div.*?)';/)![1];
|
||||||
break;
|
break;
|
||||||
case 'mirilu':
|
case 'mirilu':
|
||||||
bloatClasses = ['#jp-post-flair'];
|
bloatClasses = ['#jp-post-flair'];
|
||||||
bloatClasses.map(tag => loadedCheerio(tag).remove());
|
bloatClasses.map(tag => loadedCheerio(tag).remove());
|
||||||
let titleElementMirilu = loadedCheerio(
|
const titleElementMirilu = loadedCheerio(
|
||||||
'.entry-content p strong',
|
'.entry-content p strong',
|
||||||
).first();
|
).first();
|
||||||
chapterTitle = titleElementMirilu.text()!;
|
chapterTitle = titleElementMirilu.text()!;
|
||||||
@@ -360,10 +361,10 @@ class NovelUpdates implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'rainofsnow':
|
case 'rainofsnow':
|
||||||
let displayedDivSnow = loadedCheerio('.bb-item').filter(function () {
|
const displayedDivSnow = loadedCheerio('.bb-item').filter(function () {
|
||||||
return loadedCheerio(this).css('display') === 'block';
|
return loadedCheerio(this).css('display') === 'block';
|
||||||
});
|
});
|
||||||
let loadedCheerioSnow = parseHTML(displayedDivSnow.html()!);
|
const loadedCheerioSnow = parseHTML(displayedDivSnow.html()!);
|
||||||
bloatClasses = [
|
bloatClasses = [
|
||||||
'.responsivevoice-button',
|
'.responsivevoice-button',
|
||||||
'.zoomdesc-cont p img',
|
'.zoomdesc-cont p img',
|
||||||
@@ -371,7 +372,7 @@ class NovelUpdates implements Plugin.PluginBase {
|
|||||||
];
|
];
|
||||||
bloatClasses.map(tag => loadedCheerioSnow(tag).remove());
|
bloatClasses.map(tag => loadedCheerioSnow(tag).remove());
|
||||||
chapterContent = loadedCheerioSnow('.zoomdesc-cont').html()!;
|
chapterContent = loadedCheerioSnow('.zoomdesc-cont').html()!;
|
||||||
let titleElementSnow = loadedCheerioSnow('.scroller h2').first();
|
const titleElementSnow = loadedCheerioSnow('.scroller h2').first();
|
||||||
if (titleElementSnow.length) {
|
if (titleElementSnow.length) {
|
||||||
chapterTitle = titleElementSnow.text()!;
|
chapterTitle = titleElementSnow.text()!;
|
||||||
titleElementSnow.remove();
|
titleElementSnow.remove();
|
||||||
@@ -473,7 +474,7 @@ class NovelUpdates implements Plugin.PluginBase {
|
|||||||
];
|
];
|
||||||
bloatClasses.map(tag => loadedCheerioSyringe(tag).remove());
|
bloatClasses.map(tag => loadedCheerioSyringe(tag).remove());
|
||||||
chapterContent = loadedCheerioSyringe('.entry-content').html()!;
|
chapterContent = loadedCheerioSyringe('.entry-content').html()!;
|
||||||
let titleElementSyringe =
|
const titleElementSyringe =
|
||||||
loadedCheerioSyringe('.entry-content h3').first();
|
loadedCheerioSyringe('.entry-content h3').first();
|
||||||
if (titleElementSyringe.length) {
|
if (titleElementSyringe.length) {
|
||||||
chapterTitle = titleElementSyringe.text();
|
chapterTitle = titleElementSyringe.text();
|
||||||
@@ -535,7 +536,7 @@ class NovelUpdates implements Plugin.PluginBase {
|
|||||||
bloatClasses = ['hr', 'p:contains("\u00a0")'];
|
bloatClasses = ['hr', 'p:contains("\u00a0")'];
|
||||||
bloatClasses.map(tag => loadedCheerio(tag).remove());
|
bloatClasses.map(tag => loadedCheerio(tag).remove());
|
||||||
chapterContent = loadedCheerio('.text-left').html()!;
|
chapterContent = loadedCheerio('.text-left').html()!;
|
||||||
let titleElementZetro = loadedCheerio('.text-left h2').first();
|
const titleElementZetro = loadedCheerio('.text-left h2').first();
|
||||||
if (titleElementZetro.length) {
|
if (titleElementZetro.length) {
|
||||||
chapterTitle = titleElementZetro.text()!;
|
chapterTitle = titleElementZetro.text()!;
|
||||||
titleElementZetro.remove();
|
titleElementZetro.remove();
|
||||||
@@ -590,7 +591,7 @@ class NovelUpdates implements Plugin.PluginBase {
|
|||||||
/**
|
/**
|
||||||
* Detect if the site is a Blogspot site
|
* Detect if the site is a Blogspot site
|
||||||
*/
|
*/
|
||||||
let isBlogspotStr =
|
const isBlogspotStr =
|
||||||
loadedCheerio('meta[name="google-adsense-platform-domain"]').attr(
|
loadedCheerio('meta[name="google-adsense-platform-domain"]').attr(
|
||||||
'content',
|
'content',
|
||||||
) || loadedCheerio('meta[name="generator"]').attr('content');
|
) || loadedCheerio('meta[name="generator"]').attr('content');
|
||||||
@@ -604,7 +605,7 @@ class NovelUpdates implements Plugin.PluginBase {
|
|||||||
/**
|
/**
|
||||||
* Detect if the site is a WordPress site
|
* Detect if the site is a WordPress site
|
||||||
*/
|
*/
|
||||||
let isWordPressStr =
|
const isWordPressStr =
|
||||||
loadedCheerio('#dcl_comments-js-extra').html() ||
|
loadedCheerio('#dcl_comments-js-extra').html() ||
|
||||||
loadedCheerio('meta[name="generator"]').attr('content') ||
|
loadedCheerio('meta[name="generator"]').attr('content') ||
|
||||||
loadedCheerio('footer').text()!;
|
loadedCheerio('footer').text()!;
|
||||||
@@ -726,7 +727,7 @@ class NovelUpdates implements Plugin.PluginBase {
|
|||||||
loadedCheerio('.title_story').first().text() ||
|
loadedCheerio('.title_story').first().text() ||
|
||||||
loadedCheerio('.active').first().text() ||
|
loadedCheerio('.active').first().text() ||
|
||||||
loadedCheerio('head title').first().text()!;
|
loadedCheerio('head title').first().text()!;
|
||||||
let chapterSubtitle = loadedCheerio('.cat-series').first().text() || '';
|
const chapterSubtitle = loadedCheerio('.cat-series').first().text() || '';
|
||||||
if (chapterSubtitle) {
|
if (chapterSubtitle) {
|
||||||
chapterTitle = chapterSubtitle;
|
chapterTitle = chapterSubtitle;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Parser } from 'htmlparser2';
|
import { Parser } from 'htmlparser2';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
|
|
||||||
class RanobesPlugin implements Plugin.PagePlugin {
|
class RanobesPlugin implements Plugin.PagePlugin {
|
||||||
@@ -8,7 +7,6 @@ class RanobesPlugin implements Plugin.PagePlugin {
|
|||||||
name = 'Ranobes';
|
name = 'Ranobes';
|
||||||
icon = 'src/en/ranobes/icon.png';
|
icon = 'src/en/ranobes/icon.png';
|
||||||
site = 'https://ranobes.top';
|
site = 'https://ranobes.top';
|
||||||
filters?: Filters | undefined;
|
|
||||||
version = '2.0.0';
|
version = '2.0.0';
|
||||||
|
|
||||||
async sleep(ms: number) {
|
async sleep(ms: number) {
|
||||||
@@ -81,14 +79,8 @@ class RanobesPlugin implements Plugin.PagePlugin {
|
|||||||
return chapter.reverse();
|
return chapter.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(page: number): Promise<Plugin.NovelItem[]> {
|
||||||
page: number,
|
const link = `${this.site}/novels/page/${page}/`;
|
||||||
{
|
|
||||||
showLatestNovels,
|
|
||||||
filters,
|
|
||||||
}: Plugin.PopularNovelsOptions<typeof this.filters>,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
let link = `${this.site}/novels/page/${page}/`;
|
|
||||||
const body = await fetchApi(link).then(r => r.text());
|
const body = await fetchApi(link).then(r => r.text());
|
||||||
|
|
||||||
return this.parseNovels(body);
|
return this.parseNovels(body);
|
||||||
@@ -107,7 +99,7 @@ class RanobesPlugin implements Plugin.PagePlugin {
|
|||||||
chapters: [],
|
chapters: [],
|
||||||
totalPages: 1,
|
totalPages: 1,
|
||||||
};
|
};
|
||||||
let baseUrl = this.site;
|
const baseUrl = this.site;
|
||||||
let isCover = false;
|
let isCover = false;
|
||||||
let isAuthor = false;
|
let isAuthor = false;
|
||||||
let isSummary = false;
|
let isSummary = false;
|
||||||
@@ -115,7 +107,7 @@ class RanobesPlugin implements Plugin.PagePlugin {
|
|||||||
let isStatusText = false;
|
let isStatusText = false;
|
||||||
let isGenres = false;
|
let isGenres = false;
|
||||||
let isGenresText = false;
|
let isGenresText = false;
|
||||||
let genreArray: string[] = [];
|
const genreArray: string[] = [];
|
||||||
let novelId = '';
|
let novelId = '';
|
||||||
const parser = new Parser({
|
const parser = new Parser({
|
||||||
onopentag(name, attribs) {
|
onopentag(name, attribs) {
|
||||||
@@ -276,7 +268,7 @@ class RanobesPlugin implements Plugin.PagePlugin {
|
|||||||
const parser = new Parser({
|
const parser = new Parser({
|
||||||
onopentag(name, attribs) {
|
onopentag(name, attribs) {
|
||||||
if (isChapter && name === 'div') {
|
if (isChapter && name === 'div') {
|
||||||
let stylediv = attribs['style'];
|
const stylediv = attribs['style'];
|
||||||
if (stylediv) {
|
if (stylediv) {
|
||||||
chapterText += `<div style="${stylediv}">`;
|
chapterText += `<div style="${stylediv}">`;
|
||||||
isStyleText = true;
|
isStyleText = true;
|
||||||
@@ -285,7 +277,7 @@ class RanobesPlugin implements Plugin.PagePlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isChapter && name === 'table') {
|
if (isChapter && name === 'table') {
|
||||||
let w = attribs['width'];
|
const w = attribs['width'];
|
||||||
if (w) {
|
if (w) {
|
||||||
chapterText += `<table width="${w}">`;
|
chapterText += `<table width="${w}">`;
|
||||||
} else {
|
} else {
|
||||||
@@ -299,7 +291,7 @@ class RanobesPlugin implements Plugin.PagePlugin {
|
|||||||
chapterText += `<tr>`;
|
chapterText += `<tr>`;
|
||||||
}
|
}
|
||||||
if (isChapter && name === 'td') {
|
if (isChapter && name === 'td') {
|
||||||
let w1 = attribs['width'];
|
const w1 = attribs['width'];
|
||||||
if (w1) {
|
if (w1) {
|
||||||
chapterText += `<td width="${w1}">`;
|
chapterText += `<td width="${w1}">`;
|
||||||
} else {
|
} else {
|
||||||
@@ -369,7 +361,7 @@ class RanobesPlugin implements Plugin.PagePlugin {
|
|||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
page: number,
|
page: number,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
let link = `${this.site}/search/${searchTerm}/page/${page}`;
|
const link = `${this.site}/search/${searchTerm}/page/${page}`;
|
||||||
const body = await fetchApi(link).then(r => r.text());
|
const body = await fetchApi(link).then(r => r.text());
|
||||||
|
|
||||||
return this.parseNovels(body);
|
return this.parseNovels(body);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
import { isUrlAbsolute } from '@libs/isAbsoluteUrl';
|
import { isUrlAbsolute } from '@libs/isAbsoluteUrl';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@@ -152,10 +152,7 @@ class ReadLiteNovel implements Plugin.PluginBase {
|
|||||||
return chapterText;
|
return chapterText;
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
searchTerm: string,
|
|
||||||
pageNo: number,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const url =
|
const url =
|
||||||
this.site + `/search/autocomplete?dataType=json&query=${searchTerm}`;
|
this.site + `/search/autocomplete?dataType=json&query=${searchTerm}`;
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { fetchFile, fetchText } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters } from '@libs/filterInputs';
|
import { Filters } from '@libs/filterInputs';
|
||||||
import { load as loadCheerio, CheerioAPI } from 'cheerio';
|
import { load as loadCheerio } from 'cheerio';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
|
||||||
import { NovelItem } from '../../test_web/static/js';
|
import { NovelItem } from '../../test_web/static/js';
|
||||||
// import { isUrlAbsolute } from "@libs/isAbsoluteUrl";
|
// import { isUrlAbsolute } from "@libs/isAbsoluteUrl";
|
||||||
// import { parseMadaraDate } from "@libs/parseMadaraDate";
|
|
||||||
|
|
||||||
interface FuzzySearchOptions {
|
interface FuzzySearchOptions {
|
||||||
caseSensitive: boolean;
|
caseSensitive: boolean;
|
||||||
@@ -69,8 +67,8 @@ class FuzzySearch<Item> {
|
|||||||
}
|
}
|
||||||
const results: { item: Item; score: number }[] = [];
|
const results: { item: Item; score: number }[] = [];
|
||||||
|
|
||||||
for (let item of this.haystack) {
|
for (const item of this.haystack) {
|
||||||
for (let value of this.getItems(item)) {
|
for (const value of this.getItems(item)) {
|
||||||
const score = this.isMatch(value, query);
|
const score = this.isMatch(value, query);
|
||||||
if (score !== undefined) {
|
if (score !== undefined) {
|
||||||
results.push({ item, score });
|
results.push({ item, score });
|
||||||
@@ -112,11 +110,11 @@ class FuzzySearch<Item> {
|
|||||||
|
|
||||||
nearestIndexesFor(item: string, query: string): number[] | undefined {
|
nearestIndexesFor(item: string, query: string): number[] | undefined {
|
||||||
const letters = query.split('');
|
const letters = query.split('');
|
||||||
let indexes: number[][] = [];
|
const indexes: number[][] = [];
|
||||||
|
|
||||||
const idxFL = this.idxFirstLetter(item, query);
|
const idxFL = this.idxFirstLetter(item, query);
|
||||||
|
|
||||||
for (let idx of idxFL) {
|
for (const idx of idxFL) {
|
||||||
indexes.push([idx]);
|
indexes.push([idx]);
|
||||||
for (let i = 1; i < letters.length; i++) {
|
for (let i = 1; i < letters.length; i++) {
|
||||||
const letter = letters[i];
|
const letter = letters[i];
|
||||||
@@ -139,8 +137,8 @@ class FuzzySearch<Item> {
|
|||||||
return a[0] - b[0];
|
return a[0] - b[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
let res_a = a[a.length - 1] - a[0];
|
const res_a = a[a.length - 1] - a[0];
|
||||||
let res_b = b[b.length - 1] - b[0];
|
const res_b = b[b.length - 1] - b[0];
|
||||||
|
|
||||||
return res_a - res_b;
|
return res_a - res_b;
|
||||||
})[0];
|
})[0];
|
||||||
@@ -167,21 +165,20 @@ class ReLibraryPlugin implements Plugin.PluginBase {
|
|||||||
icon = 'src/en/relibrary/icon.png';
|
icon = 'src/en/relibrary/icon.png';
|
||||||
site = 'https://re-library.com';
|
site = 'https://re-library.com';
|
||||||
version = '1.0.0';
|
version = '1.0.0';
|
||||||
filters: Filters | undefined = undefined;
|
|
||||||
|
|
||||||
private searchFunc: FuzzySearch<Plugin.NovelItem> = new FuzzySearch(
|
private searchFunc = new FuzzySearch<Plugin.NovelItem>(item => [item.name], {
|
||||||
item => [item.name],
|
sort: true,
|
||||||
{ sort: true, caseSensitive: false },
|
caseSensitive: false,
|
||||||
);
|
});
|
||||||
|
|
||||||
private async popularNovelsInner(url: string): Promise<Plugin.NovelItem[]> {
|
private async popularNovelsInner(url: string): Promise<Plugin.NovelItem[]> {
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
const result = await fetch(url);
|
const result = await fetchApi(url);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
const loadedCheerio = loadCheerio(body);
|
const loadedCheerio = loadCheerio(body);
|
||||||
loadedCheerio('.entry-content > ol > li').each((_i, el) => {
|
loadedCheerio('.entry-content > ol > li').each((_i, el) => {
|
||||||
let novel: Partial<NovelItem> = {};
|
const novel: Partial<NovelItem> = {};
|
||||||
novel.name = loadedCheerio(el).find('h3 > a').text();
|
novel.name = loadedCheerio(el).find('h3 > a').text();
|
||||||
novel.path = loadedCheerio(el)
|
novel.path = loadedCheerio(el)
|
||||||
.find('table > tbody > tr > td > a')
|
.find('table > tbody > tr > td > a')
|
||||||
@@ -201,12 +198,12 @@ class ReLibraryPlugin implements Plugin.PluginBase {
|
|||||||
|
|
||||||
private async lastestNovelsInner(url: string): Promise<Plugin.NovelItem[]> {
|
private async lastestNovelsInner(url: string): Promise<Plugin.NovelItem[]> {
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
const result = await fetch(url);
|
const result = await fetchApi(url);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
const loadedCheerio = loadCheerio(body);
|
const loadedCheerio = loadCheerio(body);
|
||||||
loadedCheerio('article.type-page.page').each((_i, el) => {
|
loadedCheerio('article.type-page.page').each((_i, el) => {
|
||||||
let novel: Partial<Plugin.NovelItem> = {};
|
const novel: Partial<Plugin.NovelItem> = {};
|
||||||
novel.name = loadedCheerio(el).find('.entry-title').text();
|
novel.name = loadedCheerio(el).find('.entry-title').text();
|
||||||
novel.path = loadedCheerio(el).find('.entry-title a').attr('href');
|
novel.path = loadedCheerio(el).find('.entry-title a').attr('href');
|
||||||
if (novel.path === undefined || novel.name === undefined) return;
|
if (novel.path === undefined || novel.name === undefined) return;
|
||||||
@@ -224,10 +221,7 @@ class ReLibraryPlugin implements Plugin.PluginBase {
|
|||||||
|
|
||||||
async popularNovels(
|
async popularNovels(
|
||||||
pageNo: number,
|
pageNo: number,
|
||||||
{
|
{ showLatestNovels }: Plugin.PopularNovelsOptions<Filters>,
|
||||||
showLatestNovels,
|
|
||||||
filters,
|
|
||||||
}: Plugin.PopularNovelsOptions<typeof this.filters>,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
// The most-popular page only has a single page, so we return an empty array in case you ask for the impossible
|
// The most-popular page only has a single page, so we return an empty array in case you ask for the impossible
|
||||||
// the lastest page do have paginated result, so we support that.
|
// the lastest page do have paginated result, so we support that.
|
||||||
@@ -241,7 +235,7 @@ class ReLibraryPlugin implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||||
let novel: Partial<Plugin.SourceNovel> = {
|
const novel: Partial<Plugin.SourceNovel> = {
|
||||||
path: novelPath,
|
path: novelPath,
|
||||||
};
|
};
|
||||||
// title: .entry-content > header.entry-header > .entry-title
|
// title: .entry-content > header.entry-header > .entry-title
|
||||||
@@ -250,7 +244,7 @@ class ReLibraryPlugin implements Plugin.PluginBase {
|
|||||||
// synopis: .entry-content > div.su-box > div.su-box-content
|
// synopis: .entry-content > div.su-box > div.su-box-content
|
||||||
// chapters: .entry-content > div.su-accordion <then> li.page_item[]
|
// chapters: .entry-content > div.su-accordion <then> li.page_item[]
|
||||||
|
|
||||||
const result = await fetch(`${this.site}/${novelPath}`);
|
const result = await fetchApi(`${this.site}/${novelPath}`);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
const loadedCheerio = loadCheerio(body);
|
const loadedCheerio = loadCheerio(body);
|
||||||
@@ -271,7 +265,7 @@ class ReLibraryPlugin implements Plugin.PluginBase {
|
|||||||
|
|
||||||
// Genres in comma separated "list"
|
// Genres in comma separated "list"
|
||||||
novel.genres = (() => {
|
novel.genres = (() => {
|
||||||
let genres: string[] = [];
|
const genres: string[] = [];
|
||||||
loadedCheerio(
|
loadedCheerio(
|
||||||
'.entry-content > table > tbody > tr > td > p > span > a',
|
'.entry-content > table > tbody > tr > td > p > span > a',
|
||||||
).each((_i, el) => {
|
).each((_i, el) => {
|
||||||
@@ -303,7 +297,7 @@ class ReLibraryPlugin implements Plugin.PluginBase {
|
|||||||
'.entry-content > div.su-box > div.su-box-content',
|
'.entry-content > div.su-box > div.su-box-content',
|
||||||
).text();
|
).text();
|
||||||
|
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
let chapter_idx = 0;
|
let chapter_idx = 0;
|
||||||
loadedCheerio('.entry-content > div.su-accordion').each((_i1, el) => {
|
loadedCheerio('.entry-content > div.su-accordion').each((_i1, el) => {
|
||||||
@@ -336,16 +330,16 @@ class ReLibraryPlugin implements Plugin.PluginBase {
|
|||||||
|
|
||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
// parse chapter text here
|
// parse chapter text here
|
||||||
const result = await fetch(`${this.site}/${chapterPath}`);
|
const result = await fetchApi(`${this.site}/${chapterPath}`);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
const loadedCheerio = loadCheerio(body);
|
const loadedCheerio = loadCheerio(body);
|
||||||
let text: string[] = [];
|
const text: string[] = [];
|
||||||
loadedCheerio('.entry-content > p')
|
loadedCheerio('.entry-content > p')
|
||||||
.slice(1)
|
.slice(1)
|
||||||
.each((_i, el) => {
|
.each((_i, el) => {
|
||||||
loadedCheerio(el).find('span').remove();
|
loadedCheerio(el).find('span').remove();
|
||||||
let t = loadedCheerio(el).html();
|
const t = loadedCheerio(el).html();
|
||||||
if (t === undefined) return;
|
if (t === undefined) return;
|
||||||
text.push(`<p>${t}</p>`);
|
text.push(`<p>${t}</p>`);
|
||||||
});
|
});
|
||||||
@@ -363,14 +357,14 @@ class ReLibraryPlugin implements Plugin.PluginBase {
|
|||||||
// We only want to serve a single "page" since we do the search client side.
|
// We only want to serve a single "page" since we do the search client side.
|
||||||
if (pageNo !== 1) return [];
|
if (pageNo !== 1) return [];
|
||||||
|
|
||||||
let novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
const req = await fetch(`${this.site}/translations/`);
|
const req = await fetchApi(`${this.site}/translations/`);
|
||||||
const body = await req.text();
|
const body = await req.text();
|
||||||
|
|
||||||
const loadedCheerio = loadCheerio(body);
|
const loadedCheerio = loadCheerio(body);
|
||||||
|
|
||||||
loadedCheerio('.entry-content table a').each((_i, el) => {
|
loadedCheerio('.entry-content table a').each((_i, el) => {
|
||||||
let e = loadedCheerio(el);
|
const e = loadedCheerio(el);
|
||||||
if (e && e.attr('href') && e.text()) {
|
if (e && e.attr('href') && e.text()) {
|
||||||
let path = e.attr('href')!;
|
let path = e.attr('href')!;
|
||||||
if (path.startsWith(this.site)) {
|
if (path.startsWith(this.site)) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Parser } from 'htmlparser2';
|
import { Parser } from 'htmlparser2';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
@@ -86,7 +86,7 @@ class RoyalRoad implements Plugin.PluginBase {
|
|||||||
let isSpan = 0;
|
let isSpan = 0;
|
||||||
let isTags = false;
|
let isTags = false;
|
||||||
let isGenres = false;
|
let isGenres = false;
|
||||||
let genreArray: string[] = [];
|
const genreArray: string[] = [];
|
||||||
let isFooter = false;
|
let isFooter = false;
|
||||||
let isScript = false;
|
let isScript = false;
|
||||||
let chapterJson: ChapterEntry[] = [];
|
let chapterJson: ChapterEntry[] = [];
|
||||||
@@ -226,7 +226,7 @@ class RoyalRoad implements Plugin.PluginBase {
|
|||||||
const parser = new Parser({
|
const parser = new Parser({
|
||||||
onopentag(name, attribs) {
|
onopentag(name, attribs) {
|
||||||
if (isChapter && name === 'div') {
|
if (isChapter && name === 'div') {
|
||||||
let stylediv = attribs['style'];
|
const stylediv = attribs['style'];
|
||||||
if (stylediv) {
|
if (stylediv) {
|
||||||
chapterText += `<div style="${stylediv}">`;
|
chapterText += `<div style="${stylediv}">`;
|
||||||
isStyleText = true;
|
isStyleText = true;
|
||||||
@@ -235,7 +235,7 @@ class RoyalRoad implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isChapter && name === 'table') {
|
if (isChapter && name === 'table') {
|
||||||
let w = attribs['width'];
|
const w = attribs['width'];
|
||||||
if (w) {
|
if (w) {
|
||||||
chapterText += `<table width="${w}">`;
|
chapterText += `<table width="${w}">`;
|
||||||
} else {
|
} else {
|
||||||
@@ -249,7 +249,7 @@ class RoyalRoad implements Plugin.PluginBase {
|
|||||||
chapterText += `<tr>`;
|
chapterText += `<tr>`;
|
||||||
}
|
}
|
||||||
if (isChapter && name === 'td') {
|
if (isChapter && name === 'td') {
|
||||||
let w1 = attribs['width'];
|
const w1 = attribs['width'];
|
||||||
if (w1) {
|
if (w1) {
|
||||||
chapterText += `<td width="${w1}">`;
|
chapterText += `<td width="${w1}">`;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@@ -173,10 +173,7 @@ class ScribbleHubPlugin implements Plugin.PluginBase {
|
|||||||
return chapterText;
|
return chapterText;
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
searchTerm: string,
|
|
||||||
pageNo: number,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const url = `${this.site}?s=${searchTerm}&post_type=fictionposts`;
|
const url = `${this.site}?s=${searchTerm}&post_type=fictionposts`;
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ class WTRLAB implements Plugin.PluginBase {
|
|||||||
link += `&page=${page}`; //TODO Genre & Advance Searching Filter. Ez to implement, too much manual work, too lazy.
|
link += `&page=${page}`; //TODO Genre & Advance Searching Filter. Ez to implement, too much manual work, too lazy.
|
||||||
|
|
||||||
if (showLatestNovels) {
|
if (showLatestNovels) {
|
||||||
const response = await fetch('https://wtr-lab.com/api/home/recent', {
|
const response = await fetchApi(this.site + 'api/home/recent', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -148,7 +148,7 @@ class WTRLAB implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
const response = await fetch(this.site + 'api/search', {
|
const response = await fetchApi(this.site + 'api/search', {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Referer: this.site + this.sourceLang,
|
Referer: this.site + this.sourceLang,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile, fetchProto } from '@libs/fetch';
|
import { fetchApi, fetchProto } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters } from '@libs/filterInputs';
|
import { Filters } from '@libs/filterInputs';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
@@ -105,6 +105,7 @@ class WuxiaWorld implements Plugin.PluginBase {
|
|||||||
name = 'Wuxia World';
|
name = 'Wuxia World';
|
||||||
icon = 'src/en/wuxiaworld/icon.png';
|
icon = 'src/en/wuxiaworld/icon.png';
|
||||||
site = 'https://www.wuxiaworld.com/';
|
site = 'https://www.wuxiaworld.com/';
|
||||||
|
apiSite = 'https://api2.wuxiaworld.com/wuxiaworld.api.v2.';
|
||||||
filters?: Filters | undefined;
|
filters?: Filters | undefined;
|
||||||
version = '0.5.0';
|
version = '0.5.0';
|
||||||
|
|
||||||
@@ -126,10 +127,7 @@ class WuxiaWorld implements Plugin.PluginBase {
|
|||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(): Promise<Plugin.NovelItem[]> {
|
||||||
pageNo: number,
|
|
||||||
options: Plugin.PopularNovelsOptions<Filters>,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const link = `${this.site}api/novels`;
|
const link = `${this.site}api/novels`;
|
||||||
|
|
||||||
const result = await fetchApi(link);
|
const result = await fetchApi(link);
|
||||||
@@ -146,7 +144,7 @@ class WuxiaWorld implements Plugin.PluginBase {
|
|||||||
responseType: 'GetNovelResponse',
|
responseType: 'GetNovelResponse',
|
||||||
requestData: { slug: novelPath.split('/')[1] },
|
requestData: { slug: novelPath.split('/')[1] },
|
||||||
},
|
},
|
||||||
'https://api2.wuxiaworld.com/wuxiaworld.api.v2.Novels/GetNovel',
|
this.apiSite + 'Novels/GetNovel',
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/grpc-web+proto',
|
'Content-Type': 'application/grpc-web+proto',
|
||||||
@@ -201,7 +199,7 @@ class WuxiaWorld implements Plugin.PluginBase {
|
|||||||
responseType: 'GetChapterListResponse',
|
responseType: 'GetChapterListResponse',
|
||||||
requestData: { novelId: data.item?.id },
|
requestData: { novelId: data.item?.id },
|
||||||
},
|
},
|
||||||
'https://api2.wuxiaworld.com/wuxiaworld.api.v2.Chapters/GetChapterList',
|
this.apiSite + 'Chapters/GetChapterList',
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/grpc-web+proto',
|
'Content-Type': 'application/grpc-web+proto',
|
||||||
@@ -256,7 +254,7 @@ class WuxiaWorld implements Plugin.PluginBase {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'https://api2.wuxiaworld.com/wuxiaworld.api.v2.Chapters/GetChapter',
|
this.apiSite + 'Chapters/GetChapter',
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/grpc-web+proto',
|
'Content-Type': 'application/grpc-web+proto',
|
||||||
@@ -271,13 +269,8 @@ class WuxiaWorld implements Plugin.PluginBase {
|
|||||||
return chapterText;
|
return chapterText;
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
searchTerm: string,
|
const url = this.site + 'api/novels/search?query=' + searchTerm;
|
||||||
pageNo: number,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
const searchUrl = 'https://www.wuxiaworld.com/api/novels/search?query=';
|
|
||||||
|
|
||||||
const url = searchUrl + searchTerm;
|
|
||||||
|
|
||||||
const result = await fetchApi(url);
|
const result = await fetchApi(url);
|
||||||
const data = await result.json();
|
const data = await result.json();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CheerioAPI, load } from 'cheerio';
|
import { CheerioAPI, load } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@@ -27,7 +27,7 @@ class ChireadsPlugin implements Plugin.PluginBase {
|
|||||||
{ filters, showLatestNovels }: Plugin.PopularNovelsOptions,
|
{ filters, showLatestNovels }: Plugin.PopularNovelsOptions,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
let url = this.site;
|
let url = this.site;
|
||||||
let tag: string = 'all';
|
let tag = 'all';
|
||||||
if (showLatestNovels) url += '/category/translatedtales/page/' + pageNo;
|
if (showLatestNovels) url += '/category/translatedtales/page/' + pageNo;
|
||||||
else {
|
else {
|
||||||
if (
|
if (
|
||||||
@@ -41,7 +41,7 @@ class ChireadsPlugin implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
let $ = await this.getCheerio(url);
|
let $ = await this.getCheerio(url);
|
||||||
|
|
||||||
let novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
let novel: Plugin.NovelItem;
|
let novel: Plugin.NovelItem;
|
||||||
|
|
||||||
if (showLatestNovels || tag !== 'all') {
|
if (showLatestNovels || tag !== 'all') {
|
||||||
@@ -133,7 +133,7 @@ class ChireadsPlugin implements Plugin.PluginBase {
|
|||||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||||
const novel: Plugin.SourceNovel = { path: novelPath, name: 'Sans titre' };
|
const novel: Plugin.SourceNovel = { path: novelPath, name: 'Sans titre' };
|
||||||
|
|
||||||
let $ = await this.getCheerio(this.site + novelPath);
|
const $ = await this.getCheerio(this.site + novelPath);
|
||||||
|
|
||||||
novel.name =
|
novel.name =
|
||||||
$('.inform-product-txt').first().text().trim() ||
|
$('.inform-product-txt').first().text().trim() ||
|
||||||
@@ -146,7 +146,7 @@ class ChireadsPlugin implements Plugin.PluginBase {
|
|||||||
$('.inform-inform-txt').text().trim() ||
|
$('.inform-inform-txt').text().trim() ||
|
||||||
$('.inform-intr-txt').text().trim();
|
$('.inform-intr-txt').text().trim();
|
||||||
|
|
||||||
let infos =
|
const infos =
|
||||||
$('div.inform-product-txt > div.inform-intr-col').text().trim() ||
|
$('div.inform-product-txt > div.inform-intr-col').text().trim() ||
|
||||||
$('div.inform-inform-data > h6').text().trim();
|
$('div.inform-inform-data > h6').text().trim();
|
||||||
if (infos.includes('Auteur : '))
|
if (infos.includes('Auteur : '))
|
||||||
@@ -178,7 +178,7 @@ class ChireadsPlugin implements Plugin.PluginBase {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
let chapterList = $('.chapitre-table a');
|
let chapterList = $('.chapitre-table a');
|
||||||
if (!chapterList.length) {
|
if (!chapterList.length) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { CheerioAPI, load } from 'cheerio';
|
import { CheerioAPI, load } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@@ -12,7 +11,6 @@ class HarkenEliwoodPlugin implements Plugin.PluginBase {
|
|||||||
icon = 'src/fr/harkeneliwood/icon.png';
|
icon = 'src/fr/harkeneliwood/icon.png';
|
||||||
site = 'https://harkeneliwood.wordpress.com';
|
site = 'https://harkeneliwood.wordpress.com';
|
||||||
version = '1.0.0';
|
version = '1.0.0';
|
||||||
filters: Filters | undefined = undefined;
|
|
||||||
|
|
||||||
async getCheerio(url: string): Promise<CheerioAPI> {
|
async getCheerio(url: string): Promise<CheerioAPI> {
|
||||||
const r = await fetchApi(url, {
|
const r = await fetchApi(url, {
|
||||||
@@ -23,19 +21,13 @@ class HarkenEliwoodPlugin implements Plugin.PluginBase {
|
|||||||
return $;
|
return $;
|
||||||
}
|
}
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
pageNo: number,
|
|
||||||
{
|
|
||||||
showLatestNovels,
|
|
||||||
filters,
|
|
||||||
}: Plugin.PopularNovelsOptions<typeof this.filters>,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
if (pageNo > 1) return [];
|
if (pageNo > 1) return [];
|
||||||
|
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
let novel: Plugin.NovelItem;
|
let novel: Plugin.NovelItem;
|
||||||
let url = this.site;
|
const url = this.site;
|
||||||
let $ = await this.getCheerio(url + '/projets/');
|
const $ = await this.getCheerio(url + '/projets/');
|
||||||
$('#content .entry-content [href]')
|
$('#content .entry-content [href]')
|
||||||
// We don't collect items for Facebook and Twitter.
|
// We don't collect items for Facebook and Twitter.
|
||||||
.not('[rel="nofollow noopener noreferrer"]')
|
.not('[rel="nofollow noopener noreferrer"]')
|
||||||
@@ -60,14 +52,14 @@ class HarkenEliwoodPlugin implements Plugin.PluginBase {
|
|||||||
name: 'Sans titre',
|
name: 'Sans titre',
|
||||||
};
|
};
|
||||||
|
|
||||||
let $ = await this.getCheerio(this.site + novelPath);
|
const $ = await this.getCheerio(this.site + novelPath);
|
||||||
novel.name = $('#content h1.entry-title').text().trim();
|
novel.name = $('#content h1.entry-title').text().trim();
|
||||||
novel.cover =
|
novel.cover =
|
||||||
$('#content .entry-content p img').first().attr('src') || defaultCover;
|
$('#content .entry-content p img').first().attr('src') || defaultCover;
|
||||||
novel.summary = this.getSummary($('#content .entry-content').text());
|
novel.summary = this.getSummary($('#content .entry-content').text());
|
||||||
novel.author = this.getAuthor($('#content .entry-content').text());
|
novel.author = this.getAuthor($('#content .entry-content').text());
|
||||||
novel.status = NovelStatus.Ongoing;
|
novel.status = NovelStatus.Ongoing;
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
$('#content .entry-content p a').each((i, elem) => {
|
$('#content .entry-content p a').each((i, elem) => {
|
||||||
const chapterName = $(elem).text().trim();
|
const chapterName = $(elem).text().trim();
|
||||||
const chapterUrl = $(elem).attr('href');
|
const chapterUrl = $(elem).attr('href');
|
||||||
@@ -88,14 +80,13 @@ class HarkenEliwoodPlugin implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSummary(text: string) {
|
getSummary(text: string) {
|
||||||
let resume: string = '';
|
let resume = '';
|
||||||
const regexResume1: RegExp = /Synopsis :([\s\S]*)Traduction anglaise/i;
|
const regexResume1 = /Synopsis :([\s\S]*)Traduction anglaise/i;
|
||||||
const regexResume2: RegExp = /Synopsis :([\s\S]*)Raw :/i;
|
const regexResume2 = /Synopsis :([\s\S]*)Raw :/i;
|
||||||
const regexResume3: RegExp =
|
const regexResume3 = /Synopsis 1 :([\s\S]*)Synopsis 2 :([\s\S]*)Raw :/i;
|
||||||
/Synopsis 1 :([\s\S]*)Synopsis 2 :([\s\S]*)Raw :/i;
|
const regexResume4 = /Synopsis :([\s\S]*)Prélude/i;
|
||||||
const regexResume4: RegExp = /Synopsis :([\s\S]*)Prélude/i;
|
const regexResume5 = /Synospis :([\s\S]*)Original /i;
|
||||||
const regexResume5: RegExp = /Synospis :([\s\S]*)Original /i;
|
const regexResume6 = /([\s\S]*)Raw :/i;
|
||||||
const regexResume6: RegExp = /([\s\S]*)Raw :/i;
|
|
||||||
|
|
||||||
const match1: RegExpExecArray | null = regexResume1.exec(text);
|
const match1: RegExpExecArray | null = regexResume1.exec(text);
|
||||||
const match2: RegExpExecArray | null = regexResume2.exec(text);
|
const match2: RegExpExecArray | null = regexResume2.exec(text);
|
||||||
@@ -151,12 +142,9 @@ class HarkenEliwoodPlugin implements Plugin.PluginBase {
|
|||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
if (pageNo !== 1) return [];
|
if (pageNo !== 1) return [];
|
||||||
|
|
||||||
let popularNovels = this.popularNovels(1, {
|
const popularNovels = this.popularNovels(1);
|
||||||
showLatestNovels: true,
|
|
||||||
filters: undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
let novels = (await popularNovels).filter(novel =>
|
const novels = (await popularNovels).filter(novel =>
|
||||||
novel.name
|
novel.name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize('NFD')
|
.normalize('NFD')
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { CheerioAPI, load } from 'cheerio';
|
import { CheerioAPI, load } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
|
|
||||||
@@ -11,7 +10,6 @@ class KissWoodPlugin implements Plugin.PluginBase {
|
|||||||
icon = 'src/fr/kisswood/icon.png';
|
icon = 'src/fr/kisswood/icon.png';
|
||||||
site = 'https://kisswood.eu';
|
site = 'https://kisswood.eu';
|
||||||
version = '1.0.0';
|
version = '1.0.0';
|
||||||
filters: Filters | undefined = undefined;
|
|
||||||
|
|
||||||
async getCheerio(url: string): Promise<CheerioAPI> {
|
async getCheerio(url: string): Promise<CheerioAPI> {
|
||||||
const r = await fetchApi(url);
|
const r = await fetchApi(url);
|
||||||
@@ -41,7 +39,7 @@ class KissWoodPlugin implements Plugin.PluginBase {
|
|||||||
novel: Plugin.SourceNovel,
|
novel: Plugin.SourceNovel,
|
||||||
url: string,
|
url: string,
|
||||||
): Promise<Plugin.SourceNovel> {
|
): Promise<Plugin.SourceNovel> {
|
||||||
let $ = await this.getCheerio(url);
|
const $ = await this.getCheerio(url);
|
||||||
|
|
||||||
const textArray: string[] = $('.entry-content p')
|
const textArray: string[] = $('.entry-content p')
|
||||||
.map((_, element) => $(element).text().trim())
|
.map((_, element) => $(element).text().trim())
|
||||||
@@ -88,18 +86,12 @@ class KissWoodPlugin implements Plugin.PluginBase {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
pageNo: number,
|
|
||||||
{
|
|
||||||
showLatestNovels,
|
|
||||||
filters,
|
|
||||||
}: Plugin.PopularNovelsOptions<typeof this.filters>,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
if (pageNo > 1) return [];
|
if (pageNo > 1) return [];
|
||||||
|
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
const $ = await this.getCheerio(this.site);
|
const $ = await this.getCheerio(this.site);
|
||||||
var listUrlCover: string[] = [];
|
const listUrlCover: string[] = [];
|
||||||
$('nav div div ul li ul li').each((i, elem) => {
|
$('nav div div ul li ul li').each((i, elem) => {
|
||||||
if ($(elem).text().trim() === 'Sommaire') {
|
if ($(elem).text().trim() === 'Sommaire') {
|
||||||
const novelName = $(elem)
|
const novelName = $(elem)
|
||||||
@@ -137,7 +129,7 @@ class KissWoodPlugin implements Plugin.PluginBase {
|
|||||||
status: NovelStatus.Ongoing,
|
status: NovelStatus.Ongoing,
|
||||||
};
|
};
|
||||||
|
|
||||||
let $ = await this.getCheerio(this.site + novelPath);
|
const $ = await this.getCheerio(this.site + novelPath);
|
||||||
let novelUrl = null;
|
let novelUrl = null;
|
||||||
|
|
||||||
$('nav div div ul li ul li').each((i, elem) => {
|
$('nav div div ul li ul li').each((i, elem) => {
|
||||||
@@ -160,7 +152,7 @@ class KissWoodPlugin implements Plugin.PluginBase {
|
|||||||
'.entry-content blockquote a',
|
'.entry-content blockquote a',
|
||||||
].join(', ');
|
].join(', ');
|
||||||
|
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
$(chapterSelectors).each((i, elem) => {
|
$(chapterSelectors).each((i, elem) => {
|
||||||
const chapterName = $(elem).text().trim();
|
const chapterName = $(elem).text().trim();
|
||||||
const chapterUrl = $(elem).attr('href')?.replace('http://', 'https://');
|
const chapterUrl = $(elem).attr('href')?.replace('http://', 'https://');
|
||||||
@@ -224,12 +216,9 @@ class KissWoodPlugin implements Plugin.PluginBase {
|
|||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
if (pageNo !== 1) return [];
|
if (pageNo !== 1) return [];
|
||||||
|
|
||||||
let popularNovels = this.popularNovels(1, {
|
const popularNovels = this.popularNovels(1);
|
||||||
showLatestNovels: true,
|
|
||||||
filters: undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
let novels = (await popularNovels).filter(novel =>
|
const novels = (await popularNovels).filter(novel =>
|
||||||
novel.name
|
novel.name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize('NFD')
|
.normalize('NFD')
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { load, CheerioAPI } from 'cheerio';
|
import { load, CheerioAPI } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
@@ -24,7 +24,7 @@ class NovelDeGlacePlugin implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseDate(date: string): string {
|
parseDate(date: string): string {
|
||||||
const monthMapping: { [key: string]: string } = {
|
const monthMapping: Record<string, string> = {
|
||||||
janvier: 'January',
|
janvier: 'January',
|
||||||
fevrier: 'February',
|
fevrier: 'February',
|
||||||
mars: 'March',
|
mars: 'March',
|
||||||
@@ -49,7 +49,7 @@ class NovelDeGlacePlugin implements Plugin.PluginBase {
|
|||||||
$: CheerioAPI,
|
$: CheerioAPI,
|
||||||
showLatestNovels: boolean | undefined,
|
showLatestNovels: boolean | undefined,
|
||||||
): Plugin.NovelItem[] {
|
): Plugin.NovelItem[] {
|
||||||
let novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
$('article').each((i, el) => {
|
$('article').each((i, el) => {
|
||||||
const cheerio = $(el);
|
const cheerio = $(el);
|
||||||
@@ -83,7 +83,7 @@ class NovelDeGlacePlugin implements Plugin.PluginBase {
|
|||||||
let url = this.site;
|
let url = this.site;
|
||||||
if (showLatestNovels) url += 'chapitre';
|
if (showLatestNovels) url += 'chapitre';
|
||||||
else {
|
else {
|
||||||
let cat_gen: string = 'all';
|
let cat_gen = 'all';
|
||||||
if (filters && typeof filters.categorie_genre.value == 'string')
|
if (filters && typeof filters.categorie_genre.value == 'string')
|
||||||
cat_gen = filters.categorie_genre.value;
|
cat_gen = filters.categorie_genre.value;
|
||||||
if (
|
if (
|
||||||
@@ -98,7 +98,7 @@ class NovelDeGlacePlugin implements Plugin.PluginBase {
|
|||||||
else url += 'roman';
|
else url += 'roman';
|
||||||
}
|
}
|
||||||
url += '/page/' + pageNo;
|
url += '/page/' + pageNo;
|
||||||
let $ = await this.getCheerio(url);
|
const $ = await this.getCheerio(url);
|
||||||
if (!$) return [];
|
if (!$) return [];
|
||||||
return this.parseNovels($, showLatestNovels);
|
return this.parseNovels($, showLatestNovels);
|
||||||
}
|
}
|
||||||
@@ -107,16 +107,16 @@ class NovelDeGlacePlugin implements Plugin.PluginBase {
|
|||||||
const $ = await this.getCheerio(this.site + novelPath);
|
const $ = await this.getCheerio(this.site + novelPath);
|
||||||
if (!$) throw new Error('Failed to load page (open in web view)');
|
if (!$) throw new Error('Failed to load page (open in web view)');
|
||||||
|
|
||||||
let novel: Plugin.SourceNovel = { path: novelPath, name: 'Untitled' };
|
const novel: Plugin.SourceNovel = { path: novelPath, name: 'Untitled' };
|
||||||
|
|
||||||
novel.name =
|
novel.name =
|
||||||
(
|
(
|
||||||
$('div.entry-content > div > strong')[0].nextSibling as Text | null
|
$('div.entry-content > div > strong')[0].nextSibling as string | null
|
||||||
)?.nodeValue?.trim() || 'Untitled';
|
)?.nodeValue?.trim() || 'Untitled';
|
||||||
|
|
||||||
novel.cover = $('.su-row > div > div > img').attr('src') || defaultCover;
|
novel.cover = $('.su-row > div > div > img').attr('src') || defaultCover;
|
||||||
|
|
||||||
let novelInfos = $('div[data-title=Tomes] >').toArray();
|
const novelInfos = $('div[data-title=Tomes] >').toArray();
|
||||||
novelInfos.pop();
|
novelInfos.pop();
|
||||||
novelInfos.shift();
|
novelInfos.shift();
|
||||||
novel.summary =
|
novel.summary =
|
||||||
@@ -149,7 +149,7 @@ class NovelDeGlacePlugin implements Plugin.PluginBase {
|
|||||||
if (genres)
|
if (genres)
|
||||||
novel.genres = novel.genres ? novel.genres + ',' + genres : genres;
|
novel.genres = novel.genres ? novel.genres + ',' + genres : genres;
|
||||||
|
|
||||||
let status = $("strong:contains('Statut :')").parent().attr('class');
|
const status = $("strong:contains('Statut :')").parent().attr('class');
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'type etat0':
|
case 'type etat0':
|
||||||
case 'type etat1':
|
case 'type etat1':
|
||||||
@@ -169,13 +169,13 @@ class NovelDeGlacePlugin implements Plugin.PluginBase {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let novelChapters: Plugin.ChapterItem[] = [];
|
const novelChapters: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
const volumes = $('div[data-title=Tomes] > div').last().contents();
|
const volumes = $('div[data-title=Tomes] > div').last().contents();
|
||||||
const hasMultipleVolumes = volumes.length > 1;
|
const hasMultipleVolumes = volumes.length > 1;
|
||||||
|
|
||||||
let chapterName = '';
|
let chapterName = '';
|
||||||
let site = this.site;
|
const site = this.site;
|
||||||
volumes.each((volumeIndex: number, el) => {
|
volumes.each((volumeIndex: number, el) => {
|
||||||
if (hasMultipleVolumes) chapterName = 'T.' + (volumeIndex + 1) + ' ';
|
if (hasMultipleVolumes) chapterName = 'T.' + (volumeIndex + 1) + ' ';
|
||||||
$(el)
|
$(el)
|
||||||
@@ -189,20 +189,16 @@ class NovelDeGlacePlugin implements Plugin.PluginBase {
|
|||||||
const dateHtml =
|
const dateHtml =
|
||||||
cheerio.html()?.substring(cheerio.html()?.indexOf('</a>') || 0) ||
|
cheerio.html()?.substring(cheerio.html()?.indexOf('</a>') || 0) ||
|
||||||
'';
|
'';
|
||||||
let releaseDate =
|
const releaseDate =
|
||||||
dateHtml?.substring(
|
dateHtml?.substring(
|
||||||
dateHtml.indexOf('(') + 1,
|
dateHtml.indexOf('(') + 1,
|
||||||
dateHtml.indexOf(')'),
|
dateHtml.indexOf(')'),
|
||||||
) || undefined;
|
) || undefined;
|
||||||
if (releaseDate)
|
|
||||||
try {
|
|
||||||
releaseDate = this.parseDate(releaseDate);
|
|
||||||
} catch (e) {}
|
|
||||||
const chapterUrl = cheerio.find('a').attr('href');
|
const chapterUrl = cheerio.find('a').attr('href');
|
||||||
if (chapterUrl) {
|
if (chapterUrl) {
|
||||||
const chapter: Plugin.ChapterItem = {
|
const chapter: Plugin.ChapterItem = {
|
||||||
name: newChapterName,
|
name: newChapterName,
|
||||||
releaseTime: releaseDate,
|
releaseTime: this.parseDate(releaseDate),
|
||||||
path: chapterUrl.replace(site, ''),
|
path: chapterUrl.replace(site, ''),
|
||||||
chapterNumber: chapterIndex,
|
chapterNumber: chapterIndex,
|
||||||
};
|
};
|
||||||
@@ -232,13 +228,9 @@ class NovelDeGlacePlugin implements Plugin.PluginBase {
|
|||||||
});
|
});
|
||||||
if (dates.length == hrefs.length)
|
if (dates.length == hrefs.length)
|
||||||
dates.forEach((date, index) => {
|
dates.forEach((date, index) => {
|
||||||
let releaseDate = date;
|
|
||||||
try {
|
|
||||||
releaseDate = this.parseDate(date);
|
|
||||||
} catch (e) {}
|
|
||||||
const chapter: Plugin.ChapterItem = {
|
const chapter: Plugin.ChapterItem = {
|
||||||
name: newChapterName + ' (' + (index + 1) + ')',
|
name: newChapterName + ' (' + (index + 1) + ')',
|
||||||
releaseTime: releaseDate,
|
releaseTime: this.parseDate(date),
|
||||||
path: hrefs[index].replace(site, ''),
|
path: hrefs[index].replace(site, ''),
|
||||||
chapterNumber: chapterIndex + (index + 1) / 1000,
|
chapterNumber: chapterIndex + (index + 1) / 1000,
|
||||||
};
|
};
|
||||||
@@ -258,7 +250,7 @@ class NovelDeGlacePlugin implements Plugin.PluginBase {
|
|||||||
if (!$) throw new Error('Failed to load page (open in web view)');
|
if (!$) throw new Error('Failed to load page (open in web view)');
|
||||||
|
|
||||||
$('.mistape_caption').remove();
|
$('.mistape_caption').remove();
|
||||||
let chapterText =
|
const chapterText =
|
||||||
$('.chapter-content').html() || $('.entry-content').html() || '';
|
$('.chapter-content').html() || $('.entry-content').html() || '';
|
||||||
return chapterText;
|
return chapterText;
|
||||||
}
|
}
|
||||||
@@ -268,7 +260,7 @@ class NovelDeGlacePlugin implements Plugin.PluginBase {
|
|||||||
num: number,
|
num: number,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
if (num !== 1) return []; // only 1 page of results
|
if (num !== 1) return []; // only 1 page of results
|
||||||
let url = this.site + 'roman';
|
const url = this.site + 'roman';
|
||||||
const $ = await this.getCheerio(url);
|
const $ = await this.getCheerio(url);
|
||||||
if (!$) throw new Error('Failed to load page (open in web view)');
|
if (!$) throw new Error('Failed to load page (open in web view)');
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { CheerioAPI, load } from 'cheerio';
|
import { CheerioAPI, load } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
|
|
||||||
@@ -11,7 +10,6 @@ class NovhellPlugin implements Plugin.PluginBase {
|
|||||||
icon = 'src/fr/novhell/icon.png';
|
icon = 'src/fr/novhell/icon.png';
|
||||||
site = 'https://novhell.org';
|
site = 'https://novhell.org';
|
||||||
version = '1.0.1';
|
version = '1.0.1';
|
||||||
filters: Filters | undefined = undefined;
|
|
||||||
|
|
||||||
async getCheerio(url: string): Promise<CheerioAPI> {
|
async getCheerio(url: string): Promise<CheerioAPI> {
|
||||||
const r = await fetchApi(url, {
|
const r = await fetchApi(url, {
|
||||||
@@ -22,19 +20,13 @@ class NovhellPlugin implements Plugin.PluginBase {
|
|||||||
return $;
|
return $;
|
||||||
}
|
}
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
pageNo: number,
|
|
||||||
{
|
|
||||||
showLatestNovels,
|
|
||||||
filters,
|
|
||||||
}: Plugin.PopularNovelsOptions<typeof this.filters>,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
if (pageNo > 1) return [];
|
if (pageNo > 1) return [];
|
||||||
|
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
let novel: Plugin.NovelItem;
|
let novel: Plugin.NovelItem;
|
||||||
let url = this.site;
|
const url = this.site;
|
||||||
let $ = await this.getCheerio(url);
|
const $ = await this.getCheerio(url);
|
||||||
$('article div div div figure').each((i, elem) => {
|
$('article div div div figure').each((i, elem) => {
|
||||||
let novelName = $(elem)
|
let novelName = $(elem)
|
||||||
.find('figcaption span strong')
|
.find('figcaption span strong')
|
||||||
@@ -65,7 +57,7 @@ class NovhellPlugin implements Plugin.PluginBase {
|
|||||||
name: 'Sans titre',
|
name: 'Sans titre',
|
||||||
};
|
};
|
||||||
|
|
||||||
let $ = await this.getCheerio(this.site + novelPath);
|
const $ = await this.getCheerio(this.site + novelPath);
|
||||||
|
|
||||||
novel.name =
|
novel.name =
|
||||||
$('meta[property="og:title"]')
|
$('meta[property="og:title"]')
|
||||||
@@ -114,7 +106,7 @@ class NovhellPlugin implements Plugin.PluginBase {
|
|||||||
.replace('Synopsis', '')
|
.replace('Synopsis', '')
|
||||||
.replace(':', '')
|
.replace(':', '')
|
||||||
.trim();
|
.trim();
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
$('main div article div div section div div div div div p a').each(
|
$('main div article div div section div div div div div p a').each(
|
||||||
(i, elem) => {
|
(i, elem) => {
|
||||||
@@ -190,12 +182,9 @@ class NovhellPlugin implements Plugin.PluginBase {
|
|||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
if (pageNo !== 1) return [];
|
if (pageNo !== 1) return [];
|
||||||
|
|
||||||
let popularNovels = this.popularNovels(1, {
|
const popularNovels = this.popularNovels(1);
|
||||||
showLatestNovels: true,
|
|
||||||
filters: undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
let novels = (await popularNovels).filter(novel =>
|
const novels = (await popularNovels).filter(novel =>
|
||||||
novel.name
|
novel.name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize('NFD')
|
.normalize('NFD')
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CheerioAPI, load } from 'cheerio';
|
import { CheerioAPI, load } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
@@ -39,7 +39,7 @@ class PhenixScansTradPlugin implements Plugin.PluginBase {
|
|||||||
const order = showLatestNovels ? 'update' : 'popular';
|
const order = showLatestNovels ? 'update' : 'popular';
|
||||||
const url = `${this.site}/manga/?page=${pageNo}${filter}&status=&type=novel&order=${order}`;
|
const url = `${this.site}/manga/?page=${pageNo}${filter}&status=&type=novel&order=${order}`;
|
||||||
|
|
||||||
let $ = await this.getCheerio(url);
|
const $ = await this.getCheerio(url);
|
||||||
$('div div div div a').each((i, elem) => {
|
$('div div div div a').each((i, elem) => {
|
||||||
const novelName = $(elem).attr('title')?.trim();
|
const novelName = $(elem).attr('title')?.trim();
|
||||||
const novelUrl = $(elem).attr('href');
|
const novelUrl = $(elem).attr('href');
|
||||||
@@ -63,7 +63,7 @@ class PhenixScansTradPlugin implements Plugin.PluginBase {
|
|||||||
name: 'Sans titre',
|
name: 'Sans titre',
|
||||||
};
|
};
|
||||||
|
|
||||||
let $ = await this.getCheerio(this.site + novelPath);
|
const $ = await this.getCheerio(this.site + novelPath);
|
||||||
|
|
||||||
novel.name = $('h1[itemprop=name]').text().replace('– Novel', '').trim();
|
novel.name = $('h1[itemprop=name]').text().replace('– Novel', '').trim();
|
||||||
novel.cover =
|
novel.cover =
|
||||||
@@ -80,7 +80,7 @@ class PhenixScansTradPlugin implements Plugin.PluginBase {
|
|||||||
$('.tsinfo .imptdt:contains(Statut)').text().replace('Statut', '').trim(),
|
$('.tsinfo .imptdt:contains(Statut)').text().replace('Statut', '').trim(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
$('ul li:has(div.chbox):has(div.eph-num)').each((i, elem) => {
|
$('ul li:has(div.chbox):has(div.eph-num)').each((i, elem) => {
|
||||||
const chapterName = $(elem).find('a .chapternum').text().trim();
|
const chapterName = $(elem).find('a .chapternum').text().trim();
|
||||||
const chapterUrl = $(elem).find('a').attr('href');
|
const chapterUrl = $(elem).find('a').attr('href');
|
||||||
@@ -98,7 +98,7 @@ class PhenixScansTradPlugin implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseDate(date: string): string {
|
parseDate(date: string): string {
|
||||||
const monthMapping: { [key: string]: string } = {
|
const monthMapping: Record<string, string> = {
|
||||||
janvier: 'January',
|
janvier: 'January',
|
||||||
fevrier: 'February',
|
fevrier: 'February',
|
||||||
mars: 'March',
|
mars: 'March',
|
||||||
@@ -149,12 +149,12 @@ class PhenixScansTradPlugin implements Plugin.PluginBase {
|
|||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
if (pageNo !== 1) return [];
|
if (pageNo !== 1) return [];
|
||||||
|
|
||||||
let popularNovels = this.popularNovels(1, {
|
const popularNovels = this.popularNovels(1, {
|
||||||
showLatestNovels: true,
|
showLatestNovels: true,
|
||||||
filters: undefined,
|
filters: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
let novels = (await popularNovels).filter(novel =>
|
const novels = (await popularNovels).filter(novel =>
|
||||||
novel.name
|
novel.name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize('NFD')
|
.normalize('NFD')
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { CheerioAPI, load } from 'cheerio';
|
import { CheerioAPI, load } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters } from '@libs/filterInputs';
|
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@@ -12,7 +11,6 @@ class WarriorLegendTradPlugin implements Plugin.PluginBase {
|
|||||||
icon = 'src/fr/warriorlegendtrad/icon.png';
|
icon = 'src/fr/warriorlegendtrad/icon.png';
|
||||||
site = 'https://warriorlegendtrad.fr';
|
site = 'https://warriorlegendtrad.fr';
|
||||||
version = '1.0.0';
|
version = '1.0.0';
|
||||||
filters: Filters | undefined = undefined;
|
|
||||||
|
|
||||||
regexAuthors = [/Auteur\u00A0:([^\n]*)/];
|
regexAuthors = [/Auteur\u00A0:([^\n]*)/];
|
||||||
|
|
||||||
@@ -27,13 +25,7 @@ class WarriorLegendTradPlugin implements Plugin.PluginBase {
|
|||||||
return $;
|
return $;
|
||||||
}
|
}
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
pageNo: number,
|
|
||||||
{
|
|
||||||
showLatestNovels,
|
|
||||||
filters,
|
|
||||||
}: Plugin.PopularNovelsOptions<typeof this.filters>,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
if (pageNo > 2) return [];
|
if (pageNo > 2) return [];
|
||||||
|
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
@@ -48,7 +40,7 @@ class WarriorLegendTradPlugin implements Plugin.PluginBase {
|
|||||||
url = this.site + '/crea';
|
url = this.site + '/crea';
|
||||||
}
|
}
|
||||||
|
|
||||||
let $ = await this.getCheerio(url);
|
const $ = await this.getCheerio(url);
|
||||||
$('div div div article').each((i, elem) => {
|
$('div div div article').each((i, elem) => {
|
||||||
const novelName = $(elem).find('.entry-wrapper h2').first().text().trim();
|
const novelName = $(elem).find('.entry-wrapper h2').first().text().trim();
|
||||||
const novelUrl = $(elem).find('.entry-wrapper h2 a').attr('href');
|
const novelUrl = $(elem).find('.entry-wrapper h2 a').attr('href');
|
||||||
@@ -73,7 +65,7 @@ class WarriorLegendTradPlugin implements Plugin.PluginBase {
|
|||||||
name: 'Sans titre',
|
name: 'Sans titre',
|
||||||
};
|
};
|
||||||
|
|
||||||
let $ = await this.getCheerio(this.site + novelPath);
|
const $ = await this.getCheerio(this.site + novelPath);
|
||||||
|
|
||||||
novel.name = $('.site-main article header h1').text().trim();
|
novel.name = $('.site-main article header h1').text().trim();
|
||||||
novel.cover =
|
novel.cover =
|
||||||
@@ -84,7 +76,7 @@ class WarriorLegendTradPlugin implements Plugin.PluginBase {
|
|||||||
novel.summary = this.extractInfo(entryContentText, this.regexSummary);
|
novel.summary = this.extractInfo(entryContentText, this.regexSummary);
|
||||||
novel.status = this.getStatus(entryContentText);
|
novel.status = this.getStatus(entryContentText);
|
||||||
|
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
$('div div article div')
|
$('div div article div')
|
||||||
.find('h2 a, h3 a')
|
.find('h2 a, h3 a')
|
||||||
.each((i, elem) => {
|
.each((i, elem) => {
|
||||||
@@ -150,7 +142,7 @@ class WarriorLegendTradPlugin implements Plugin.PluginBase {
|
|||||||
|
|
||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
const $ = await this.getCheerio(this.site + chapterPath);
|
const $ = await this.getCheerio(this.site + chapterPath);
|
||||||
var contenuHtml = '';
|
let contenuHtml = '';
|
||||||
$('.entry-content')
|
$('.entry-content')
|
||||||
.contents()
|
.contents()
|
||||||
.each(function () {
|
.each(function () {
|
||||||
@@ -171,12 +163,9 @@ class WarriorLegendTradPlugin implements Plugin.PluginBase {
|
|||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
if (pageNo !== 1) return [];
|
if (pageNo !== 1) return [];
|
||||||
|
|
||||||
let popularNovels = this.popularNovels(1, {
|
const popularNovels = this.popularNovels(1);
|
||||||
showLatestNovels: true,
|
|
||||||
filters: undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
let novels = (await popularNovels).filter(novel =>
|
const novels = (await popularNovels).filter(novel =>
|
||||||
novel.name
|
novel.name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize('NFD')
|
.normalize('NFD')
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { CheerioAPI, load } from 'cheerio';
|
import { CheerioAPI, load } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@@ -12,7 +11,6 @@ class WuxialnscantradPlugin implements Plugin.PluginBase {
|
|||||||
icon = 'src/fr/wuxialnscantrad/icon.png';
|
icon = 'src/fr/wuxialnscantrad/icon.png';
|
||||||
site = 'https://wuxialnscantrad.wordpress.com';
|
site = 'https://wuxialnscantrad.wordpress.com';
|
||||||
version = '1.0.0';
|
version = '1.0.0';
|
||||||
filters: Filters | undefined = undefined;
|
|
||||||
|
|
||||||
async getCheerio(url: string): Promise<CheerioAPI> {
|
async getCheerio(url: string): Promise<CheerioAPI> {
|
||||||
const r = await fetchApi(url, {
|
const r = await fetchApi(url, {
|
||||||
@@ -23,19 +21,13 @@ class WuxialnscantradPlugin implements Plugin.PluginBase {
|
|||||||
return $;
|
return $;
|
||||||
}
|
}
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
pageNo: number,
|
|
||||||
{
|
|
||||||
showLatestNovels,
|
|
||||||
filters,
|
|
||||||
}: Plugin.PopularNovelsOptions<typeof this.filters>,
|
|
||||||
): Promise<Plugin.NovelItem[]> {
|
|
||||||
if (pageNo > 1) return [];
|
if (pageNo > 1) return [];
|
||||||
|
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
let novel: Plugin.NovelItem;
|
let novel: Plugin.NovelItem;
|
||||||
let url = this.site;
|
const url = this.site;
|
||||||
let $ = await this.getCheerio(url);
|
const $ = await this.getCheerio(url);
|
||||||
$('#menu-item-2210 ul li').each((i, elem) => {
|
$('#menu-item-2210 ul li').each((i, elem) => {
|
||||||
const novelName = $(elem).first().text().trim();
|
const novelName = $(elem).first().text().trim();
|
||||||
const novelUrl = $(elem).find('a').attr('href');
|
const novelUrl = $(elem).find('a').attr('href');
|
||||||
@@ -58,7 +50,7 @@ class WuxialnscantradPlugin implements Plugin.PluginBase {
|
|||||||
name: 'Sans titre',
|
name: 'Sans titre',
|
||||||
};
|
};
|
||||||
|
|
||||||
let $ = await this.getCheerio(this.site + novelPath);
|
const $ = await this.getCheerio(this.site + novelPath);
|
||||||
|
|
||||||
novel.name = $('.entry-title').text().trim();
|
novel.name = $('.entry-title').text().trim();
|
||||||
novel.cover =
|
novel.cover =
|
||||||
@@ -72,8 +64,8 @@ class WuxialnscantradPlugin implements Plugin.PluginBase {
|
|||||||
novel.summary = this.getSummary(entryContentText);
|
novel.summary = this.getSummary(entryContentText);
|
||||||
novel.status = this.getStatus(entryContentText);
|
novel.status = this.getStatus(entryContentText);
|
||||||
|
|
||||||
var pathChapter = $('.entry-content ul').first().children('li');
|
const pathChapter = $('.entry-content ul').first().children('li');
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
pathChapter.each((i, elem) => {
|
pathChapter.each((i, elem) => {
|
||||||
const chapterName = $(elem).text().trim();
|
const chapterName = $(elem).text().trim();
|
||||||
const chapterUrl = $(elem).find('a').attr('href');
|
const chapterUrl = $(elem).find('a').attr('href');
|
||||||
@@ -164,7 +156,7 @@ class WuxialnscantradPlugin implements Plugin.PluginBase {
|
|||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
const $ = await this.getCheerio(this.site + chapterPath);
|
const $ = await this.getCheerio(this.site + chapterPath);
|
||||||
|
|
||||||
var contenuHtml = '';
|
let contenuHtml = '';
|
||||||
$('.entry-content')
|
$('.entry-content')
|
||||||
.contents()
|
.contents()
|
||||||
.each(function () {
|
.each(function () {
|
||||||
@@ -189,12 +181,9 @@ class WuxialnscantradPlugin implements Plugin.PluginBase {
|
|||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
if (pageNo !== 1) return [];
|
if (pageNo !== 1) return [];
|
||||||
|
|
||||||
let popularNovels = this.popularNovels(1, {
|
const popularNovels = this.popularNovels(1);
|
||||||
showLatestNovels: true,
|
|
||||||
filters: undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
let novels = (await popularNovels).filter(novel =>
|
const novels = (await popularNovels).filter(novel =>
|
||||||
novel.name
|
novel.name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize('NFD')
|
.normalize('NFD')
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Cheerio, CheerioAPI, load, Element } from 'cheerio';
|
import { Cheerio, CheerioAPI, load, Element } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
|
|
||||||
@@ -11,7 +10,6 @@ class XiaowazPlugin implements Plugin.PluginBase {
|
|||||||
icon = 'src/fr/xiaowaz/icon.png';
|
icon = 'src/fr/xiaowaz/icon.png';
|
||||||
site = 'https://xiaowaz.fr';
|
site = 'https://xiaowaz.fr';
|
||||||
version = '1.0.1';
|
version = '1.0.1';
|
||||||
filters: Filters | undefined = undefined;
|
|
||||||
static novels: Plugin.NovelItem[] | undefined;
|
static novels: Plugin.NovelItem[] | undefined;
|
||||||
|
|
||||||
async getCheerio(url: string): Promise<CheerioAPI> {
|
async getCheerio(url: string): Promise<CheerioAPI> {
|
||||||
@@ -35,9 +33,9 @@ class XiaowazPlugin implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getAllNovels(): Promise<Plugin.NovelItem[]> {
|
async getAllNovels(): Promise<Plugin.NovelItem[]> {
|
||||||
let novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
let novel: Plugin.NovelItem;
|
let novel: Plugin.NovelItem;
|
||||||
let $ = await this.getCheerio(this.site);
|
const $ = await this.getCheerio(this.site);
|
||||||
|
|
||||||
const categories: Cheerio<Element>[] = [
|
const categories: Cheerio<Element>[] = [
|
||||||
$('li.page_item').find('a:contains("Séries")').parent(),
|
$('li.page_item').find('a:contains("Séries")').parent(),
|
||||||
@@ -78,7 +76,7 @@ class XiaowazPlugin implements Plugin.PluginBase {
|
|||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
async popularNovels(pageNo: number, {}: any): Promise<Plugin.NovelItem[]> {
|
async popularNovels(pageNo: number): Promise<Plugin.NovelItem[]> {
|
||||||
const PAGE_SIZE = 5;
|
const PAGE_SIZE = 5;
|
||||||
|
|
||||||
if (!XiaowazPlugin.novels) XiaowazPlugin.novels = await this.getAllNovels();
|
if (!XiaowazPlugin.novels) XiaowazPlugin.novels = await this.getAllNovels();
|
||||||
@@ -99,7 +97,7 @@ class XiaowazPlugin implements Plugin.PluginBase {
|
|||||||
name: 'Sans titre',
|
name: 'Sans titre',
|
||||||
};
|
};
|
||||||
|
|
||||||
let $ = await this.getCheerio(this.site + novelPath);
|
const $ = await this.getCheerio(this.site + novelPath);
|
||||||
|
|
||||||
novel.name = $('.card_title').text().trim();
|
novel.name = $('.card_title').text().trim();
|
||||||
novel.cover =
|
novel.cover =
|
||||||
@@ -164,12 +162,12 @@ class XiaowazPlugin implements Plugin.PluginBase {
|
|||||||
|
|
||||||
//Search for chapter links within ul/li tags; otherwise, within p/a tags,
|
//Search for chapter links within ul/li tags; otherwise, within p/a tags,
|
||||||
//but not both at the same time because otherwise, on the TDG page, it redirects to a PDF download link.
|
//but not both at the same time because otherwise, on the TDG page, it redirects to a PDF download link.
|
||||||
var pathChapter = $('.entry-content ul li a');
|
let pathChapter = $('.entry-content ul li a');
|
||||||
if (pathChapter.length === 0) {
|
if (pathChapter.length === 0) {
|
||||||
pathChapter = $('.entry-content p a');
|
pathChapter = $('.entry-content p a');
|
||||||
}
|
}
|
||||||
|
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
pathChapter.each((i, elem) => {
|
pathChapter.each((i, elem) => {
|
||||||
const chapterName = $(elem).text().trim();
|
const chapterName = $(elem).text().trim();
|
||||||
const chapterUrl = $(elem).attr('href');
|
const chapterUrl = $(elem).attr('href');
|
||||||
@@ -272,11 +270,11 @@ class XiaowazPlugin implements Plugin.PluginBase {
|
|||||||
if (pageNo !== 1) return [];
|
if (pageNo !== 1) return [];
|
||||||
|
|
||||||
if (!XiaowazPlugin.novels) XiaowazPlugin.novels = await this.getAllNovels();
|
if (!XiaowazPlugin.novels) XiaowazPlugin.novels = await this.getAllNovels();
|
||||||
let popularNovels = XiaowazPlugin.novels;
|
const popularNovels = XiaowazPlugin.novels;
|
||||||
|
|
||||||
// Normalize the text to remove accents and other special characters
|
// Normalize the text to remove accents and other special characters
|
||||||
// This ensures that the search term and novel names are compared accurately
|
// This ensures that the search term and novel names are compared accurately
|
||||||
let novels = popularNovels.filter(novel =>
|
const novels = popularNovels.filter(novel =>
|
||||||
novel.name
|
novel.name
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize('NFD')
|
.normalize('NFD')
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ class IndoWebNovel implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(
|
||||||
page: number = 1,
|
page = 1,
|
||||||
{ filters }: Plugin.PopularNovelsOptions<typeof this.filters>,
|
{ filters }: Plugin.PopularNovelsOptions<typeof this.filters>,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
/*
|
/*
|
||||||
@@ -50,7 +50,7 @@ class IndoWebNovel implements Plugin.PluginBase {
|
|||||||
if (filters.genre.value.length)
|
if (filters.genre.value.length)
|
||||||
link += filters.genre.value.map(i => `&genre[]=${i}`).join('');
|
link += filters.genre.value.map(i => `&genre[]=${i}`).join('');
|
||||||
*/
|
*/
|
||||||
let link = this.site + `page/${page}/?s`;
|
const link = this.site + `page/${page}/?s`;
|
||||||
const result = await fetchApi(link);
|
const result = await fetchApi(link);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ class IndoWebNovel implements Plugin.PluginBase {
|
|||||||
|
|
||||||
async searchNovels(
|
async searchNovels(
|
||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
page: number = 1,
|
page = 1,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
/*
|
/*
|
||||||
let link = `${this.site}advanced-search/page/${page}/?title=${searchTerm}&author=&yearx=`;
|
let link = `${this.site}advanced-search/page/${page}/?title=${searchTerm}&author=&yearx=`;
|
||||||
@@ -123,7 +123,7 @@ class IndoWebNovel implements Plugin.PluginBase {
|
|||||||
link += `&order=${this.filters.sort.value}`;
|
link += `&order=${this.filters.sort.value}`;
|
||||||
link += this.filters.lang.value.map(i => `&country[]=${i}`).join('');
|
link += this.filters.lang.value.map(i => `&country[]=${i}`).join('');
|
||||||
*/
|
*/
|
||||||
let link = this.site + `page/${page}/?s=${searchTerm}`;
|
const link = this.site + `page/${page}/?s=${searchTerm}`;
|
||||||
const result = await fetchApi(link);
|
const result = await fetchApi(link);
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
|
|
||||||
@@ -80,8 +80,8 @@ class NovelRingan implements Plugin.PluginBase {
|
|||||||
loadedCheerio('img.ts-post-image').attr('data-sxrx');
|
loadedCheerio('img.ts-post-image').attr('data-sxrx');
|
||||||
|
|
||||||
loadedCheerio('.maininfo li').each(function () {
|
loadedCheerio('.maininfo li').each(function () {
|
||||||
let detailName = loadedCheerio(this).find('b').text().trim();
|
const detailName = loadedCheerio(this).find('b').text().trim();
|
||||||
let detail = loadedCheerio(this).find('b').remove().end().text().trim();
|
const detail = loadedCheerio(this).find('b').remove().end().text().trim();
|
||||||
|
|
||||||
switch (detailName) {
|
switch (detailName) {
|
||||||
case 'Status:':
|
case 'Status:':
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
import { CheerioAPI, load as parseHTML } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { load, load as loadCheerio } from 'cheerio';
|
import { load as loadCheerio } from 'cheerio';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
@@ -20,7 +20,7 @@ class Syosetu implements Plugin.PluginBase {
|
|||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||||
};
|
};
|
||||||
searchUrl = (pagenum?: number, order?: string) => {
|
searchUrl = (pagenum?: number, order?: string) => {
|
||||||
return `https://yomou.syosetu.com/search.php?order=${order || 'hyoka'}${
|
return `${this.site}search.php?order=${order || 'hyoka'}${
|
||||||
pagenum !== undefined
|
pagenum !== undefined
|
||||||
? `&p=${pagenum <= 1 || pagenum > 100 ? '1' : pagenum}` // check if pagenum is between 1 and 100
|
? `&p=${pagenum <= 1 || pagenum > 100 ? '1' : pagenum}` // check if pagenum is between 1 and 100
|
||||||
: '' // if isn't don't set ?p
|
: '' // if isn't don't set ?p
|
||||||
@@ -71,15 +71,15 @@ class Syosetu implements Plugin.PluginBase {
|
|||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
const result = await fetch(this.novelPrefix + novelPath, {
|
const result = await fetchApi(this.novelPrefix + novelPath, {
|
||||||
headers: this.headers,
|
headers: this.headers,
|
||||||
});
|
});
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
const loadedCheerio = loadCheerio(body, { decodeEntities: false });
|
const loadedCheerio = loadCheerio(body, { decodeEntities: false });
|
||||||
|
|
||||||
// create novel object
|
// create novel object
|
||||||
let novel: Plugin.SourceNovel = {
|
const novel: Plugin.SourceNovel = {
|
||||||
path: novelPath,
|
path: novelPath,
|
||||||
name: loadedCheerio('.novel_title').text(),
|
name: loadedCheerio('.novel_title').text(),
|
||||||
author: loadedCheerio('.novel_writername').text().replace('作者:', ''),
|
author: loadedCheerio('.novel_writername').text().replace('作者:', ''),
|
||||||
@@ -93,7 +93,7 @@ class Syosetu implements Plugin.PluginBase {
|
|||||||
novel.summary = loadedCheerio('#novel_ex')
|
novel.summary = loadedCheerio('#novel_ex')
|
||||||
.text()
|
.text()
|
||||||
.replace(/<\s*br.*?>/g, '\n');
|
.replace(/<\s*br.*?>/g, '\n');
|
||||||
cqGetChapters.each(function (i, e) {
|
cqGetChapters.each(() => {
|
||||||
const chapterA = loadedCheerio(this).find('a');
|
const chapterA = loadedCheerio(this).find('a');
|
||||||
const [chapterName, releaseDate, chapterUrl] = [
|
const [chapterName, releaseDate, chapterUrl] = [
|
||||||
// set the variables
|
// set the variables
|
||||||
@@ -121,9 +121,12 @@ class Syosetu implements Plugin.PluginBase {
|
|||||||
*/
|
*/
|
||||||
// get summary for oneshot chapter
|
// get summary for oneshot chapter
|
||||||
|
|
||||||
const nameResult = await fetch(this.searchUrl() + `&word=${novel.name}`, {
|
const nameResult = await fetchApi(
|
||||||
headers: this.headers,
|
this.searchUrl() + `&word=${novel.name}`,
|
||||||
});
|
{
|
||||||
|
headers: this.headers,
|
||||||
|
},
|
||||||
|
);
|
||||||
const nameBody = await nameResult.text();
|
const nameBody = await nameResult.text();
|
||||||
const summaryQuery = loadCheerio(nameBody, {
|
const summaryQuery = loadCheerio(nameBody, {
|
||||||
decodeEntities: false,
|
decodeEntities: false,
|
||||||
@@ -148,7 +151,7 @@ class Syosetu implements Plugin.PluginBase {
|
|||||||
return novel;
|
return novel;
|
||||||
}
|
}
|
||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
const result = await fetch(this.novelPrefix + chapterPath, {
|
const result = await fetchApi(this.novelPrefix + chapterPath, {
|
||||||
headers: this.headers,
|
headers: this.headers,
|
||||||
});
|
});
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
@@ -158,7 +161,7 @@ class Syosetu implements Plugin.PluginBase {
|
|||||||
decodeEntities: false,
|
decodeEntities: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let chapterText =
|
const chapterText =
|
||||||
cheerioQuery('#novel_honbun') // get chapter text
|
cheerioQuery('#novel_honbun') // get chapter text
|
||||||
.html() || '';
|
.html() || '';
|
||||||
return chapterText;
|
return chapterText;
|
||||||
@@ -173,12 +176,12 @@ class Syosetu implements Plugin.PluginBase {
|
|||||||
const getNovelsFromPage = async (pagenumber: number) => {
|
const getNovelsFromPage = async (pagenumber: number) => {
|
||||||
// load page
|
// load page
|
||||||
const url = this.searchUrl(pagenumber) + `&word=${searchTerm}`;
|
const url = this.searchUrl(pagenumber) + `&word=${searchTerm}`;
|
||||||
const result = await fetch(url, { headers: this.headers });
|
const result = await fetchApi(url, { headers: this.headers });
|
||||||
const body = await result.text();
|
const body = await result.text();
|
||||||
// Cheerio it!
|
// Cheerio it!
|
||||||
const cheerioQuery = loadCheerio(body, { decodeEntities: false });
|
const cheerioQuery = loadCheerio(body, { decodeEntities: false });
|
||||||
|
|
||||||
let pageNovels: Plugin.NovelItem[] = [];
|
const pageNovels: Plugin.NovelItem[] = [];
|
||||||
// find class=searchkekka_box
|
// find class=searchkekka_box
|
||||||
cheerioQuery('.searchkekka_box').each((i, e) => {
|
cheerioQuery('.searchkekka_box').each((i, e) => {
|
||||||
// get div with link and name
|
// get div with link and name
|
||||||
@@ -218,7 +221,7 @@ class Syosetu implements Plugin.PluginBase {
|
|||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveUrl(path: string, isNovel?: boolean | undefined): string {
|
resolveUrl(path: string): string {
|
||||||
return this.novelPrefix + path;
|
return this.novelPrefix + path;
|
||||||
}
|
}
|
||||||
filters = {
|
filters = {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { fetchFile, fetchApi } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
import qs from 'qs';
|
import qs from 'qs';
|
||||||
@@ -7,12 +7,12 @@ class Agitoon implements Plugin.PluginBase {
|
|||||||
id = 'agit.xyz';
|
id = 'agit.xyz';
|
||||||
name = 'Agitoon';
|
name = 'Agitoon';
|
||||||
icon = 'src/kr/agitoon/icon.png';
|
icon = 'src/kr/agitoon/icon.png';
|
||||||
site = 'https://agit659.xyz';
|
site = 'https://agit660.xyz';
|
||||||
version = '3.0.2';
|
version = '3.0.3';
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(
|
||||||
pageNo: number,
|
pageNo: number,
|
||||||
{ filters, showLatestNovels }: Plugin.PopularNovelsOptions,
|
{ showLatestNovels }: Plugin.PopularNovelsOptions,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
const res = await fetchApi(this.site + '/novel/index.update.php', {
|
const res = await fetchApi(this.site + '/novel/index.update.php', {
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
@@ -100,11 +100,11 @@ class NovelkiPL implements Plugin.PluginBase {
|
|||||||
|
|
||||||
//TODO: Dodać tłumacza danej serii :D
|
//TODO: Dodać tłumacza danej serii :D
|
||||||
|
|
||||||
let chapters: Plugin.ChapterItem[] = [];
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
|
|
||||||
let chaptersList = loadedCheerio('.chapters > .col-md-3 > div').get();
|
const chaptersList = loadedCheerio('.chapters > .col-md-3 > div').get();
|
||||||
chaptersList.forEach((e, i) => {
|
chaptersList.forEach((e, i) => {
|
||||||
let urlChapters = loadedCheerio(e).find('a').attr('href') || '';
|
const urlChapters = loadedCheerio(e).find('a').attr('href') || '';
|
||||||
|
|
||||||
const chapter: Plugin.ChapterItem = {
|
const chapter: Plugin.ChapterItem = {
|
||||||
name: loadedCheerio(e).find('a')?.text().trim(),
|
name: loadedCheerio(e).find('a')?.text().trim(),
|
||||||
@@ -125,8 +125,8 @@ class NovelkiPL implements Plugin.PluginBase {
|
|||||||
return novel;
|
return novel;
|
||||||
}
|
}
|
||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
var pattern = /\/projekty\/([^\/]+)\/([^\/]+)/;
|
const pattern = /\/projekty\/([^/]+)\/([^/]+)/;
|
||||||
let codeChapter = pattern.exec(chapterPath) || '';
|
const codeChapter = pattern.exec(chapterPath) || '';
|
||||||
const body = await fetchApi(
|
const body = await fetchApi(
|
||||||
`${this.site}/api/reader/chapters/${codeChapter[2]}`,
|
`${this.site}/api/reader/chapters/${codeChapter[2]}`,
|
||||||
).then(res => {
|
).then(res => {
|
||||||
@@ -151,7 +151,7 @@ class NovelkiPL implements Plugin.PluginBase {
|
|||||||
});
|
});
|
||||||
const loadedCheerio = parseHTML(body);
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
let novels: Plugin.NovelItem[] = loadedCheerio('#projects > div')
|
const novels: Plugin.NovelItem[] = loadedCheerio('#projects > div')
|
||||||
.map((index, element) => ({
|
.map((index, element) => ({
|
||||||
name: loadedCheerio(element)
|
name: loadedCheerio(element)
|
||||||
.find('.card-title')
|
.find('.card-title')
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
|
||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
@@ -129,7 +128,7 @@ class freedlit implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseDate = (dateString: string | undefined = '') => {
|
parseDate = (dateString: string | undefined = '') => {
|
||||||
const months: { [key: string]: number } = {
|
const months: Record<string, number> = {
|
||||||
января: 1,
|
января: 1,
|
||||||
февраля: 2,
|
февраля: 2,
|
||||||
марта: 3,
|
марта: 3,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
import { storage } from '@libs/storage';
|
import { storage } from '@libs/storage';
|
||||||
@@ -12,14 +12,15 @@ class AuthorToday implements Plugin.PluginBase {
|
|||||||
name = 'Автор Тудей';
|
name = 'Автор Тудей';
|
||||||
icon = 'src/ru/authortoday/icon.png';
|
icon = 'src/ru/authortoday/icon.png';
|
||||||
site = 'https://author.today';
|
site = 'https://author.today';
|
||||||
apiUrl = 'https://api.author.today/v1/';
|
apiSite = 'https://api.author.today/v1/';
|
||||||
|
imageSite = 'https://cm.author.today/content/';
|
||||||
version = '1.1.0';
|
version = '1.1.0';
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(
|
||||||
pageNo: number,
|
pageNo: number,
|
||||||
{ showLatestNovels, filters }: Plugin.PopularNovelsOptions,
|
{ showLatestNovels, filters }: Plugin.PopularNovelsOptions,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
let url = this.apiUrl + 'catalog/search?page=' + pageNo;
|
let url = this.apiSite + 'catalog/search?page=' + pageNo;
|
||||||
if (filters?.genre?.value) {
|
if (filters?.genre?.value) {
|
||||||
url += '&genre=' + filters.genre.value;
|
url += '&genre=' + filters.genre.value;
|
||||||
}
|
}
|
||||||
@@ -48,9 +49,7 @@ class AuthorToday implements Plugin.PluginBase {
|
|||||||
result?.searchResults?.forEach(novel =>
|
result?.searchResults?.forEach(novel =>
|
||||||
novels.push({
|
novels.push({
|
||||||
name: novel.title,
|
name: novel.title,
|
||||||
cover: novel.coverUrl
|
cover: novel.coverUrl ? this.imageSite + novel.coverUrl : defaultCover,
|
||||||
? 'https://cm.author.today/content/' + novel.coverUrl
|
|
||||||
: defaultCover,
|
|
||||||
path: novel.id.toString(),
|
path: novel.id.toString(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -61,7 +60,7 @@ class AuthorToday implements Plugin.PluginBase {
|
|||||||
async parseNovel(workID: string): Promise<Plugin.SourceNovel> {
|
async parseNovel(workID: string): Promise<Plugin.SourceNovel> {
|
||||||
if (!this.user) this.user = await this.getUser();
|
if (!this.user) this.user = await this.getUser();
|
||||||
const book: responseBook = await fetchApi(
|
const book: responseBook = await fetchApi(
|
||||||
this.apiUrl + 'work/' + workID + '/details',
|
this.apiSite + 'work/' + workID + '/details',
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'Bearer ' + this.user?.token || 'guest',
|
Authorization: 'Bearer ' + this.user?.token || 'guest',
|
||||||
@@ -93,7 +92,7 @@ class AuthorToday implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const chaptersJSON: ChaptersEntity[] = await fetchApi(
|
const chaptersJSON: ChaptersEntity[] = await fetchApi(
|
||||||
this.apiUrl + 'work/' + workID + '/content',
|
this.apiSite + 'work/' + workID + '/content',
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'Bearer ' + this.user?.token || 'guest',
|
Authorization: 'Bearer ' + this.user?.token || 'guest',
|
||||||
@@ -123,7 +122,7 @@ class AuthorToday implements Plugin.PluginBase {
|
|||||||
const [workID, chapterID] = chapterPath.split('/');
|
const [workID, chapterID] = chapterPath.split('/');
|
||||||
if (!this.user) this.user = await this.getUser();
|
if (!this.user) this.user = await this.getUser();
|
||||||
const result: encryptedСhapter = await fetchApi(
|
const result: encryptedСhapter = await fetchApi(
|
||||||
this.apiUrl + `work/${workID}/chapter/${chapterID}/text`,
|
this.apiSite + `work/${workID}/chapter/${chapterID}/text`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'Bearer ' + this.user?.token || 'guest',
|
Authorization: 'Bearer ' + this.user?.token || 'guest',
|
||||||
@@ -183,7 +182,7 @@ class AuthorToday implements Plugin.PluginBase {
|
|||||||
let user = storage.get('user') || { userId: '', token: 'guest' };
|
let user = storage.get('user') || { userId: '', token: 'guest' };
|
||||||
if (user && user.userId && user.token) {
|
if (user && user.userId && user.token) {
|
||||||
const currentUser: currentUser = await fetchApi(
|
const currentUser: currentUser = await fetchApi(
|
||||||
this.apiUrl + 'account/current-user',
|
this.apiSite + 'account/current-user',
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'Bearer ' + user.token || 'guest',
|
Authorization: 'Bearer ' + user.token || 'guest',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ class Bookriver implements Plugin.PluginBase {
|
|||||||
id = 'bookriver';
|
id = 'bookriver';
|
||||||
name = 'Bookriver';
|
name = 'Bookriver';
|
||||||
site = 'https://bookriver.ru';
|
site = 'https://bookriver.ru';
|
||||||
|
apiSite = 'https://api.bookriver.ru/api/v1/';
|
||||||
version = '1.0.1';
|
version = '1.0.1';
|
||||||
icon = 'src/ru/bookriver/icon.png';
|
icon = 'src/ru/bookriver/icon.png';
|
||||||
|
|
||||||
@@ -99,7 +100,7 @@ class Bookriver implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
const url = 'https://api.bookriver.ru/api/v1/books/chapter/text/';
|
const url = this.apiSite + 'books/chapter/text/';
|
||||||
const { data }: responseChapter = await fetchApi(
|
const { data }: responseChapter = await fetchApi(
|
||||||
url + chapterPath.split('/').pop(),
|
url + chapterPath.split('/').pop(),
|
||||||
).then(res => res.json());
|
).then(res => res.json());
|
||||||
@@ -117,7 +118,8 @@ class Bookriver implements Plugin.PluginBase {
|
|||||||
pageNo: number | undefined = 1,
|
pageNo: number | undefined = 1,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
const url =
|
const url =
|
||||||
'https://api.bookriver.ru/api/v1/search/autocomplete?keyword=' +
|
this.apiSite +
|
||||||
|
'search/autocomplete?keyword=' +
|
||||||
searchTerm +
|
searchTerm +
|
||||||
'&page=' +
|
'&page=' +
|
||||||
pageNo +
|
pageNo +
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
@@ -10,12 +10,13 @@ class ficbook implements Plugin.PluginBase {
|
|||||||
id = 'ficbook';
|
id = 'ficbook';
|
||||||
name = 'ficbook';
|
name = 'ficbook';
|
||||||
site = 'https://ficbook.net';
|
site = 'https://ficbook.net';
|
||||||
|
imageSite = 'https://images.ficbook.net/fanfic-covers/';
|
||||||
version = '1.0.1';
|
version = '1.0.1';
|
||||||
icon = 'src/ru/ficbook/icon.png';
|
icon = 'src/ru/ficbook/icon.png';
|
||||||
|
|
||||||
async popularNovels(
|
async popularNovels(
|
||||||
pageNo: number,
|
pageNo: number,
|
||||||
{ showLatestNovels, filters }: Plugin.PopularNovelsOptions,
|
{ filters }: Plugin.PopularNovelsOptions,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
let url = this.site;
|
let url = this.site;
|
||||||
|
|
||||||
@@ -162,9 +163,7 @@ class ficbook implements Plugin.PluginBase {
|
|||||||
data?.data?.forEach(novel => {
|
data?.data?.forEach(novel => {
|
||||||
const name = novel.title.trim();
|
const name = novel.title.trim();
|
||||||
const path = '/readfic/' + novel.slug;
|
const path = '/readfic/' + novel.slug;
|
||||||
const cover = novel.cover
|
const cover = novel.cover ? this.imageSite + novel.cover : defaultCover;
|
||||||
? 'https://images.ficbook.net/fanfic-covers/' + novel.cover
|
|
||||||
: defaultCover;
|
|
||||||
|
|
||||||
novels.push({ name, cover, path });
|
novels.push({ name, cover, path });
|
||||||
});
|
});
|
||||||
@@ -173,7 +172,7 @@ class ficbook implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseDate = (dateString: string | undefined = '') => {
|
parseDate = (dateString: string | undefined = '') => {
|
||||||
const months: { [key: string]: number } = {
|
const months: Record<string, number> = {
|
||||||
января: 1,
|
января: 1,
|
||||||
февраля: 2,
|
февраля: 2,
|
||||||
марта: 3,
|
марта: 3,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@@ -84,7 +84,7 @@ class Jaomix implements Plugin.PluginBase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
loadedCheerio('#info-book > p').each(function () {
|
loadedCheerio('#info-book > p').each(function () {
|
||||||
let text = loadedCheerio(this).text().replace(/,/g, '').split(' ');
|
const text = loadedCheerio(this).text().replace(/,/g, '').split(' ');
|
||||||
if (text[0] === 'Автор:') {
|
if (text[0] === 'Автор:') {
|
||||||
novel.author = text.splice(1).join(' ');
|
novel.author = text.splice(1).join(' ');
|
||||||
} else if (text[0] === 'Жанры:') {
|
} else if (text[0] === 'Жанры:') {
|
||||||
@@ -168,7 +168,7 @@ class Jaomix implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseDate = (dateString: string | undefined = '') => {
|
parseDate = (dateString: string | undefined = '') => {
|
||||||
const months: { [key: string]: number } = {
|
const months: Record<string, number> = {
|
||||||
Янв: 1,
|
Янв: 1,
|
||||||
Фев: 2,
|
Фев: 2,
|
||||||
Мар: 3,
|
Мар: 3,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
|
|
||||||
const statusKey: { [key: string]: string } = {
|
const statusKey: Record<string, string> = {
|
||||||
'0': NovelStatus.Unknown,
|
'0': NovelStatus.Unknown,
|
||||||
'1': NovelStatus.Ongoing,
|
'1': NovelStatus.Ongoing,
|
||||||
'2': NovelStatus.Completed,
|
'2': NovelStatus.Completed,
|
||||||
@@ -16,6 +16,7 @@ class Neobook implements Plugin.PluginBase {
|
|||||||
id = 'neobook';
|
id = 'neobook';
|
||||||
name = 'Neobook';
|
name = 'Neobook';
|
||||||
site = 'https://neobook.org';
|
site = 'https://neobook.org';
|
||||||
|
apiSite = 'https://api.neobook.org/';
|
||||||
version = '1.0.1';
|
version = '1.0.1';
|
||||||
icon = 'src/ru/neobook/icon.png';
|
icon = 'src/ru/neobook/icon.png';
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ class Neobook implements Plugin.PluginBase {
|
|||||||
formData.append('filter_timeread', filters?.timeread?.value || '0-999999');
|
formData.append('filter_timeread', filters?.timeread?.value || '0-999999');
|
||||||
|
|
||||||
const { bundle_books }: { bundle_books: BundleBooks } = await fetchApi(
|
const { bundle_books }: { bundle_books: BundleBooks } = await fetchApi(
|
||||||
'https://api.neobook.org/',
|
this.apiSite,
|
||||||
{
|
{
|
||||||
method: 'post',
|
method: 'post',
|
||||||
body: formData,
|
body: formData,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
@@ -8,6 +8,7 @@ class novelOvh implements Plugin.PluginBase {
|
|||||||
id = 'novelovh';
|
id = 'novelovh';
|
||||||
name = 'НовелОВХ';
|
name = 'НовелОВХ';
|
||||||
site = 'https://novel.ovh';
|
site = 'https://novel.ovh';
|
||||||
|
apiSite = 'https://api.novel.ovh/v2/';
|
||||||
version = '1.0.2';
|
version = '1.0.2';
|
||||||
icon = 'src/ru/novelovh/icon.png';
|
icon = 'src/ru/novelovh/icon.png';
|
||||||
|
|
||||||
@@ -61,8 +62,10 @@ class novelOvh implements Plugin.PluginBase {
|
|||||||
switch (person.type) {
|
switch (person.type) {
|
||||||
case 'AUTHOR':
|
case 'AUTHOR':
|
||||||
novel.author = person.publisher.name;
|
novel.author = person.publisher.name;
|
||||||
|
break;
|
||||||
case 'ARTIST':
|
case 'ARTIST':
|
||||||
novel.artist = person.publisher.name;
|
novel.artist = person.publisher.name;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -89,7 +92,7 @@ class novelOvh implements Plugin.PluginBase {
|
|||||||
|
|
||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
const book: responseChapter = await fetchApi(
|
const book: responseChapter = await fetchApi(
|
||||||
'https://api.novel.ovh/v2/chapters/' + chapterPath.split('/')[1],
|
this.apiSite + 'chapters/' + chapterPath.split('/')[1],
|
||||||
).then(res => res.json());
|
).then(res => res.json());
|
||||||
|
|
||||||
const image = Object.fromEntries(
|
const image = Object.fromEntries(
|
||||||
@@ -101,7 +104,7 @@ class novelOvh implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
|
||||||
const url = `https://api.novel.ovh/v2/books?type=NOVEL&search=${searchTerm}`;
|
const url = this.apiSite + 'books?type=NOVEL&search=' + searchTerm;
|
||||||
const books: BooksEntity[] = await fetchApi(url).then(res => res.json());
|
const books: BooksEntity[] = await fetchApi(url).then(res => res.json());
|
||||||
|
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
@@ -118,8 +121,8 @@ class novelOvh implements Plugin.PluginBase {
|
|||||||
|
|
||||||
jsonToHtml = (
|
jsonToHtml = (
|
||||||
json: ContentEntity[],
|
json: ContentEntity[],
|
||||||
image: { [key: string]: string },
|
image: Record<string, string>,
|
||||||
html: string = '',
|
html = '',
|
||||||
) => {
|
) => {
|
||||||
json.forEach(element => {
|
json.forEach(element => {
|
||||||
switch (element.type) {
|
switch (element.type) {
|
||||||
@@ -158,8 +161,7 @@ class novelOvh implements Plugin.PluginBase {
|
|||||||
return html;
|
return html;
|
||||||
};
|
};
|
||||||
|
|
||||||
resolveUrl = (path: string, isNovel?: boolean) =>
|
resolveUrl = (path: string) => this.site + '/novel/' + path;
|
||||||
this.site + '/novel/' + path;
|
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
sort: {
|
sort: {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
const statusKey: { [key: string]: string } = {
|
const statusKey: Record<string, string> = {
|
||||||
'active': NovelStatus.Ongoing,
|
'active': NovelStatus.Ongoing,
|
||||||
'completed': NovelStatus.Completed,
|
'completed': NovelStatus.Completed,
|
||||||
'freezed': NovelStatus.OnHiatus,
|
'freezed': NovelStatus.OnHiatus,
|
||||||
@@ -195,7 +195,7 @@ class TL implements Plugin.PluginBase {
|
|||||||
return chapterText;
|
return chapterText;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveUrl = (path: string, isNovel?: boolean) => 'https://' + path;
|
resolveUrl = (path: string) => 'https://' + path;
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
tags: {
|
tags: {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
const statusKey: { [key: number]: string } = {
|
const statusKey: Record<number, string> = {
|
||||||
1: NovelStatus.Ongoing,
|
1: NovelStatus.Ongoing,
|
||||||
2: NovelStatus.Completed,
|
2: NovelStatus.Completed,
|
||||||
3: NovelStatus.OnHiatus,
|
3: NovelStatus.OnHiatus,
|
||||||
@@ -133,7 +133,7 @@ class RNBH implements Plugin.PluginBase {
|
|||||||
.substring(indexA, indexB)
|
.substring(indexA, indexB)
|
||||||
.replace(
|
.replace(
|
||||||
/<img data-media-id="(.*?)".*?>/g,
|
/<img data-media-id="(.*?)".*?>/g,
|
||||||
`<img src="${this.site}/api/media/$1"/>`,
|
`<img src="${this.site}/api/media/$1">`,
|
||||||
);
|
);
|
||||||
|
|
||||||
return chapterText;
|
return chapterText;
|
||||||
@@ -164,8 +164,7 @@ class RNBH implements Plugin.PluginBase {
|
|||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveUrl = (path: string, isNovel?: boolean) =>
|
resolveUrl = (path: string) => this.site + '/ranobe/' + path;
|
||||||
this.site + '/ranobe/' + path;
|
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
sort: {
|
sort: {
|
||||||
@@ -1236,10 +1235,6 @@ interface GenresOrEntity {
|
|||||||
description?: string | null;
|
description?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface responseChapters {
|
|
||||||
marked_chapters_ids?: null[] | null;
|
|
||||||
volumes?: VolumesEntity[] | null;
|
|
||||||
}
|
|
||||||
interface VolumesEntity {
|
interface VolumesEntity {
|
||||||
id: number;
|
id: number;
|
||||||
num: number;
|
num: number;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import { storage, localStorage } from '@libs/storage';
|
import { storage, localStorage } from '@libs/storage';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
const statusKey: { [key: number]: string } = {
|
const statusKey: Record<number, string> = {
|
||||||
1: NovelStatus.Ongoing,
|
1: NovelStatus.Ongoing,
|
||||||
2: NovelStatus.Completed,
|
2: NovelStatus.Completed,
|
||||||
3: NovelStatus.OnHiatus,
|
3: NovelStatus.OnHiatus,
|
||||||
@@ -126,7 +126,7 @@ class RLIB implements Plugin.PluginBase {
|
|||||||
novel.genres = genres.join(', ');
|
novel.genres = genres.join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
const branch_id: { [key: number]: string } = {};
|
const branch_id: Record<number, string> = {};
|
||||||
if (data.teams.length) {
|
if (data.teams.length) {
|
||||||
data.teams.forEach(
|
data.teams.forEach(
|
||||||
({ name, details }) => (branch_id[details?.branch_id || '0'] = name),
|
({ name, details }) => (branch_id[details?.branch_id || '0'] = name),
|
||||||
@@ -477,7 +477,7 @@ class RLIB implements Plugin.PluginBase {
|
|||||||
|
|
||||||
export default new RLIB();
|
export default new RLIB();
|
||||||
|
|
||||||
function jsonToHtml(json: HTML[], html: string = '') {
|
function jsonToHtml(json: HTML[], html = '') {
|
||||||
json.forEach(element => {
|
json.forEach(element => {
|
||||||
switch (element.type) {
|
switch (element.type) {
|
||||||
case 'hardBreak':
|
case 'hardBreak':
|
||||||
@@ -592,12 +592,6 @@ interface User {
|
|||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Rating {
|
|
||||||
average: string;
|
|
||||||
votes: number;
|
|
||||||
votesFormated: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DataClass {
|
interface DataClass {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -717,10 +711,3 @@ interface DataChapter {
|
|||||||
branches_count: number;
|
branches_count: number;
|
||||||
branches: Branch[];
|
branches: Branch[];
|
||||||
}
|
}
|
||||||
interface BranchesEntity {
|
|
||||||
id: number;
|
|
||||||
branch_id?: number;
|
|
||||||
created_at: string;
|
|
||||||
teams?: BranchTeam[];
|
|
||||||
user: User;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ class RNRF implements Plugin.PluginBase {
|
|||||||
|
|
||||||
const jsonRaw = body.match(regex);
|
const jsonRaw = body.match(regex);
|
||||||
if (jsonRaw instanceof Array && jsonRaw[1]) {
|
if (jsonRaw instanceof Array && jsonRaw[1]) {
|
||||||
let chapter: string =
|
const chapter: string =
|
||||||
JSON.parse(jsonRaw[1])?.props?.pageProps?.chapter?.content?.text || '';
|
JSON.parse(jsonRaw[1])?.props?.pageProps?.chapter?.content?.text || '';
|
||||||
|
|
||||||
if (chapter.includes('<img')) {
|
if (chapter.includes('<img')) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
@@ -147,8 +147,7 @@ class ReN implements Plugin.PluginBase {
|
|||||||
return novels;
|
return novels;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveUrl = (path: string, isNovel?: boolean) =>
|
resolveUrl = (path: string) => this.site + '/novel/' + path;
|
||||||
this.site + '/novel/' + path;
|
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
sort: {
|
sort: {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { fetchApi, fetchFile } from '@libs/fetch';
|
import { fetchApi } from '@libs/fetch';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
//import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
class RV implements Plugin.PluginBase {
|
class RV implements Plugin.PluginBase {
|
||||||
id = 'RV';
|
id = 'RV';
|
||||||
@@ -85,7 +85,7 @@ class RV implements Plugin.PluginBase {
|
|||||||
chapters.push({
|
chapters.push({
|
||||||
name: 'Глава ' + chapter.number + ' ' + (chapter.name || ''),
|
name: 'Глава ' + chapter.number + ' ' + (chapter.name || ''),
|
||||||
path: novelPath + '/' + chapter.id,
|
path: novelPath + '/' + chapter.id,
|
||||||
//releaseTime: dayjs(chapter.created_at).format('LLL'),
|
releaseTime: dayjs(chapter.created_at).format('LLL'),
|
||||||
chapterNumber: chapterIndex + 1,
|
chapterNumber: chapterIndex + 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -176,7 +176,7 @@ interface chapters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function unicodeToUtf8(unicode: string) {
|
function unicodeToUtf8(unicode: string) {
|
||||||
return unicode.replace(/\\u([\d\w]{4})/gi, (match, hex) => {
|
return unicode.replace(/\\u([\d\w]{4})/gi, (match, hex) =>
|
||||||
return String.fromCharCode(parseInt(hex, 16));
|
String.fromCharCode(parseInt(hex, 16)),
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
import { Plugin } from '@typings/plugin';
|
||||||
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
|
import { fetchApi } from '@libs/fetch';
|
||||||
|
import { load as parseHTML } from 'cheerio';
|
||||||
|
|
||||||
|
class TopLiba implements Plugin.PluginBase {
|
||||||
|
id = 'TopLiba';
|
||||||
|
name = 'ТопЛиба';
|
||||||
|
site = 'https://topliba.com';
|
||||||
|
version = '1.0.0';
|
||||||
|
icon = 'src/ru/topliba/icon.png';
|
||||||
|
_token = ''; //I think it's permanent?
|
||||||
|
//PgRebp4cULzQKfJBjsEPeECcDQyrVfP5DvcxIxRo
|
||||||
|
|
||||||
|
async fetchNovels(
|
||||||
|
page: number,
|
||||||
|
{
|
||||||
|
showLatestNovels,
|
||||||
|
filters,
|
||||||
|
}: Plugin.PopularNovelsOptions<typeof this.filters>,
|
||||||
|
searchTerm?: string,
|
||||||
|
): Promise<Plugin.NovelItem[]> {
|
||||||
|
const data = new URLSearchParams({
|
||||||
|
order_field: showLatestNovels ? 'date' : filters?.sort?.value || 'rating',
|
||||||
|
p: page,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (searchTerm) data.append('q', searchTerm);
|
||||||
|
|
||||||
|
const body = await fetchApi(this.site + '/?' + data.toString()).then(res =>
|
||||||
|
res.text(),
|
||||||
|
);
|
||||||
|
const novels: Plugin.NovelItem[] = [];
|
||||||
|
|
||||||
|
this._token = body.match(/<meta name="_token" content="(.*?)"/)?.[1];
|
||||||
|
|
||||||
|
const elements = body.match(/<img class="cover" data-original=".*>/g) || [];
|
||||||
|
elements.forEach(element => {
|
||||||
|
const [, path, name] =
|
||||||
|
element.match(/data-original=".*covers\/(.*?)_.*title="(.*?)"/) || [];
|
||||||
|
if (path && name) {
|
||||||
|
novels.push({
|
||||||
|
name,
|
||||||
|
cover: this.site + '/covers/' + path + '.jpg',
|
||||||
|
path,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return novels;
|
||||||
|
}
|
||||||
|
|
||||||
|
popularNovels = this.fetchNovels;
|
||||||
|
|
||||||
|
async searchNovels(
|
||||||
|
searchTerm: string,
|
||||||
|
page: number,
|
||||||
|
): Promise<Plugin.NovelItem[]> {
|
||||||
|
const defaultOptions: any = {
|
||||||
|
showLatestNovels: false,
|
||||||
|
filters: {},
|
||||||
|
};
|
||||||
|
return this.fetchNovels(page, defaultOptions, searchTerm);
|
||||||
|
}
|
||||||
|
|
||||||
|
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
|
||||||
|
const body = await fetchApi(this.resolveUrl(novelPath, true)).then(res =>
|
||||||
|
res.text(),
|
||||||
|
);
|
||||||
|
const loadedCheerio = parseHTML(body);
|
||||||
|
|
||||||
|
const novel: Plugin.SourceNovel = {
|
||||||
|
path: novelPath,
|
||||||
|
name: loadedCheerio('div > h1').text().trim(),
|
||||||
|
cover: this.site + '/covers/' + novelPath + '.jpg',
|
||||||
|
summary: loadedCheerio('.description').text().trim(),
|
||||||
|
author: loadedCheerio('.book-author > a').text().trim(),
|
||||||
|
genres: loadedCheerio('.book-genres > div > a')
|
||||||
|
.map((index, element) => loadedCheerio(element).text())
|
||||||
|
.get()
|
||||||
|
.join(','),
|
||||||
|
};
|
||||||
|
|
||||||
|
const chaptersHTML = await fetchApi(this.resolveUrl(novelPath)).then(res =>
|
||||||
|
res.text(),
|
||||||
|
);
|
||||||
|
|
||||||
|
this._token =
|
||||||
|
chaptersHTML.match(/<meta name="_token" content="(.*?)"/)?.[1] ||
|
||||||
|
body.match(/<meta name="_token" content="(.*?)"/)?.[1] ||
|
||||||
|
this._token;
|
||||||
|
|
||||||
|
const chapters: Plugin.ChapterItem[] = [];
|
||||||
|
const elements =
|
||||||
|
chaptersHTML.match(
|
||||||
|
/<li class="padding-\d+" data-capter="\d+">([\s\S]*?)</g,
|
||||||
|
) || [];
|
||||||
|
elements.forEach((chapter, chapterIndex) => {
|
||||||
|
const [, padding, capter, name] =
|
||||||
|
chapter.match(
|
||||||
|
/class="padding-(\d+)" data-capter="(\d+)">([\s\S]*?)</,
|
||||||
|
) || [];
|
||||||
|
|
||||||
|
if (padding && capter && name) {
|
||||||
|
const id =
|
||||||
|
padding === '0' ? capter : padding + '-' + (parseInt(capter, 10) - 1);
|
||||||
|
|
||||||
|
chapters.push({
|
||||||
|
name: name.trim(),
|
||||||
|
path: novelPath + '?' + id,
|
||||||
|
releaseTime: null,
|
||||||
|
chapterNumber: chapterIndex + 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
novel.chapters = chapters;
|
||||||
|
return novel;
|
||||||
|
}
|
||||||
|
|
||||||
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
|
const [bookID, chapterID] = chapterPath.split('?');
|
||||||
|
if (!this._token) {
|
||||||
|
const chaptersHTML = await fetchApi(this.resolveUrl(bookID)).then(res =>
|
||||||
|
res.text(),
|
||||||
|
);
|
||||||
|
this._token = chaptersHTML.match(
|
||||||
|
/<meta name="_token" content="(.*?)"/,
|
||||||
|
)?.[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
const chapterText = await fetchApi(this.resolveUrl(bookID) + '/chapter', {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||||
|
Referer: this.resolveUrl(bookID),
|
||||||
|
Origin: this.site,
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
body: new URLSearchParams({
|
||||||
|
chapter: chapterID,
|
||||||
|
_token: this._token,
|
||||||
|
}).toString(),
|
||||||
|
}).then(res => res.text());
|
||||||
|
|
||||||
|
return chapterText;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveUrl = (path: string, isNovel?: boolean) =>
|
||||||
|
this.site + (isNovel ? '/books/' + path : '/reader/' + path.split('?')[0]);
|
||||||
|
|
||||||
|
filters = {
|
||||||
|
sort: {
|
||||||
|
label: 'Сортировка:',
|
||||||
|
value: 'rating',
|
||||||
|
options: [
|
||||||
|
{ label: 'По рейтингу', value: 'rating' },
|
||||||
|
{ label: 'По популярности', value: 'num_downloads' },
|
||||||
|
{ label: 'По году выхода', value: 'year' },
|
||||||
|
{ label: 'По дате добавления', value: 'date' },
|
||||||
|
{ label: 'По названию', value: 'title' },
|
||||||
|
],
|
||||||
|
type: FilterTypes.Picker,
|
||||||
|
},
|
||||||
|
} satisfies Filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new TopLiba();
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user