Upload files to "/"
This commit is contained in:
113
ZeroChan.js
Normal file
113
ZeroChan.js
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
class ZeroChan {
|
||||||
|
baseUrl = "https://zerochan.net";
|
||||||
|
|
||||||
|
constructor(fetchPath, cheerioPath, browser) {
|
||||||
|
this.browser = browser;
|
||||||
|
this.type = "image-board";
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchSearchResult(query = "hello", page = 1, perPage = 48) {
|
||||||
|
const url = `${this.baseUrl}/${query.trim().replace(/\s+/g, "+")}?p=${page}`;
|
||||||
|
|
||||||
|
const data = await this.browser.scrape(
|
||||||
|
url,
|
||||||
|
() => {
|
||||||
|
const list = document.querySelectorAll("#thumbs2 li");
|
||||||
|
if (list.length === 0) {
|
||||||
|
return { results: [], hasNextPage: false };
|
||||||
|
}
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
list.forEach(li => {
|
||||||
|
const id = li.getAttribute("data-id");
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
const img = li.querySelector("img");
|
||||||
|
const imgUrl =
|
||||||
|
img?.getAttribute("data-src") ||
|
||||||
|
img?.getAttribute("src") ||
|
||||||
|
null;
|
||||||
|
|
||||||
|
if (!imgUrl) return;
|
||||||
|
|
||||||
|
const tagLinks = li.querySelectorAll("p a");
|
||||||
|
const tags = [...tagLinks]
|
||||||
|
.map(a => a.textContent.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
results.push({
|
||||||
|
id,
|
||||||
|
image: imgUrl,
|
||||||
|
sampleImageUrl: imgUrl,
|
||||||
|
tags,
|
||||||
|
type: "preview"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasNextPage = document.querySelector('nav.pagination a[rel="next"]') !== null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
results,
|
||||||
|
hasNextPage
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ waitSelector: "#thumbs2 li", timeout: 15000, renderWaitTime: 3000, loadImages: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(data)
|
||||||
|
|
||||||
|
return {
|
||||||
|
results: data.results,
|
||||||
|
hasNextPage: data.hasNextPage,
|
||||||
|
page
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchInfo(id) {
|
||||||
|
const url = `${this.baseUrl}/${id}`;
|
||||||
|
|
||||||
|
const data = await this.browser.scrape(
|
||||||
|
url,
|
||||||
|
() => {
|
||||||
|
const preview = document.querySelector("a.preview");
|
||||||
|
if (!preview) {
|
||||||
|
return {
|
||||||
|
fullImage: null,
|
||||||
|
tags: [],
|
||||||
|
createdAt: Date.now()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullImage = preview.getAttribute("href") || null;
|
||||||
|
const img = preview.querySelector("img");
|
||||||
|
const alt = img?.getAttribute("alt") || "";
|
||||||
|
|
||||||
|
let tags = [];
|
||||||
|
if (alt.startsWith("Tags:")) {
|
||||||
|
tags = alt
|
||||||
|
.replace("Tags:", "")
|
||||||
|
.split(",")
|
||||||
|
.map(t => t.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
fullImage,
|
||||||
|
tags,
|
||||||
|
createdAt: Date.now()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ waitSelector: "a.preview img", timeout: 15000 }
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
fullImage: data.fullImage,
|
||||||
|
tags: data.tags,
|
||||||
|
createdAt: data.createdAt,
|
||||||
|
rating: "Unknown"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { ZeroChan };
|
||||||
154
novelfire.js
Normal file
154
novelfire.js
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
class novelfire {
|
||||||
|
constructor(fetchPath, cheerioPath, browser) {
|
||||||
|
this.browser = browser;
|
||||||
|
this.fetch = require(fetchPath);
|
||||||
|
this.cheerio = require(cheerioPath);
|
||||||
|
this.baseUrl = "https://novelfire.net";
|
||||||
|
this.type = "book-board";
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchSearchResult(query = "", page = 1) {
|
||||||
|
let html;
|
||||||
|
|
||||||
|
if (query.trim() === "") {
|
||||||
|
const res = await this.fetch(`${this.baseUrl}/home`);
|
||||||
|
html = await res.text();
|
||||||
|
} else {
|
||||||
|
const res = await this.fetch(
|
||||||
|
`${this.baseUrl}/ajax/searchLive?inputContent=${encodeURIComponent(query)}`
|
||||||
|
);
|
||||||
|
const data = await res.json();
|
||||||
|
html = data.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
const $ = this.cheerio.load(html);
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
$(".novel-item").each((_, el) => {
|
||||||
|
const a = $(el).find("a");
|
||||||
|
const href = a.attr("href") || "";
|
||||||
|
const title = $(el).find(".novel-title").text().trim();
|
||||||
|
|
||||||
|
const img = $(el).find("img");
|
||||||
|
const image = img.attr("data-src") || img.attr("src") || "";
|
||||||
|
|
||||||
|
const id = href.replace("https://novelfire.net/book/", "").replace(/\/$/, "");
|
||||||
|
|
||||||
|
results.push({
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
image,
|
||||||
|
sampleImageUrl: image,
|
||||||
|
tags: [],
|
||||||
|
type: "book"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
results,
|
||||||
|
hasNextPage: false,
|
||||||
|
page
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async findChapters(bookId) {
|
||||||
|
const base = `https://novelfire.net/book/${bookId}/chapters`;
|
||||||
|
const chapters = [];
|
||||||
|
|
||||||
|
const firstRes = await this.fetch(base);
|
||||||
|
const firstHtml = await firstRes.text();
|
||||||
|
let $ = this.cheerio.load(firstHtml);
|
||||||
|
|
||||||
|
let totalPages = 1;
|
||||||
|
const pageLinks = $(".pagination a.page-link");
|
||||||
|
pageLinks.each((_, el) => {
|
||||||
|
const num = parseInt($(el).text().trim(), 10);
|
||||||
|
if (!isNaN(num) && num > totalPages) totalPages = num;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let page = 1; page <= totalPages; page++) {
|
||||||
|
const url = page === 1 ? base : `${base}?page=${page}`;
|
||||||
|
|
||||||
|
const res = await this.fetch(url);
|
||||||
|
const html = await res.text();
|
||||||
|
$ = this.cheerio.load(html);
|
||||||
|
|
||||||
|
$(".chapter-list li").each((_, el) => {
|
||||||
|
const a = $(el).find("a");
|
||||||
|
const href = a.attr("href");
|
||||||
|
const title = a.find(".chapter-title").text().trim();
|
||||||
|
const chapterNumber = parseInt(a.find(".chapter-no").text().trim(), 10);
|
||||||
|
|
||||||
|
chapters.push({
|
||||||
|
id: href,
|
||||||
|
title,
|
||||||
|
chapter: chapterNumber,
|
||||||
|
language: "en"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return chapters;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findChapterPages(chapterId) {
|
||||||
|
const res = await this.fetch(chapterId);
|
||||||
|
const html = await res.text();
|
||||||
|
|
||||||
|
const $ = this.cheerio.load(html);
|
||||||
|
const contentDiv = $("#content");
|
||||||
|
|
||||||
|
if (!contentDiv || contentDiv.length === 0) {
|
||||||
|
return [{
|
||||||
|
type: "text",
|
||||||
|
content: "<p>Error: content not found</p>",
|
||||||
|
index: 0
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
contentDiv.find("script").remove();
|
||||||
|
contentDiv.find("ins").remove();
|
||||||
|
contentDiv.find("[id^='pf-']").remove();
|
||||||
|
contentDiv.find(".ads").remove();
|
||||||
|
contentDiv.find(".adsbygoogle").remove();
|
||||||
|
contentDiv.find("div[style*='text-align:center']").remove();
|
||||||
|
contentDiv.find("div[align='center']").remove();
|
||||||
|
contentDiv.find(".nf-ads").remove();
|
||||||
|
contentDiv.find("nfne597").remove();
|
||||||
|
|
||||||
|
const paragraphs = contentDiv.find("p");
|
||||||
|
let cleanHtml = "";
|
||||||
|
|
||||||
|
paragraphs.each((_, el) => {
|
||||||
|
const p = $(el);
|
||||||
|
|
||||||
|
let text = p.text() || "";
|
||||||
|
|
||||||
|
text = text.replace(/△▼△▼△▼△/g, "");
|
||||||
|
text = text.replace(/[※]+/g, "");
|
||||||
|
text = text.replace(/\s{2,}/g, " ");
|
||||||
|
|
||||||
|
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: novelfire };
|
||||||
170
realbooru.js
Normal file
170
realbooru.js
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
class Realbooru {
|
||||||
|
|
||||||
|
baseUrl = "https://realbooru.com";
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(fetchPath, cheerioPath) {
|
||||||
|
this.fetch = require(fetchPath);
|
||||||
|
this.cheerio = require(cheerioPath);
|
||||||
|
this.type = "image-board";
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadDoc(body) {
|
||||||
|
return this.cheerio.load(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchSearchResult(query, page = 1, perPage = 42) {
|
||||||
|
if (!query) query = "original";
|
||||||
|
|
||||||
|
const offset = (page - 1) * perPage;
|
||||||
|
const url = `${this.baseUrl}/index.php?page=post&s=list&tags=${query}&pid=${offset}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.fetch(url, { headers: this.headers });
|
||||||
|
const data = await response.text();
|
||||||
|
|
||||||
|
const $ = this.cheerio.load(data);
|
||||||
|
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
$('#post-list a[id^="p"], #post-list a[href*="&s=view"], .thumb a').each((i, e) => {
|
||||||
|
const $a = $(e);
|
||||||
|
|
||||||
|
const href = $a.attr('href');
|
||||||
|
let id = null;
|
||||||
|
if (href) {
|
||||||
|
const idMatch = href.match(/&id=(\d+)/);
|
||||||
|
if (idMatch) {
|
||||||
|
id = idMatch[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
id = $a.closest('span, div').attr('id')?.replace('s', '').replace('post_', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageElement = $a.find('img').first();
|
||||||
|
let image = imageElement.attr('src');
|
||||||
|
|
||||||
|
if (image && !image.startsWith('http')) {
|
||||||
|
image = `https:${image}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tags = imageElement.attr('alt')?.trim()?.split(' ').filter(tag => tag !== "");
|
||||||
|
if (!tags || tags.length === 0) {
|
||||||
|
tags = $a.attr('title')?.trim()?.split(' ').filter(tag => tag !== "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id && image) {
|
||||||
|
results.push({
|
||||||
|
id: id,
|
||||||
|
image: image,
|
||||||
|
tags: tags,
|
||||||
|
type: 'preview'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const pagination = $('#paginator .pagination');
|
||||||
|
const lastPageLink = pagination.find('a[alt="last page"]');
|
||||||
|
let totalPages = 1;
|
||||||
|
|
||||||
|
if (lastPageLink.length > 0) {
|
||||||
|
const pid = lastPageLink.attr('href')?.split('pid=')[1];
|
||||||
|
totalPages = Math.ceil(parseInt(pid || "0", 10) / perPage) + 1;
|
||||||
|
} else {
|
||||||
|
const pageLinks = pagination.find('a');
|
||||||
|
if (pageLinks.length > 0) {
|
||||||
|
const lastLinkText = pageLinks.eq(-2).text();
|
||||||
|
totalPages = parseInt(lastLinkText, 10) || 1;
|
||||||
|
} else if (results.length > 0) {
|
||||||
|
totalPages = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentPage = page;
|
||||||
|
const hasNextPage = currentPage < totalPages;
|
||||||
|
const next = hasNextPage ? (currentPage + 1) : 0;
|
||||||
|
const previous = currentPage > 1 ? (currentPage - 1) : 0;
|
||||||
|
|
||||||
|
const total = totalPages * perPage;
|
||||||
|
|
||||||
|
return { total, next, previous, pages: totalPages, page: currentPage, hasNextPage, results };
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error during Realbooru search:", e);
|
||||||
|
|
||||||
|
return { total: 0, next: 0, previous: 0, pages: 1, page: 1, hasNextPage: false, results: [] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchInfo(id) {
|
||||||
|
const url = `${this.baseUrl}/index.php?page=post&s=view&id=${id}`;
|
||||||
|
|
||||||
|
const fetchHeaders = { ...this.headers };
|
||||||
|
|
||||||
|
const response = await this.fetch(url, { headers: fetchHeaders });
|
||||||
|
const original = await response.text();
|
||||||
|
|
||||||
|
const $ = this.cheerio.load(original);
|
||||||
|
|
||||||
|
let fullImage = $('#image').attr('src') || $('video').attr('src');
|
||||||
|
|
||||||
|
const originalLink = $('div.link-list a:contains("Original image")').attr('href');
|
||||||
|
if (originalLink) {
|
||||||
|
fullImage = originalLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullImage && !fullImage.startsWith('http')) {
|
||||||
|
fullImage = `https:${fullImage}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let resizedImageUrl = $('#image-holder img').attr('src');
|
||||||
|
if (resizedImageUrl && !resizedImageUrl.startsWith('http')) {
|
||||||
|
resizedImageUrl = `https:${resizedImageUrl}`;
|
||||||
|
} else if (!resizedImageUrl) {
|
||||||
|
|
||||||
|
resizedImageUrl = fullImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tags = $('.tag-list a.tag-link').map((i, el) => $(el).text().trim()).get();
|
||||||
|
|
||||||
|
const stats = $('#stats ul');
|
||||||
|
|
||||||
|
const postedData = stats.find("li:contains('Posted:')").text().trim();
|
||||||
|
const postedDateMatch = postedData.match(/Posted: (.*?) by/);
|
||||||
|
const createdAt = postedDateMatch ? new Date(postedDateMatch[1]).getTime() : undefined;
|
||||||
|
|
||||||
|
const publishedByMatch = postedData.match(/by\s*(.*)/);
|
||||||
|
const publishedBy = publishedByMatch ? publishedByMatch[1].trim() : undefined;
|
||||||
|
|
||||||
|
const rating = stats.find("li:contains('Rating:')").text().trim().split("Rating: ")[1] || undefined;
|
||||||
|
|
||||||
|
const comments = $('#comment-list div').map((i, el) => {
|
||||||
|
const $el = $(el);
|
||||||
|
const id = $el.attr('id')?.replace('c', '');
|
||||||
|
const user = $el.find('.col1').text().trim().split("\n")[0];
|
||||||
|
const comment = $el.find('.col2').text().trim();
|
||||||
|
if (id && user && comment) {
|
||||||
|
return { id, user, comment };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}).get().filter(Boolean);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
fullImage,
|
||||||
|
resizedImageUrl,
|
||||||
|
tags,
|
||||||
|
createdAt,
|
||||||
|
publishedBy,
|
||||||
|
rating,
|
||||||
|
comments
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { Realbooru };
|
||||||
145
rule34.js
Normal file
145
rule34.js
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
class Rule34 {
|
||||||
|
baseUrl = "https://rule34.xxx";
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(fetchPath, cheerioPath) {
|
||||||
|
this.fetch = require(fetchPath);
|
||||||
|
this.cheerio = require(cheerioPath);
|
||||||
|
this.type = "image-board";
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchSearchResult(query, page = 1, perPage = 42) {
|
||||||
|
if (!query) query = "alisa_mikhailovna_kujou";
|
||||||
|
|
||||||
|
const offset = (page - 1) * perPage;
|
||||||
|
const url = `${this.baseUrl}/index.php?page=post&s=list&tags=${query}&pid=${offset}`;
|
||||||
|
|
||||||
|
const response = await this.fetch(url, { headers: this.headers });
|
||||||
|
const data = await response.text();
|
||||||
|
|
||||||
|
const $ = this.cheerio.load(data);
|
||||||
|
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
$('.image-list span').each((i, e) => {
|
||||||
|
const $e = $(e);
|
||||||
|
const id = $e.attr('id')?.replace('s', '');
|
||||||
|
|
||||||
|
let image = $e.find('img').attr('src');
|
||||||
|
if (image && !image.startsWith('http')) {
|
||||||
|
image = `https:${image}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tags = $e.find('img').attr('alt')?.trim()?.split(' ').filter(tag => tag !== "");
|
||||||
|
|
||||||
|
if (id && image) {
|
||||||
|
results.push({
|
||||||
|
id: id,
|
||||||
|
image: image,
|
||||||
|
tags: tags,
|
||||||
|
type: 'preview'
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const pagination = $('#paginator .pagination');
|
||||||
|
const lastPageLink = pagination.find('a[alt="last page"]');
|
||||||
|
let totalPages = 1;
|
||||||
|
|
||||||
|
if (lastPageLink.length > 0) {
|
||||||
|
|
||||||
|
const pid = lastPageLink.attr('href')?.split('pid=')[1];
|
||||||
|
totalPages = Math.ceil(parseInt(pid || "0", 10) / perPage) + 1;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
const pageLinks = pagination.find('a');
|
||||||
|
if (pageLinks.length > 0) {
|
||||||
|
|
||||||
|
const lastLinkText = pageLinks.eq(-2).text();
|
||||||
|
totalPages = parseInt(lastLinkText, 10) || 1;
|
||||||
|
} else if (results.length > 0) {
|
||||||
|
totalPages = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentPage = page;
|
||||||
|
const hasNextPage = currentPage < totalPages;
|
||||||
|
const next = hasNextPage ? (currentPage + 1) : 0;
|
||||||
|
const previous = currentPage > 1 ? (currentPage - 1) : 0;
|
||||||
|
|
||||||
|
const total = totalPages * perPage;
|
||||||
|
|
||||||
|
return { total, next, previous, pages: totalPages, page: currentPage, hasNextPage, results };
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchInfo(id) {
|
||||||
|
const url = `${this.baseUrl}/index.php?page=post&s=view&id=${id}`;
|
||||||
|
|
||||||
|
const resizeCookies = {
|
||||||
|
'resize-notification': 1,
|
||||||
|
'resize-original': 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const cookieString = Object.entries(resizeCookies).map(([key, value]) => `${key}=${value}`).join('; ');
|
||||||
|
|
||||||
|
const fetchHeaders = { ...this.headers };
|
||||||
|
const resizeHeaders = { ...this.headers, 'cookie': cookieString };
|
||||||
|
|
||||||
|
const [resizedResponse, nonResizedResponse] = await Promise.all([
|
||||||
|
this.fetch(url, { headers: resizeHeaders }),
|
||||||
|
this.fetch(url, { headers: fetchHeaders })
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [resized, original] = await Promise.all([resizedResponse.text(), nonResizedResponse.text()]);
|
||||||
|
|
||||||
|
const $resized = this.cheerio.load(resized);
|
||||||
|
const $ = this.cheerio.load(original);
|
||||||
|
|
||||||
|
let resizedImageUrl = $resized('#image').attr('src');
|
||||||
|
if (resizedImageUrl && !resizedImageUrl.startsWith('http')) {
|
||||||
|
resizedImageUrl = `https:${resizedImageUrl}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fullImage = $('#image').attr('src');
|
||||||
|
if (fullImage && !fullImage.startsWith('http')) {
|
||||||
|
fullImage = `https:${fullImage}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tags = $('#image').attr('alt')?.trim()?.split(' ').filter(tag => tag !== "");
|
||||||
|
const stats = $('#stats ul');
|
||||||
|
|
||||||
|
const postedData = stats.find('li:nth-child(2)').text().trim();
|
||||||
|
const createdAt = new Date(postedData.split("Posted: ")[1].split("by")[0]).getTime();
|
||||||
|
const publishedBy = postedData.split("by")[1].trim();
|
||||||
|
const rating = stats.find("li:contains('Rating:')").text().trim().split("Rating: ")[1];
|
||||||
|
|
||||||
|
const comments = $('#comment-list div').map((i, el) => {
|
||||||
|
const $el = $(el);
|
||||||
|
const id = $el.attr('id')?.replace('c', '');
|
||||||
|
const user = $el.find('.col1').text().trim().split("\n")[0];
|
||||||
|
const comment = $el.find('.col2').text().trim();
|
||||||
|
if (id && user && comment) {
|
||||||
|
return { id, user, comment };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}).get().filter(Boolean);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
fullImage,
|
||||||
|
resizedImageUrl,
|
||||||
|
tags,
|
||||||
|
createdAt,
|
||||||
|
publishedBy,
|
||||||
|
rating,
|
||||||
|
comments
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { Rule34 };
|
||||||
101
waifupics.js
Normal file
101
waifupics.js
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
class WaifuPics {
|
||||||
|
baseUrl = "https://api.waifu.pics";
|
||||||
|
|
||||||
|
SFW_CATEGORIES = [
|
||||||
|
'waifu', 'neko', 'shinobu', 'megumin', 'bully', 'cuddle', 'cry', 'hug', 'awoo',
|
||||||
|
'kiss', 'lick', 'pat', 'smug', 'bonk', 'yeet', 'blush', 'smile', 'wave',
|
||||||
|
'highfive', 'handhold', 'nom', 'bite', 'glomp', 'slap', 'kill', 'kick',
|
||||||
|
'happy', 'wink', 'poke', 'dance', 'cringe'
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor(fetchPath, cheerioPath) {
|
||||||
|
this.fetch = require(fetchPath);
|
||||||
|
this.type = "image-board";
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchSearchResult(query, page = 1, perPage = 42) {
|
||||||
|
if (!query) query = "waifu";
|
||||||
|
|
||||||
|
const category = query.trim().split(' ')[0];
|
||||||
|
|
||||||
|
if (!this.SFW_CATEGORIES.includes(category)) {
|
||||||
|
console.warn(`[WaifuPics] Category '${category}' not supported.`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
total: 0,
|
||||||
|
next: 0,
|
||||||
|
previous: 0,
|
||||||
|
pages: 1,
|
||||||
|
page: 1,
|
||||||
|
hasNextPage: false,
|
||||||
|
results: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const response = await this.fetch(`${this.baseUrl}/many/sfw/${category}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ exclude: [] }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`API returned ${response.status}: ${await response.text()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
const results = data.files.map((url, index) => {
|
||||||
|
|
||||||
|
const id = url.substring(url.lastIndexOf('/') + 1) || `${category}-${index}`;
|
||||||
|
const uniqueId = `${page}-${id}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: uniqueId,
|
||||||
|
image: url,
|
||||||
|
tags: [category],
|
||||||
|
type: 'preview'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
total: 30,
|
||||||
|
next: page + 1,
|
||||||
|
previous: page > 1 ? page - 1 : 0,
|
||||||
|
pages: page + 1,
|
||||||
|
page: page,
|
||||||
|
hasNextPage: true,
|
||||||
|
results: results
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[WaifuPics] Error fetching images:`, error);
|
||||||
|
return {
|
||||||
|
total: 0,
|
||||||
|
next: 0,
|
||||||
|
previous: 0,
|
||||||
|
pages: 1,
|
||||||
|
page: 1,
|
||||||
|
hasNextPage: false,
|
||||||
|
results: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchInfo(id) {
|
||||||
|
console.log(`[WaifuPics] fetchInfo called for ${id}, but this API only provides direct URLs.`);
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
fullImage: `https://i.waifu.pics/${id}`,
|
||||||
|
resizedImageUrl: `https://i.waifu.pics/${id}`,
|
||||||
|
tags: [],
|
||||||
|
createdAt: null,
|
||||||
|
publishedBy: 'Waifu.pics',
|
||||||
|
rating: 'sfw',
|
||||||
|
comments: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { WaifuPics };
|
||||||
Reference in New Issue
Block a user