feat: Add customJs Option to ReadNovelFull multiSrc (#2132)
This commit is contained in:
@@ -27,7 +27,7 @@ const generator = function generator(source) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const pluginScript = `
|
const pluginScript = `
|
||||||
${readNovelFullTemplate}
|
${readNovelFullTemplate.replace('// CustomJS HERE', source.options?.customJs || '')}
|
||||||
const plugin = new ReadNovelFullPlugin(${JSON.stringify(source)});
|
const plugin = new ReadNovelFullPlugin(${JSON.stringify(source)});
|
||||||
export default plugin;
|
export default plugin;
|
||||||
`.trim();
|
`.trim();
|
||||||
|
|||||||
@@ -66,20 +66,6 @@
|
|||||||
"pageAsPath": true
|
"pageAsPath": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "FWN.com",
|
|
||||||
"sourceSite": "https://freewebnovel.com/",
|
|
||||||
"sourceName": "Free Web Novel",
|
|
||||||
"options": {
|
|
||||||
"latestPage": "sort/latest-novels",
|
|
||||||
"searchPage": "search",
|
|
||||||
"searchKey": "searchkey",
|
|
||||||
"postSearch": true,
|
|
||||||
"noAjax": true,
|
|
||||||
"noPages": ["sort/most-popular"],
|
|
||||||
"pageAsPath": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "anf.net",
|
"id": "anf.net",
|
||||||
"sourceSite": "https://novgo.net/",
|
"sourceSite": "https://novgo.net/",
|
||||||
@@ -98,5 +84,20 @@
|
|||||||
"latestPage": "sort/latest",
|
"latestPage": "sort/latest",
|
||||||
"searchPage": "search"
|
"searchPage": "search"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FWN.com",
|
||||||
|
"sourceSite": "https://freewebnovel.com/",
|
||||||
|
"sourceName": "Free Web Novel",
|
||||||
|
"options": {
|
||||||
|
"latestPage": "sort/latest-novels",
|
||||||
|
"searchPage": "search",
|
||||||
|
"searchKey": "searchkey",
|
||||||
|
"postSearch": true,
|
||||||
|
"noAjax": true,
|
||||||
|
"noPages": ["sort/most-popular"],
|
||||||
|
"pageAsPath": true,
|
||||||
|
"customJs": "$('.txt, #chr-content, #chapter-content').find('*').addBack().contents().filter((_, el) => el.type === 'text').each((_, el) => { el.data = el.data.replace(/(?:𝐟|ᵮ|𝑓|𝒇|𝒻|𝓯|𝔣|𝕗|𝖿|𝗳|𝙛|𝚏|ꬵ|ꞙ|ẝ|𝖋|ⓕ|f|ƒ|ḟ|ʃ|բ|ᶠ|⒡|ſ|ꊰ|ʄ|∱|ᶂ|𝘧|\\bf)(?:𝚛|ꭇ|ᣴ|ℾ|𝚪|𝛤|𝜞|𝝘|𝞒|Ⲅ|Г|Ꮁ|ᒥ|ꭈ|ⲅ|ꮁ|ⓡ|r|ŕ|ṙ|ř|ȑ|ȓ|ṛ|ṝ|ŗ|г|Ր|ɾ|ᥬ|ṟ|ɍ|ʳ|⒭|ɼ|ѓ|ᴦ|ᶉ|𝐫|𝑟|𝒓|𝓇|𝓻|𝔯|𝕣|𝖗|𝗋|𝗿|𝘳|𝙧|ᵲ|ґ|ᵣ|r)(?:ə|ә|ⅇ|ꬲ|ꞓ|⋴|𝛆|𝛜|𝜀|𝜖|𝜺|𝝐|𝝴|𝞊|𝞮|𝟄|ⲉ|ꮛ|𐐩|Ꞓ|Ⲉ|⍷|𝑒|𝓮|𝕖|𝖊|𝘦|𝗲|𝚎|𝙚|𝒆|𝔢|𝖾|𝐞|Ҿ|ҿ|ⓔ|e|⒠|è|ᧉ|é|ᶒ|ê|ɘ|ἔ|ề|ế|ễ|૯|ǝ|є|ε|ē|ҽ|ɛ|ể|ẽ|ḕ|ḗ|ĕ|ė|ë|ẻ|ě|ȅ|ȇ|ẹ|ệ|ȩ|ɇ|ₑ|ę|ḝ|ḙ|ḛ|℮|е|ԑ|ѐ|ӗ|ᥱ|ё|ἐ|ἑ|ἒ|ἓ|ἕ|ℯ|e)+(?:𝐰|ꝡ|𝑤|𝒘|𝓌|𝔀|𝔴|𝕨|𝖜|𝗐|𝘄|𝘸|𝙬|𝚠|ա|ẁ|ꮃ|ẃ|ⓦ|⍵|ŵ|ẇ|ẅ|ẘ|ẉ|ⱳ|ὼ|ὠ|ὡ|ὢ|ὣ|ω|ὤ|ὥ|ὦ|ὧ|ῲ|ῳ|ῴ|ῶ|ῷ|Ⱳ|ѡ|ԝ|ᴡ|ώ|ᾠ|ᾡ|ᾢ|ᾣ|ᾤ|ᾥ|ᾦ|ɯ|𝝕|𝟉|𝞏|w)(?:ə|ә|ⅇ|ꬲ|ꞓ|⋴|𝛆|𝛜|𝜀|𝜖|𝜺|𝝐|𝝴|𝞊|𝞮|𝟄|ⲉ|ꮛ|𐐩|Ꞓ|Ⲉ|⍷|𝑒|𝓮|𝕖|𝖊|𝘦|𝗲|𝚎|𝙚|𝒆|𝔢|𝖾|𝐞|Ҿ|ҿ|ⓔ|e|⒠|è|ᧉ|é|ᶒ|ê|ɘ|ἔ|ề|ế|ễ|૯|ǝ|є|ε|ē|ҽ|ɛ|ể|ẽ|ḕ|ḗ|ĕ|ė|ë|ẻ|ě|ȅ|ȇ|ẹ|ệ|ȩ|ɇ|ₑ|ę|ḝ|ḙ|ḛ|℮|е|ԑ|ѐ|ӗ|ᥱ|ё|ἐ|ἑ|ἒ|ἓ|ἕ|ℯ|e)(?:ꮟ|Ꮟ|𝐛|𝘣|𝒷|𝔟|𝓫|𝖇|𝖻|𝑏|𝙗|𝕓|𝒃|𝗯|𝚋|♭|ᑳ|ᒈ|b|ᖚ|ᕹ|ᕺ|ⓑ|ḃ|ḅ|ҍ|ъ|ḇ|ƃ|ɓ|ƅ|ᖯ|Ƅ|Ь|ᑲ|þ|Ƃ|⒝|Ъ|ᶀ|ᑿ|ᒀ|ᒂ|ᒁ|ᑾ|ь|ƀ|Ҍ|Ѣ|ѣ|ᔎ |b)(?:ո|ռ|ח|𝒏|𝓷|𝙣|𝑛|𝖓|𝔫|𝗇|𝚗|𝗻|ᥒ|ⓝ|ή|n|ǹ|ᴒ|ń|ñ|ᾗ|η|ṅ|ň|ṇ|ɲ|ņ|ṋ|ṉ|ղ|ຖ|Ռ|ƞ|ŋ|⒩|ภ|ก|ɳ|п|ʼn|л|ԉ|Ƞ|ἠ|ἡ|ῃ|դ|ᾐ|ᾑ|ᾒ|ᾓ|ᾔ|ᾕ|ᾖ|ῄ|ῆ|ῇ|ῂ|ἢ|ἣ|ἤ|ἥ|ἦ|ἧ|ὴ|ή|በ|ቡ|ቢ|ባ|ቤ|ብ|ቦ|ȵ|𝛈|𝜂|𝜼|𝝶|𝞰|𝕟|𝘯|𝐧|𝓃|ᶇ|ᵰ|ᥥ|∩|n)(?:ం|ಂ|ം|ං|૦|௦|۵|ℴ|𝑜|𝒐|𝖔|ꬽ|𝝄|𝛔|𝜎|𝝈|𝞂|ჿ|𝚘|০|୦|ዐ|𝛐|𝗈|𝞼|ဝ|ⲟ|𝙤|၀|𐐬|𝔬|𐓪|𝓸|🇴|⍤|○|ϙ|🅾|𝒪|𝖮|𝟢|𝟶|𝙾|𝘰|𝗼|𝕠|𝜊|𝐨|𝝾|𝞸|ᐤ|ⓞ|ѳ|᧐|ᥲ|ð|o|ఠ|ᦞ|Փ|ò|ө|ӧ|ó|º|ō|ô|ǒ|ȏ|ŏ|ồ|ȭ|ṏ|ὄ|ṑ|ṓ|ȯ|ȫ|๏|ᴏ|ő|ö|ѻ|о|ዐ|ǭ|ȱ|০|୦|٥|౦|೦|൦|๐|໐|ο|օ|ᴑ|०|੦|ỏ|ơ|ờ|ớ|ỡ|ở|ợ|ọ|ộ|ǫ|ø|ǿ|ɵ|ծ|ὀ|ὁ|ό|ὸ|ό|ὂ|ὃ|ὅ|o)(?:∨|⌄|⋁|ⅴ|𝐯|𝑣|𝒗|𝓋|𝔳|𝕧|𝖛|𝗏|ꮩ|ሀ|ⓥ|v|𝜐|𝝊|ṽ|ṿ|౮|ง|ѵ|ע|ᴠ|ν|ט|ᵥ|ѷ|៴|ᘁ|𝙫|𝚟|𝛎|𝜈|𝝂|𝝼|𝞶|𝘷|𝘃|𝓿|v)(?:ə|ә|ⅇ|ꬲ|ꞓ|⋴|𝛆|𝛜|𝜀|𝜖|𝜺|𝝐|𝝴|𝞊|𝞮|𝟄|ⲉ|ꮛ|𐐩|Ꞓ|Ⲉ|⍷|𝑒|𝓮|𝕖|𝖊|𝘦|𝗲|𝚎|𝙚|𝒆|𝔢|𝖾|𝐞|Ҿ|ҿ|ⓔ|e|⒠|è|ᧉ|é|ᶒ|ê|ɘ|ἔ|ề|ế|ễ|૯|ǝ|є|ε|ē|ҽ|ɛ|ể|ẽ|ḕ|ḗ|ĕ|ė|ë|ẻ|ě|ȅ|ȇ|ẹ|ệ|ȩ|ɇ|ₑ|ę|ḝ|ḙ|ḛ|℮|е|ԑ|ѐ|ӗ|ᥱ|ё|ἐ|ἑ|ἒ|ἓ|ἕ|ℯ|e)(?:ⓛ|l|ŀ|ĺ|ľ|ḷ|ḹ|ļ|Ӏ|ℓ|ḽ|ḻ|ł|レ|ɭ|ƚ|ɫ|ⱡ|\\||Ɩ|⒧|ʅ|ǀ|ו|ן|Ι|І|||ᶩ|ӏ|𝓘|𝕀|𝖨|𝗜|𝘐|𝐥|𝑙|𝒍|𝓁|𝔩|𝕝|𝖑|𝗅|𝗹|𝘭|𝚕|𝜤|𝝞|ı|𝚤|ɩ|ι|𝛊|𝜄|𝜾|𝞲|I|l)(?:.?(?:🝌|c|ⅽ|𝐜|𝑐|𝒄|𝒸|𝓬|𝔠|𝕔|𝖈|𝖼|𝗰|𝘤|𝙘|𝚌|ᴄ|ϲ|ⲥ|с|ꮯ|𐐽|ⲥ|𐐽|ꮯ|ĉ|c|ⓒ|ć|č|ċ|ç|ҁ|ƈ|ḉ|ȼ|ↄ|с|ር|ᴄ|ϲ|ҫ|꒝|ς|ɽ|ϛ|𝙲|ᑦ|᧚|𝐜|𝑐|𝒄|𝒸|𝓬|𝔠|𝕔|𝖈|𝖼|𝗰|𝘤|𝙘|𝚌|₵|🇨|ᥴ|ᒼ|ⅽ|c)(?:ం|ಂ|ം|ං|૦|௦|۵|ℴ|𝑜|𝒐|𝖔|ꬽ|𝝄|𝛔|𝜎|𝝈|𝞂|ჿ|𝚘|০|୦|ዐ|𝛐|𝗈|𝞼|ဝ|ⲟ|𝙤|၀|𐐬|𝔬|𐓪|𝓸|🇴|⍤|○|ϙ|🅾|𝒪|𝖮|𝟢|𝟶|𝙾|𝘰|𝗼|𝕠|𝜊|𝐨|𝝾|𝞸|ᐤ|ⓞ|ѳ|᧐|ᥲ|ð|o|ఠ|ᦞ|Փ|ò|ө|ӧ|ó|º|ō|ô|ǒ|ȏ|ŏ|ồ|ȭ|ṏ|ὄ|ṑ|ṓ|ȯ|ȫ|๏|ᴏ|ő|ö|ѻ|о|ዐ|ǭ|ȱ|০|୦|٥|౦|೦|൦|๐|໐|ο|օ|ᴑ|०|੦|ỏ|ơ|ờ|ớ|ỡ|ở|ợ|ọ|ộ|ǫ|ø|ǿ|ɵ|ծ|ὀ|ὁ|ό|ὸ|ό|ὂ|ὃ|ὅ|o)(?:₥|ᵯ|𝖒|𝐦|𝗆|𝔪|𝕞|𝓂|ⓜ|m|ന|ᙢ|൩|ḿ|ṁ|ⅿ|ϻ|ṃ|ጠ|ɱ|៳|ᶆ|𝒎|𝙢|𝓶|𝚖|𝑚|𝗺|᧕|᧗|m))?/g, ''); });"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { fetchApi } from '@libs/fetch';
|
|||||||
import { Plugin } from '@/types/plugin';
|
import { Plugin } from '@/types/plugin';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
import { Filters } from '@libs/filterInputs';
|
import { Filters } from '@libs/filterInputs';
|
||||||
|
import { load } from 'cheerio';
|
||||||
|
|
||||||
type ReadNovelFullOptions = {
|
type ReadNovelFullOptions = {
|
||||||
lang?: string;
|
lang?: string;
|
||||||
@@ -23,6 +24,7 @@ type ReadNovelFullOptions = {
|
|||||||
noAjax?: boolean;
|
noAjax?: boolean;
|
||||||
noPages?: string[];
|
noPages?: string[];
|
||||||
pageAsPath?: boolean;
|
pageAsPath?: boolean;
|
||||||
|
customJs?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ReadNovelFullMetadata = {
|
export type ReadNovelFullMetadata = {
|
||||||
@@ -30,7 +32,7 @@ export type ReadNovelFullMetadata = {
|
|||||||
sourceSite: string;
|
sourceSite: string;
|
||||||
sourceName: string;
|
sourceName: string;
|
||||||
options: ReadNovelFullOptions;
|
options: ReadNovelFullOptions;
|
||||||
filters?: any;
|
filters?: Filters;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ReadNovelFullPlugin implements Plugin.PluginBase {
|
class ReadNovelFullPlugin implements Plugin.PluginBase {
|
||||||
@@ -48,7 +50,7 @@ class ReadNovelFullPlugin implements Plugin.PluginBase {
|
|||||||
this.icon = `multisrc/readnovelfull/${metadata.id.toLowerCase()}/icon.png`;
|
this.icon = `multisrc/readnovelfull/${metadata.id.toLowerCase()}/icon.png`;
|
||||||
this.site = metadata.sourceSite;
|
this.site = metadata.sourceSite;
|
||||||
const versionIncrements = metadata.options?.versionIncrements || 0;
|
const versionIncrements = metadata.options?.versionIncrements || 0;
|
||||||
this.version = `2.1.${2 + versionIncrements}`;
|
this.version = `2.2.${0 + versionIncrements}`;
|
||||||
this.options = metadata.options;
|
this.options = metadata.options;
|
||||||
this.filters = metadata.filters;
|
this.filters = metadata.filters;
|
||||||
}
|
}
|
||||||
@@ -90,9 +92,11 @@ class ReadNovelFullPlugin implements Plugin.PluginBase {
|
|||||||
|
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'img':
|
case 'img':
|
||||||
const cover = attribs['data-src'] || attribs.src;
|
{
|
||||||
if (cover) {
|
const cover = attribs['data-src'] || attribs.src;
|
||||||
tempNovel.cover = new URL(cover, this.site).href;
|
if (cover) {
|
||||||
|
tempNovel.cover = new URL(cover, this.site).href;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'h3':
|
case 'h3':
|
||||||
@@ -431,15 +435,17 @@ class ReadNovelFullPlugin implements Plugin.PluginBase {
|
|||||||
novel.genres = detail;
|
novel.genres = detail;
|
||||||
break;
|
break;
|
||||||
case 'status':
|
case 'status':
|
||||||
const map: Record<string, string> = {
|
{
|
||||||
ongoing: NovelStatus.Ongoing,
|
const map: Record<string, string> = {
|
||||||
hiatus: NovelStatus.OnHiatus,
|
ongoing: NovelStatus.Ongoing,
|
||||||
dropped: NovelStatus.Cancelled,
|
hiatus: NovelStatus.OnHiatus,
|
||||||
cancelled: NovelStatus.Cancelled,
|
dropped: NovelStatus.Cancelled,
|
||||||
completed: NovelStatus.Completed,
|
cancelled: NovelStatus.Cancelled,
|
||||||
};
|
completed: NovelStatus.Completed,
|
||||||
novel.status =
|
};
|
||||||
map[detail.toLowerCase()] ?? NovelStatus.Unknown;
|
novel.status =
|
||||||
|
map[detail.toLowerCase()] ?? NovelStatus.Unknown;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
@@ -545,7 +551,17 @@ class ReadNovelFullPlugin implements Plugin.PluginBase {
|
|||||||
|
|
||||||
async parseChapter(chapterPath: string): Promise<string> {
|
async parseChapter(chapterPath: string): Promise<string> {
|
||||||
const response = await fetchApi(this.site + chapterPath);
|
const response = await fetchApi(this.site + chapterPath);
|
||||||
const html = await response.text();
|
let html = await response.text();
|
||||||
|
if (this.options?.customJs) {
|
||||||
|
try {
|
||||||
|
const $ = load(html);
|
||||||
|
// CustomJS HERE
|
||||||
|
html = $.html();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error executing customJs:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let depth: number;
|
let depth: number;
|
||||||
let depthHide: number;
|
let depthHide: number;
|
||||||
@@ -559,9 +575,7 @@ class ReadNovelFullPlugin implements Plugin.PluginBase {
|
|||||||
const popState = () =>
|
const popState = () =>
|
||||||
stateStack.length > 1 ? stateStack.pop() : currentState();
|
stateStack.length > 1 ? stateStack.pop() : currentState();
|
||||||
|
|
||||||
type EscapeChar = '&' | '<' | '>' | '"' | "'" | ' ';
|
const escapeMap: Record<string, string> = {
|
||||||
const escapeRegex = /[&<>"' ]/g;
|
|
||||||
const escapeMap: Record<EscapeChar, string> = {
|
|
||||||
'&': '&',
|
'&': '&',
|
||||||
'<': '<',
|
'<': '<',
|
||||||
'>': '>',
|
'>': '>',
|
||||||
@@ -570,7 +584,7 @@ class ReadNovelFullPlugin implements Plugin.PluginBase {
|
|||||||
' ': ' ',
|
' ': ' ',
|
||||||
};
|
};
|
||||||
const escapeHtml = (text: string): string =>
|
const escapeHtml = (text: string): string =>
|
||||||
text.replace(escapeRegex, char => escapeMap[char as EscapeChar]);
|
text.replace(/[&<>"' ]/g, char => escapeMap[char]);
|
||||||
|
|
||||||
const parser = new Parser({
|
const parser = new Parser({
|
||||||
onopentag(name, attribs) {
|
onopentag(name, attribs) {
|
||||||
|
|||||||
Reference in New Issue
Block a user