Files
lnreader-plugins/plugins/vietnamese/nettruyen.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

118 lines
4.0 KiB
TypeScript

import { fetchApi } from '@libs/fetch';
import { NovelStatus } from '@libs/novelStatus';
import { Plugin } from '@/types/plugin';
import { CheerioAPI, load as parseHTML } from 'cheerio';
class Nettruyen implements Plugin.PagePlugin {
id = 'nettruyen';
name = 'Nettruyen';
icon = 'src/vi/nettruyen/icon.png';
site = 'https://nettruyen.com.vn';
version = '1.0.0';
parseNovels(loadedCheerio: CheerioAPI) {
const novels: Plugin.NovelItem[] = [];
loadedCheerio('#listchuong > ul > li > a.thumb').each((idx, ele) => {
const path = ele.attribs['href'];
if (!path) return;
const name = ele.attribs['title'];
const cover =
this.site + '/' + loadedCheerio(ele).find('img').first().attr('src');
novels.push({
path: '/' + path,
name,
cover,
});
});
return novels;
}
async popularNovels(pageNo: number): Promise<Plugin.NovelItem[]> {
const url = `${this.site}/xem-nhieu/trang-${pageNo}.html`;
const body = await fetchApi(url).then(r => r.text());
return this.parseNovels(parseHTML(body));
}
parseChapters(loadedCheerio: CheerioAPI) {
const chapters: Plugin.ChapterItem[] = [];
loadedCheerio('ul.list-unstyled > li > a').each((idx, ele) => {
const path = ele.attribs['href'];
if (!path) return;
const name = ele.attribs['title'];
chapters.push({
path: '/' + path,
name,
chapterNumber: Number(name.match(/Chương (\d+)/)?.[1]),
});
});
return chapters;
}
async parseNovel(
novelPath: string,
): Promise<Plugin.SourceNovel & { totalPages: number }> {
const url = this.site + novelPath;
const body = await fetchApi(url).then(r => r.text());
const loadedCheerio = parseHTML(body);
const novel: Plugin.SourceNovel & { totalPages: number } = {
path: novelPath,
name: loadedCheerio('.gioithieutruyen.profile-info > h1').text().trim(),
chapters: [],
totalPages:
Number(loadedCheerio('.phantrang > span:nth-child(2)').text()) || 1,
};
novel.cover =
this.site + '/' + loadedCheerio('.img-thumb > img').attr('src');
loadedCheerio('.thongtintruyen.note.note-info').each((idx, ele) => {
const strong = loadedCheerio(ele).find('strong').text().trim();
switch (strong) {
case 'Tác giả':
novel.author = loadedCheerio(ele)
.text()
.replace(/Tác giả\s+\n?:/, '');
break;
case 'Trạng thái': {
const text = loadedCheerio(ele).text();
if (text.includes('Đang ra')) {
novel.status = NovelStatus.Ongoing;
} else if (text.includes('Hoàn thành')) {
novel.status = NovelStatus.Completed;
} else {
novel.status = NovelStatus.Unknown;
}
break;
}
case 'Thể loại':
novel.genres = loadedCheerio('a > span')
.toArray()
.map(gEle => loadedCheerio(gEle).text())
.join(',');
break;
default:
break;
}
});
novel.summary = loadedCheerio('.gioithieutruyen > .gioithieutruyen')
.text()
.trim();
novel.chapters = this.parseChapters(loadedCheerio);
return novel;
}
async parsePage(novelPath: string, page: string): Promise<Plugin.SourcePage> {
const id = novelPath.match(/-(\d+)\//)?.[1];
if (!id) throw new Error('Cant parse page');
const url = `${this.site}/ajax.chuong.php?id=${id}&page=${page}&url=${novelPath.replace(/\//g, '')}&loai=truyendich`;
const body = await fetchApi(url).then(r => r.text());
const chapters = this.parseChapters(parseHTML(body));
return {
chapters,
};
}
async parseChapter(chapterPath: string): Promise<string> {
const url = this.site + chapterPath;
const body = await fetchApi(url).then(r => r.text());
const loadedCheerio = parseHTML(body);
return loadedCheerio('#noidungchap').html() || '';
}
async searchNovels(): Promise<Plugin.NovelItem[]> {
throw new Error('Method not implemented.');
}
}
export default new Nettruyen();