class WeebCentral { constructor() { this.baseUrl = "https://weebcentral.com"; this.type = "book-board"; this.version = "1.0"; this.mediaType = "manga"; } async fetch(url, options = {}) { return fetch(url, { ...options, headers: { "User-Agent": "Mozilla/5.0", ...(options.headers || {}), }, }); } async search(queryObj) { const query = queryObj.query || ""; const form = new URLSearchParams(); form.set("text", query); const res = await this.fetch( `${this.baseUrl}/search/simple?location=main`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", "HX-Request": "true", "HX-Trigger": "quick-search-input", "HX-Target": "quick-search-result", "HX-Current-URL": `${this.baseUrl}/`, }, body: form.toString(), } ); const html = await res.text(); const $ = this.cheerio.load(html); const results = []; $("#quick-search-result > div > a").each((_, el) => { const link = $(el).attr("href"); if (!link) return; const idMatch = link.match(/\/series\/([^/]+)/); if (!idMatch) return; const title = $(el).find(".flex-1").text().trim(); let image = $(el).find("source").attr("srcset") || $(el).find("img").attr("src") || null; results.push({ id: idMatch[1], title, image, rating: null, type: "book", }); }); return results; } async getMetadata(id) { const res = await this.fetch(`${this.baseUrl}/series/${id}`, { headers: { Referer: `${this.baseUrl}/series/${id}` }, }); if (!res.ok) throw new Error("Metadata failed"); const html = await res.text(); const $ = this.cheerio.load(html); const title = $("section.md\\:w-8\\/12 h1").first().text().trim() || ""; const genres = []; $("li strong") .filter((_, el) => $(el).text().includes("Tags")) .parent() .find("a") .each((_, a) => { genres.push($(a).text().trim()); }); const status = $("li strong") .filter((_, el) => $(el).text().includes("Status")) .parent() .find("a") .first() .text() .trim() || "unknown"; const published = $("li strong") .filter((_, el) => $(el).text().includes("Released")) .parent() .find("span") .first() .text() .trim() || ""; const summary = $("li strong") .filter((_, el) => $(el).text().includes("Description")) .parent() .find("p") .text() .trim() || ""; const image = $("section.flex picture source").attr("srcset") || $("section.flex picture img").attr("src") || null; return { id, title, format: "MANGA", score: 0, genres: genres.join(", "), status, published, summary, chapters: "???", image, }; } async findChapters(mangaId) { const res = await this.fetch( `${this.baseUrl}/series/${mangaId}/full-chapter-list`, { headers: { "HX-Request": "true", "HX-Target": "chapter-list", "HX-Current-URL": `${this.baseUrl}/series/${mangaId}`, Referer: `${this.baseUrl}/series/${mangaId}`, }, } ); const html = await res.text(); const $ = this.cheerio.load(html); const chapters = []; const numRegex = /(\d+(?:\.\d+)?)/; $("div.flex.items-center").each((_, el) => { const a = $(el).find("a"); if (!a.length) return; const href = a.attr("href"); if (!href) return; const idMatch = href.match(/\/chapters\/([^/]+)/); if (!idMatch) return; const title = a.find("span.grow > span").first().text().trim(); const numMatch = title.match(numRegex); chapters.push({ id: idMatch[1], title, number: numMatch ? Number(numMatch[1]) : 0, releaseDate: null, index: 0, }); }); chapters.reverse(); chapters.forEach((c, i) => (c.index = i)); return chapters; } async findChapterPages(chapterId) { const res = await this.fetch( `${this.baseUrl}/chapters/${chapterId}/images?is_prev=False&reading_style=long_strip`, { headers: { "HX-Request": "true", "HX-Current-URL": `${this.baseUrl}/chapters/${chapterId}`, Referer: `${this.baseUrl}/chapters/${chapterId}`, }, } ); const html = await res.text(); const $ = this.cheerio.load(html); const pages = []; $("section.flex-1 img").each((i, el) => { const src = $(el).attr("src"); if (src) { pages.push({ url: src, index: i, headers: { Referer: this.baseUrl }, }); } }); if (pages.length === 0) { $("img").each((i, el) => { const src = $(el).attr("src"); if (src) { pages.push({ url: src, index: i, headers: { Referer: this.baseUrl }, }); } }); } return pages; } } module.exports = WeebCentral;