From 33ce64188f37315e72f71993516d60a1e68517ab Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 16:35:40 +0100 Subject: [PATCH 01/16] Add GreenzTL --- package.json | 2 +- plugins/english/novelupdates.ts | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1d30a90d6..7035e42c4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lnreader-plugins", - "version": "3.0.0", + "version": "3.1.0", "description": "Plugins repo for LNReader", "main": "index.js", "type": "module", diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index 60ba55a55..624406885 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -6,7 +6,7 @@ import { Plugin } from '@/types/plugin'; class NovelUpdates implements Plugin.PluginBase { id = 'novelupdates'; name = 'Novel Updates'; - version = '0.9.4'; + version = '0.9.5'; icon = 'src/en/novelupdates/icon.png'; customCSS = 'src/en/novelupdates/customCSS.css'; site = 'https://www.novelupdates.com/'; @@ -411,6 +411,15 @@ class NovelUpdates implements Plugin.PluginBase { } break; } + // Last edited in 0.9.5 by Batorian - 26/12/2025 + case 'greenztl': { + const chapterSlug = chapterPath.split('/').pop(); + const apiUrl = `https://greenztl.com/api/chapters/slug/${chapterSlug}`; + const response = await fetchApi(apiUrl); + const json = await response.json(); + + chapterContent = json.data.content; + } // Last edited in 0.9.0 by Batorian - 19/03/2025 case 'helscans': { chapterTitle = loadedCheerio('.entry-title-main').first().text(); From 5987365a087d0c9695a36c0935a98fcea1842a81 Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 16:49:31 +0100 Subject: [PATCH 02/16] fix --- plugins/english/novelupdates.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index 624406885..15d995782 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -418,7 +418,7 @@ class NovelUpdates implements Plugin.PluginBase { const response = await fetchApi(apiUrl); const json = await response.json(); - chapterContent = json.data.content; + chapterContent = json?.data?.content?.trim() || ''; } // Last edited in 0.9.0 by Batorian - 19/03/2025 case 'helscans': { From 1687d7c97e23ce82c28e8bfdf7a904b29c7e9202 Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 16:58:04 +0100 Subject: [PATCH 03/16] fix --- plugins/english/novelupdates.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index 15d995782..de47d28b6 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -418,7 +418,8 @@ class NovelUpdates implements Plugin.PluginBase { const response = await fetchApi(apiUrl); const json = await response.json(); - chapterContent = json?.data?.content?.trim() || ''; + const chapterCheerio = parseHTML(json.data.content); + chapterContent = chapterCheerio.html()!; } // Last edited in 0.9.0 by Batorian - 19/03/2025 case 'helscans': { From b7950eff8004506a58bad4dab89256c6427cee60 Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 17:01:00 +0100 Subject: [PATCH 04/16] Version update --- plugins/english/novelupdates.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index de47d28b6..5b533770f 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -6,7 +6,7 @@ import { Plugin } from '@/types/plugin'; class NovelUpdates implements Plugin.PluginBase { id = 'novelupdates'; name = 'Novel Updates'; - version = '0.9.5'; + version = '0.9.6'; icon = 'src/en/novelupdates/icon.png'; customCSS = 'src/en/novelupdates/customCSS.css'; site = 'https://www.novelupdates.com/'; From 1707236ef4c1bef3990c89e7d82c4bd52acdce2f Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 17:42:50 +0100 Subject: [PATCH 05/16] fix --- plugins/english/novelupdates.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index 5b533770f..b474aa05f 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -6,7 +6,7 @@ import { Plugin } from '@/types/plugin'; class NovelUpdates implements Plugin.PluginBase { id = 'novelupdates'; name = 'Novel Updates'; - version = '0.9.6'; + version = '0.9.7'; icon = 'src/en/novelupdates/icon.png'; customCSS = 'src/en/novelupdates/customCSS.css'; site = 'https://www.novelupdates.com/'; @@ -419,7 +419,17 @@ class NovelUpdates implements Plugin.PluginBase { const json = await response.json(); const chapterCheerio = parseHTML(json.data.content); - chapterContent = chapterCheerio.html()!; + + //chapterContent = chapterCheerio.html()!; + + const paragraphs = chapterCheerio('p') + .map((_, el) => { + const pContent = chapterCheerio(el).html(); + return pContent ? `

${pContent}

