125 lines
3.9 KiB
JavaScript
125 lines
3.9 KiB
JavaScript
class asmhentai {
|
|
constructor(fetchPath, cheerioPath, browser) {
|
|
this.baseUrl = "https://asmhentai.com/";
|
|
this.fetch = require(fetchPath);
|
|
this.cheerio = require(cheerioPath);
|
|
this.browser = browser;
|
|
this.type = "book-board";
|
|
}
|
|
|
|
async fetchSearchResult(query = "", page = 1) {
|
|
const q = query.trim().replace(/\s+/g, "+");
|
|
const url = q ? `${this.baseUrl}/search/?q=${q}&page=${page}` : `${this.baseUrl}/?q=&page=${page}`;
|
|
|
|
const res = await this.fetch(url);
|
|
const html = await res.text();
|
|
const $ = this.cheerio.load(html);
|
|
|
|
const items = $(".ov_item .preview_item");
|
|
const results = [];
|
|
|
|
items.each((_, el) => {
|
|
const $el = $(el);
|
|
|
|
const href = $el.find(".image a").attr("href") || "";
|
|
const id = href.match(/\d+/)?.[0] || null;
|
|
|
|
const img = $el.find(".image img");
|
|
const raw = img.attr("data-src") || img.attr("src") || "";
|
|
let image = raw.startsWith("//") ? "https:" + raw : raw;
|
|
const sampleImageUrl = image.replace("thumb", "cover");
|
|
|
|
image = image.replace(/[^\/]+$/, "1.jpg");
|
|
|
|
const title = ($el.find(".cpt h2.caption").text() || "").trim();
|
|
|
|
const tagsRaw = $el.attr("data-tags") || "";
|
|
const tags = tagsRaw.split(" ").filter(Boolean);
|
|
|
|
results.push({
|
|
id,
|
|
image,
|
|
sampleImageUrl,
|
|
title,
|
|
tags,
|
|
type: "book"
|
|
});
|
|
});
|
|
|
|
const hasNextPage = $('ul.pagination a.page-link').filter((_, el) => $(el).text().trim().toLowerCase() === "next").length > 0;
|
|
|
|
return {
|
|
results,
|
|
hasNextPage,
|
|
page
|
|
};
|
|
}
|
|
|
|
async findChapters(mangaId) {
|
|
const res = await this.fetch(`${this.baseUrl}/g/${mangaId}/`);
|
|
const html = await res.text();
|
|
const $ = this.cheerio.load(html);
|
|
|
|
const title = $(".right .info h1").first().text().trim() || "";
|
|
|
|
let cover = $(".cover img").attr("data-src") || $(".cover img").attr("src") || "";
|
|
if (cover.startsWith("//")) cover = "https:" + cover;
|
|
|
|
const firstThumb = $('.gallery a img').first();
|
|
let t = firstThumb.attr("data-src") || "";
|
|
|
|
if (t.startsWith("//")) t = "https:" + t;
|
|
|
|
// ex: https://images.asmhentai.com/017/598614/8t.jpg
|
|
const baseMatch = t.match(/https:\/\/[^\/]+\/\d+\/\d+\//);
|
|
const basePath = baseMatch ? baseMatch[0] : null;
|
|
|
|
const pagesText = $(".pages h3").text(); // "Pages: 39"
|
|
const pagesMatch = pagesText.match(/(\d+)/);
|
|
const pages = pagesMatch ? parseInt(pagesMatch[1]) : 0;
|
|
|
|
let ext = "jpg";
|
|
const extMatch = t.match(/\.(jpg|png|jpeg|gif)/i);
|
|
if (extMatch) ext = extMatch[1];
|
|
|
|
let language = "unknown";
|
|
const langFlag = $('.info a[href^="/language/"] img').attr("src") || "";
|
|
if (langFlag.includes("en")) language = "english";
|
|
if (langFlag.includes("jp")) language = "japanese";
|
|
|
|
const encodedChapterId = Buffer.from(
|
|
JSON.stringify({
|
|
base: basePath,
|
|
pages,
|
|
ext
|
|
})
|
|
).toString("base64");
|
|
|
|
return {
|
|
chapters: [
|
|
{
|
|
id: encodedChapterId,
|
|
title,
|
|
chapter: 1,
|
|
index: 0,
|
|
language
|
|
}
|
|
],
|
|
cover
|
|
};
|
|
}
|
|
|
|
async findChapterPages(chapterId) {
|
|
const decoded = JSON.parse(
|
|
Buffer.from(chapterId, "base64").toString("utf8")
|
|
);
|
|
const { base, pages, ext } = decoded;
|
|
|
|
return Array.from({ length: pages }, (_, i) => ({
|
|
url: `${base}${i + 1}.${ext}`,
|
|
index: i,
|
|
}));
|
|
}
|
|
}
|
|
|
|
module.exports = { asmhentai }; |