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 { 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 { 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 { 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 { 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 { throw new Error('Method not implemented.'); } } export default new Nettruyen();