` : null; + }) + .get(); + + chapterContent = paragraphs.filter(Boolean).join('
'); } // Last edited in 0.9.0 by Batorian - 19/03/2025 case 'helscans': { From 8b0f2c0dfe6626fefd09dfaf28cb6ad3cb794e86 Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 17:52:14 +0100 Subject: [PATCH 06/16] add debug --- plugins/english/novelupdates.ts | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index b474aa05f..04621b775 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -6,7 +6,7 @@ import { Plugin } from '@/types/plugin'; class NovelUpdates implements Plugin.PluginBase { id = 'novelupdates'; name = 'Novel Updates'; - version = '0.9.7'; + version = '0.9.8'; icon = 'src/en/novelupdates/icon.png'; customCSS = 'src/en/novelupdates/customCSS.css'; site = 'https://www.novelupdates.com/'; @@ -415,21 +415,26 @@ class NovelUpdates implements Plugin.PluginBase { case 'greenztl': { const chapterSlug = chapterPath.split('/').pop(); const apiUrl = `https://greenztl.com/api/chapters/slug/${chapterSlug}`; - const response = await fetchApi(apiUrl); - const json = await response.json(); - const chapterCheerio = parseHTML(json.data.content); + try { + const response = await fetchApi(apiUrl); + const json = await response.json(); - //chapterContent = chapterCheerio.html()!; + const chapterCheerio = parseHTML(json.data.content); - const paragraphs = chapterCheerio('p') - .map((_, el) => { - const pContent = chapterCheerio(el).html(); - return pContent ? `

${pContent}

` : null; - }) - .get(); + //chapterContent = chapterCheerio.html()!; - chapterContent = paragraphs.filter(Boolean).join('
'); + const paragraphs = chapterCheerio('p') + .map((_, el) => { + const pContent = chapterCheerio(el).html(); + return pContent ? `

${pContent}

