From 22cea67d110d6ff90f1c69b0f2fa6cc589535c55 Mon Sep 17 00:00:00 2001 From: 52191314 Date: Sun, 26 Jul 2026 14:55:24 +0700 Subject: [PATCH] feat(english): add NovelPhoenix plugin --- plugins/english/novelphoenix.ts | 178 ++++++++++++++++++ .../static/src/english/novelphoenix/icon.png | Bin 0 -> 20535 bytes 2 files changed, 178 insertions(+) create mode 100644 plugins/english/novelphoenix.ts create mode 100644 public/static/src/english/novelphoenix/icon.png diff --git a/plugins/english/novelphoenix.ts b/plugins/english/novelphoenix.ts new file mode 100644 index 000000000..cf1f51c4c --- /dev/null +++ b/plugins/english/novelphoenix.ts @@ -0,0 +1,178 @@ +import { fetchText } from '@libs/fetch'; +import { load as loadCheerio } from 'cheerio'; +import { Plugin } from '@/types/plugin'; +import { NovelStatus } from '@libs/novelStatus'; + +const STATUS_MAP: Record = { + 'ongoing': NovelStatus.Ongoing, + 'completed': NovelStatus.Completed, + 'complete': NovelStatus.Completed, + 'hiatus': NovelStatus.OnHiatus, + 'paused': NovelStatus.OnHiatus, + 'cancelled': NovelStatus.Cancelled, + 'dropped': NovelStatus.Cancelled, +}; + +export class NovelPhoenixPlugin implements Plugin.PluginBase { + id = "novelphoenix"; + name = "NovelPhoenix"; + icon = "src/english/novelphoenix/icon.png"; + site = "https://novelphoenix.com/"; + version = "1.0.0"; + + async popularNovels( + pageNo: number, + options?: Plugin.PopularNovelsOptions + ): Promise { + const order = options?.showLatestNovels ? 'sort-new' : 'sort-popular'; + const url = `${this.site}genre-all/${order}/status-all/all-novel?page=${pageNo}`; + const html = await fetchText(url); + return this.parseNovelList(html); + } + + async fetchAllChapters(slug: string): Promise { + const firstPageUrl = `${this.site}novel/${slug}/chapters`; + const firstHtml = await fetchText(firstPageUrl); + const $first = loadCheerio(firstHtml); + + const pageNums = $first('.pagination a') + .map((_, el) => parseInt($first(el).text().trim())) + .get() + .filter(n => !isNaN(n)); + + const maxPage = pageNums.length > 0 ? Math.max(...pageNums) : 1; + const pageHtmls: string[] = [firstHtml]; + + if (maxPage > 1) { + const promises: Promise[] = []; + for (let p = 2; p <= maxPage; p++) { + promises.push(fetchText(`${this.site}novel/${slug}/chapters?page=${p}`)); + } + const remainingHtmls = await Promise.all(promises); + pageHtmls.push(...remainingHtmls); + } + + const chapters: Plugin.ChapterItem[] = []; + + pageHtmls.forEach(html => { + const $ = loadCheerio(html); + $('.chapter-list li').each((_, el) => { + const link = $(el).find('a'); + const rawPath = link.attr('href'); + if (!rawPath) return; + + const name = link.find('.chapter-title').text().trim() || link.text().trim(); + const releaseTime = link.find('.chapter-update').attr('datetime') || link.find('.chapter-update').text().trim(); + + chapters.push({ + name, + path: rawPath.replace(/^\//, ''), + releaseTime, + }); + }); + }); + + return chapters; + } + + async parseNovel(novelPath: string): Promise { + const html = await fetchText(`${this.site}${novelPath}`); + const $ = loadCheerio(html); + + const title = $('.novel-title').text().trim(); + + const $cover = $('.cover img'); + const rawCover = $cover.attr('src') || $cover.attr('data-src'); + const cover = rawCover ? `${this.site}${rawCover.replace(/^\//, '')}` : ''; + + const summary = $('.summary').text().trim(); + const author = $('.author a').text().trim(); + + const rawStatus = $('.header-stats strong').last().text().trim().toLowerCase(); + const status = STATUS_MAP[rawStatus] || NovelStatus.Unknown; + + const genres = $('.categories a').map((_, el) => $(el).text().trim()).get().join(', '); + + const slug = novelPath.replace(/^\/?novel\//, '').replace(/\/$/, ''); + let chapters: Plugin.ChapterItem[] = []; + + if (slug) { + try { + chapters = await this.fetchAllChapters(slug); + } catch { + chapters = []; + } + } + + return { + path: novelPath, + name: title, + cover, + summary, + author, + status, + genres, + chapters, + }; + } + + async parseChapter(chapterPath: string): Promise { + const html = await fetchText(`${this.site}${chapterPath}`); + const $ = loadCheerio(html); + + const container = $('#chapter-container'); + if (!container.length) { + return ''; + } + + container.find('script, style, ins, iframe, .ads, .ad-container, .watermark, div.text-center, #restore-scroll-btn').remove(); + container.find('[style]').removeAttr('style'); + + return container.html()?.trim() || ''; + } + + async searchNovels( + searchTerm: string, + pageNo: number + ): Promise { + searchTerm = searchTerm.trim(); + if (!searchTerm) return []; + const url = `${this.site}search?keyword=${encodeURIComponent(searchTerm)}&page=${pageNo}`; + try { + const html = await fetchText(url); + return this.parseNovelList(html); + } catch { + return []; + } + } + + parseNovelList(html: string): Plugin.NovelItem[] { + const $ = loadCheerio(html); + const novels: Plugin.NovelItem[] = []; + const seen = new Set(); + + $('.novel-item').each((_, el) => { + const $a = $(el).find('a'); + const rawPath = $a.attr('href'); + if (!rawPath) return; + + const name = $a.find('.novel-title').text().trim(); + const $img = $a.find('img'); + const rawCover = $img.attr('data-src') || $img.attr('src'); + + if (name && rawPath) { + const cleanPath = rawPath.replace(/^\//, ''); + if (seen.has(cleanPath)) return; + seen.add(cleanPath); + + const cover = rawCover ? `${this.site}${rawCover.replace(/^\//, '')}` : ''; + novels.push({ name, cover, path: cleanPath }); + } + }); + + return novels; + } +} + +export default new NovelPhoenixPlugin(); + diff --git a/public/static/src/english/novelphoenix/icon.png b/public/static/src/english/novelphoenix/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..351f921153d564c9488f5f63e0c0f408b9fb22c0 GIT binary patch literal 20535 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 KMNUMnLSTYe$dDEQ literal 0 HcmV?d00001