From c265c13141c9dc0371ec7eec64c75c118ee0a061 Mon Sep 17 00:00:00 2001 From: 7ui77 Date: Thu, 30 Apr 2026 23:45:08 +0000 Subject: [PATCH 1/7] fix(inoveltranslation): move icon to correct static path --- .../static/src}/en/inoveltranslation/icon.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename {src => public/static/src}/en/inoveltranslation/icon.png (100%) diff --git a/src/en/inoveltranslation/icon.png b/public/static/src/en/inoveltranslation/icon.png similarity index 100% rename from src/en/inoveltranslation/icon.png rename to public/static/src/en/inoveltranslation/icon.png From 591603475951f3a5cfc84a86f42bd185df364cf7 Mon Sep 17 00:00:00 2001 From: 7ui77 Date: Sat, 2 May 2026 16:17:51 +0700 Subject: [PATCH 2/7] fix(inoveltranslation): robust lexical extraction and html fallback --- plugins/english/inoveltranslation.ts | 108 ++++++++++++++++----------- 1 file changed, 65 insertions(+), 43 deletions(-) diff --git a/plugins/english/inoveltranslation.ts b/plugins/english/inoveltranslation.ts index 7ca3591f5..2e22e7ee6 100644 --- a/plugins/english/inoveltranslation.ts +++ b/plugins/english/inoveltranslation.ts @@ -170,35 +170,46 @@ class INovelTranslation implements Plugin.PluginBase { } // ========================================== - // 2. DEEP LEXICAL EXTRACTION ALGORITHM + // 2. ROBUST LEXICAL EXTRACTION ALGORITHM // ========================================== - // 2.1. Basic cleanup of escaped characters in the RSC stream - let cleanText = rscText.replace(/\\"/g, '"').replace(/\\\\/g, '\\'); - - // 2.2. Locate the core content signature (first paragraph children) - const signature = '"children":[{"type":"paragraph"'; - let sigIndex = cleanText.indexOf(signature); + // Use a more reliable signature: the start of the root Lexical object + const signatures = [ + '"root":{"type":"root"', + '\\"root\\":{\\"type\\":\\"root\\"', + '"children":[{"type":"paragraph"', + '\\"children\\":[{\\"type\\":\\"paragraph\\"', + ]; + + let sigIndex = -1; + for (const sig of signatures) { + sigIndex = rscText.indexOf(sig); + if (sigIndex !== -1) break; + } if (sigIndex !== -1) { - // 2.3. Backtrack to find the opening brace { of the Lexical Object - let startIndex = cleanText.lastIndexOf('{', sigIndex); - - // Check if it is within a "root": { ... } object to backtrack one level further - const rootIndex = cleanText.lastIndexOf('"root"', sigIndex); - if (rootIndex !== -1 && rootIndex > startIndex - 30) { - startIndex = cleanText.lastIndexOf('{', rootIndex); + // Backtrack to find the opening brace { of the Lexical Object + let startIndex = rscText.lastIndexOf('{', sigIndex); + + // Check for "content" or "root" before to find the start of the relevant object + const contextKeys = ['"content"', '\\"content\\"', '"root"', '\\"root\\"']; + for (const key of contextKeys) { + const keyIndex = rscText.lastIndexOf(key, sigIndex); + if (keyIndex !== -1 && keyIndex > startIndex - 50) { + startIndex = rscText.lastIndexOf('{', keyIndex); + break; + } } if (startIndex !== -1) { - // 2.4. High-performance Brace Balancing algorithm let braces = 0; let inString = false; let escape = false; let jsonStr = ''; - for (let i = startIndex; i < cleanText.length; i++) { - const char = cleanText[i]; + // Perform brace balancing on the raw stream to preserve escaping + for (let i = startIndex; i < rscText.length; i++) { + const char = rscText[i]; if (escape) { escape = false; continue; @@ -218,59 +229,70 @@ class INovelTranslation implements Plugin.PluginBase { } if (braces === 0 && i > startIndex) { - jsonStr = cleanText.substring(startIndex, i + 1); + jsonStr = rscText.substring(startIndex, i + 1); break; } } if (jsonStr) { try { - // 2.5 Standardize and Parse JSON - // Strip control characters that might break JSON.parse - const safeJson = jsonStr.replace(/[\x00-\x1F\x7F-\x9F]/g, ''); - const parsedData = JSON.parse(safeJson); - let lexicalRoot = parsedData.root || parsedData; + // Attempt to parse directly (if it's unescaped RSC) + let safeJson = jsonStr.replace(/[\x00-\x1F\x7F-\x9F]/g, ''); + let parsedData; + try { + parsedData = JSON.parse(safeJson); + } catch { + // If fails, it might be escaped, so clean it up and try again + const cleanJson = jsonStr + .replace(/\\"/g, '"') + .replace(/\\\\/g, '\\') + .replace(/[\x00-\x1F\x7F-\x9F]/g, ''); + parsedData = JSON.parse(cleanJson); + } + let lexicalRoot = parsedData.root || parsedData.content?.root || parsedData; if (lexicalRoot && lexicalRoot.children) { return this.lexicalToHtml(lexicalRoot); } } catch (e: any) { - // ========================================== - // 3. ULTIMATE FAILSAFE (REGEX TEXT EXTRACTION) - // ========================================== - // If JSON parsing fails due to corrupted RSC stream segments, - // we extract all "text":"..." fragments to reconstruct the story. + // Fallback to regex text extraction if JSON parsing fails let fallbackHtml = ''; - const textMatches = jsonStr.match(/"text":"(.*?)"/g); + const textMatches = jsonStr.match(/\\?"text\\?"\s*:\s*\\?"(.*?)\\?"/g); if (textMatches && textMatches.length > 0) { textMatches.forEach(m => { - let text = m.substring(8, m.length - 1); - if (text.trim() && text !== ' ') { + let text = m.match(/: ?"?(.*?)"?$/)?.[1] || ''; + text = text.replace(/\\"/g, '"').replace(/\\\\/g, '\\'); + if (text.trim() && text !== ' ' && !text.startsWith('Ch. ')) { fallbackHtml += `