` : null; + }) + .get(); + + chapterContent = paragraphs.filter(Boolean).join('
'); + } catch (error) { + throw new Error(`Failed to parse GreenzTL chapter: ${error}`); + } } // Last edited in 0.9.0 by Batorian - 19/03/2025 case 'helscans': { From beeab9bdbf9f734e6ec1629d0a91cd392e62cbb0 Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 18:37:30 +0100 Subject: [PATCH 07/16] Improve Wordpress and Blogspot detection --- plugins/english/novelupdates.ts | 235 +++++++++++++++++++------------- 1 file changed, 142 insertions(+), 93 deletions(-) diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index 04621b775..6c0394312 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -6,7 +6,7 @@ import { Plugin } from '@/types/plugin'; class NovelUpdates implements Plugin.PluginBase { id = 'novelupdates'; name = 'Novel Updates'; - version = '0.9.8'; + version = '0.9.9'; icon = 'src/en/novelupdates/icon.png'; customCSS = 'src/en/novelupdates/customCSS.css'; site = 'https://www.novelupdates.com/'; @@ -858,30 +858,65 @@ class NovelUpdates implements Plugin.PluginBase { ); } - // 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)), - ); - - 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)), - ); + // Helper to safely check if any element matches a regex or string + const matches = (selector: string, attr: string | null, regex: RegExp) => { + let found = false; + loadedCheerio(selector).each((_, el) => { + const val = attr + ? loadedCheerio(el).attr(attr) + : loadedCheerio(el).html() || loadedCheerio(el).text(); + if (val && regex.test(val.toLowerCase())) { + found = true; + return false; + } + }); + return found; + }; - // Manually set WordPress flag for known sites - const manualWordPress = ['etherreads', 'greenztl2', 'soafp']; - if (!isWordPress && domainParts.some(wp => manualWordPress.includes(wp))) { - isWordPress = true; - } + // --- WordPress Detection --- + let isWordPress = [ + // 1. Meta Generator + matches('meta[name="generator"]', 'content', /wordpress|site kit/i), + // 2. Resource Paths (The most reliable way) + matches('link, script, img', 'src', /\/wp-content\/|\/wp-includes\//i), + matches('link', 'href', /\/wp-content\/|\/wp-includes\//i), + // 3. Header Links (REST API, RSD, etc.) + matches('link[rel="https://api.w.org/"]', 'href', /.*/), + matches('link[rel="EditURI"]', 'href', /xmlrpc\.php/i), + // 4. Common Body Classes + matches('body', 'class', /wp-admin|wp-custom-logo|logged-in/i), + // 5. Scripts containing WP globals + matches('script', null, /wp-embed|wp-emoji|wp-block/i), + ].some(Boolean); + + // --- Blogspot / Blogger Detection --- + let isBlogspot = [ + // 1. Meta Tags + matches('meta[name="generator"]', 'content', /blogger/i), + matches( + 'meta[name="google-adsense-platform-domain"]', + 'content', + /blogspot/i, + ), + // 2. Feed links + matches( + 'link[rel="alternate"]', + 'href', + /blogger\.com\/feeds|blogspot\.com\/feeds/i, + ), + // 3. Specific Blogger CSS/Template markers + matches( + 'link', + 'href', + /www\.blogger\.com\/static|www\.blogger\.com\/dyn-css/i, + ), + // 4. Blogger Script Widget Manager + matches( + 'script', + null, + /_WidgetManager\._Init|_WidgetManager\._RegisterWidget/i, + ), + ].some(Boolean); // Handle outlier sites const outliers = [ @@ -947,88 +982,102 @@ class NovelUpdates implements Plugin.PluginBase { * - Zetro Translation (Outlier) */ - // Fetch chapter content based on detected platform + // Define Platform Configurations + const PLATFORM_CONFIG = { + wordpress: { + bloat: [ + '.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-dark-mode-switcher', + '.wp-next-post-navi', + '#hpk', + '#jp-post-flair', + '#textbox', + ], + title: [ + '.entry-title', + '.chapter__title', + '.title-content', + '.wp-block-post-title', + '.title_story', + '#chapter-heading', + '.chapter-title', + 'head title', + 'h1:first-of-type', + 'h2:first-of-type', + '.active', + ], + content: [ + '.chapter__content', + '.entry-content', + '.text_story', + '.post-content', + '.contenta', + '.single_post', + '.main-content', + '.reader-content', + '#content', + '#the-content', + 'article.post', + '.chp_raw', + ], + }, + blogspot: { + bloat: ['.button-container', '.ChapterNav', '.ch-bottom', '.separator'], + title: ['.entry-title', '.post-title', 'head title'], + content: ['.content-post', '.entry-content', '.post-body'], + }, + }; + + // Extraction Logic if (!isWordPress && !isBlogspot) { 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()); - - // 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', - '.chapter-title', - '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 config = isWordPress + ? PLATFORM_CONFIG.wordpress + : PLATFORM_CONFIG.blogspot; + + // Remove platform-specific bloat + config.bloat.forEach(tag => loadedCheerio(tag).remove()); + + // Extract Title (Simplified find) + let chapterTitle = config.title + .map(sel => loadedCheerio(sel).first().text().trim()) + .find(text => text.length > 0); + + // Handle Subtitles const chapterSubtitle = loadedCheerio('.cat-series').first().text() || 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', - '.chp_raw', - ]; - const chapterContent = contentSelectors - .map(sel => loadedCheerio(sel).html()!) + // Extract Content (Scoped search) + const chapterContent = config.content + .map(sel => { + const el = loadedCheerio(sel).first(); + // Ensure we don't pick up empty containers + return el.text().trim().length > 50 ? el.html() : null; + }) .find(html => html); + // Construct Final Text if (chapterTitle && chapterContent) { chapterText = `

${chapterTitle}



