Files
lnreader-plugins/plugins/english/novelhall.ts
T
Rajarshee Chatterjee 20c2b75b01 refactor: Overhaul Plugin Architecture & Playground UI (#1846)
* refactor: Move Plugins Out and Consolidate with Multisrc

* feat: Overhaul Playground UI

* feat: Add Filters to New UI

* feat: Persist Selected Plugin

* feat: Add Direct Parse Novel/Chapter Options and Feedback Toasts

[skip ci]

* feat: UX Improvements

* chore: Update Plugin Path in Publish Workflow

* fix: Add Backward Compatibility

* chore: Update Plugin Publishing Script
2025-10-20 22:54:08 +05:30

124 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { load as parseHTML } from 'cheerio';
import { fetchApi } from '@libs/fetch';
import { Plugin } from '@/types/plugin';
import { defaultCover } from '@libs/defaultCover';
class NovelHall implements Plugin.PluginBase {
id = 'novelhall';
name = 'Novel Hall';
version = '1.0.2';
icon = 'src/en/novelhall/icon.png';
site = 'https://novelhall.com/';
async popularNovels(page: number): Promise<Plugin.NovelItem[]> {
const url = `${this.site}all2022-${page}.html`;
const body = await fetchApi(url).then(r => r.text());
const loadedCheerio = parseHTML(body);
const novels: Plugin.NovelItem[] = [];
loadedCheerio('li.btm').each((idx, ele) => {
const novelName = loadedCheerio(ele).text().trim();
const novelUrl = loadedCheerio(ele).find('a').attr('href');
if (!novelUrl) return;
const novel = {
name: novelName,
cover: defaultCover,
path: novelUrl,
};
novels.push(novel);
});
return novels;
}
async parseNovel(novelPath: string): Promise<Plugin.SourceNovel> {
const body = await fetchApi(this.site + novelPath).then(r => r.text());
const loadedCheerio = parseHTML(body);
const novel: Plugin.SourceNovel = {
path: novelPath,
name: loadedCheerio('.book-info > h1').text() || 'Untitled',
cover: loadedCheerio('meta[property="og:image"]').attr('content'),
summary: loadedCheerio('.intro').text().trim(),
chapters: [],
};
loadedCheerio('.total').find('p').remove();
novel.author = loadedCheerio('.total span:contains("Author")')
.text()
.replace('Author:', '')
.trim();
novel.status = loadedCheerio('.total span:contains("Status")')
.text()
.replace('Status:', '')
.replace('Active', 'Ongoing')
.trim();
novel.genres = loadedCheerio('.total a')
.map((a, ex) => loadedCheerio(ex).text())
.toArray()
.join(',');
const chapter: Plugin.ChapterItem[] = [];
loadedCheerio('#morelist ul > li').each((idx, ele) => {
const chapterName = loadedCheerio(ele).find('a').text().trim();
const chapterUrl = loadedCheerio(ele).find('a').attr('href');
if (!chapterUrl) return;
chapter.push({
name: chapterName,
path: chapterUrl,
});
});
novel.chapters = chapter;
return novel;
}
async parseChapter(chapterPath: string): Promise<string> {
const body = await fetchApi(this.site + chapterPath).then(r => r.text());
const loadedCheerio = parseHTML(body);
const chapterText = loadedCheerio('.content').html() || '';
return chapterText;
}
async searchNovels(searchTerm: string): Promise<Plugin.NovelItem[]> {
const url = `${this.site}index.php?s=so&module=book&keyword=${encodeURIComponent(searchTerm)}`;
const body = await fetchApi(url).then(r => r.text());
const loadedCheerio = parseHTML(body);
const novels: Plugin.NovelItem[] = [];
loadedCheerio('table tr').each((idx, ele) => {
const novelName = loadedCheerio(ele)
.find('td:nth-child(2)')
.text()
.replace(/\t+/g, '')
.replace(/\n/g, ' ');
const novelUrl = loadedCheerio(ele)
.find('td:nth-child(2) a')
.attr('href');
if (!novelUrl) return;
const novel = {
name: novelName,
cover: defaultCover,
path: novelUrl,
};
novels.push(novel);
});
return novels;
}
}
export default new NovelHall();