Loading snippets...
pinterest search dan download. reupload, modif atau apapun selama itu berasal dari sini wajib cantumkan sumber nya yak
/**
* Pinterest Search dan download
* base: https://pinterest.com
* Creator: ShanMolvyr
* reupload/modif cantumkan sumber ini woii parah
*
* Note: cek https://snippet.vyr.my.id/shanmolvyr/pinterest/README.md
* Sumber: https://whatsapp.com/channel/0029VbB4Kw8EFeXfeExaXc3Q
*/
const https = require("https");
const crypto = require("crypto");
const fs = require("fs");
const path = require("path");
// ─── HTTP
function httpsGet(url, headers = {}) {
return new Promise((resolve, reject) => {
const u = new URL(url);
const req = https.get(
{
hostname: u.hostname,
path: u.pathname + u.search,
headers: {
"User-Agent": "Mozilla/5.0 (Android 15; Mobile; rv:150.0) Gecko/150.0 Firefox/150.0",
Accept: "application/json, text/javascript, */*; q=0.01",
"Accept-Language": "id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7",
"Accept-Encoding": "identity",
"X-Requested-With": "XMLHttpRequest",
"X-Pinterest-AppState": "active",
"X-Pinterest-PWS-Handler": "www/search/[scope].js",
...headers,
},
},
(res) => {
let body = "";
res.on("data", (c) => (body += c));
res.on("end", () =>
resolve({ status: res.statusCode, body, headers: res.headers })
);
}
);
req.on("error", reject);
req.setTimeout(15000, () => {
req.destroy();
reject(new Error("Timeout"));
});
});
}
function httpsGetStream(url, destPath) {
return new Promise((resolve, reject) => {
const u = new URL(url);
const writer = fs.createWriteStream(destPath);
const req = https.get(
{
hostname: u.hostname,
path: u.pathname + u.search,
headers: {
"User-Agent": "Mozilla/5.0 (Android 15; Mobile; rv:150.0) Gecko/150.0 Firefox/150.0",
Referer: "https://www.pinterest.com/",
},
},
(res) => {
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
writer.close();
return httpsGetStream(res.headers.location, destPath).then(resolve).catch(reject);
}
res.pipe(writer);
writer.on("finish", () =>
resolve({ path: destPath, size: fs.statSync(destPath).size })
);
writer.on("error", reject);
}
);
req.on("error", reject);
req.setTimeout(30000, () => {
req.destroy();
reject(new Error("Download timeout"));
});
});
}
// ─── HELPERS
const rand = (n) => crypto.randomBytes(n).toString("hex");
const appVer = () => rand(4).slice(0, 7);
const ts = () => Date.now();
function buildSearchUrl(query, pageSize, bookmark) {
const sourceUrl = `/search/pins/?q=${encodeURIComponent(query)}&rs=typed`;
const options = {
query,
scope: "pins",
page_size: pageSize,
rs: "typed",
redux_normalize_feed: true,
appliedProductFilters: "---",
auto_correction_disabled: false,
static_feed: false,
...(bookmark ? { bookmarks: [bookmark] } : {}),
};
const data = encodeURIComponent(JSON.stringify({ options, context: {} }));
return (
`https://id.pinterest.com/resource/BaseSearchResource/get/` +
`?source_url=${encodeURIComponent(sourceUrl)}&data=${data}&_=${ts()}`
);
}
function buildPinUrl(pinId) {
const options = { id: pinId, field_set_key: "detailed" };
const data = encodeURIComponent(JSON.stringify({ options, context: {} }));
return (
`https://id.pinterest.com/resource/PinResource/get/` +
`?source_url=${encodeURIComponent(`/pin/${pinId}/`)}&data=${data}&_=${ts()}`
);
}
function apiHeaders(query) {
const trace = rand(8);
return {
"X-APP-VERSION": appVer(),
"X-B3-TraceId": trace,
"X-B3-SpanId": rand(8),
"X-B3-ParentSpanId": trace,
"X-B3-Flags": "0",
"screen-dpr": "2.857142857142857",
"X-Pinterest-Source-Url": `/search/pins/?rs=typed&q=${encodeURIComponent(query || "")}`,
Referer: `https://id.pinterest.com/search/pins/?rs=typed&q=${encodeURIComponent(query || "")}`,
};
}
// ─── PARSE
function parsePin(p) {
if (!p || !p.id) return null;
const images = p.images || {};
const videos = p.videos?.video_list || null;
let type = "image";
let mediaUrl = images["736x"]?.url || images.orig?.url || null;
let sizes = {
orig: images.orig || null,
"736x": images["736x"] || null,
"474x": images["474x"] || null,
"236x": images["236x"] || null,
};
if (videos && Object.keys(videos).length) {
type = "video";
const sorted = Object.entries(videos).sort(
(a, b) => (b[1]?.width || 0) - (a[1]?.width || 0)
);
mediaUrl = sorted[0]?.[1]?.url || null;
sizes = videos;
} else if (mediaUrl?.includes(".gif") || p.is_gif) {
type = "gif";
}
return {
id: p.id,
type,
title: p.title || p.grid_title || p.description?.slice(0, 80) || "",
description: p.description || "",
pinUrl: `https://www.pinterest.com/pin/${p.id}/`,
mediaUrl,
thumbnail: images["236x"]?.url || images["474x"]?.url || mediaUrl,
sizes,
link: p.link || null,
board: p.board ? { id: p.board.id, name: p.board.name } : null,
pinner: p.pinner
? {
username: p.pinner.username,
fullName: p.pinner.full_name,
avatar: p.pinner.image_small_url || null,
}
: null,
stats: {
saves: p.save_count || p.repin_count || 0,
comments: p.comment_count || 0,
},
dominantColor: p.dominant_color || null,
createdAt: p.created_at || null,
};
}
// ─── SEARCH
async function search(query, opts = {}) {
const { limit = 25, bookmark = null } = opts;
const url = buildSearchUrl(query, limit, bookmark);
const { status, body } = await httpsGet(url, apiHeaders(query));
if (status !== 200) throw new Error(`HTTP ${status}`);
const json = JSON.parse(body);
const rr = json?.resource_response;
if (!rr || rr.status !== "success") {
throw new Error(rr?.message || "Pinterest API engrorr");
}
const pins = rr.data?.results || [];
const nextBookmark = rr.data?.bookmark || null;
return {
query,
total: pins.length,
bookmark: nextBookmark,
hasMore: !!nextBookmark,
results: pins.map(parsePin).filter(Boolean),
};
}
async function searchAll(query, opts = {}) {
const { total = 50 } = opts;
let all = [];
let bookmark = null;
while (all.length < total) {
const res = await search(query, { limit: Math.min(25, total - all.length), bookmark });
all = all.concat(res.results);
bookmark = res.bookmark;
if (!res.hasMore) break;
await new Promise((r) => setTimeout(r, 800));
}
return { query, total: all.length, results: all.slice(0, total) };
}
// ─── DETAIL
async function getPin(pinId) {
const url = buildPinUrl(pinId);
const { status, body } = await httpsGet(url, {
...apiHeaders(""),
Referer: `https://id.pinterest.com/pin/${pinId}/`,
"X-Pinterest-Source-Url": `/pin/${pinId}/`,
});
if (status !== 200) throw new Error(`HTTP ${status}`);
const json = JSON.parse(body);
const pin = json?.resource_response?.data;
if (!pin) throw new Error(`Pin ${pinId} gk ditemukan`);
return parsePin(pin);
}
// ─── DOnglod
async function downloadPin(pinId, outputDir = "./downloads") {
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
const pin = await getPin(pinId);
if (!pin.mediaUrl) throw new Error(`gak ada mediaUrl untuk pin ${pinId}`);
const url = pin.mediaUrl;
let ext = "jpg";
if (pin.type === "gif" || url.includes(".gif")) ext = "gif";
else if (pin.type === "video" || url.includes(".mp4")) ext = "mp4";
else if (url.includes(".webp")) ext = "webp";
else if (url.includes(".png")) ext = "png";
const filename = `${pinId}.${ext}`;
const outputPath = path.join(outputDir, filename);
const dl = await httpsGetStream(url, outputPath);
return { pinId, type: pin.type, title: pin.title, filename, ...dl, pin };
}
async function downloadFromUrl(pinUrl, outputDir = "./downloads") {
const match = pinUrl.match(/\/pin\/(\d+)/);
if (!match) throw new Error("URL nya tidak valid");
return downloadPin(match[1], outputDir);
}
// ─── EXPORTS
module.exports = { search, searchAll, getPin, downloadPin, downloadFromUrl };
// ─── CLI Nyah
if (require.main === module) {
(async () => {
const [, , command, ...args] = process.argv;
try {
if (command === "search") {
const [query, limitStr] = args;
if (!query) throw new Error("Usage: node pinterest.js search <query> [limit]");
const result = await search(query, { limit: parseInt(limitStr) || 25 });
console.log(JSON.stringify(result, null, 2));
} else if (command === "searchall") {
const [query, totalStr] = args;
if (!query) throw new Error("Usage: node pinterest.js searchall <query> [total]");
const result = await searchAll(query, { total: parseInt(totalStr) || 50 });
console.log(JSON.stringify(result, null, 2));
} else if (command === "pin") {
if (!args[0]) throw new Error("Usage: node pinterest.js pin <pinId>");
console.log(JSON.stringify(await getPin(args[0]), null, 2));
} else if (command === "download") {
const [target, outDir] = args;
if (!target) throw new Error("Usage: node pinterest.js download <pinId|url> [outputDir]");
const result = target.startsWith("http")
? await downloadFromUrl(target, outDir || "./downloads")
: await downloadPin(target, outDir || "./downloads");
console.log(JSON.stringify(result, null, 2));
} else {
console.error("Commands:");
console.error(" search <query> [limit]");
console.error(" searchall <query> [total]");
console.error(" pin <pinId>");
console.error(" download <pinId|url> [outputDir]");
}
} catch (err) {
console.error("[!] Error:", err.message);
if (process.env.DEBUG) console.error(err.stack);
process.exit(1);
}
})();
}
bashnpm install axios
bashnode pinterest.js search <query> [limit] node pinterest.js searchall <query> [total] node pinterest.js pin <pinid> node pinterest.js download <pinid|url> [outputDir]