class ligntnovelworld { constructor(fetchPath, cheerioPath, browser) { this.browser = browser; this.fetch = require(fetchPath); this.cheerio = require(cheerioPath); this.baseUrl = "https://lightnovelworld.org/api"; this.type = "book-board"; } async fetchSearchResult(query = "", page = 1) { if (query.trim() !== "") { const res = await this.fetch(`${this.baseUrl}/search/?q=${encodeURIComponent(query)}&search_type=title`); const data = await res.json(); const results = data.novels.map(n => ({ id: n.slug, title: n.title, image: `https://lightnovelworld.org/${n.cover_path}`, sampleImageUrl: `https://lightnovelworld.org/${n.cover_path}`, tags: [], type: "book" })); return { results, hasNextPage: false, page }; } const res = await this.fetch("https://lightnovelworld.org/"); const html = await res.text(); const $ = this.cheerio.load(html); const cards = $(".recommendations-grid .recommendation-card"); const results = []; cards.each((_, el) => { const card = $(el); const link = card.find("a.card-cover-link").attr("href") || ""; const id = link.replace(/^\/novel\//, "").replace(/\/$/, ""); const title = card.find(".card-title").text().trim(); const img = card.find(".card-cover img").attr("src") || ""; const imageUrl = img.startsWith("http") ? img : `https://lightnovelworld.org${img}`; const tags = card.find(".card-genres .genre-tag").map((_, t) => $(t).text().trim()).get(); results.push({ id, title, image: imageUrl, sampleImageUrl: imageUrl, tags, type: "book" }); }); return { results, hasNextPage: false, page }; } async findChapters(bookId) { let offset = 0; const limit = 500; const chapters = []; while (true) { const res = await this.fetch(`https://lightnovelworld.org/api/novel/${bookId}/chapters/?offset=${offset}&limit=${limit}`); const data = await res.json(); chapters.push( ...data.chapters.map(c => ({ id: `https://lightnovelworld.org/novel/${bookId}/chapter/${c.number}/`, title: c.title, chapter: c.number, language: 'en' })) ); if (!data.has_more) break; offset += limit; } return chapters; } async findChapterPages(chapterId) { const res = await this.fetch(chapterId, { headers: { 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', 'referer': chapterId.replace(/\/\d+\/$/, ''), 'sec-ch-ua': '"Chromium";v="139", "Not;A=Brand";v="99"', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36' } }); const html = await res.text(); const $ = this.cheerio.load(html); const contentDiv = $('#chapterText'); if (!contentDiv || contentDiv.length === 0) { return [{ type: 'text', content: '
Error: content not found
', index: 0 }]; } contentDiv.find('script').remove(); contentDiv.find('style').remove(); contentDiv.find('ins').remove(); contentDiv.find("[id^='pf-']").remove(); contentDiv.find('.chapter-ad-container').remove(); contentDiv.find('.ad-unit').remove(); contentDiv.find('.ads').remove(); contentDiv.find('.adsbygoogle').remove(); contentDiv.find('.nf-ads').remove(); contentDiv.find('div[align="center"]').remove(); contentDiv.find("div[style*='text-align:center']").remove(); const paragraphs = contentDiv.find('p'); let cleanHtml = ''; paragraphs.each((_, el) => { const p = $(el); let text = p.text() || ''; text = text.replace(/△▼△▼△▼△/g, '').replace(/[※]+/g, '').replace(/\s{2,}/g, ' ').trim(); const htmlP = p.html()?.trim() || ''; const isEmpty = htmlP === '' || htmlP === ' ' || text.trim() === ''; const isAd = text.includes('Remove Ads') || text.includes('Buy no ads') || text.includes('novelfire'); if (!isEmpty && !isAd) { if (p.text() !== text) p.text(text); cleanHtml += $.html(p); } }); if (!cleanHtml.trim()) { cleanHtml = contentDiv.html() || ''; } return [ { type: 'text', content: cleanHtml.trim(), index: 0 } ]; } } module.exports = { novelupdates: ligntnovelworld };