DashX-API / plugins /pinlens.js
HerzaJ's picture
Create pinlens.js
e49ea3c verified
const axios = require('axios');
const FormData = require('form-data');
const crypto = require('crypto');
// SCRAPE BY SIPUTZX
class PinterestLensScraper {
constructor(authToken) {
this.authToken = authToken;
}
getRandomHeaders() {
const devices = [
{ model: 'SM-G991B', manufacturer: 'samsung', name: 'Samsung Galaxy S21' },
{ model: 'SM-A525F', manufacturer: 'samsung', name: 'Samsung Galaxy A52' },
{ model: 'Pixel 6', manufacturer: 'Google', name: 'Google Pixel 6' },
{ model: 'Pixel 7 Pro', manufacturer: 'Google', name: 'Google Pixel 7 Pro' },
{ model: 'M2101K6G', manufacturer: 'Xiaomi', name: 'Xiaomi Redmi Note 10' },
{ model: '2201117TG', manufacturer: 'Xiaomi', name: 'Xiaomi 11T' },
{ model: 'CPH2121', manufacturer: 'OPPO', name: 'OPPO Reno5' },
{ model: 'RMX3085', manufacturer: 'realme', name: 'realme 8 Pro' },
{ model: 'itel S665L', manufacturer: 'ITEL', name: 'itel S665L' },
{ model: 'TECNO KE5', manufacturer: 'TECNO', name: 'TECNO Spark 7' }
];
const versions = ['13.36.2', '13.35.0', '13.34.1', '13.33.0', '13.32.1'];
const androidVersions = ['11', '12', '13'];
const device = devices[Math.floor(Math.random() * devices.length)];
const version = versions[Math.floor(Math.random() * versions.length)];
const androidVer = androidVersions[Math.floor(Math.random() * androidVersions.length)];
const advertisingId = crypto.randomUUID();
const hardwareId = crypto.randomBytes(8).toString('hex');
const installId = crypto.randomBytes(16).toString('hex');
return {
'User-Agent': `Pinterest for Android/${version} (${device.model}; ${androidVer})`,
'accept-language': 'id-ID',
'x-pinterest-advertising-id': advertisingId,
'x-pinterest-app-type-detailed': '3',
'x-pinterest-device': device.model,
'x-pinterest-device-hardwareid': hardwareId,
'x-pinterest-device-manufacturer': device.manufacturer,
'x-pinterest-installid': installId,
'x-pinterest-webview-supported': 'false',
'x-pinterest-appstate': 'active',
'x-node-id': 'true',
'authorization': `Bearer ${this.authToken}`
};
}
async searchByImage(imageUrl, pageSize = 12) {
const data = new FormData();
data.append('camera_type', '0');
data.append('source_type', '1');
data.append('video_autoplay_disabled', '0');
data.append('fields', this.getFields());
data.append('page_size', pageSize.toString());
data.append('image_url', imageUrl);
const headers = this.getRandomHeaders();
const response = await axios.post(
'https://api.pinterest.com/v3/visual_search/lens/search/',
data,
{ headers: { ...headers, ...data.getHeaders() } }
);
return this.parseResults(response.data);
}
async getMoreResults(bookmark, url, pageSize = 12) {
const params = new URLSearchParams({
bookmark,
camera_type: '0',
source_type: '1',
video_autoplay_disabled: '0',
fields: this.getFields(),
url,
page_size: pageSize.toString(),
view_type: '119',
view_parameter: '3064'
});
const headers = this.getRandomHeaders();
const response = await axios.get(
`https://api.pinterest.com/v3/visual_search/lens/search/?${params}`,
{ headers }
);
return this.parseResults(response.data);
}
async scrapeAll(imageUrl, maxPages = 5) {
const allResults = [];
const firstPage = await this.searchByImage(imageUrl);
allResults.push(...firstPage.pins);
let bookmark = firstPage.bookmark;
let url = firstPage.url;
let page = 2;
while (bookmark && page <= maxPages) {
const nextPage = await this.getMoreResults(bookmark, url);
allResults.push(...nextPage.pins);
bookmark = nextPage.bookmark;
page++;
await this.delay(100);
}
return {
total: allResults.length,
pins: allResults,
visualObjects: firstPage.visualObjects,
searchIdentifier: firstPage.searchIdentifier
};
}
parseResults(response) {
const pins = response.data.map(pin => ({
id: pin.id,
title: pin.title || '',
description: pin.description || '',
imageUrl: pin.images?.['736x']?.url || pin.images?.originals?.url,
thumbnailUrl: pin.images?.['236x']?.url,
dominantColor: pin.dominant_color,
creator: {
id: pin.pinner?.id,
username: pin.pinner?.username,
fullName: pin.pinner?.full_name,
imageUrl: pin.pinner?.image_medium_url
},
board: {
id: pin.board?.id,
name: pin.board?.name,
url: pin.board?.url
},
stats: {
saves: pin.aggregated_pin_data?.aggregated_stats?.saves || 0,
comments: pin.comment_count || 0
},
createdAt: pin.created_at,
isVideo: pin.is_video || false,
link: pin.link,
domain: pin.domain
}));
return {
pins,
bookmark: response.bookmark,
url: response.url,
visualObjects: response.visual_objects,
searchIdentifier: response.search_identifier
};
}
getFields() {
return 'pin.{id,title,description,images[736x,236x],dominant_color,pinner(),board(),aggregated_pin_data(),comment_count,created_at,is_video,link,domain},user.{id,username,full_name,image_medium_url},board.{id,name,url},aggregatedpindata.{aggregated_stats}';
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
const handler = async (req, res) => {
try {
const { imageUrl, token, maxPages = 5, key } = req.query;
if (!imageUrl) {
return res.status(400).json({
success: false,
error: 'Missing required parameter: imageUrl'
});
}
if (!token) {
return res.status(400).json({
success: false,
error: 'Missing required parameter: token'
});
}
const scraper = new PinterestLensScraper(token);
const results = await scraper.scrapeAll(imageUrl, parseInt(maxPages));
res.json({
author: 'siputzx',
success: true,
data: results
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
};
module.exports = {
name: 'Pinterest Lens Scraper',
description: 'Scrape Pinterest pins using image search with lens',
type: 'GET',
routes: ['api/tools/pinlens'],
tags: ['tools', 'pinterest', 'image-search'],
main: ['tools', 'Search'],
parameters: ['imageUrl', 'token', 'maxPages', 'key'],
enabled: true,
limit: 10,
handler
};