From 302a8d70e1bfa2632fd92cbe449f83288b062104 Mon Sep 17 00:00:00 2001 From: Clyde <161568257+clydeclamonte79@users.noreply.github.com> Date: Sat, 17 Jan 2026 17:07:53 +0800 Subject: [PATCH 1/5] Add files via upload --- plugins/english/novelrest.ts | 227 +++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 plugins/english/novelrest.ts diff --git a/plugins/english/novelrest.ts b/plugins/english/novelrest.ts new file mode 100644 index 000000000..685400892 --- /dev/null +++ b/plugins/english/novelrest.ts @@ -0,0 +1,227 @@ +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'; + +/** + * NovelRest LNReader Plugin + * + * This plugin allows LNReader to fetch novels from NovelRest (novelrest.vercel.app) + * + * Features: + * - Browse popular novels with pagination + * - Search novels + * - Read chapters + * - Filter by status + * - Sort by latest/popular/rating/updated + */ +class NovelRestPlugin implements Plugin.PluginBase { + id = 'novelrest'; + name = 'NovelRest'; + icon = 'src/en/novelrest/icon.png'; + site = 'https://novelrest.vercel.app'; + apiBase = 'https://novelrest.vercel.app/api/lnreader'; + version = '1.0.0'; + + filters: Filters = { + status: { + type: FilterTypes.Picker, + label: 'Status', + value: '', + options: [ + { label: 'All', value: '' }, + { label: 'Ongoing', value: 'ONGOING' }, + { label: 'Completed', value: 'COMPLETED' }, + { label: 'Hiatus', value: 'HIATUS' }, + ], + }, + sort: { + type: FilterTypes.Picker, + label: 'Sort By', + value: 'latest', + options: [ + { label: 'Latest', value: 'latest' }, + { label: 'Popular', value: 'popular' }, + { label: 'Rating', value: 'rating' }, + { label: 'Updated', value: 'updated' }, + ], + }, + }; + + /** + * Fetch popular/latest novels with pagination + */ + async popularNovels( + pageNo: number, + { showLatestNovels, filters }: Plugin.PopularNovelsOptions, + ): Promise { + const novels: Plugin.NovelItem[] = []; + + // Build query parameters + const params = new URLSearchParams(); + params.set('page', pageNo.toString()); + params.set('limit', '20'); + + if (filters?.status?.value) { + params.set('status', filters.status.value as string); + } + + const sortBy = showLatestNovels ? 'latest' : (filters?.sort?.value as string || 'popular'); + params.set('sort', sortBy); + + try { + const url = `${this.apiBase}/novels?${params.toString()}`; + const response = await fetchApi(url); + const data = await response.json(); + + if (data.novels && Array.isArray(data.novels)) { + for (const novel of data.novels) { + novels.push({ + name: novel.title, + path: novel.slug, // Just the slug, we'll build full path in parseNovel + cover: novel.coverImage || defaultCover, + }); + } + } + } catch (error) { + console.error('NovelRest: Error fetching popular novels:', error); + } + + return novels; + } + + /** + * Parse novel details and chapter list + */ + async parseNovel(novelPath: string): Promise { + const novel: Plugin.SourceNovel = { + path: novelPath, + name: 'Untitled', + }; + + try { + // novelPath is just the slug + const slug = novelPath.replace(/^\/novels\//, ''); // Clean up if path prefix exists + + const response = await fetchApi(`${this.apiBase}/novels/${slug}`); + const data = await response.json(); + + if (data) { + novel.name = data.title || 'Untitled'; + novel.author = data.author || ''; + novel.cover = data.coverImage || defaultCover; + novel.genres = Array.isArray(data.genres) + ? data.genres.map((g: { name: string } | string) => typeof g === 'string' ? g : g.name).join(', ') + : ''; + novel.summary = data.description || ''; + + // Map status + switch (data.status) { + case 'COMPLETED': + novel.status = NovelStatus.Completed; + break; + case 'ONGOING': + novel.status = NovelStatus.Ongoing; + break; + case 'HIATUS': + novel.status = NovelStatus.OnHiatus; + break; + default: + novel.status = NovelStatus.Unknown; + } + + // Parse chapters + const chapters: Plugin.ChapterItem[] = []; + + if (data.chapters && Array.isArray(data.chapters)) { + for (const chapter of data.chapters) { + chapters.push({ + name: chapter.title || `Chapter ${chapter.number}`, + path: `${slug}/${chapter.number}`, // slug/chapterNumber format + releaseTime: chapter.createdAt || undefined, + chapterNumber: chapter.number, + }); + } + } + + novel.chapters = chapters; + } + } catch (error) { + console.error('NovelRest: Error parsing novel:', error); + } + + return novel; + } + + /** + * Parse chapter content + */ + async parseChapter(chapterPath: string): Promise { + try { + // chapterPath format: "slug/chapterNumber" + const parts = chapterPath.split('/'); + const chapterNumber = parts.pop(); + const slug = parts.join('/'); + + const response = await fetchApi(`${this.apiBase}/novels/${slug}/chapters/${chapterNumber}`); + const data = await response.json(); + + if (data && data.contentHtml) { + return data.contentHtml; + } + + return '