${text}

`; } }); - return fallbackHtml; + if (fallbackHtml) return fallbackHtml; } - - throw new Error( - `JSON Parse error: ${e.message}. Data snippet: ${jsonStr.substring(0, 500)}`, - ); } } } } // ========================================== - // 4. Final HTML Scavenger Fallback + // 3. HTML SCAVENGER FALLBACK // ========================================== - const $ = loadCheerio(rscText); - let htmlContent = $( - 'main > section[data-sentry-component="RichText"]', - ).html(); - if (htmlContent) return htmlContent; + // If RSC extraction failed, try fetching the standard HTML page + try { + const htmlResponse = await fetchApi(this.site + chapterPath, { + headers: this.HEADERS, + }); + const htmlText = await htmlResponse.text(); + const $ = loadCheerio(htmlText); + const htmlContent = $( + 'main > section[data-sentry-component="RichText"]', + ).html(); + if (htmlContent) return htmlContent; + } catch (e) { + // Ignore fallback errors and throw the final error below + } throw new Error( - 'Story content not found. Cloudflare might be blocking the request or the page structure has changed. Please try opening in WebView first.', + 'Story content not found. The page structure might have changed. Please try opening in WebView to verify.', ); } From c746376625dcfe52826fbbab8c9d39276fde7eab Mon Sep 17 00:00:00 2001 From: 7ui77 Date: Sat, 2 May 2026 16:23:45 +0700 Subject: [PATCH 3/7] bump: inoveltranslation version to 1.0.1 --- plugins/english/inoveltranslation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/english/inoveltranslation.ts b/plugins/english/inoveltranslation.ts index 2e22e7ee6..7188062eb 100644 --- a/plugins/english/inoveltranslation.ts +++ b/plugins/english/inoveltranslation.ts @@ -11,7 +11,7 @@ class INovelTranslation implements Plugin.PluginBase { name = 'iNovelTranslation'; icon = 'src/en/inoveltranslation/icon.png'; site = 'https://inoveltranslation.com'; - version = '1.0.0'; + version = '1.0.1'; filters: Filters | undefined = undefined; pluginSettings = { From 09b66def9beb9608f2f03322f91d8a3027973f19 Mon Sep 17 00:00:00 2001 From: 7ui77 Date: Sat, 2 May 2026 19:52:48 +0700 Subject: [PATCH 4/7] feat(en): add Wuxia Dreams plugin --- plugins/english/wuxiadreams.ts | 250 ++++++++++++++++++++++ public/static/src/en/wuxiadreams/icon.png | Bin 0 -> 29400 bytes 2 files changed, 250 insertions(+) create mode 100644 plugins/english/wuxiadreams.ts create mode 100644 public/static/src/en/wuxiadreams/icon.png diff --git a/plugins/english/wuxiadreams.ts b/plugins/english/wuxiadreams.ts new file mode 100644 index 000000000..83ae92543 --- /dev/null +++ b/plugins/english/wuxiadreams.ts @@ -0,0 +1,250 @@ +import { CheerioAPI, load as parseHTML } from 'cheerio'; +import { fetchApi } from '@libs/fetch'; +import { Plugin } from '@/types/plugin'; +import { Filters, FilterTypes } from '@libs/filterInputs'; +import { defaultCover } from '@libs/defaultCover'; +import { NovelStatus } from '@libs/novelStatus'; + +class WuxiaDreams implements Plugin.PluginBase { + id = 'wuxiadreams'; + name = 'Wuxia Dreams'; + version = '1.0.0'; + icon = 'src/en/wuxiadreams/icon.png'; + site = 'https://wuxiadreams.com/'; + + async popularNovels( + page: number, + { + showLatestNovels, + filters, + }: Plugin.PopularNovelsOptions, + ): Promise { + let url = this.site; + + if (showLatestNovels) { + if (page > 1) { + url = `${this.site}novels?page=${page}`; + } else { + url = this.site; + } + } else if (filters?.genre?.value) { + url = `${this.site}genre/${filters.genre.value}?page=${page}`; + } else { + if (page > 1) { + url = `${this.site}novels?page=${page}`; + } else { + url = this.site; + } + } + + const body = await fetchApi(url).then(r => r.text()); + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + + if (url === this.site) { + const sectionTitle = showLatestNovels ? 'Latest updates' : 'Most Viewed'; + loadedCheerio('section').each((i, section) => { + const title = loadedCheerio(section).find('h2').text().trim(); + if (title.includes(sectionTitle)) { + loadedCheerio(section) + .find('a[href^="/novel/"]') + .each((j, ele) => { + const novelName = loadedCheerio(ele).find('h3').text().trim(); + const novelCover = loadedCheerio(ele).find('img').attr('src'); + const novelUrl = loadedCheerio(ele).attr('href'); + + if (novelName && novelUrl) { + novels.push({ + name: novelName, + cover: novelCover || defaultCover, + path: novelUrl.replace('/novel/', ''), + }); + } + }); + } + }); + } else { + loadedCheerio('a[href^="/novel/"]').each((i, ele) => { + const novelName = loadedCheerio(ele).find('h3').text().trim(); + const novelCover = loadedCheerio(ele).find('img').attr('src'); + const novelUrl = loadedCheerio(ele).attr('href'); + + if (novelName && novelUrl) { + // Avoid duplicates if same novel is linked multiple times + if (!novels.find(n => n.path === novelUrl.replace('/novel/', ''))) { + novels.push({ + name: novelName, + cover: novelCover || defaultCover, + path: novelUrl.replace('/novel/', ''), + }); + } + } + }); + } + + return novels; + } + + async parseNovel(novelPath: string): Promise { + const url = `${this.site}novel/${novelPath}`; + const body = await fetchApi(url).then(r => r.text()); + const loadedCheerio = parseHTML(body); + + const novel: Plugin.SourceNovel = { + path: novelPath, + name: + loadedCheerio('h1').text().trim() || + loadedCheerio('title') + .text() + .replace(' (novel)', '') + .split(' - ')[0] + .trim(), + cover: + loadedCheerio('meta[property="og:image"]').attr('content') || + defaultCover, + summary: loadedCheerio('h3:contains("Synopsis")').next().text().trim(), + status: NovelStatus.Unknown, + }; + + // Author + novel.author = loadedCheerio('span:contains("Author:")') + .next('a') + .find('span') + .first() + .text() + .trim(); + + // Genres + const genres: string[] = []; + loadedCheerio('div:contains("Genres")') + .parent() + .find('a[href^="/genre/"]') + .each((i, ele) => { + genres.push(loadedCheerio(ele).text().trim()); + }); + novel.genres = genres.join(', '); + + // Status + const statusLabel = loadedCheerio('span:contains("Status")') + .next() + .text() + .trim(); + if (statusLabel.toLowerCase().includes('completed')) { + novel.status = NovelStatus.Completed; + } else if (statusLabel.toLowerCase().includes('ongoing')) { + novel.status = NovelStatus.Ongoing; + } + + // Chapters + const chapters: Plugin.ChapterItem[] = []; + + // Handle pagination + let totalPages = 1; + + const pageInfo = loadedCheerio('div:contains("Page")').text(); + const match = pageInfo.match(/Page\s+\d+\s+of\s+(\d+)/i); + if (match) { + totalPages = parseInt(match[1]); + } + + for (let p = 1; p <= totalPages; p++) { + let pageCheerio = loadedCheerio; + if (p > 1) { + const pageUrl = `${url}?page=${p}`; + const pageBody = await fetchApi(pageUrl).then(r => r.text()); + pageCheerio = parseHTML(pageBody); + } + + pageCheerio('a[href^="/novel/' + novelPath + '/chapter-"]').each( + (i, ele) => { + const chapterName = pageCheerio(ele) + .find('span') + .first() + .text() + .trim(); + const chapterUrl = pageCheerio(ele).attr('href'); + const releaseDate = pageCheerio(ele) + .find('div > span:first-child') + .text() + .trim(); + + if (chapterName && chapterUrl) { + chapters.push({ + name: chapterName, + path: chapterUrl.substring(1), // remove leading slash + releaseTime: releaseDate, + }); + } + }, + ); + } + + novel.chapters = chapters; + return novel; + } + + async parseChapter(chapterPath: string): Promise { + const url = `${this.site}${chapterPath}`; + const body = await fetchApi(url).then(r => r.text()); + const loadedCheerio = parseHTML(body); + + // Remove ads or unwanted elements if necessary + loadedCheerio('script').remove(); + loadedCheerio('style').remove(); + + const chapterContent = + loadedCheerio('article.chapter-content-container').html() || ''; + return chapterContent; + } + + async searchNovels( + searchTerm: string, + page: number, + ): Promise { + const url = `${this.site}novels?q=${encodeURIComponent(searchTerm)}&page=${page}`; + const body = await fetchApi(url).then(r => r.text()); + const loadedCheerio = parseHTML(body); + + const novels: Plugin.NovelItem[] = []; + loadedCheerio('a[href^="/novel/"]').each((i, ele) => { + const novelName = loadedCheerio(ele).find('h3').text().trim(); + const novelCover = loadedCheerio(ele).find('img').attr('src'); + const novelUrl = loadedCheerio(ele).attr('href'); + + if (novelName && novelUrl) { + if (!novels.find(n => n.path === novelUrl.replace('/novel/', ''))) { + novels.push({ + name: novelName, + cover: novelCover || defaultCover, + path: novelUrl.replace('/novel/', ''), + }); + } + } + }); + + return novels; + } + + filters = { + genre: { + type: FilterTypes.Picker, + label: 'Genre', + value: '', + options: [ + { label: 'None', value: '' }, + { label: 'Wuxia', value: 'wuxia' }, + { label: 'Xianxia', value: 'xianxia' }, + { label: 'Xuanhuan', value: 'xuanhuan' }, + { label: 'Historical', value: 'historical' }, + { label: 'Romance', value: 'romance' }, + { label: 'Fantasy', value: 'fantasy' }, + { label: 'Drama', value: 'drama' }, + { label: 'Adventure', value: 'adventure' }, + { label: 'Action', value: 'action' }, + ], + }, + } satisfies Filters; +} + +export default new WuxiaDreams(); diff --git a/public/static/src/en/wuxiadreams/icon.png b/public/static/src/en/wuxiadreams/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a7d9e19cb3587491941d84951c98730496dbb5ad GIT binary patch literal 29400 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 Date: Tue, 5 May 2026 04:25:00 +0000 Subject: [PATCH 5/7] feat: add Wuxia Dreams plugin --- plugins/english/wuxiadreams.ts | 219 ++++++++++++++++++++++ public/static/src/en/wuxiadreams/icon.png | Bin 0 -> 29400 bytes 2 files changed, 219 insertions(+) create mode 100644 plugins/english/wuxiadreams.ts create mode 100644 public/static/src/en/wuxiadreams/icon.png diff --git a/plugins/english/wuxiadreams.ts b/plugins/english/wuxiadreams.ts new file mode 100644 index 000000000..f63b5c61a --- /dev/null +++ b/plugins/english/wuxiadreams.ts @@ -0,0 +1,219 @@ +import { CheerioAPI, load as parseHTML } from 'cheerio'; +import { fetchApi } from '@libs/fetch'; +import { Plugin } from '@/types/plugin'; +import { NovelStatus } from '@libs/novelStatus'; +import { FilterTypes, Filters } from '@libs/filterInputs'; + +class WuxiaDreams implements Plugin.PagePlugin { + id = 'wuxiadreams'; + name = 'Wuxia Dreams'; + icon = 'src/en/wuxiadreams/icon.png'; + site = 'https://wuxiadreams.com/'; + version = '1.0.0'; + + parseNovels(loadedCheerio: CheerioAPI): Plugin.NovelItem[] { + const novels: Plugin.NovelItem[] = []; + + loadedCheerio('div.grid > a[href^="/novel/"]').each((idx, ele) => { + const name = loadedCheerio(ele).find('h3').text().trim(); + const cover = loadedCheerio(ele).find('img').attr('src'); + const url = loadedCheerio(ele).attr('href'); + + if (name && url) { + novels.push({ + name, + cover: cover?.startsWith('http') + ? cover + : cover + ? this.site + cover.replace(/^\//, '') + : undefined, + path: url.replace(/^\//, ''), + }); + } + }); + + return novels; + } + + async popularNovels( + pageNo: number, + { filters }: Plugin.PopularNovelsOptions, + ): Promise { + let url = `${this.site}novels?page=${pageNo}`; + + if (filters.sort?.value) { + url += `&sort=${filters.sort.value}`; + } + + const response = await fetchApi(url); + const body = await response.text(); + const $ = parseHTML(body); + + return this.parseNovels($); + } + + async parseNovel( + novelPath: string, + ): Promise { + const response = await fetchApi(`${this.site}${novelPath}`); + const body = await response.text(); + const $ = parseHTML(body); + + const cover = $('main img').first().attr('src'); + const novel: Plugin.SourceNovel & { totalPages: number } = { + path: novelPath, + name: $('h1').text().trim() || 'Untitled', + cover: cover?.startsWith('http') + ? cover + : cover + ? this.site + cover.replace(/^\//, '') + : undefined, + summary: '', + chapters: [], + totalPages: 1, + }; + + // Summary + const summaryElement = $('h3:contains("Synopsis")').next(); + summaryElement.find('br').replaceWith('\n'); + novel.summary = summaryElement + .text() + .split('\n') + .map(line => line.trim()) + .join('\n') + .trim(); + + // Author + const authorLink = $('a[href^="/author/"]'); + if (authorLink.length) { + novel.author = authorLink.text().trim(); + } else { + novel.author = $('span:contains("Author:")').next().text().trim(); + } + + if (!novel.author) { + novel.author = $('div:contains("Author")').next().text().trim(); + } + + // Status + const statusText = $('span:contains("completed"), span:contains("ongoing")') + .first() + .text() + .toLowerCase(); + + if (statusText.includes('completed')) { + novel.status = NovelStatus.Completed; + } else if (statusText.includes('ongoing')) { + novel.status = NovelStatus.Ongoing; + } else { + novel.status = NovelStatus.Unknown; + } + + // Tags as Genres + const tags: string[] = []; + $('div:contains("Tags")') + .next() + .find('a[href^="/tag/"]') + .each((i, e) => { + tags.push($(e).text().trim()); + }); + novel.genres = tags.join(','); + + // Chapters + novel.chapters = this.parseChapters($); + + // Pagination + const lastPageLink = $('a[aria-label="Last page"]').attr('href'); + if (lastPageLink) { + const match = lastPageLink.match(/page=(\d+)/); + if (match) { + novel.totalPages = parseInt(match[1], 10); + } + } else { + const pageText = $('div:contains("Page")').text(); + const match = pageText.match(/Page\s+\d+\s+of\s+(\d+)/); + if (match) { + novel.totalPages = parseInt(match[1], 10); + } + } + + return novel; + } + + parseChapters($: CheerioAPI): Plugin.ChapterItem[] { + const chapters: Plugin.ChapterItem[] = []; + + $('a[href*="/chapter-"]').each((i, e) => { + const href = $(e).attr('href'); + if (href?.includes('/chapter-index-drawer')) return; + + const name = + $(e).find('span').first().text().trim() || $(e).text().trim(); + + if (name.toLowerCase().includes('start reading')) return; + + const path = href?.replace(/^\//, ''); + const releaseTime = $(e) + .find('span:contains("202")') + .first() + .text() + .trim(); + + if (name && path) { + chapters.push({ + name, + path, + releaseTime, + }); + } + }); + + return chapters; + } + + async parsePage(novelPath: string, page: string): Promise { + const response = await fetchApi(`${this.site}${novelPath}?page=${page}`); + const body = await response.text(); + const $ = parseHTML(body); + + return { + chapters: this.parseChapters($), + }; + } + + async parseChapter(chapterPath: string): Promise { + const response = await fetchApi(`${this.site}${chapterPath}`); + const body = await response.text(); + const $ = parseHTML(body); + + const content = $('article.chapter-content-container').html(); + return content || 'Content not found.'; + } + + async searchNovels( + searchTerm: string, + pageNo: number, + ): Promise { + const url = `${this.site}novels?q=${encodeURIComponent(searchTerm)}&page=${pageNo}`; + + const response = await fetchApi(url); + const body = await response.text(); + const $ = parseHTML(body); + + return this.parseNovels($); + } + + filters = { + sort: { + label: 'Sort by', + value: 'update', + options: [ + { label: 'Latest Update', value: 'update' }, + { label: 'Highest Rated', value: 'score' }, + ], + type: FilterTypes.Picker, + }, + } satisfies Filters; +} + +export default new WuxiaDreams(); diff --git a/public/static/src/en/wuxiadreams/icon.png b/public/static/src/en/wuxiadreams/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a7d9e19cb3587491941d84951c98730496dbb5ad GIT binary patch literal 29400 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 Date: Tue, 5 May 2026 06:01:37 +0000 Subject: [PATCH 6/7] refactor(wuxiadreams): align with NovelBuddy-style standards and improve robustness --- plugins/english/wuxiadreams.ts | 82 +++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/plugins/english/wuxiadreams.ts b/plugins/english/wuxiadreams.ts index f63b5c61a..8d393132d 100644 --- a/plugins/english/wuxiadreams.ts +++ b/plugins/english/wuxiadreams.ts @@ -9,7 +9,27 @@ class WuxiaDreams implements Plugin.PagePlugin { name = 'Wuxia Dreams'; icon = 'src/en/wuxiadreams/icon.png'; site = 'https://wuxiadreams.com/'; - version = '1.0.0'; + version = '1.0.1'; + + private resolveUrl(path?: string) { + if (!path) return undefined; + return path.startsWith('http') ? path : this.site + path.replace(/^\//, ''); + } + + async getCheerio(url: string): Promise { + const r = await fetchApi(url); + if (!r.ok) + throw new Error( + 'Could not reach site (' + r.status + ') try to open in webview.', + ); + const $ = parseHTML(await r.text()); + + if ($('title').text().includes('Cloudflare')) { + throw new Error('Cloudflare is blocking requests. Try again later.'); + } + + return $; + } parseNovels(loadedCheerio: CheerioAPI): Plugin.NovelItem[] { const novels: Plugin.NovelItem[] = []; @@ -22,11 +42,7 @@ class WuxiaDreams implements Plugin.PagePlugin { if (name && url) { novels.push({ name, - cover: cover?.startsWith('http') - ? cover - : cover - ? this.site + cover.replace(/^\//, '') - : undefined, + cover: this.resolveUrl(cover), path: url.replace(/^\//, ''), }); } @@ -45,29 +61,20 @@ class WuxiaDreams implements Plugin.PagePlugin { url += `&sort=${filters.sort.value}`; } - const response = await fetchApi(url); - const body = await response.text(); - const $ = parseHTML(body); - + const $ = await this.getCheerio(url); return this.parseNovels($); } async parseNovel( novelPath: string, ): Promise { - const response = await fetchApi(`${this.site}${novelPath}`); - const body = await response.text(); - const $ = parseHTML(body); + const $ = await this.getCheerio(`${this.site}${novelPath}`); const cover = $('main img').first().attr('src'); const novel: Plugin.SourceNovel & { totalPages: number } = { path: novelPath, name: $('h1').text().trim() || 'Untitled', - cover: cover?.startsWith('http') - ? cover - : cover - ? this.site + cover.replace(/^\//, '') - : undefined, + cover: this.resolveUrl(cover), summary: '', chapters: [], totalPages: 1, @@ -76,7 +83,10 @@ class WuxiaDreams implements Plugin.PagePlugin { // Summary const summaryElement = $('h3:contains("Synopsis")').next(); summaryElement.find('br').replaceWith('\n'); - novel.summary = summaryElement + + // Wrap in
to prevent selector parsing errors on plain text + const summary = $('
' + (summaryElement.html() || '') + '
'); + novel.summary = summary .text() .split('\n') .map(line => line.trim()) @@ -101,12 +111,17 @@ class WuxiaDreams implements Plugin.PagePlugin { .text() .toLowerCase(); - if (statusText.includes('completed')) { - novel.status = NovelStatus.Completed; - } else if (statusText.includes('ongoing')) { - novel.status = NovelStatus.Ongoing; - } else { - novel.status = NovelStatus.Unknown; + const statusMap: Record = { + 'completed': NovelStatus.Completed, + 'ongoing': NovelStatus.Ongoing, + }; + + novel.status = NovelStatus.Unknown; + for (const key in statusMap) { + if (statusText.includes(key)) { + novel.status = statusMap[key]; + break; + } } // Tags as Genres @@ -172,9 +187,7 @@ class WuxiaDreams implements Plugin.PagePlugin { } async parsePage(novelPath: string, page: string): Promise { - const response = await fetchApi(`${this.site}${novelPath}?page=${page}`); - const body = await response.text(); - const $ = parseHTML(body); + const $ = await this.getCheerio(`${this.site}${novelPath}?page=${page}`); return { chapters: this.parseChapters($), @@ -182,12 +195,13 @@ class WuxiaDreams implements Plugin.PagePlugin { } async parseChapter(chapterPath: string): Promise { - const response = await fetchApi(`${this.site}${chapterPath}`); - const body = await response.text(); - const $ = parseHTML(body); + const $ = await this.getCheerio(`${this.site}${chapterPath}`); const content = $('article.chapter-content-container').html(); - return content || 'Content not found.'; + if (!content) { + throw new Error('Chapter content not found.'); + } + return content; } async searchNovels( @@ -196,9 +210,7 @@ class WuxiaDreams implements Plugin.PagePlugin { ): Promise { const url = `${this.site}novels?q=${encodeURIComponent(searchTerm)}&page=${pageNo}`; - const response = await fetchApi(url); - const body = await response.text(); - const $ = parseHTML(body); + const $ = await this.getCheerio(url); return this.parseNovels($); } From 7f31b860046e8ebcf53af03eabe72bef8e38b92b Mon Sep 17 00:00:00 2001 From: 7ui77 <99854073+7ui77@users.noreply.github.com> Date: Tue, 5 May 2026 13:04:34 +0700 Subject: [PATCH 7/7] Update wuxiadreams.ts 1 --- plugins/english/wuxiadreams.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/english/wuxiadreams.ts b/plugins/english/wuxiadreams.ts index 8d393132d..ac949c791 100644 --- a/plugins/english/wuxiadreams.ts +++ b/plugins/english/wuxiadreams.ts @@ -9,7 +9,7 @@ class WuxiaDreams implements Plugin.PagePlugin { name = 'Wuxia Dreams'; icon = 'src/en/wuxiadreams/icon.png'; site = 'https://wuxiadreams.com/'; - version = '1.0.1'; + version = '1.0.0'; private resolveUrl(path?: string) { if (!path) return undefined;