new format and marketplace
This commit is contained in:
119
image/ZeroChan.js
Normal file
119
image/ZeroChan.js
Normal file
@@ -0,0 +1,119 @@
|
||||
class ZeroChan {
|
||||
baseUrl = "https://zerochan.net";
|
||||
|
||||
constructor() {
|
||||
this.type = "image-board";
|
||||
}
|
||||
|
||||
async search(query = "thighs", page = 1, perPage = 48) {
|
||||
const url = `${this.baseUrl}/${query.trim().replace(/\s+/g, "+")}?p=${page}`;
|
||||
|
||||
const { result } = await this.scrape(
|
||||
url,
|
||||
async (page) => {
|
||||
return page.evaluate(() => {
|
||||
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,
|
||||
tags,
|
||||
});
|
||||
});
|
||||
|
||||
const hasNextPage =
|
||||
document.querySelector('nav.pagination a[rel="next"]') !== null;
|
||||
|
||||
return {results, hasNextPage};
|
||||
});
|
||||
},
|
||||
{
|
||||
waitSelector: "#thumbs2 li",
|
||||
timeout: 15000,
|
||||
renderWaitTime: 3000,
|
||||
loadImages: true
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
results: result.results.map(r => ({
|
||||
id: r.id,
|
||||
image: r.image,
|
||||
tags: r.tags
|
||||
})),
|
||||
hasNextPage: result.hasNextPage,
|
||||
page
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
async getInfo(id) {
|
||||
const url = `${this.baseUrl}/${id}`;
|
||||
|
||||
const { result } = await this.scrape(
|
||||
url,
|
||||
async (page) => {
|
||||
return page.evaluate(() => {
|
||||
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,
|
||||
image: result.fullImage,
|
||||
tags: result.tags
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ZeroChan;
|
||||
94
image/animepictures.js
Normal file
94
image/animepictures.js
Normal file
@@ -0,0 +1,94 @@
|
||||
class Animepictures {
|
||||
baseUrl = "https://anime-pictures.net";
|
||||
|
||||
constructor() {
|
||||
this.type = "image-board";
|
||||
}
|
||||
|
||||
async search(query = "thighs", page = 1, perPage = 42) {
|
||||
const url = `${this.baseUrl}/posts?page=${page - 1}&search_tag=${query}&order_by=date&lang=en`;
|
||||
|
||||
const { result } = await this.scrape(
|
||||
url,
|
||||
async (page) => {
|
||||
return page.evaluate(() => {
|
||||
const items = document.querySelectorAll('.img-block.img-block-big');
|
||||
const results = [];
|
||||
|
||||
items.forEach(div => {
|
||||
const link = div.querySelector('a');
|
||||
const img = div.querySelector('img');
|
||||
if (!link || !img) return;
|
||||
|
||||
const href = link.getAttribute('href') || "";
|
||||
const idMatch = href.match(/\/posts\/(\d+)/);
|
||||
const id = idMatch ? idMatch[1] : null;
|
||||
|
||||
const imgUrl = img.getAttribute('src');
|
||||
const tagsRaw = img.getAttribute('alt') || "";
|
||||
const tags = tagsRaw.trim().split(/\s+/).filter(Boolean);
|
||||
|
||||
if (id && imgUrl) {
|
||||
results.push({
|
||||
id: id,
|
||||
//full res image: imgUrl.replace("opreviews", "oimages").replace("_cp.avif", ".jpeg"),
|
||||
image: imgUrl,
|
||||
tags: tags,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const nextPageBtn = document.querySelector('.numeric_pages a.desktop_only');
|
||||
const hasNextPage = !!nextPageBtn;
|
||||
|
||||
return {results, hasNextPage};
|
||||
});
|
||||
},
|
||||
{ waitSelector: '.img-block.img-block-big', timeout: 15000 }
|
||||
);
|
||||
|
||||
return {
|
||||
results: result.results,
|
||||
hasNextPage: result.hasNextPage,
|
||||
page,
|
||||
//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": "image/avif,image/webp,image/apng,image/*,*/*;q=0.8",
|
||||
// "Accept-Language": "en-US,en;q=0.9",
|
||||
// "Referer": "https://anime-pictures.net/",
|
||||
// "Sec-Fetch-Dest": "document",
|
||||
// "Sec-Fetch-Mode": "navigate",
|
||||
// "Sec-Fetch-Site": "none",
|
||||
// "Sec-Fetch-User": "?1"
|
||||
//}
|
||||
};
|
||||
}
|
||||
|
||||
async getInfo(id) {
|
||||
const url = `${this.baseUrl}/posts/${id}?lang=en`;
|
||||
|
||||
const { result } = await this.scrape(
|
||||
url,
|
||||
async (page) => {
|
||||
return page.evaluate(() => {
|
||||
const img = document.querySelector('#big_preview');
|
||||
const image = img ? img.src : null;
|
||||
|
||||
const tagLinks = document.querySelectorAll('.tags li a');
|
||||
const tags = [...tagLinks].map(a => a.textContent.trim());
|
||||
|
||||
return {image, tags};
|
||||
});
|
||||
},
|
||||
{ waitSelector: '#big_preview', timeout: 15000 }
|
||||
);
|
||||
|
||||
return {
|
||||
id,
|
||||
image: result.image,
|
||||
tags: result.tags,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Animepictures;
|
||||
83
image/gelbooru.js
Normal file
83
image/gelbooru.js
Normal file
@@ -0,0 +1,83 @@
|
||||
class Gelbooru {
|
||||
baseUrl = "https://gelbooru.com";
|
||||
|
||||
constructor() {
|
||||
this.type = "image-board";
|
||||
}
|
||||
|
||||
async search(query = "thighs", page = 1, perPage = 42) {
|
||||
const url = `${this.baseUrl}/index.php?page=post&s=list&tags=${encodeURIComponent(query)}&pid=${(page - 1) * perPage}`;
|
||||
|
||||
const html = await fetch(url, {
|
||||
headers: { "User-Agent": "Mozilla/5.0" }
|
||||
}).then(r => r.text());
|
||||
|
||||
const $ = this.cheerio.load(html);
|
||||
const results = [];
|
||||
|
||||
$("article.thumbnail-preview > a[id^='p']").each((_, el) => {
|
||||
const id = $(el).attr("id")?.slice(1); // p13123834 → 13123834
|
||||
if (!id) return;
|
||||
|
||||
const img = $(el).find("img");
|
||||
const image = img.attr("src");
|
||||
|
||||
const tags = img.attr("alt")
|
||||
?.replace(/^Rule 34 \|\s*/, "")
|
||||
?.split(",")
|
||||
?.map(t => t.trim())
|
||||
?.filter(Boolean) || [];
|
||||
|
||||
results.push({ id, image, tags });
|
||||
});
|
||||
|
||||
// pagination
|
||||
const totalPages = Math.max(
|
||||
page,
|
||||
...$("a[href*='pid=']")
|
||||
.map((_, el) =>
|
||||
Math.floor(
|
||||
parseInt($(el).attr("href")?.match(/pid=(\d+)/)?.[1] || 0) / perPage
|
||||
) + 1
|
||||
)
|
||||
.get()
|
||||
);
|
||||
|
||||
return {
|
||||
results,
|
||||
page,
|
||||
hasNextPage: page < totalPages
|
||||
};
|
||||
}
|
||||
|
||||
async getInfo(id) {
|
||||
const html = await fetch(
|
||||
`${this.baseUrl}/index.php?page=post&s=view&id=${id}`,
|
||||
{ headers: { "User-Agent": "Mozilla/5.0" } }
|
||||
).then(r => r.text());
|
||||
|
||||
const $ = this.cheerio.load(html);
|
||||
const container = $("section.image-container");
|
||||
|
||||
let image =
|
||||
container.find("#image").attr("src") ||
|
||||
container.attr("data-file-url") ||
|
||||
container.attr("data-large-file-url") ||
|
||||
null;
|
||||
|
||||
// tags
|
||||
const tags = container
|
||||
.attr("data-tags")
|
||||
?.trim()
|
||||
?.split(/\s+/)
|
||||
?.filter(Boolean) || [];
|
||||
|
||||
return {
|
||||
id,
|
||||
image,
|
||||
tags
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Gelbooru;
|
||||
100
image/giphy.js
Normal file
100
image/giphy.js
Normal file
@@ -0,0 +1,100 @@
|
||||
class Giphy {
|
||||
baseUrl = "https://giphy.com";
|
||||
|
||||
constructor() {
|
||||
this.type = "image-board";
|
||||
}
|
||||
|
||||
async search(query = "hello", page = 1, perPage = 48) {
|
||||
const url = `${this.baseUrl}/search/${query.trim().replace(/\s+/g, "-")}`;
|
||||
|
||||
const data = await this.scrape(
|
||||
url,
|
||||
(page) => page.evaluate(() => {
|
||||
const items = document.querySelectorAll('a[data-giphy-id]');
|
||||
const results = [];
|
||||
|
||||
items.forEach(el => {
|
||||
const id = el.getAttribute('data-giphy-id');
|
||||
|
||||
const srcWebp = el.querySelector('source[type="image/webp"][srcset^="http"]');
|
||||
const srcImg = el.querySelector('img');
|
||||
|
||||
let rawSrc =
|
||||
srcWebp?.getAttribute("srcset")?.split(" ")[0] ||
|
||||
srcImg?.src ||
|
||||
null;
|
||||
|
||||
if (!rawSrc || rawSrc.startsWith("data:")) return;
|
||||
|
||||
const alt = srcImg?.getAttribute("alt") || "";
|
||||
const tags = alt.trim().split(/\s+/).filter(Boolean);
|
||||
|
||||
results.push({
|
||||
id,
|
||||
image: rawSrc,
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
results,
|
||||
hasNextPage: false
|
||||
};
|
||||
}),
|
||||
{
|
||||
waitSelector: 'picture img, a[data-giphy-id] img',
|
||||
scrollToBottom: true,
|
||||
timeout: 15000
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
results: data.result.results.map(r => ({
|
||||
id: r.id,
|
||||
image: r.image
|
||||
})),
|
||||
hasNextPage: data.result.hasNextPage,
|
||||
page
|
||||
};
|
||||
}
|
||||
|
||||
async getInfo(id) {
|
||||
const url = `https://giphy.com/gifs/${id}`;
|
||||
|
||||
const data = await this.scrape(
|
||||
url,
|
||||
(page) => page.evaluate(() => {
|
||||
const scripts = document.querySelectorAll(
|
||||
'script[type="application/ld+json"]'
|
||||
);
|
||||
|
||||
let imgsrc = null;
|
||||
|
||||
scripts.forEach(script => {
|
||||
try {
|
||||
const json = JSON.parse(script.textContent);
|
||||
|
||||
if (json?.["@type"] === "Article" && json?.image?.url) {
|
||||
imgsrc = json.image.url;
|
||||
}
|
||||
} catch {}
|
||||
});
|
||||
|
||||
return {
|
||||
image: imgsrc
|
||||
};
|
||||
}),
|
||||
{
|
||||
waitSelector: 'script[type="application/ld+json"]',
|
||||
timeout: 15000
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
id,
|
||||
image: data.result.image
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Giphy;
|
||||
84
image/realbooru.js
Normal file
84
image/realbooru.js
Normal file
@@ -0,0 +1,84 @@
|
||||
class Realbooru {
|
||||
baseUrl = "https://realbooru.com";
|
||||
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0"
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.type = "image-board";
|
||||
}
|
||||
|
||||
async search(query = "original", page = 1, perPage = 42) {
|
||||
const offset = (page - 1) * perPage;
|
||||
|
||||
const tags = query
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.join("+") + "+";
|
||||
|
||||
const url = `${this.baseUrl}/index.php?page=post&s=list&tags=${tags}&pid=${offset}`;
|
||||
const html = await fetch(url, { headers: this.headers }).then(r => r.text());
|
||||
const $ = this.cheerio.load(html);
|
||||
|
||||
const results = [];
|
||||
|
||||
$('div.col.thumb').each((_, el) => {
|
||||
const id = ($(el).attr('id') || "").replace('s', '');
|
||||
const img = $(el).find('img');
|
||||
|
||||
let image = img.attr('src');
|
||||
if (image && !image.startsWith('http')) image = 'https:' + image;
|
||||
|
||||
const title = img.attr('title') || '';
|
||||
const tags = title
|
||||
.split(',')
|
||||
.map(t => t.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
if (id && image) {
|
||||
results.push({ id, image, tags });
|
||||
}
|
||||
});
|
||||
|
||||
let totalPages = page;
|
||||
const lastPid = $('a[alt="last page"]').attr('href')?.match(/pid=(\d+)/);
|
||||
if (lastPid) {
|
||||
totalPages = Math.floor(parseInt(lastPid[1], 10) / perPage) + 1;
|
||||
}
|
||||
|
||||
return {
|
||||
results,
|
||||
page,
|
||||
hasNextPage: page < totalPages
|
||||
};
|
||||
}
|
||||
|
||||
async getInfo(id) {
|
||||
const url = `${this.baseUrl}/index.php?page=post&s=view&id=${id}`;
|
||||
const html = await fetch(url, { headers: this.headers }).then(r => r.text());
|
||||
const $ = this.cheerio.load(html);
|
||||
|
||||
let image =
|
||||
$('video source').attr('src') ||
|
||||
$('#image').attr('src') ||
|
||||
null;
|
||||
|
||||
if (image && !image.startsWith('http')) {
|
||||
image = this.baseUrl + image;
|
||||
}
|
||||
|
||||
const tags = [];
|
||||
$('#tagLink a').each((_, el) => {
|
||||
tags.push($(el).text().trim());
|
||||
});
|
||||
|
||||
return {
|
||||
id,
|
||||
image,
|
||||
tags
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Realbooru;
|
||||
99
image/rule34.js
Normal file
99
image/rule34.js
Normal file
@@ -0,0 +1,99 @@
|
||||
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() {
|
||||
this.type = "image-board";
|
||||
}
|
||||
|
||||
async search(query = "alisa_mikhailovna_kujou", page = 1, perPage = 42) {
|
||||
const offset = (page - 1) * perPage;
|
||||
const url = `${this.baseUrl}/index.php?page=post&s=list&tags=${query}&pid=${offset}`;
|
||||
|
||||
const response = await fetch(url, { headers: this.headers });
|
||||
const data = await response.text();
|
||||
const $ = this.cheerio.load(data);
|
||||
|
||||
const results = [];
|
||||
|
||||
$('.image-list span').each((_, 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(Boolean);
|
||||
|
||||
if (id && image) {
|
||||
results.push({
|
||||
id,
|
||||
image,
|
||||
tags
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const pagination = $('#paginator .pagination');
|
||||
const lastPageLink = pagination.find('a[alt="last page"]');
|
||||
|
||||
let totalPages = 1;
|
||||
if (lastPageLink.length) {
|
||||
const pid = Number(lastPageLink.attr('href')?.split('pid=')[1] ?? 0);
|
||||
totalPages = Math.ceil(pid / perPage) + 1;
|
||||
}
|
||||
|
||||
return {
|
||||
page,
|
||||
hasNextPage: page < totalPages,
|
||||
results
|
||||
};
|
||||
}
|
||||
|
||||
async getInfo(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([
|
||||
fetch(url, { headers: resizeHeaders }),
|
||||
fetch(url, { headers: fetchHeaders })
|
||||
]);
|
||||
|
||||
const [resized, original] = await Promise.all([resizedResponse.text(), nonResizedResponse.text()]);
|
||||
|
||||
const $ = this.cheerio.load(original);
|
||||
|
||||
let fullImage = $('#image').attr('src');
|
||||
if (fullImage && !fullImage.startsWith('http')) {
|
||||
fullImage = `https:${fullImage}`;
|
||||
}
|
||||
|
||||
const tags = $('#image').attr('alt')?.trim()?.split(' ').filter(tag => tag !== "");
|
||||
|
||||
return {
|
||||
id,
|
||||
image: fullImage,
|
||||
tags
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Rule34;
|
||||
115
image/tenor.js
Normal file
115
image/tenor.js
Normal file
@@ -0,0 +1,115 @@
|
||||
class Tenor {
|
||||
baseUrl = "https://tenor.com";
|
||||
|
||||
constructor() {
|
||||
this.type = "image-board";
|
||||
this.lastQuery = null;
|
||||
this.seenIds = new Set();
|
||||
}
|
||||
|
||||
async search(query, page = 1, perPage = 48) {
|
||||
query = query?.trim() || "thighs";
|
||||
|
||||
if (query !== this.lastQuery) {
|
||||
this.lastQuery = query;
|
||||
this.seenIds.clear();
|
||||
}
|
||||
|
||||
const url = `${this.baseUrl}/search/${query.replaceAll(" ", "-")}-gifs`;
|
||||
|
||||
const { result } = await this.scrape(
|
||||
url,
|
||||
async (page) => {
|
||||
return page.evaluate(() => {
|
||||
const items = document.querySelectorAll('div.GifList figure, figure');
|
||||
const results = [];
|
||||
|
||||
items.forEach(fig => {
|
||||
const link = fig.querySelector('a');
|
||||
const img = fig.querySelector('img');
|
||||
if (!link || !img) return;
|
||||
|
||||
const href = link.getAttribute('href') || "";
|
||||
const idMatch = href.match(/-(\d+)(?:$|\/?$)/);
|
||||
const id = idMatch ? idMatch[1] : null;
|
||||
|
||||
const imgUrl =
|
||||
img.getAttribute('src') ||
|
||||
img.getAttribute('data-src');
|
||||
|
||||
const tagsRaw = img.getAttribute('alt') || "";
|
||||
const tags = tagsRaw.trim().split(/\s+/).filter(Boolean);
|
||||
|
||||
if (id && imgUrl && !imgUrl.includes("placeholder")) {
|
||||
results.push({
|
||||
id,
|
||||
image: imgUrl,
|
||||
sampleImageUrl: imgUrl,
|
||||
tags,
|
||||
type: "preview"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const uniqueResults = Array.from(
|
||||
new Map(results.map(r => [r.id, r])).values()
|
||||
);
|
||||
|
||||
return {results: uniqueResults, hasNextPage: true};
|
||||
});
|
||||
},
|
||||
{
|
||||
waitSelector: "figure",
|
||||
timeout: 30000,
|
||||
scrollToBottom: true,
|
||||
renderWaitTime: 3000,
|
||||
loadImages: true
|
||||
}
|
||||
);
|
||||
|
||||
const newResults = result.results.filter(r => !this.seenIds.has(r.id));
|
||||
newResults.forEach(r => this.seenIds.add(r.id));
|
||||
|
||||
return {
|
||||
results: newResults.map(r => ({
|
||||
id: r.id,
|
||||
image: r.image,
|
||||
tags: r.tags,
|
||||
})),
|
||||
hasNextPage: result.hasNextPage,
|
||||
page
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
async getInfo(id) {
|
||||
const url = `${this.baseUrl}/view/gif-${id}`;
|
||||
|
||||
const { result } = await this.scrape(
|
||||
url,
|
||||
async (page) => {
|
||||
return page.evaluate(() => {
|
||||
const img = document.querySelector(".Gif img");
|
||||
const fullImage = img?.src || null;
|
||||
|
||||
const tags = [...document.querySelectorAll(".tag-list li a .RelatedTag")]
|
||||
.map(t => t.textContent.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
return { fullImage, tags };
|
||||
});
|
||||
},
|
||||
{ waitSelector: ".Gif img", timeout: 15000 }
|
||||
);
|
||||
|
||||
return {
|
||||
id,
|
||||
image: result.fullImage,
|
||||
tags: result.tags,
|
||||
title: result.tags?.join(" ") || `Tenor GIF ${id}`,
|
||||
headers: ""
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Tenor;
|
||||
93
image/waifupics.js
Normal file
93
image/waifupics.js
Normal file
@@ -0,0 +1,93 @@
|
||||
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() {
|
||||
this.type = "image-board";
|
||||
}
|
||||
|
||||
async search(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 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 = `${id}`;
|
||||
|
||||
return {
|
||||
id: uniqueId,
|
||||
image: url,
|
||||
tags: [category],
|
||||
};
|
||||
});
|
||||
|
||||
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 getInfo(id) {
|
||||
return {
|
||||
id,
|
||||
image: `https://i.waifu.pics/${id}`,
|
||||
tags: []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WaifuPics;
|
||||
Reference in New Issue
Block a user