${chapterContent}`; } else { - chapterText = chapterContent; + chapterText = chapterContent || ''; } } From 347c07b27d94cb55259cf04aff00a180ace23bc1 Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 20:06:28 +0100 Subject: [PATCH 08/16] further updates --- plugins/english/novelupdates.ts | 95 ++++----------------------------- 1 file changed, 10 insertions(+), 85 deletions(-) diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index 6c0394312..1422aa29c 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -6,7 +6,7 @@ import { Plugin } from '@/types/plugin'; class NovelUpdates implements Plugin.PluginBase { id = 'novelupdates'; name = 'Novel Updates'; - version = '0.9.9'; + version = '0.9.10'; icon = 'src/en/novelupdates/icon.png'; customCSS = 'src/en/novelupdates/customCSS.css'; site = 'https://www.novelupdates.com/'; @@ -196,43 +196,6 @@ class NovelUpdates implements Plugin.PluginBase { } } // 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()!; - break; - } - // 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)) { - el.removeAttr('style').addClass('arcane_box_white'); // White box - } else if ( - style.includes('text-transform: uppercase') && - /text-shadow:.*blue/.test(style) - ) { - el.removeAttr('style').addClass('arcane_title_blue'); // Blue title - } else if (/text-shadow:.*blue/.test(style)) { - el.removeAttr('style').addClass('arcane_text_blue'); // Blue text - } else if (/text-shadow:.*lightyellow/.test(style)) { - el.removeAttr('style').addClass('arcane_text_lightyellow'); // Lightyellow text - } else if (/color:.*#ff00ff/.test(style)) { - el.removeAttr('style').addClass('arcane_text_pink'); // Pink text - } - }, - ); - chapterContent = loadedCheerio('.entry-content').html()!; - break; - } - // Last edited in 0.9.0 by Batorian - 19/03/2025 case 'asuratls': { const titleElement = loadedCheerio('.post-body div b').first(); chapterTitle = titleElement.text(); @@ -437,24 +400,6 @@ class NovelUpdates implements Plugin.PluginBase { } } // 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((_, el) => { - const elementText = loadedCheerio(el).text(); - if (elementText.includes(chapterString_helscans)) { - chapterTitle = elementText; - loadedCheerio(el).remove(); - return false; - } - }); - chapterContent = loadedCheerio('#readerarea.rdminimal').html()!; - break; - } - // Last edited in 0.9.0 by Batorian - 19/03/2025 case 'hiraethtranslation': { chapterTitle = loadedCheerio('li.active').first().text(); chapterContent = loadedCheerio('.text-left').html()!; @@ -466,7 +411,7 @@ class NovelUpdates implements Plugin.PluginBase { chapterContent = loadedCheerio('#chapter-content').html()!; break; } - // Last edited in 0.9.0 by Batorian - 19/03/2025 + // Last edited in 0.9.5 by Batorian - 26/12/2025 case 'infinitenoveltranslations': { // Get the chapter link from the main page const url = loadedCheerio('article > p > a').first().attr('href')!; @@ -475,8 +420,8 @@ class NovelUpdates implements Plugin.PluginBase { const body = await response.text(); loadedCheerio = parseHTML(body); } - chapterContent = loadedCheerio('.hentry').html()!; - chapterTitle = loadedCheerio('.page-entry-title').text(); + chapterContent = loadedCheerio('.entry-content').html()!; + chapterTitle = loadedCheerio('.entry-title').text(); break; } // Last edited in 0.9.0 by Batorian - 19/03/2025 @@ -804,19 +749,6 @@ class NovelUpdates implements Plugin.PluginBase { chapterText = await fetchApi(json).then(r => r.text()); break; } - // Last edited in 0.9.0 by Batorian - 19/03/2025 - case 'zetrotranslation': { - chapterContent = loadedCheerio('.text-left').html()!; - const titleElement = loadedCheerio('.text-left h2').first(); - if (titleElement.length) { - chapterTitle = titleElement.text(); - titleElement.remove(); - chapterContent = loadedCheerio('.text-left').html()!; - } else if (chapterContent) { - chapterTitle = loadedCheerio('.active').first().text(); - } - break; - } } if (!chapterText) { if (chapterTitle) { @@ -920,11 +852,8 @@ class NovelUpdates implements Plugin.PluginBase { // Handle outlier sites const outliers = [ - 'anotivereads', - 'arcanetranslations', 'asuratls', 'fictionread', - 'helscans', 'hiraethtranslation', 'infinitenoveltranslations', 'machineslicedbread', @@ -934,7 +863,6 @@ class NovelUpdates implements Plugin.PluginBase { 'stabbingwithasyringe', 'tinytranslation', 'vampiramtl', - 'zetrotranslation', ]; if (domainParts.some(d => outliers.includes(d))) { isWordPress = false; @@ -952,18 +880,13 @@ class NovelUpdates implements Plugin.PluginBase { * - Toasteful * * WordPress sites: - * - Anomlaously Creative (Outlier) - * - Arcane Translations (Outlier) - * - Blossom Translation * - Dumah's Translations - * - ElloMTL * - Ether Reads * - Femme Fables * - Gadgetized Panda Translation - * - Gem Novels * - Goblinslate - * - Hel Scans (Outlier) * - Hiraeth Translation (Outlier) + * - Infinite Novel Translations (Outlier) * - ippotranslations * - JATranslations * - Light Novels Translations @@ -972,14 +895,15 @@ class NovelUpdates implements Plugin.PluginBase { * - Neosekai Translations * - Noice Translations * - Shanghai Fantasy - * - Soafp (Manually added) + * - Sleepy Translations + * - Soafp * - Stabbing with a Syringe (Outlier) * - StoneScape * - TinyTL (Outlier) * - VampiraMTL (Outlier) * - Wonder Novels * - Yong Library - * - Zetro Translation (Outlier) + * - Zetro Translation */ // Define Platform Configurations @@ -1060,7 +984,8 @@ class NovelUpdates implements Plugin.PluginBase { // Handle Subtitles const chapterSubtitle = loadedCheerio('.cat-series').first().text() || - loadedCheerio('h1.leading-none ~ span').first().text(); + loadedCheerio('h1.leading-none ~ span').first().text() || + loadedCheerio('.breadcrumb .active').first().text(); if (chapterSubtitle) chapterTitle = chapterSubtitle; From 8f61c9586f53b10319477ea7a5a33436d5b1e7e7 Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 20:23:52 +0100 Subject: [PATCH 09/16] Added Dreamy Translations --- plugins/english/novelupdates.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index 1422aa29c..a54bfacbd 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -6,7 +6,7 @@ import { Plugin } from '@/types/plugin'; class NovelUpdates implements Plugin.PluginBase { id = 'novelupdates'; name = 'Novel Updates'; - version = '0.9.10'; + version = '0.9.11'; icon = 'src/en/novelupdates/icon.png'; customCSS = 'src/en/novelupdates/customCSS.css'; site = 'https://www.novelupdates.com/'; @@ -311,6 +311,11 @@ class NovelUpdates implements Plugin.PluginBase { chapterContent = loadedCheerio('.chapter__content').html()!; break; } + // Last edited in 0.9.5 by Batorian - 26/12/2025 + case 'dreamy-translations': { + chapterTitle = loadedCheerio('h1 > span').first().text(); + chapterContent = loadedCheerio('.chapter-content').html()!; + } // Last edited in 0.9.0 by Batorian - 19/03/2025 case 'fictionread': { bloatElements = [ @@ -869,7 +874,7 @@ class NovelUpdates implements Plugin.PluginBase { isBlogspot = false; } - // Last edited in 0.9.2 - 08/09/2025 + // Last edited in 0.9.5 - 26/12/2025 /** * Blogspot sites: * - ΒΌ-Assed From b0aa6760ed604a8d4f2ff58ccfad421dc921d490 Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 21:04:47 +0100 Subject: [PATCH 10/16] fix --- plugins/english/novelupdates.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index a54bfacbd..cffa142d2 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -6,7 +6,7 @@ import { Plugin } from '@/types/plugin'; class NovelUpdates implements Plugin.PluginBase { id = 'novelupdates'; name = 'Novel Updates'; - version = '0.9.11'; + version = '0.9.12'; icon = 'src/en/novelupdates/icon.png'; customCSS = 'src/en/novelupdates/customCSS.css'; site = 'https://www.novelupdates.com/'; @@ -399,7 +399,8 @@ class NovelUpdates implements Plugin.PluginBase { }) .get(); - chapterContent = paragraphs.filter(Boolean).join('
'); + chapterContent = + paragraphs.filter(Boolean).join('
') + 'It works!'; } catch (error) { throw new Error(`Failed to parse GreenzTL chapter: ${error}`); } From f4e90bbb42533cfe76b4e0f739f226e277f4a1f3 Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 21:12:56 +0100 Subject: [PATCH 11/16] fix --- plugins/english/novelupdates.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index cffa142d2..e5d5a792e 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -6,7 +6,7 @@ import { Plugin } from '@/types/plugin'; class NovelUpdates implements Plugin.PluginBase { id = 'novelupdates'; name = 'Novel Updates'; - version = '0.9.12'; + version = '0.9.13'; icon = 'src/en/novelupdates/icon.png'; customCSS = 'src/en/novelupdates/customCSS.css'; site = 'https://www.novelupdates.com/'; @@ -975,6 +975,7 @@ class NovelUpdates implements Plugin.PluginBase { if (!isWordPress && !isBlogspot) { chapterText = await this.getChapterBody(loadedCheerio, domainParts, url); } else { + throw new Error('Triggered platform-specific extraction.'); const config = isWordPress ? PLATFORM_CONFIG.wordpress : PLATFORM_CONFIG.blogspot; From a01ddd0c0c77041efb9a94bb57ab01effe220579 Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 21:20:21 +0100 Subject: [PATCH 12/16] fix --- plugins/english/novelupdates.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index e5d5a792e..8a71acede 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -6,7 +6,7 @@ import { Plugin } from '@/types/plugin'; class NovelUpdates implements Plugin.PluginBase { id = 'novelupdates'; name = 'Novel Updates'; - version = '0.9.13'; + version = '0.9.14'; icon = 'src/en/novelupdates/icon.png'; customCSS = 'src/en/novelupdates/customCSS.css'; site = 'https://www.novelupdates.com/'; @@ -404,6 +404,7 @@ class NovelUpdates implements Plugin.PluginBase { } catch (error) { throw new Error(`Failed to parse GreenzTL chapter: ${error}`); } + break; } // Last edited in 0.9.0 by Batorian - 19/03/2025 case 'hiraethtranslation': { @@ -975,7 +976,6 @@ class NovelUpdates implements Plugin.PluginBase { if (!isWordPress && !isBlogspot) { chapterText = await this.getChapterBody(loadedCheerio, domainParts, url); } else { - throw new Error('Triggered platform-specific extraction.'); const config = isWordPress ? PLATFORM_CONFIG.wordpress : PLATFORM_CONFIG.blogspot; From 17f50d17beb8e63a9e9b2e71e726d5b3b692d9d5 Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 21:28:06 +0100 Subject: [PATCH 13/16] fix --- plugins/english/novelupdates.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index 8a71acede..64a597473 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -6,7 +6,7 @@ import { Plugin } from '@/types/plugin'; class NovelUpdates implements Plugin.PluginBase { id = 'novelupdates'; name = 'Novel Updates'; - version = '0.9.14'; + version = '0.9.15'; icon = 'src/en/novelupdates/icon.png'; customCSS = 'src/en/novelupdates/customCSS.css'; site = 'https://www.novelupdates.com/'; @@ -390,9 +390,9 @@ class NovelUpdates implements Plugin.PluginBase { const chapterCheerio = parseHTML(json.data.content); - //chapterContent = chapterCheerio.html()!; + chapterContent = chapterCheerio('.doc-content').html()!; - const paragraphs = chapterCheerio('p') + /* const paragraphs = chapterCheerio('p') .map((_, el) => { const pContent = chapterCheerio(el).html(); return pContent ? `