Chapter content could not be loaded.

'; + } catch (error) { + console.error('NovelRest: Error parsing chapter:', error); + return '

Error loading chapter content.

'; + } + } + + /** + * Search novels by term + */ + async searchNovels( + searchTerm: string, + pageNo: number, + ): Promise { + const novels: Plugin.NovelItem[] = []; + + try { + const params = new URLSearchParams(); + params.set('q', searchTerm); + params.set('page', pageNo.toString()); + params.set('limit', '20'); + + const response = await fetchApi(`${this.apiBase}/novels?${params.toString()}`); + const data = await response.json(); + + if (data.novels && Array.isArray(data.novels)) { + for (const novel of data.novels) { + novels.push({ + name: novel.title, + path: novel.slug, + cover: novel.coverImage || defaultCover, + }); + } + } + } catch (error) { + console.error('NovelRest: Error searching novels:', error); + } + + return novels; + } + + /** + * Resolve full URL for novel or chapter + */ + resolveUrl = (path: string, isNovel?: boolean): string => { + if (isNovel) { + return `${this.site}/novels/${path}`; + } + // For chapters, path is "slug/chapterNumber" + return `${this.site}/novels/${path}`; + }; +} + +export default new NovelRestPlugin(); From 017e6a4492f7211f2276a681e381ae994b6a1758 Mon Sep 17 00:00:00 2001 From: Clyde <161568257+clydeclamonte79@users.noreply.github.com> Date: Sat, 17 Jan 2026 17:14:25 +0800 Subject: [PATCH 2/5] Add files via upload --- icon.png | Bin 0 -> 13258 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 icon.png diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..11703697dfb55714e7e47814efa3041f16462b24 GIT binary patch literal 13258 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 IM6N<$f=+Qo1^@s6 literal 0 HcmV?d00001 From 3722a196b3bd913114d586a8a533f9a3ea118a28 Mon Sep 17 00:00:00 2001 From: Clyde <161568257+clydeclamonte79@users.noreply.github.com> Date: Sat, 17 Jan 2026 18:25:52 +0800 Subject: [PATCH 3/5] Rename icon.png to icons/src/en/novelrest/icon.png --- icon.png => icons/src/en/novelrest/icon.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename icon.png => icons/src/en/novelrest/icon.png (100%) diff --git a/icon.png b/icons/src/en/novelrest/icon.png similarity index 100% rename from icon.png rename to icons/src/en/novelrest/icon.png From 9054b92253fb5d2c91033f95e106b200832f696c Mon Sep 17 00:00:00 2001 From: Clyde <161568257+clydeclamonte79@users.noreply.github.com> Date: Sat, 17 Jan 2026 18:32:02 +0800 Subject: [PATCH 4/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7c6c1d79d..6a24a9c95 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Community-driven plugin repository for [LNReader](https://github.com/LNReader/ln ## Quick Start -**Prerequisites:** Node.js >= 20 +**Prerequisites:** Node.js >= 20 ```bash npm install From 5d9477b2e0fd711226327844b11a183a85882071 Mon Sep 17 00:00:00 2001 From: Clyde <161568257+clydeclamonte79@users.noreply.github.com> Date: Sat, 17 Jan 2026 18:38:56 +0800 Subject: [PATCH 5/5] Refactor NovelRestPlugin for improved clarity --- plugins/english/novelrest.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/english/novelrest.ts b/plugins/english/novelrest.ts index 685400892..ea1c33427 100644 --- a/plugins/english/novelrest.ts +++ b/plugins/english/novelrest.ts @@ -225,3 +225,5 @@ class NovelRestPlugin implements Plugin.PluginBase { } export default new NovelRestPlugin(); + +// trigger build