Files
WaifuBoard-Extensions/book/novelbin.js

122 lines
3.8 KiB
JavaScript

class NovelBin {
constructor() {
this.baseUrl = "https://novelbin.me";
this.type = "book-board";
this.mediaType = "ln";
this.version = "1.0"
}
async search(queryObj) {
const query = queryObj.query || "";
const url = `${this.baseUrl}/search?keyword=${encodeURIComponent(query)}`;
const res = await fetch(url, {
headers: {
"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",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"referer": this.baseUrl + "/"
}
});
const html = await res.text();
const $ = this.cheerio.load(html);
const results = [];
$('h3.novel-title a').each((i, el) => {
const href = $(el).attr('href');
const title = $(el).text().trim();
const idMatch = href.match(/novel-book\/([^/?]+)/);
const id = idMatch ? idMatch[1] : null;
const img = `${this.baseUrl}/media/novel/${id}.jpg`;
results.push({
id,
title,
image: img,
rating: null,
type: "book"
});
});
return results;
}
async getMetadata(id) {
const res = await fetch(`${this.baseUrl}/novel-book/${id}`);
const html = await res.text();
const $ = this.cheerio.load(html);
const getMeta = (property) => $(`meta[property='${property}']`).attr('content') || "";
const title = getMeta("og:novel:novel_name") || $('title').text() || "";
const summary = $('meta[name="description"]').attr('content') || "";
const genresRaw = getMeta("og:novel:genre");
const genres = genresRaw ? genresRaw.split(',').map(g => g.trim()) : [];
const status = getMeta("og:novel:status") || "";
const image = getMeta("og:image");
const lastChapterName = getMeta("og:novel:lastest_chapter_name");
const chaptersMatch = lastChapterName.match(/Chapter\s+(\d+)/i);
const chapters = chaptersMatch ? Number(chaptersMatch[1]) : 0;
return {
id,
title,
format: "Light Novel",
score: 0,
genres,
status,
published: "???",
summary,
chapters,
image
};
}
async findChapters(bookId) {
const res = await fetch(`${this.baseUrl}/ajax/chapter-archive?novelId=${bookId}`, {
headers: {
"user-agent": "Mozilla/5.0"
}
});
const html = await res.text();
const $ = this.cheerio.load(html);
const chapters = [];
$('a[title]').each((i, el) => {
const fullUrl = $(el).attr('href');
const title = $(el).attr('title').trim();
const numMatch = title.match(/chapter\s+(\d+(?:\.\d+)?)/i);
chapters.push({
id: fullUrl,
title,
number: numMatch ? numMatch[1] : "0",
releaseDate: null,
index: i
});
});
return chapters;
}
async findChapterPages(chapterUrl) {
const {result} = await this.scrape(chapterUrl, async (page) => {
return page.evaluate(() => {
document.querySelectorAll('div[id^="pf-"]').forEach(e => e.remove());
const ps = Array.from(document.querySelectorAll("p")).map(p => p.outerHTML.trim()).filter(p => p.length > 7);
return ps.join("\n");
});
}, {
waitUntil: "domcontentloaded",
renderWaitTime: 300
});
return result || "<p>Error: chapter text not found</p>";
}
}
module.exports = NovelBin;