Back to Home
Gistify Snippet
kyiov
14h ago·1 file(s)
Public

Pinterest Video & Image Downloader (PinDown)

Pinterest scraper & downloader module extracting dynamic anti-bot tokens and parsing video/image download URLs.

#scraper#pinterest#downloader#javascript#nodejs
0
Raw
pindown.jsjavascript
163 lines
1// pindown.js — bundle standalone. npm deps: axios, cheerio, node:url
2
3/**
4 * Scraper: pindown.js
5 * Source : kyioapi/pindown.js
6 *
7 * Input : Function parameters / CLI args (e.g. query: string, url: string, options: object)
8 * Output : Promise<Object | Array | Buffer> (JSON formatted scraping response / stream URL / media metadata)
9 *
10 * CLI Run:
11 * node kyioapi/pindown.js [functionName] [args...]
12 * node cli.js kyioapi/pindown.js [functionName] [args...]
13 */
14
15import * as __ns_283 from 'axios'
16const axios = __ns_283.default
17import * as __ns_284 from 'cheerio'
18const cheerio = __ns_284
19import * as __ns_285 from 'node:url'
20const pathToFileURL = __ns_285.pathToFileURL
21
22
23
24
25class PinDownAPI {
26 constructor() {
27 this.baseURL = "https://pindown.io";
28 }
29
30 async getDynamicToken() {
31 const response = await axios.get(`${this.baseURL}/en1`, {
32 headers: {
33 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'
34 }
35 });
36
37 const $ = cheerio.load(response.data);
38
39 let tokenName = '';
40 let tokenValue = '';
41
42 $('form input').each((i, el) => {
43 const name = $(el).attr('name');
44 if (name && name !== 'url' && name !== 'lang') {
45 tokenName = name;
46 tokenValue = $(el).attr('value') || '';
47 }
48 });
49
50 const rawCookies = response.headers['set-cookie'] || [];
51 const cookieString = rawCookies.map(c => c.split(';')[0]).join('; ');
52
53 return { tokenName, tokenValue, cookieString };
54 }
55
56 async download(url, lang = "en") {
57 const { tokenName, tokenValue, cookieString } = await this.getDynamicToken();
58 if (!tokenName) throw new Error("Gagal mengekstrak Anti-Bot Key dinamis dari halaman utama.");
59
60 const formData = new URLSearchParams();
61 formData.append("url", url);
62 formData.append(tokenName, tokenValue);
63 formData.append("lang", lang);
64
65 const response = await axios({
66 method: "POST",
67 url: `${this.baseURL}/action`,
68 headers: {
69 "accept": "*/*",
70 "accept-language": "en-US,en;q=0.9",
71 "cookie": cookieString,
72 "origin": this.baseURL,
73 "referer": `${this.baseURL}/en1`,
74 "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
75 "Content-Type": "application/x-www-form-urlencoded"
76 },
77 data: formData.toString(),
78 });
79
80 return response.data;
81 }
82
83 parseDownloadLinks(html) {
84 const $ = cheerio.load(html);
85 const links = [];
86
87 $('table tbody tr').each((i, el) => {
88 const quality = $(el).find('td.video-quality').text().trim();
89 const downloadUrl = $(el).find('a').attr('href');
90
91 if (downloadUrl && quality) {
92 links.push({
93 quality: quality,
94 url: downloadUrl
95 });
96 }
97 });
98
99 return links;
100 }
101
102 async getDownloadLinks(url, lang = "en") {
103 try {
104 const result = await this.download(url, lang);
105
106 if (result.success && result.html) {
107 const links = this.parseDownloadLinks(result.html);
108 return {
109 status: true,
110 result: links
111 };
112 }
113
114 return {
115 status: false,
116 creator: "ONLym-Api",
117 message: result.message || "Gagal mendapatkan data"
118 };
119 } catch (error) {
120 return {
121 status: false,
122 creator: "ONLym-Api",
123 message: error.message
124 };
125 }
126 }
127}
128
129const pindown = new PinDownAPI();
130
131// ─────────────────────────────────────────────────────────────────────
132// Self-test: jalankan langsung dengan `node pindown.js [args...]`
133// ─────────────────────────────────────────────────────────────────────
134if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
135 const __exports = { pindown: pindown }
136 const __fns = Object.fromEntries(Object.entries(__exports).filter(([, v]) => typeof v === 'function'))
137 const __names = Object.keys(__fns)
138 if (__names.length === 0) {
139 console.log('Tidak ada fungsi yang bisa di-demo. Import modul ini dari kode lain.')
140 } else {
141 const __search = __names.find((n) => /search|cari/i.test(n))
142 const __name = __search || __names[0]
143 const __fn = __fns[__name]
144 const __isClass = /^class\s/.test(Function.prototype.toString.call(__fn))
145 let __args = process.argv.slice(2).map((a) => { try { return JSON.parse(a) } catch { return a } })
146 if (__args.length === 0 && __search) __args.push('naruto')
147 if (__args.length === 0 && !__search && !__isClass && __fn.length > 0) {
148 console.log('Fungsi: ' + __names.join(', '))
149 console.log('Contoh: node ' + "pindown.js" + ' ' + __name + ' <arg>')
150 } else {
151 ;(async () => {
152 try {
153 const r = __isClass ? new __fn(...__args) : await __fn(...__args)
154 console.log(JSON.stringify(r, null, 2))
155 } catch (e) {
156 console.error('Error: ' + (e && e.message ? e.message : e))
157 process.exitCode = 1
158 }
159 })()
160 }
161 }
162}
163
Discussion & Comments0

No comments yet — be the first to leave feedback or start a code discussion.

Sign in with your GitHub account to write comments and join the discussion.