all extensions to new format :P

This commit is contained in:
2025-12-15 19:40:07 +01:00
parent 9986b64ace
commit 9fe48f93fe
19 changed files with 1725 additions and 1375 deletions

138
tenor.js
View File

@@ -1,61 +1,65 @@
class Tenor {
baseUrl = "https://tenor.com";
constructor(fetchPath, cheerioPath, browser) {
this.browser = browser;
constructor() {
this.type = "image-board";
this.lastQuery = null;
this.seenIds = new Set();
}
async fetchSearchResult(query = "hello", page = 1, perPage = 48) {
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.replace(" ", "-")}-gifs`;
const url = `${this.baseUrl}/search/${query.replaceAll(" ", "-")}-gifs`;
const data = await this.browser.scrape(
const { result } = await this.scrape(
url,
() => {
// Fallback selectors: try specific class first, then generic figure
const items = document.querySelectorAll('div.GifList figure, figure');
const results = [];
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;
items.forEach(fig => {
const link = fig.querySelector('a');
const img = fig.querySelector('img');
if (!link || !img) return;
const href = link.getAttribute('href') || "";
const href = link.getAttribute('href') || "";
const idMatch = href.match(/-(\d+)(?:$|\/?$)/);
const id = idMatch ? idMatch[1] : null;
let idMatch = href.match(/-(\d+)(?:$|\/?$)/);
const id = idMatch ? idMatch[1] : null;
const imgUrl =
img.getAttribute('src') ||
img.getAttribute('data-src');
// Tenor lazy loads images, so we check 'src' AND 'data-src'
const imgUrl = img.getAttribute('src') || img.getAttribute('data-src');
const tagsRaw = img.getAttribute('alt') || "";
const tags = tagsRaw.trim().split(/\s+/).filter(Boolean);
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"
});
}
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};
});
const uniqueResults = Array.from(new Map(results.map(item => [item.id, item])).values());
return { results: uniqueResults, hasNextPage: true };
},
{
waitSelector: 'figure',
waitSelector: "figure",
timeout: 30000,
scrollToBottom: true,
renderWaitTime: 3000,
@@ -63,61 +67,49 @@ class Tenor {
}
);
const newResults = data.results.filter(item => !this.seenIds.has(item.id));
newResults.forEach(item => this.seenIds.add(item.id));
const newResults = result.results.filter(r => !this.seenIds.has(r.id));
newResults.forEach(r => this.seenIds.add(r.id));
return {
results: newResults,
hasNextPage: data.hasNextPage,
results: newResults.map(r => ({
id: r.id,
image: r.image,
tags: r.tags,
})),
hasNextPage: result.hasNextPage,
page
};
}
async fetchInfo(id) {
async getInfo(id) {
const url = `${this.baseUrl}/view/gif-${id}`;
const data = await this.browser.scrape(
const { result } = await this.scrape(
url,
() => {
const img = document.querySelector('img[alt]');
const fullImage = img?.src || null;
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(tag => tag.textContent.trim())
.filter(Boolean);
const tags = [...document.querySelectorAll(".tag-list li a .RelatedTag")]
.map(t => t.textContent.trim())
.filter(Boolean);
let createdAt = Date.now();
const detailNodes = [...document.querySelectorAll('.gif-details dd')];
const createdNode = detailNodes.find(n => n.textContent.includes("Created:"));
if (createdNode) {
const raw = createdNode.textContent.replace("Created:", "").trim();
const parts = raw.split(/[\/,: ]+/);
if (parts.length >= 6) {
let [dd, mm, yyyy, hh, min, ss] = parts.map(p => parseInt(p, 10));
createdAt = new Date(yyyy, mm - 1, dd, hh, min, ss).getTime();
}
}
return {
fullImage,
tags,
createdAt
};
return { fullImage, tags };
});
},
{ waitSelector: 'img[alt]', timeout: 15000 }
{ waitSelector: ".Gif img", timeout: 15000 }
);
return {
id,
fullImage: data.fullImage,
tags: data.tags,
createdAt: data.createdAt,
rating: "Unknown"
image: result.fullImage,
tags: result.tags,
title: result.tags?.join(" ") || `Tenor GIF ${id}`,
headers: ""
};
}
}
module.exports = { Tenor };
module.exports = Tenor;