${pContent}

` : null; @@ -400,7 +400,7 @@ class NovelUpdates implements Plugin.PluginBase { .get(); chapterContent = - paragraphs.filter(Boolean).join('
') + 'It works!'; + paragraphs.filter(Boolean).join(''); */ } catch (error) { throw new Error(`Failed to parse GreenzTL chapter: ${error}`); } From cc3f9c6e4801753de565eb25a8e4b4e961cbd358 Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 21:39:30 +0100 Subject: [PATCH 14/16] test --- plugins/english/novelupdates.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index 64a597473..664def7cc 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -6,7 +6,7 @@ import { Plugin } from '@/types/plugin'; class NovelUpdates implements Plugin.PluginBase { id = 'novelupdates'; name = 'Novel Updates'; - version = '0.9.15'; + version = '0.9.16'; icon = 'src/en/novelupdates/icon.png'; customCSS = 'src/en/novelupdates/customCSS.css'; site = 'https://www.novelupdates.com/'; @@ -390,7 +390,8 @@ class NovelUpdates implements Plugin.PluginBase { const chapterCheerio = parseHTML(json.data.content); - chapterContent = chapterCheerio('.doc-content').html()!; + //chapterContent = chapterCheerio('.doc-content').html()!; + chapterContent = chapterCheerio.html()!; /* const paragraphs = chapterCheerio('p') .map((_, el) => { From 05952a08602048af22ecd6b1e78dce23e2b74d79 Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 23:34:17 +0100 Subject: [PATCH 15/16] Add Novels Hub --- plugins/english/novelupdates.ts | 90 ++++++++++-- .../static/src/en/novelupdates/customCSS.css | 128 +++++++++++------- 2 files changed, 156 insertions(+), 62 deletions(-) diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index 664def7cc..6fa8ac8d7 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -6,7 +6,7 @@ import { Plugin } from '@/types/plugin'; class NovelUpdates implements Plugin.PluginBase { id = 'novelupdates'; name = 'Novel Updates'; - version = '0.9.16'; + version = '0.9.17'; icon = 'src/en/novelupdates/icon.png'; customCSS = 'src/en/novelupdates/customCSS.css'; site = 'https://www.novelupdates.com/'; @@ -388,20 +388,12 @@ class NovelUpdates implements Plugin.PluginBase { const response = await fetchApi(apiUrl); const json = await response.json(); + const chapterName = json.data.name; + const chapterNumber = json.data.chapterNumber; const chapterCheerio = parseHTML(json.data.content); - //chapterContent = chapterCheerio('.doc-content').html()!; + chapterTitle = `Chapter ${chapterNumber} - ${chapterName}`; chapterContent = chapterCheerio.html()!; - - /* const paragraphs = chapterCheerio('p') - .map((_, el) => { - const pContent = chapterCheerio(el).html(); - return pContent ? `

