156 lines
5.4 KiB
JavaScript
156 lines
5.4 KiB
JavaScript
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: '<p>Error: content not found</p>',
|
|
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 }; |