diff --git a/src/plugins/english/genesis.ts b/src/plugins/english/genesis.ts
index f69094e68..0c7ae9854 100644
--- a/src/plugins/english/genesis.ts
+++ b/src/plugins/english/genesis.ts
@@ -18,7 +18,7 @@ class Genesis implements Plugin.PluginBase {
icon = 'src/en/genesis/icon.png';
customCSS = 'src/en/genesis/customCSS.css';
site = 'https://genesistudio.com';
- version = '1.1.1';
+ version = '1.1.2';
imageRequestInit?: Plugin.ImageRequestInit | undefined = {
headers: {
@@ -421,6 +421,7 @@ class Genesis implements Plugin.PluginBase {
// Extract the main novel data from the nodes
const data = this.extractData(nodes);
+ // Look for chapter container with required fields
const contentKey = 'content';
const notesKey = 'notes';
const footnotesKey = 'footnotes';
@@ -429,7 +430,7 @@ class Genesis implements Plugin.PluginBase {
for (const key in data) {
const mapping = data[key];
- // Look for an object with a 'chapters' key
+ // Check container for keys that match the required fields
if (
mapping &&
typeof mapping === 'object' &&
@@ -437,21 +438,16 @@ class Genesis implements Plugin.PluginBase {
notesKey in mapping &&
footnotesKey in mapping
) {
- // Use the found mapping object to determine the actual keys.
- const contentMappingKey = mapping[contentKey];
- const notesMappingKey = mapping[notesKey];
- const footnotesMappingKey = mapping[footnotesKey];
-
// Retrieve the chapter's content, notes, and footnotes using the mapping.
- // Provide a fallback for content if needed.
- const content = data[contentMappingKey] ?? data[19];
- const notes = data[notesMappingKey];
- const footnotes = data[footnotesMappingKey];
+ const content = data[mapping[contentKey]];
+ const notes = data[mapping[notesKey]];
+ const footnotes = data[mapping[footnotesKey]];
+ // Return the combined parts with appropriate formatting
return (
content +
- (notes ? `Notes
${notes}` : '') +
- (footnotes ? `Footnotes
${footnotes}` : '')
+ (notes ? `
Notes
${notes}` : '') +
+ (footnotes ?? '')
);
}
}
diff --git a/src/plugins/english/novelupdates.ts b/src/plugins/english/novelupdates.ts
index 9a944d28e..e6ecd07da 100644
--- a/src/plugins/english/novelupdates.ts
+++ b/src/plugins/english/novelupdates.ts
@@ -6,32 +6,22 @@ import { Plugin } from '@typings/plugin';
class NovelUpdates implements Plugin.PluginBase {
id = 'novelupdates';
name = 'Novel Updates';
- version = '0.8.0';
+ version = '0.9.0';
icon = 'src/en/novelupdates/icon.png';
customCSS = 'src/en/novelupdates/customCSS.css';
site = 'https://www.novelupdates.com/';
parseNovels(loadedCheerio: CheerioAPI) {
const novels: Plugin.NovelItem[] = [];
-
- loadedCheerio('div.search_main_box_nu').each((idx, ele) => {
- const novelCover = loadedCheerio(ele).find('img').attr('src');
- const novelName = loadedCheerio(ele).find('.search_title > a').text();
- const novelUrl = loadedCheerio(ele)
- .find('.search_title > a')
- .attr('href');
-
+ loadedCheerio('div.search_main_box_nu').each((_, el) => {
+ const novelUrl = loadedCheerio(el).find('.search_title > a').attr('href');
if (!novelUrl) return;
-
- const novel = {
- name: novelName,
- cover: novelCover,
+ novels.push({
+ name: loadedCheerio(el).find('.search_title > a').text(),
+ cover: loadedCheerio(el).find('img').attr('src'),
path: novelUrl.replace(this.site, ''),
- };
-
- novels.push(novel);
+ });
});
-
return novels;
}
@@ -42,70 +32,49 @@ class NovelUpdates implements Plugin.PluginBase {
filters,
}: Plugin.PopularNovelsOptions,
): Promise {
- let link = `${this.site}`;
+ let url = this.site;
+
+ // Build the URL based on filters
if (showLatestNovels) {
- link += 'series-finder/?sf=1&sort=sdate&order=desc';
+ url += 'series-finder/?sf=1&sort=sdate&order=desc';
} else if (
filters?.sort.value === 'popmonth' ||
filters?.sort.value === 'popular'
) {
- link += 'series-ranking/?rank=' + filters.sort.value;
- } else if (
- filters?.sort.value !== 'popmonth' &&
- filters?.sort.value !== 'popular'
- ) {
- link += 'series-finder/?sf=1';
-
+ url += 'series-ranking/?rank=' + filters.sort.value;
+ } else {
+ url += 'series-finder/?sf=1';
if (
filters?.genres.value.include?.length ||
filters?.genres.value.exclude?.length
) {
- link += '&mgi=' + filters.genre_operator.value;
+ url += '&mgi=' + filters.genre_operator.value;
}
-
if (filters?.novelType.value.length) {
- link += '&nt=' + filters.novelType.value.join(',');
+ url += '&nt=' + filters.novelType.value.join(',');
}
-
if (filters?.reading_lists.value.length) {
- link += '&hd=' + filters?.reading_lists.value.join(',');
- link += '&mRLi=' + filters?.reading_list_operator.value;
+ url += '&hd=' + filters?.reading_lists.value.join(',');
+ url += '&mRLi=' + filters?.reading_list_operator.value;
}
-
- link += '&sort=' + filters?.sort.value;
-
- link += '&order=' + filters?.order.value;
+ url += '&sort=' + filters?.sort.value;
+ url += '&order=' + filters?.order.value;
}
- if (
- (!showLatestNovels && filters?.language.value.length) ||
- filters?.genres.value.include?.length ||
- filters?.genres.value.exclude?.length ||
- filters?.storyStatus.value !== ''
- ) {
- if (filters?.language.value.length) {
- link += '&org=' + filters.language.value.join(',');
- }
- if (filters?.genres.value.include?.length) {
- link += '&gi=' + filters.genres.value.include.join(',');
- }
+ // Add common filters
+ if (filters?.language.value.length)
+ url += '&org=' + filters.language.value.join(',');
+ if (filters?.genres.value.include?.length)
+ url += '&gi=' + filters.genres.value.include.join(',');
+ if (filters?.genres.value.exclude?.length)
+ url += '&ge=' + filters.genres.value.exclude.join(',');
+ if (filters?.storyStatus.value) url += '&ss=' + filters.storyStatus.value;
- if (filters?.genres.value.exclude?.length) {
- link += '&ge=' + filters.genres.value.exclude.join(',');
- }
+ url += '&pg=' + page;
- if (filters?.storyStatus.value.length) {
- link += '&ss=' + filters.storyStatus.value;
- }
- }
-
- link += '&pg=' + page;
-
- const result = await fetchApi(link);
+ const result = await fetchApi(url);
const body = await result.text();
-
const loadedCheerio = parseHTML(body);
-
return this.parseNovels(loadedCheerio);
}
@@ -113,8 +82,7 @@ class NovelUpdates implements Plugin.PluginBase {
const url = this.site + novelPath;
const result = await fetchApi(url);
const body = await result.text();
-
- let loadedCheerio = parseHTML(body);
+ const loadedCheerio = parseHTML(body);
const novel: Plugin.SourceNovel = {
path: novelPath,
@@ -122,78 +90,64 @@ class NovelUpdates implements Plugin.PluginBase {
cover: loadedCheerio('.wpb_wrapper img').attr('src'),
chapters: [],
};
-
novel.author = loadedCheerio('#authtag')
- .map((i, el) => loadedCheerio(el).text().trim())
+ .map((_, el) => loadedCheerio(el).text().trim())
.toArray()
.join(', ');
-
novel.genres = loadedCheerio('#seriesgenre')
.children('a')
- .map((i, el) => loadedCheerio(el).text())
+ .map((_, el) => loadedCheerio(el).text())
.toArray()
.join(',');
-
novel.status = loadedCheerio('#editstatus').text().includes('Ongoing')
? 'Ongoing'
: 'Completed';
const type = loadedCheerio('#showtype').text().trim();
-
const summary = loadedCheerio('#editdescription').text().trim();
-
novel.summary = summary + `\n\nType: ${type}`;
-
const rating = loadedCheerio('.seriesother .uvotes')
.text()
.match(/(\d+\.\d+) \/ \d+\.\d+/)?.[1];
-
if (rating) {
novel.rating = parseFloat(rating);
}
- const chapter: Plugin.ChapterItem[] = [];
-
const novelId = loadedCheerio('input#mypostid').attr('value')!;
-
const formData = new FormData();
formData.append('action', 'nd_getchapters');
formData.append('mygrr', '0');
formData.append('mypostid', novelId);
- const link = `${this.site}wp-admin/admin-ajax.php`;
-
- const text = await fetchApi(link, {
+ const chaptersHtml = await fetchApi(`${this.site}wp-admin/admin-ajax.php`, {
method: 'POST',
body: formData,
}).then(data => data.text());
- loadedCheerio = parseHTML(text);
-
- const nameReplacements: Record = {
- 'v': 'volume ',
- 'c': ' chapter ',
- 'part': 'part ',
- 'ss': 'SS',
- };
-
- loadedCheerio('li.sp_li_chp').each((i, el) => {
- let chapterName = loadedCheerio(el).text();
- for (const name in nameReplacements) {
- chapterName = chapterName.replace(name, nameReplacements[name]);
- }
- chapterName = chapterName.replace(/\b\w/g, l => l.toUpperCase()).trim();
- const chapterUrl =
- 'https:' + loadedCheerio(el).find('a').first().next().attr('href');
-
- chapter.push({
- name: chapterName,
- path: chapterUrl.replace(this.site, ''),
- });
+ const chaptersCheerio = parseHTML(chaptersHtml);
+ const chapters: Plugin.ChapterItem[] = [];
+
+ chaptersCheerio('li.sp_li_chp').each((_, el) => {
+ const chapterName = chaptersCheerio(el)
+ .text()
+ .replace('v', 'volume ')
+ .replace('c', ' chapter ')
+ .replace('part', 'part ')
+ .replace('ss', 'SS')
+ .replace(/\b\w/g, l => l.toUpperCase())
+ .trim();
+
+ const chapterPath =
+ 'https:' + chaptersCheerio(el).find('a').first().next().attr('href');
+
+ if (chapterPath)
+ chapters.push({
+ name: chapterName,
+ path: chapterPath.replace(this.site, ''),
+ });
});
- novel.chapters = chapter.reverse();
-
+ novel.chapters = chapters.reverse();
return novel;
}
@@ -207,7 +161,7 @@ class NovelUpdates implements Plugin.PluginBase {
async getChapterBody(
loadedCheerio: CheerioAPI,
domain: string[],
- url: string,
+ chapterPath: string,
) {
let bloatElements = [];
let chapterTitle = '';
@@ -218,25 +172,22 @@ class NovelUpdates implements Plugin.PluginBase {
const targetDomain = domain.find(d => !unwanted.includes(d));
switch (targetDomain) {
- case 'anotivereads':
- chapterTitle =
- loadedCheerio('#comic-nav-name').first().text() || 'Title not found';
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'anotivereads': {
+ chapterTitle = loadedCheerio('#comic-nav-name').first().text();
chapterContent = loadedCheerio('#spliced-comic').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- // Last edited in 0.7.14 - 22/01/2025
- case 'arcanetranslations':
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'arcanetranslations': {
bloatElements = ['.bottomnav'];
bloatElements.forEach(tag => loadedCheerio(tag).remove());
chapterTitle = loadedCheerio('.epwrapper .cat-series').first().text();
-
loadedCheerio('.entry-content div, .entry-content span').each(
(_, element) => {
const el = loadedCheerio(element);
const style = el.attr('style');
-
if (!style) return; // Skip elements without inline styles
-
if (/border:.*#00219b/.test(style)) {
el.removeAttr('style').addClass('arcane_box_blue'); // Blue box
} else if (/border:.*white/.test(style)) {
@@ -255,45 +206,39 @@ class NovelUpdates implements Plugin.PluginBase {
}
},
);
-
chapterContent = loadedCheerio('.entry-content').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- case 'asuratls':
- const titleElement_asura = loadedCheerio('.post-body div b').first();
- chapterTitle = titleElement_asura.text() || 'Title not found';
- titleElement_asura.remove();
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'asuratls': {
+ const titleElement = loadedCheerio('.post-body div b').first();
+ chapterTitle = titleElement.text();
+ titleElement.remove();
chapterContent = loadedCheerio('.post-body').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- case 'daoist':
- chapterTitle =
- loadedCheerio('.chapter__title').first().text() || 'Title not found';
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'daoist': {
+ chapterTitle = loadedCheerio('.chapter__title').first().text();
chapterContent = loadedCheerio('.chapter__content').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- // Last edited in 0.7.12 - 08/12/2024
- case 'darkstartranslations':
- chapterTitle =
- loadedCheerio('ol.breadcrumb li').last().text().trim() ||
- 'Title not found';
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'darkstartranslations': {
+ chapterTitle = loadedCheerio('ol.breadcrumb li').last().text().trim();
chapterContent = loadedCheerio('.text-left').html()!;
-
// Load the extracted chapter content into Cheerio
- const chapterCheerio_darkstar = parseHTML(chapterContent);
-
+ const chapterCheerio = parseHTML(chapterContent);
// Add an empty row (extra
) after each
element
- chapterCheerio_darkstar('br').each((_, el) => {
- chapterCheerio_darkstar(el).after('
'); // Add one more
for an empty row
+ chapterCheerio('br').each((_, el) => {
+ chapterCheerio(el).after('
'); // Add one more
for an empty row
});
-
// Get the updated content
- chapterContent = chapterCheerio_darkstar.html();
-
- // Combine the title and the updated content into the final chapter text
- chapterText = `${chapterTitle}
${chapterContent}`;
+ chapterContent = chapterCheerio.html();
break;
- case 'fictionread':
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'fictionread': {
bloatElements = [
'.content > style',
'.highlight-ad-container',
@@ -301,114 +246,113 @@ class NovelUpdates implements Plugin.PluginBase {
'.word',
];
bloatElements.forEach(tag => loadedCheerio(tag).remove());
- chapterTitle =
- loadedCheerio('.title-image span').first().text() ||
- 'Title not found';
+ chapterTitle = loadedCheerio('.title-image span').first().text();
loadedCheerio('.content')
.children()
- .each((_, ele) => {
- if (loadedCheerio(ele).attr('id')?.includes('Chaptertitle-info')) {
- loadedCheerio(ele).remove();
+ .each((_, el) => {
+ if (loadedCheerio(el).attr('id')?.includes('Chaptertitle-info')) {
+ loadedCheerio(el).remove();
return false;
}
});
chapterContent = loadedCheerio('.content').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
- break;
- case 'genesistudio':
- const url_genesis = `${url}/__data.json?x-sveltekit-invalidated=001`;
- const json_genesis = await fetchApi(url_genesis).then(r => r.json());
-
- const nodes_genesis = json_genesis.nodes;
- const data_genesis = nodes_genesis
- .filter((node: { type: string }) => node.type === 'data')
- .map((node: { data: any }) => node.data)[0];
-
- /** May change in the future */
- const content_genesis = data_genesis[19];
- const footnotes_genesis = data_genesis[data_genesis[0].footnotes];
-
- chapterText = content_genesis + footnotes_genesis ?? '';
break;
- // Last edited in 0.7.13 - 21/01/2025
- case 'greenztl':
- const chapterId_greenz = url.split('/').pop();
- const url_greenz = `https://api.greenztl.com/api//chapters/${chapterId_greenz}`;
- const json_greenz = await fetchApi(url_greenz).then(r => r.json());
-
- chapterText = json_greenz.currentChapter.content;
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'genesistudio': {
+ const url = `${chapterPath}/__data.json?x-sveltekit-invalidated=001`;
+ try {
+ // Fetch the chapter's data in JSON format
+ const json = await fetchApi(url).then(r => r.json());
+ const nodes = json.nodes;
+ const data = nodes
+ .filter((node: { type: string }) => node.type === 'data')
+ .map((node: { data: any }) => node.data)[0];
+ // Look for chapter container with required fields
+ const contentKey = 'content';
+ const notesKey = 'notes';
+ const footnotesKey = 'footnotes';
+ // Iterate over each property in data to find chapter containers
+ for (const key in data) {
+ const mapping = data[key];
+ // Check container for keys that match the required fields
+ if (
+ mapping &&
+ typeof mapping === 'object' &&
+ contentKey in mapping &&
+ notesKey in mapping &&
+ footnotesKey in mapping
+ ) {
+ // Retrieve the chapter's content, notes, and footnotes using the mapping.
+ const content = data[mapping[contentKey]];
+ const notes = data[mapping[notesKey]];
+ const footnotes = data[mapping[footnotesKey]];
+ // Combine the parts with appropriate formatting
+ chapterText =
+ content +
+ (notes ? `Notes
${notes}` : '') +
+ (footnotes ?? '');
+ break;
+ }
+ }
+ } catch (error) {
+ throw new Error(`Failed to fetch chapter data: ${error}`);
+ }
break;
- case 'helscans':
- chapterTitle =
- loadedCheerio('.entry-title-main').first().text() ||
- 'Title not found';
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'helscans': {
+ chapterTitle = loadedCheerio('.entry-title-main').first().text();
const chapterString_helscans =
'Chapter ' + chapterTitle.split('Chapter')[1].trim();
loadedCheerio('#readerarea.rdminimal')
.children()
- .each((_, ele) => {
- const elementText = loadedCheerio(ele).text();
+ .each((_, el) => {
+ const elementText = loadedCheerio(el).text();
if (elementText.includes(chapterString_helscans)) {
chapterTitle = elementText;
- loadedCheerio(ele).remove();
+ loadedCheerio(el).remove();
return false;
}
});
chapterContent = loadedCheerio('#readerarea.rdminimal').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- case 'hiraethtranslation':
- const bloatAttributes = [
- 'data-lazy-srcset',
- 'data-lazy-src',
- 'data-lazy-sizes',
- 'data-ll-status',
- ];
- // Iterate over each selector for images that may have these attributes
- ['img.entered', 'img.lazyloaded'].forEach(selector => {
- loadedCheerio(selector).each(function () {
- // Loop through the attributes and remove them from the image
- bloatAttributes.forEach(attr => {
- loadedCheerio(this).removeAttr(attr); // Remove specified attribute
- });
- // Optionally, remove the class if you want
- loadedCheerio(this).removeClass('entered lazyloaded'); // Remove class if needed
- });
- });
- chapterTitle =
- loadedCheerio('li.active').first().text() || 'Title not found';
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'hiraethtranslation': {
+ chapterTitle = loadedCheerio('li.active').first().text();
chapterContent = loadedCheerio('.text-left').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- case 'hostednovel':
- chapterTitle =
- loadedCheerio('#chapter-title').first().text() || 'Title not found';
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'hostednovel': {
+ chapterTitle = loadedCheerio('#chapter-title').first().text();
chapterContent = loadedCheerio('#chapter-content').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- case 'infinitenoveltranslations':
- /**
- * Get the chapter link from the main page
- */
- const link_infinite = loadedCheerio('article > p > a')
- .first()
- .attr('href')!;
- if (link_infinite) {
- const result_infinite = await fetchApi(link_infinite);
- const body_infinite = await result_infinite.text();
- loadedCheerio = parseHTML(body_infinite);
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'infinitenoveltranslations': {
+ // Get the chapter link from the main page
+ const url = loadedCheerio('article > p > a').first().attr('href')!;
+ if (url) {
+ const result = await fetchApi(url);
+ const body = await result.text();
+ loadedCheerio = parseHTML(body);
}
chapterContent = loadedCheerio('.hentry').html()!;
- chapterTitle =
- loadedCheerio('.page-entry-title').text() || 'Title not found';
- chapterText = `${chapterTitle}
${chapterContent}`;
+ chapterTitle = loadedCheerio('.page-entry-title').text();
break;
- case 'inoveltranslation':
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'inoveltranslation': {
bloatElements = ['header', 'section'];
bloatElements.forEach(tag => loadedCheerio(tag).remove());
chapterText = loadedCheerio('.styles_content__JHK8G').html()!;
break;
- case 'isotls': // mii translates
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ // mii translates
+ case 'isotls': {
bloatElements = [
'footer',
'header',
@@ -418,47 +362,44 @@ class NovelUpdates implements Plugin.PluginBase {
'.ezoic-videopicker-video',
];
bloatElements.forEach(tag => loadedCheerio(tag).remove());
- chapterTitle =
- loadedCheerio('head title').first().text() || 'Title not found';
+ chapterTitle = loadedCheerio('head title').first().text();
chapterContent = loadedCheerio('main article').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- case 'ko-fi':
- const matchResult_kofi = loadedCheerio(
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'ko-fi': {
+ const matchResult = loadedCheerio(
'script:contains("shadowDom.innerHTML")',
)
.html()
?.match(/shadowDom\.innerHTML \+= '( loadedCheerio(tag).remove());
- const titleElement_mirilu = loadedCheerio(
- '.entry-content p strong',
- ).first();
- chapterTitle = titleElement_mirilu.text() || 'Title not found';
- titleElement_mirilu.remove();
+ const titleElement = loadedCheerio('.entry-content p strong').first();
+ chapterTitle = titleElement.text();
+ titleElement.remove();
chapterContent = loadedCheerio('.entry-content').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- case 'novelplex':
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'novelplex': {
bloatElements = ['.passingthrough_adreminder'];
bloatElements.forEach(tag => loadedCheerio(tag).remove());
- chapterTitle =
- loadedCheerio('.halChap--jud').first().text() || 'Title not found';
+ chapterTitle = loadedCheerio('.halChap--jud').first().text();
chapterContent = loadedCheerio('.halChap--kontenInner ').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- // Last edited in 0.7.12 - 08/12/2024
- case 'novelworldtranslations':
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'novelworldtranslations': {
bloatElements = ['.separator img'];
bloatElements.forEach(tag => loadedCheerio(tag).remove());
-
loadedCheerio('.entry-content a')
.filter((_, el) => {
return (
@@ -471,48 +412,46 @@ class NovelUpdates implements Plugin.PluginBase {
.each((_, el) => {
loadedCheerio(el).parent().remove();
});
-
- chapterTitle =
- loadedCheerio('.entry-title').first().text() || 'Title not found';
+ chapterTitle = loadedCheerio('.entry-title').first().text();
chapterContent = loadedCheerio('.entry-content')
.html()!
.replace(/ /g, '')
.replace(/\n/g, '
');
-
// Load the chapter content into Cheerio and clean up empty elements
- const chapterCheerio_novelworld = parseHTML(chapterContent);
- chapterCheerio_novelworld('span, p, div').each((_, el) => {
- if (chapterCheerio_novelworld(el).text().trim() === '') {
- chapterCheerio_novelworld(el).remove();
+ const chapterCheerio = parseHTML(chapterContent);
+ chapterCheerio('span, p, div').each((_, el) => {
+ if (chapterCheerio(el).text().trim() === '') {
+ chapterCheerio(el).remove();
}
});
- chapterContent = chapterCheerio_novelworld.html()!;
-
- chapterText = `${chapterTitle}
${chapterContent}`;
+ chapterContent = chapterCheerio.html()!;
break;
- case 'raeitranslations':
- const parts = url.split('/');
- const link_raei = `${parts[0]}//api.${parts[2]}/api/chapters/single?id=${parts[3]}&num=${parts[4]}`;
- const json_raei = await fetchApi(link_raei).then(r => r.json());
- const titleElement_raei = `Chapter ${json_raei.currentChapter.chapTag}`;
- chapterTitle = json_raei.currentChapter.chapTitle
- ? `${titleElement_raei} - ${json_raei.currentChapter.chapTitle}`
- : titleElement_raei;
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'raeitranslations': {
+ const parts = chapterPath.split('/');
+ const url = `${parts[0]}//api.${parts[2]}/api/chapters/single?id=${parts[3]}&num=${parts[4]}`;
+ const json = await fetchApi(url).then(r => r.json());
+ const titleElement = `Chapter ${json.currentChapter.chapTag}`;
+ chapterTitle = json.currentChapter.chapTitle
+ ? `${titleElement} - ${json.currentChapter.chapTitle}`
+ : titleElement;
chapterContent = [
- json_raei.novelHead,
+ json.novelHead,
`
`,
- json_raei.currentChapter.body,
+ json.currentChapter.body,
`
Translator's Note:
`,
- json_raei.currentChapter.note,
+ json.currentChapter.note,
].join('');
chapterContent = chapterContent.replace(/\n/g, '
');
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- case 'rainofsnow':
- const displayedDiv_snow = loadedCheerio('.bb-item').filter(function () {
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'rainofsnow': {
+ const displayedDiv = loadedCheerio('.bb-item').filter(function () {
return loadedCheerio(this).css('display') === 'block';
});
- const loadedCheerioSnow = parseHTML(displayedDiv_snow.html()!);
+ const loadedCheerioSnow = parseHTML(displayedDiv.html()!);
bloatElements = [
'.responsivevoice-button',
'.zoomdesc-cont p img',
@@ -520,34 +459,33 @@ class NovelUpdates implements Plugin.PluginBase {
];
bloatElements.forEach(tag => loadedCheerioSnow(tag).remove());
chapterContent = loadedCheerioSnow('.zoomdesc-cont').html()!;
- const titleElement_snow = loadedCheerioSnow('.scroller h2').first();
- if (titleElement_snow.length) {
- chapterTitle = titleElement_snow.text() || 'Title not found';
- titleElement_snow.remove();
+ const titleElement = loadedCheerioSnow('.scroller h2').first();
+ if (titleElement.length) {
+ chapterTitle = titleElement.text();
+ titleElement.remove();
chapterContent = loadedCheerioSnow('.zoomdesc-cont').html()!;
- if (chapterTitle && chapterContent) {
- chapterText = `${chapterTitle}
${chapterContent}`;
- }
- } else if (chapterContent) {
- chapterText = chapterContent;
}
break;
- case 'readingpia':
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'readingpia': {
bloatElements = ['.ezoic-ad', '.ezoic-adpicker-ad', '.ez-video-wrap'];
bloatElements.forEach(tag => loadedCheerio(tag).remove());
- chapterText = loadedCheerio('.chapter-body').html() || 'Text not found';
+ chapterText = loadedCheerio('.chapter-body').html()!;
break;
- case 'redoxtranslation':
- const chapterID_redox = url.split('/').pop();
- chapterTitle = `Chapter ${chapterID_redox}`;
- const url_redox = `${url.split('chapter')[0]}txt/${chapterID_redox}.txt`;
- chapterContent = await fetchApi(url_redox)
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'redoxtranslation': {
+ const chapterId = chapterPath.split('/').pop();
+ chapterTitle = `Chapter ${chapterId}`;
+ const url = `${chapterPath.split('chapter')[0]}txt/${chapterId}.txt`;
+ chapterContent = await fetchApi(url)
.then(r => r.text())
.then(text => {
// Split text into sentences based on newline characters
- const sentences_redox = text.split('\n');
+ const sentences = text.split('\n');
// Process each sentence individually
- const formattedSentences_redox = sentences_redox.map(sentence => {
+ const formattedSentences = sentences.map(sentence => {
// Check if the sentence contains "
"
if (sentence.includes('{break}')) {
// Create a centered sentence with three stars
@@ -564,63 +502,49 @@ class NovelUpdates implements Plugin.PluginBase {
}
});
// Join the formatted sentences back together with newline characters
- return formattedSentences_redox.join('
');
+ return formattedSentences.join('
');
});
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- case 'sacredtexttranslations':
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'sacredtexttranslations': {
bloatElements = [
'.entry-content blockquote',
'.entry-content div',
'.reaction-buttons',
];
bloatElements.forEach(tag => loadedCheerio(tag).remove());
- chapterTitle =
- loadedCheerio('.entry-title').first().text() || 'Title not found';
+ chapterTitle = loadedCheerio('.entry-title').first().text();
chapterContent = loadedCheerio('.entry-content').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- case 'scribblehub':
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'scribblehub': {
bloatElements = ['.wi_authornotes'];
bloatElements.forEach(tag => loadedCheerio(tag).remove());
- chapterTitle =
- loadedCheerio('.chapter-title').first().text() || 'Title not found';
+ chapterTitle = loadedCheerio('.chapter-title').first().text();
chapterContent = loadedCheerio('.chp_raw').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- // Last edited in 0.7.13 - 21/01/2025
- case 'skydemonorder':
- /**
- * Check for age verification
- */
- const ageVerification_skydemon = loadedCheerio('main')
- .text()
- .toLowerCase()!;
- if (ageVerification_skydemon.includes('age verification required')) {
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'skydemonorder': {
+ // Check for age verification
+ const ageVerification = loadedCheerio('main').text().toLowerCase()!;
+ if (ageVerification.includes('age verification required')) {
throw new Error('Age verification required, please open in webview.');
}
chapterTitle = `${loadedCheerio('header .font-medium.text-sm').first().text().trim()}`;
- chapterContent = loadedCheerio('#startContainer + * > *')
- .first()
- .html()!;
- if (!chapterContent) {
- chapterContent = `${loadedCheerio('#chapter-body').html()!}
There could be missing content, please check in webview.`;
- }
- if (chapterTitle) {
- chapterText = `${chapterTitle}
${chapterContent}`;
- } else {
- chapterText = chapterContent;
- }
+ chapterContent = loadedCheerio('#chapter-body').html()!;
break;
- case 'stabbingwithasyringe':
- /**
- * Get the chapter link from the main page
- */
- const link_syringe = loadedCheerio('.entry-content a').attr('href')!;
- if (link_syringe) {
- const result_syringe = await fetchApi(link_syringe);
- const body_syringe = await result_syringe.text();
- loadedCheerio = parseHTML(body_syringe);
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'stabbingwithasyringe': {
+ // Get the chapter link from the main page
+ const url = loadedCheerio('.entry-content a').attr('href')!;
+ if (url) {
+ const result = await fetchApi(url);
+ const body = await result.text();
+ loadedCheerio = parseHTML(body);
}
bloatElements = [
'.has-inline-color',
@@ -630,17 +554,16 @@ class NovelUpdates implements Plugin.PluginBase {
];
bloatElements.forEach(tag => loadedCheerio(tag).remove());
chapterContent = loadedCheerio('.entry-content').html()!;
- const titleElement_syringe = loadedCheerio('.entry-content h3').first();
- if (titleElement_syringe.length) {
- chapterTitle = titleElement_syringe.text();
- titleElement_syringe.remove();
+ const titleElement = loadedCheerio('.entry-content h3').first();
+ if (titleElement.length) {
+ chapterTitle = titleElement.text();
+ titleElement.remove();
chapterContent = loadedCheerio('.entry-content').html()!;
- } else {
- chapterTitle = 'Title not found';
}
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- case 'tinytranslation':
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'tinytranslation': {
bloatElements = [
'.content noscript',
'.google_translate_element',
@@ -649,160 +572,152 @@ class NovelUpdates implements Plugin.PluginBase {
'br',
];
bloatElements.forEach(tag => loadedCheerio(tag).remove());
- chapterTitle =
- loadedCheerio('.title-content').first().text() || 'Title not found';
+ chapterTitle = loadedCheerio('.title-content').first().text();
loadedCheerio('.title-content').first().remove();
chapterContent = loadedCheerio('.content').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- case 'tumblr':
- chapterText = loadedCheerio('.post').html() || 'Text not found';
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'tumblr': {
+ chapterText = loadedCheerio('.post').html()!;
+ break;
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'vampiramtl': {
+ // Get the chapter link from the main page
+ const url = loadedCheerio('.entry-content a').attr('href')!;
+ if (url) {
+ const result = await fetchApi(chapterPath + url);
+ const body = await result.text();
+ loadedCheerio = parseHTML(body);
+ }
+ chapterTitle = loadedCheerio('.entry-title').first().text();
+ chapterContent = loadedCheerio('.entry-content').html()!;
break;
- case 'wattpad':
- chapterTitle = loadedCheerio('.h2').first().text() || 'Title not found';
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'wattpad': {
+ chapterTitle = loadedCheerio('.h2').first().text();
chapterContent = loadedCheerio('.part-content pre').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- case 'webnovel':
- chapterTitle =
- loadedCheerio('.cha-tit .pr .dib').first().text() ||
- 'Title not found';
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'webnovel': {
+ chapterTitle = loadedCheerio('.cha-tit .pr .dib').first().text();
chapterContent = loadedCheerio('.cha-words').html()!;
if (!chapterContent) {
chapterContent = loadedCheerio('._content').html()!;
}
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- case 'wetriedtls':
- const scriptContent_wetried =
+ }
+ case 'wetriedtls': {
+ const scriptContent =
loadedCheerio('script:contains("p dir=")').html() ||
loadedCheerio('script:contains("u003c")').html();
- if (scriptContent_wetried) {
- const jsonString_wetried = scriptContent_wetried.slice(
- scriptContent_wetried.indexOf('.push(') + '.push('.length,
- scriptContent_wetried.lastIndexOf(')'),
+ if (scriptContent) {
+ const jsonString_wetried = scriptContent.slice(
+ scriptContent.indexOf('.push(') + '.push('.length,
+ scriptContent.lastIndexOf(')'),
);
chapterText = JSON.parse(jsonString_wetried)[1];
}
break;
- case 'wuxiaworld':
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'wuxiaworld': {
bloatElements = ['.MuiLink-root'];
bloatElements.forEach(tag => loadedCheerio(tag).remove());
- chapterTitle =
- loadedCheerio('h4 span').first().text() || 'Title not found';
+ chapterTitle = loadedCheerio('h4 span').first().text();
chapterContent = loadedCheerio('.chapter-content').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
break;
- case 'yoru':
- const chapterId_yoru = url.split('/').pop();
- const link_yoru = `https://pxp-main-531j.onrender.com/api/v1/book_chapters/${chapterId_yoru}/content`;
- const json_yoru = await fetchApi(link_yoru).then(r => r.json());
- chapterText = await fetchApi(json_yoru).then(r => r.text());
+ }
+ case 'yoru': {
+ const chapterId = chapterPath.split('/').pop();
+ const url = `https://pxp-main-531j.onrender.com/api/v1/book_chapters/${chapterId}/content`;
+ const json = await fetchApi(url).then(r => r.json());
+ chapterText = await fetchApi(json).then(r => r.text());
break;
- case 'zetrotranslation':
- bloatElements = ['hr', 'p:contains("\u00a0")'];
- bloatElements.forEach(tag => loadedCheerio(tag).remove());
+ }
+ // Last edited in 0.9.0 by Batorian - 19/03/2025
+ case 'zetrotranslation': {
chapterContent = loadedCheerio('.text-left').html()!;
- const titleElement_zetro = loadedCheerio('.text-left h2').first();
- if (titleElement_zetro.length) {
- chapterTitle = titleElement_zetro.text() || 'Title not found';
- titleElement_zetro.remove();
+ const titleElement = loadedCheerio('.text-left h2').first();
+ if (titleElement.length) {
+ chapterTitle = titleElement.text();
+ titleElement.remove();
chapterContent = loadedCheerio('.text-left').html()!;
- chapterText = `${chapterTitle}
${chapterContent}`;
} else if (chapterContent) {
- chapterTitle =
- loadedCheerio('.active').first().text() || 'Title not found';
- chapterText = `${chapterTitle}
${chapterContent}`;
+ chapterTitle = loadedCheerio('.active').first().text();
}
break;
+ }
+ }
+ if (!chapterText) {
+ if (chapterTitle) {
+ chapterText = `${chapterTitle}
${chapterContent}`;
+ } else {
+ chapterText = chapterContent;
+ }
}
return chapterText;
}
async parseChapter(chapterPath: string): Promise {
- let bloatElements = [];
- let chapterTitle = '';
- let chapterContent = '';
- let chapterText = '';
+ let chapterText;
const result = await fetchApi(this.site + chapterPath);
const body = await result.text();
const url = result.url;
- const domain = url.toLowerCase().split('/')[2].split('.');
+ const domainParts = url.toLowerCase().split('/')[2].split('.');
const loadedCheerio = parseHTML(body);
- /**
- * Check for Captcha
- */
- const title = loadedCheerio('title').text().toLowerCase().trim();
- if (
- title == 'bot verification' ||
- title == 'just a moment...' ||
- title == 'redirecting...' ||
- title == 'un instant...' ||
- title == 'you are being redirected...'
- ) {
- throw new Error('Captcha error, please open in webview.');
+ // Handle CAPTCHA cases
+ const blockedTitles = [
+ 'bot verification',
+ 'just a moment...',
+ 'redirecting...',
+ 'un instant...',
+ 'you are being redirected...',
+ ];
+ const title = loadedCheerio('title').text().trim().toLowerCase();
+ if (blockedTitles.includes(title)) {
+ throw new Error('Captcha detected, please open in webview.');
}
+
+ // Check if chapter url is wrong or site is down
if (!result.ok) {
- /**
- * Check if the chapter url is wrong or the site is genuinely down
- */
throw new Error(
- `Could not reach site (${result.status}), try to open in webview.`,
+ `Failed to fetch ${result.url}: ${result.status} ${result.statusText}`,
);
}
- /**
- * Detect if the site is a Blogspot site
- */
- const blogspotSources = [
- loadedCheerio('meta[name="google-adsense-platform-domain"]').attr(
- 'content',
- ),
- loadedCheerio('meta[name="generator"]').attr('content'),
- ];
-
- const blogspotKeywords = ['blogspot', 'blogger'];
- let isBlogspot = blogspotSources.some(
- source =>
- source &&
- blogspotKeywords.some(keyword =>
- source.toLowerCase().includes(keyword),
+ // Detect platforms
+ let isBlogspot = ['blogspot', 'blogger'].some(keyword =>
+ [
+ loadedCheerio('meta[name="google-adsense-platform-domain"]').attr(
+ 'content',
),
+ loadedCheerio('meta[name="generator"]').attr('content'),
+ ].some(meta => meta?.toLowerCase().includes(keyword)),
);
- /**
- * Detect if the site is a WordPress site
- */
- const wordpressSources = [
- loadedCheerio('#dcl_comments-js-extra').html(),
- loadedCheerio('meta[name="generator"]').attr('content'),
- loadedCheerio('.powered-by').text(),
- loadedCheerio('footer').text(),
- ];
-
- const wordpressKeywords = ['wordpress', 'site kit by google'];
- let isWordPress = wordpressSources.some(
- source =>
- source &&
- wordpressKeywords.some(keyword =>
- source.toLowerCase().includes(keyword),
- ),
+ let isWordPress = ['wordpress', 'site kit by google'].some(keyword =>
+ [
+ loadedCheerio('#dcl_comments-js-extra').html(),
+ loadedCheerio('meta[name="generator"]').attr('content'),
+ loadedCheerio('.powered-by').text(),
+ loadedCheerio('footer').text(),
+ ].some(meta => meta?.toLowerCase().includes(keyword)),
);
- /**
- * In case sites are not detected correctly
- */
- const manualWordPress = ['etherreads', 'soafp'];
- if (!isWordPress && domain.find(wp => manualWordPress.includes(wp))) {
+ // Manually set WordPress flag for known sites
+ const manualWordPress = ['etherreads', 'greenztl2', 'soafp'];
+ if (!isWordPress && domainParts.some(wp => manualWordPress.includes(wp))) {
isWordPress = true;
}
- /**
- * Sites that are WordPress or Blogspot but have different structure
- */
+ // Handle outlier sites
const outliers = [
'anotivereads',
'arcanetranslations',
@@ -816,14 +731,15 @@ class NovelUpdates implements Plugin.PluginBase {
'sacredtexttranslations',
'stabbingwithasyringe',
'tinytranslation',
+ 'vampiramtl',
'zetrotranslation',
];
- if (domain.find(d => outliers.includes(d))) {
+ if (domainParts.some(d => outliers.includes(d))) {
isWordPress = false;
isBlogspot = false;
}
- // Last edited in 0.7.13 - 21/01/2025
+ // Last edited in 0.9.0 - 19/03/2025
/**
* Blogspot sites:
* - ΒΌ-Assed
@@ -841,8 +757,10 @@ class NovelUpdates implements Plugin.PluginBase {
* - Dumahs Translations
* - ElloMTL
* - Femme Fables
+ * - Gadgetized Panda Translation
* - Gem Novels
* - Goblinslate
+ * - GreenzTL
* - Hel Scans (Outlier)
* - ippotranslations
* - JATranslations
@@ -854,157 +772,138 @@ class NovelUpdates implements Plugin.PluginBase {
* - Stabbing with a Syringe (Outlier)
* - StoneScape
* - TinyTL (Outlier)
+ * - VampiraMTL (Outlier)
* - Wonder Novels
* - Yong Library
* - Zetro Translation (Outlier)
*/
+
+ // Fetch chapter content based on detected platform
if (!isWordPress && !isBlogspot) {
- chapterText = await this.getChapterBody(loadedCheerio, domain, url);
- } else if (isBlogspot) {
- bloatElements = [
- '.button-container',
- '.ChapterNav',
- '.ch-bottom',
- '.separator',
- ];
- bloatElements.forEach(tag => loadedCheerio(tag).remove());
- chapterTitle =
- loadedCheerio('.entry-title').first().text() ||
- loadedCheerio('.post-title').first().text() ||
- 'Title not found';
- chapterContent =
- loadedCheerio('.content-post').html() ||
- loadedCheerio('.entry-content').html() ||
- loadedCheerio('.post-body').html()!;
- if (chapterTitle && chapterContent) {
- chapterText = `${chapterTitle}
${chapterContent}`;
- }
- } else if (isWordPress) {
- bloatElements = [
- '.ad',
- '.author-avatar',
- '.chapter-warning',
- '.entry-meta',
- '.ezoic-ad',
- '.mb-center',
- '.modern-footnotes-footnote__note',
- '.patreon-widget',
- '.post-cats',
- '.pre-bar',
- '.sharedaddy',
- '.sidebar',
- '.swg-button-v2-light',
- '.wp-block-buttons',
- '.wp-block-image',
- '.wp-dark-mode-switcher',
- '.wp-next-post-navi',
- '#hpk',
- '#jp-post-flair',
- '#textbox',
- ];
+ chapterText = await this.getChapterBody(loadedCheerio, domainParts, url);
+ } else {
+ const bloatElements = isBlogspot
+ ? ['.button-container', '.ChapterNav', '.ch-bottom', '.separator']
+ : [
+ '.ad',
+ '.author-avatar',
+ '.chapter-warning',
+ '.entry-meta',
+ '.ezoic-ad',
+ '.mb-center',
+ '.modern-footnotes-footnote__note',
+ '.patreon-widget',
+ '.post-cats',
+ '.pre-bar',
+ '.sharedaddy',
+ '.sidebar',
+ '.swg-button-v2-light',
+ '.wp-block-buttons',
+ '.wp-block-columns',
+ '.wp-dark-mode-switcher',
+ '.wp-next-post-navi',
+ '#hpk',
+ '#jp-post-flair',
+ '#textbox',
+ ];
+
bloatElements.forEach(tag => loadedCheerio(tag).remove());
- chapterTitle =
- loadedCheerio('.entry-title').first().text() ||
- loadedCheerio('.entry-title-main').first().text() ||
- loadedCheerio('.chapter__title').first().text() ||
- loadedCheerio('.sp-title').first().text() ||
- loadedCheerio('.title-content').first().text() ||
- loadedCheerio('.wp-block-post-title').first().text() ||
- loadedCheerio('.title_story').first().text() ||
- loadedCheerio('.active').first().text() ||
- loadedCheerio('head title').first().text() ||
- loadedCheerio('h1.leading-none ~ h2').first().text() ||
- 'Title not found';
+
+ // Extract title
+ const titleSelectors = isBlogspot
+ ? ['.entry-title', '.post-title', 'head title']
+ : [
+ '.entry-title',
+ '.chapter__title',
+ '.title-content',
+ '.wp-block-post-title',
+ '.title_story',
+ '#chapter-heading',
+ 'head title',
+ 'h1:first-of-type',
+ 'h2:first-of-type',
+ '.active',
+ ];
+ let chapterTitle = titleSelectors
+ .map(sel => loadedCheerio(sel).first().text())
+ .find(text => text);
+
+ // Extract subtitle (if any)
const chapterSubtitle =
loadedCheerio('.cat-series').first().text() ||
- loadedCheerio('h1.leading-none ~ span').first().text() ||
- '';
- if (chapterSubtitle) {
- chapterTitle = chapterSubtitle;
- }
- chapterContent =
- loadedCheerio('.rdminimal').html() ||
- loadedCheerio('.entry-content').html() ||
- loadedCheerio('.chapter__content').html() ||
- loadedCheerio('.prevent-select').html() ||
- loadedCheerio('.text_story').html() ||
- loadedCheerio('.contenta').html() ||
- loadedCheerio('.single_post').html() ||
- loadedCheerio('.post-entry').html() ||
- loadedCheerio('.main-content').html() ||
- loadedCheerio('.post-content').html() ||
- loadedCheerio('.content').html() ||
- loadedCheerio('.page-body').html() ||
- loadedCheerio('.td-page-content').html() ||
- loadedCheerio('.reader-content').html() ||
- loadedCheerio('#content').html() ||
- loadedCheerio('#the-content').html() ||
- loadedCheerio('article.post').html()!;
- if (chapterTitle && chapterContent) {
+ loadedCheerio('h1.leading-none ~ span').first().text();
+ if (chapterSubtitle) chapterTitle = chapterSubtitle;
+
+ // Extract content
+ const contentSelectors = isBlogspot
+ ? ['.content-post', '.entry-content', '.post-body']
+ : [
+ '.chapter__content',
+ '.entry-content',
+ '.text_story',
+ '.post-content',
+ '.contenta',
+ '.single_post',
+ '.main-content',
+ '.reader-content',
+ '#content',
+ '#the-content',
+ 'article.post',
+ ];
+ const chapterContent = contentSelectors
+ .map(sel => loadedCheerio(sel).html()!)
+ .find(html => html);
+
+ if (chapterTitle) {
chapterText = `${chapterTitle}
${chapterContent}`;
+ } else {
+ chapterText = chapterContent;
}
}
+ // Fallback content extraction
if (!chapterText) {
- /**
- * Remove unnecessary tags
- */
- const tags = ['nav', 'header', 'footer', '.hidden'];
- tags.map(tag => loadedCheerio(tag).remove());
- chapterText = loadedCheerio('body').html() || 'Text not found';
- }
-
- if (chapterText) {
- /**
- * Convert relative urls to absolute
- */
- chapterText = chapterText.replace(
- /href="\//g,
- `href="${this.getLocation(result.url)}/`,
+ ['nav', 'header', 'footer', '.hidden'].forEach(tag =>
+ loadedCheerio(tag).remove(),
);
+ chapterText = loadedCheerio('body').html()!;
}
- // Parse the HTML with Cheerio
- const chapterCheerio = parseHTML(chapterText);
+ // Convert relative URLs to absolute
+ chapterText = chapterText.replace(
+ /href="\//g,
+ `href="${this.getLocation(result.url)}/`,
+ );
- // Remove unwanted elements
+ // Process images
+ const chapterCheerio = parseHTML(chapterText);
chapterCheerio('noscript').remove();
- // Process the images
- chapterCheerio('img').each((i, el) => {
+ chapterCheerio('img').each((_, el) => {
const $el = chapterCheerio(el);
- // Prioritize data-lazy-src or src for the main src attribute
- const imgSrc = $el.attr('data-lazy-src') || $el.attr('src');
-
- if (imgSrc) {
- $el.attr('src', imgSrc); // Set the src value
+ // Only update if the lazy-loaded attribute exists
+ if ($el.attr('data-lazy-src')) {
+ $el.attr('src', $el.attr('data-lazy-src'));
}
-
- // Prioritize data-lazy-srcset or srcset for the srcset attribute
- const imgSrcset = $el.attr('data-lazy-srcset') || $el.attr('srcset');
-
- if (imgSrcset) {
- $el.attr('srcset', imgSrcset); // Set the srcset value
+ if ($el.attr('data-lazy-srcset')) {
+ $el.attr('srcset', $el.attr('data-lazy-srcset'));
}
- // Remove lazy-loading classes
- $el.removeClass('lazyloaded');
+ // Remove lazy-loading class if it exists
+ if ($el.hasClass('lazyloaded')) {
+ $el.removeClass('lazyloaded');
+ }
});
- // Extract the updated HTML
- chapterText = chapterCheerio.html();
-
- return chapterText;
+ return chapterCheerio.html()!;
}
async searchNovels(
searchTerm: string,
page: number,
): Promise {
- /**
- * Split searchTerm by specific special characters and find the longest split
- */
+ // Split searchTerm by specific special characters and find the longest split
const splits = searchTerm.split('*');
const longestSearchTerm = splits.reduce(
(a, b) => (a.length > b.length ? a : b),