${pContent}

` : null; - }) - .get(); - - chapterContent = - paragraphs.filter(Boolean).join(''); */ } catch (error) { throw new Error(`Failed to parse GreenzTL chapter: ${error}`); } @@ -515,6 +507,80 @@ class NovelUpdates implements Plugin.PluginBase { chapterContent = loadedCheerio('.halChap--kontenInner ').html()!; break; } + case 'novelshub': { + const segments = chapterPath.split('/'); + const novelSlug = segments[segments.length - 2]; + const chapterSlug = segments[segments.length - 1]; + const apiUrl = `https://api.novelshub.org/api/chapter?mangaslug=${novelSlug}&chapterslug=${chapterSlug}`; + + try { + const response = await fetchApi(apiUrl); + const json = await response.json(); + + const chapterNumber = json.chapter.number; + const chapterCheerio = parseHTML(json.chapter.content); + + chapterTitle = `Chapter ${chapterNumber}`; + chapterCheerio('div').each((_, element) => { + const el = chapterCheerio(element); + const style = el.attr('style'); + if (!style) return; // Skip elements without inline styles + // Orange box + if (/border:.*#ff6b00/.test(style)) { + el.removeAttr('style').addClass('novels-hub_box_orange'); + // Orange box title + } else if ( + /color:.*#ff6b00.*text-transform:.*uppercase/.test(style) + ) { + el.removeAttr('style').addClass('novels-hub_box-title_orange'); + // Orange box text + } else if (/color:.*white.*border-top:.*#ff6b00/.test(style)) { + el.removeAttr('style').addClass('novels-hub_box-text_orange'); + // Green box + } else if (/border:.*#00ff88/.test(style)) { + el.removeAttr('style').addClass('novels-hub_box_green'); + // Green box title + } else if ( + /color:.*#00ff88.*text-transform:.*uppercase/.test(style) + ) { + el.removeAttr('style').addClass('novels-hub_box-title_green'); + // Green comment + } else if (/border-left:.*#00ff88/.test(style)) { + el.removeAttr('style').addClass('novels-hub_comment_green'); + // Blue box + } else if (/border:.*#0066ff/.test(style)) { + el.removeAttr('style').addClass('novels-hub_box_blue'); + // Blue box title + } else if ( + /color:.*#0099ff.*text-transform:.*uppercase/.test(style) + ) { + el.removeAttr('style').addClass('novels-hub_box-title_blue'); + // Blue box text + } else if (/color:.*#d0d0d0/.test(style)) { + el.removeAttr('style').addClass('novels-hub_box-text_blue'); + } + }); + chapterCheerio('span').each((_, element) => { + const el = chapterCheerio(element); + const style = el.attr('style'); + if (!style) return; // Skip elements without inline styles + // Red text + if (/color:.*#ff6b6b/.test(style)) { + el.removeAttr('style').addClass('novels-hub_text_red'); + // Blue text + } else if (/color:.*#4d9fff/.test(style)) { + el.removeAttr('style').addClass('novels-hub_text_blue'); + // Purple text + } else if (/color:.*#a78bfa/.test(style)) { + el.removeAttr('style').addClass('novels-hub_text_purple'); + } + }); + chapterContent = chapterCheerio.html()!; + } catch (error) { + throw new Error(`Failed to parse GreenzTL chapter: ${error}`); + } + break; + } // Last edited in 0.9.0 by Batorian - 19/03/2025 case 'novelworldtranslations': { bloatElements = ['.separator img']; diff --git a/public/static/src/en/novelupdates/customCSS.css b/public/static/src/en/novelupdates/customCSS.css index f9c023c1a..0c809db66 100644 --- a/public/static/src/en/novelupdates/customCSS.css +++ b/public/static/src/en/novelupdates/customCSS.css @@ -1,53 +1,3 @@ -/** - * Novel Updates -> Arcane Translations - * https://www.novelupdates.com/group/arcane-translations/ - * Necessary to display chapterContent correctly - */ - -.arcane_box_blue { - text-align: center; - border: 2px solid #00219b; - padding: 20px; - box-shadow: 0 0 10px #00219b; - background-color: black; - position: relative; - z-index: 1; -} - -.arcane_box_white { - display: block; - text-align: center; - border: 2px solid white; - padding: 20px; - box-shadow: 0 0 10px white; - background-color: black; -} - -.arcane_title_blue { - color: white; - font-weight: bold; - text-transform: uppercase; - margin-bottom: 10px; - text-shadow: 0 0 10px blue; -} - -.arcane_text_blue { - color: white; - border-top: 2px solid white; - padding-top: 10px; - text-shadow: 0 0 8px blue; -} - -.arcane_text_lightyellow { - color: white; - text-shadow: 0 0 8px lightyellow; - font-family: 'Amita', cursive; -} - -.arcane_text_pink { - color: #ff00ff; -} - /** * Novel Updates -> Daoist Quest * https://www.novelupdates.com/group/daoist-quest/ @@ -436,6 +386,84 @@ div.blue-text { flex-direction:column } +/** + * Novel Updates -> Novels Hub + * https://www.novelupdates.com/group/novelshub/ + * Necessary to display chapterContent correctly + */ + .novels-hub_box_orange { + text-align: center; + border: 2px solid #ff6b00; + padding: 20px; + box-shadow: 0 0 15px rgba(255, 107, 0, 0.4); + position: relative; + margin-bottom: 20px; + } + .novels-hub_box-title_orange { + color: #ff6b00; + font-weight: bold; + text-transform: uppercase; + letter-spacing: 2px; + margin-bottom: 15px; + text-shadow: 0 0 10px rgba(255, 107, 0, 0.6); + } + .novels-hub_box-text_orange { + color: white; + line-height: 1.6; + border-top: 1px solid #ff6b00; + padding-top: 15px; + text-shadow: 0 0 5px rgba(255, 255, 255, 0.3); + } + .novels-hub_box_green { + background: #0d0d0d; + border: 2px solid #00ff88; + padding: 15px 20px; + box-shadow: 0 0 10px rgba(0, 255, 136, 0.3); + margin-bottom: 15px; + } + .novels-hub_box-title_green { + color: #00ff88; + font-weight: bold; + text-transform: uppercase; + letter-spacing: 1px; + text-shadow: 0 0 8px rgba(0, 255, 136, 0.5); + } + .novels-hub_comment_green { + background: linear-gradient(135deg, #1a1a1a 0%, #0f0f0f 100%); + border-left: 4px solid #00ff88; + padding: 15px 20px; + margin-bottom: 15px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5); + } + .novels-hub_box_blue { + background: rgba(0, 33, 155, 0.15); + border: 1px solid #0066ff; + border-radius: 4px; + padding: 15px 20px; + margin-bottom: 15px; + backdrop-filter: blur(5px); + box-shadow: inset 0 0 10px rgba(0, 102, 255, 0.1); + } + .novels-hub_box-title_blue { + color: #0099ff; + margin-bottom: 8px; + text-transform: uppercase; + letter-spacing: 1px; + } + .novels-hub_box-text_blue { + color: #d0d0d0; + line-height: 1.6; + } + .novels-hub_text_red { + color: #ff6b6b; + } + .novels-hub_text_blue { + color: #4d9fff; + } + .novels-hub_text_purple { + color: #a78bfa; + } + /** * Novel Updates -> Raei Translations * https://www.novelupdates.com/group/raei-translations/ From ad731de85ee6c4017c8bc9c7ed8b4b5ea9381f52 Mon Sep 17 00:00:00 2001 From: Batorian Date: Fri, 26 Dec 2025 23:37:28 +0100 Subject: [PATCH 16/16] Revert versions --- package.json | 2 +- plugins/english/novelupdates.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7035e42c4..1d30a90d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lnreader-plugins", - "version": "3.1.0", + "version": "3.0.0", "description": "Plugins repo for LNReader", "main": "index.js", "type": "module", diff --git a/plugins/english/novelupdates.ts b/plugins/english/novelupdates.ts index 6fa8ac8d7..26782eb46 100644 --- a/plugins/english/novelupdates.ts +++ b/plugins/english/novelupdates.ts @@ -6,7 +6,7 @@ import { Plugin } from '@/types/plugin'; class NovelUpdates implements Plugin.PluginBase { id = 'novelupdates'; name = 'Novel Updates'; - version = '0.9.17'; + version = '0.9.5'; icon = 'src/en/novelupdates/icon.png'; customCSS = 'src/en/novelupdates/customCSS.css'; site = 'https://www.novelupdates.com/';