-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Expand file tree
/
Copy pathpinterest.js
More file actions
111 lines (86 loc) · 3.79 KB
/
pinterest.js
File metadata and controls
111 lines (86 loc) · 3.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { genericUserAgent } from "../../config.js";
import { resolveRedirectingURL } from "../url.js";
const videoRegex = /"url":"(https:\/\/v1\.pinimg\.com\/videos\/.*?)"/g;
const imageRegex = /src="(https:\/\/i\.pinimg\.com\/(?:\d+x|orig)\/[0-9a-f/]{41}\.(jpg|gif))"/g;
const notFoundRegex = /"__typename"\s*:\s*"PinNotFound"/;
export default async function(o) {
let id = o.id;
if (!o.id && o.shortLink) {
const patternMatch = await resolveRedirectingURL(`https://api.pinterest.com/url_shortener/${o.shortLink}/redirect/`);
id = patternMatch?.id;
}
if (id.includes("--")) id = id.split("--")[1];
if (!id) return { error: "fetch.fail" };
const html = await fetch(`https://www.pinterest.com/pin/${id}/`, {
headers: { "user-agent": genericUserAgent }
}).then(r => r.text()).catch(() => {});
const invalidPin = html.match(notFoundRegex);
if (invalidPin) return { error: "fetch.empty" };
if (!html) return { error: "fetch.fail" };
const videoLink = [...html.matchAll(videoRegex)]
.map(([, link]) => link)
.find(a => a.endsWith('.mp4'));
if (videoLink) return {
urls: videoLink,
filename: `pinterest_${id}.mp4`,
audioFilename: `pinterest_${id}_audio`
}
const allImageMatches = [...html.matchAll(imageRegex)];
if (allImageMatches.length === 0) {
// Fallback to broader regex if precise one finds nothing
const fallbackRegex = /src="(https:\/\/i\.pinimg\.com\/.*\.(jpg|gif))"/g;
const fallbackMatches = [...html.matchAll(fallbackRegex)];
if (fallbackMatches.length > 0) {
// Use first fallback image
const fallbackUrl = fallbackMatches[0][1];
const imageType = fallbackUrl.endsWith(".gif") ? "gif" : "jpg";
return {
urls: fallbackUrl,
isPhoto: true,
filename: `pinterest_${id}.${imageType}`
};
}
return { error: "fetch.empty" };
}
// Step 1: Get the first image (always main content)
const firstImageUrl = allImageMatches[0][1];
// Step 2: Extract the image hash/identifier
const hashMatch = firstImageUrl.match(/\/(?:\d+x|orig)\/([0-9a-f]{2}\/[0-9a-f]{2}\/[0-9a-f]{2}\/[0-9a-f]{32})\.(jpg|gif)/);
if (!hashMatch) {
// Fallback to first image if we can't parse the hash
const imageType = firstImageUrl.endsWith(".gif") ? "gif" : "jpg";
return {
urls: firstImageUrl,
isPhoto: true,
filename: `pinterest_${id}.${imageType}`
};
}
const imageHash = hashMatch[1]; // e.g., "7c/0a/1c/7c0a1c5f1c999a4a67f3c5b847da093c"
const extension = hashMatch[2];
// Step 3: Find all variations of this specific image
const sameImageUrls = allImageMatches
.map(([, url]) => url)
.filter(url => url.includes(imageHash))
.filter(url => url.endsWith(`.${extension}`));
// Step 4: Sort by quality and take the best
const bestQualityUrl = sameImageUrls.sort((a, b) => {
const getQualityScore = (url) => {
// Check for originals (highest quality)
if (url.includes('/orig/')) return Infinity;
// Extract resolution number (e.g., "736" from "/736x/")
const resolutionMatch = url.match(/\/(\d+)x\//);
if (resolutionMatch) {
return parseInt(resolutionMatch[1], 10);
}
return 0;
};
return getQualityScore(b) - getQualityScore(a);
})[0];
const imageType = extension;
if (bestQualityUrl) return {
urls: bestQualityUrl,
isPhoto: true,
filename: `pinterest_${id}.${imageType}`
}
return { error: "fetch.empty" };
}