n8n-nodes-pinterest 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/nodes/PinterestCookie/PinterestCookie.node.js +216 -146
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Supports:
|
|
|
6
6
|
|
|
7
7
|
- Cookie-based authentication (session cookie)
|
|
8
8
|
- List boards (current user)
|
|
9
|
-
- Create a Pin
|
|
9
|
+
- Create a Pin by uploading binary data or by letting Pinterest scrape a public image URL
|
|
10
10
|
|
|
11
11
|
Notes:
|
|
12
12
|
|
|
@@ -37,14 +37,16 @@ Creates a pin using cookie-based authentication.
|
|
|
37
37
|
Inputs:
|
|
38
38
|
|
|
39
39
|
- Board (select from available boards)
|
|
40
|
-
-
|
|
40
|
+
- Image Source:
|
|
41
|
+
- **Upload Binary**: supply a binary property (e.g. `data`). The node uploads the file to Pinterest's S3 slot, builds the CDN URL from the returned `ETag`, and creates the pin with `method="uploaded"` (same as FS Poster).
|
|
42
|
+
- **Scraped Image URL**: supply a direct image URL. The node calls the same `/pin/find/?url=...` flow as wp-pinterest-automatic and creates the pin with `method="scraped"`—no binary data required.
|
|
41
43
|
- Optional: Title, Description, Link, Alt Text
|
|
42
44
|
|
|
43
|
-
Binary
|
|
45
|
+
Binary uploads follow the VIPResource → S3 → PinResource pipeline. Scraped mode just replays the legacy "pin finder" payload and works for images that are accessible over HTTPS.
|
|
44
46
|
|
|
45
47
|
Additional behavior:
|
|
46
48
|
|
|
47
|
-
- "Attach Link" (default true)
|
|
49
|
+
- "Attach Link" (default true) still controls whether the destination link is sent. If Pinterest responds with errors such as "This site doesn't allow you to save Pins" or "Please make sure the URL is correct", the node automatically retries without the link unless Strict Plugin Mode is enabled.
|
|
48
50
|
|
|
49
51
|
## Disclaimer
|
|
50
52
|
|
|
@@ -23,6 +23,21 @@ function buildCookieHeader(sess, csrf) {
|
|
|
23
23
|
return `_pinterest_sess=${sess}; csrftoken=${csrf};`;
|
|
24
24
|
}
|
|
25
25
|
const PINTEREST_BASE = 'https://www.pinterest.com';
|
|
26
|
+
function sanitizePinterestText(value, limit) {
|
|
27
|
+
if (!value)
|
|
28
|
+
return '';
|
|
29
|
+
let sanitized = value.replace(/&/g, '&').replace(/&/g, '&');
|
|
30
|
+
sanitized = sanitized.replace(/\r\n|\r|\n/g, '\n');
|
|
31
|
+
sanitized = sanitized.replace(/\t/g, ' ').trim();
|
|
32
|
+
if (sanitized.length > limit) {
|
|
33
|
+
sanitized = sanitized.slice(0, limit - 3) + '...';
|
|
34
|
+
}
|
|
35
|
+
sanitized = sanitized.replace(/"/g, '\"');
|
|
36
|
+
return sanitized;
|
|
37
|
+
}
|
|
38
|
+
function doubleEncodeUrl(value) {
|
|
39
|
+
return encodeURIComponent(encodeURIComponent(value));
|
|
40
|
+
}
|
|
26
41
|
async function resolveBaseUrl(ctx, headers, proxy) {
|
|
27
42
|
try {
|
|
28
43
|
const resp = (await ctx.helpers.request({
|
|
@@ -96,15 +111,36 @@ class PinterestCookie {
|
|
|
96
111
|
required: true,
|
|
97
112
|
description: 'Board to pin to',
|
|
98
113
|
},
|
|
114
|
+
{
|
|
115
|
+
displayName: 'Image Source',
|
|
116
|
+
name: 'imageSource',
|
|
117
|
+
type: 'options',
|
|
118
|
+
options: [
|
|
119
|
+
{ name: 'Upload Binary', value: 'uploadBinary' },
|
|
120
|
+
{ name: 'Scraped Image URL', value: 'scrapedUrl' },
|
|
121
|
+
],
|
|
122
|
+
default: 'uploadBinary',
|
|
123
|
+
displayOptions: { show: { resource: ['pin'], operation: ['create'] } },
|
|
124
|
+
description: 'Choose whether to upload a binary image or let Pinterest scrape an external URL',
|
|
125
|
+
},
|
|
99
126
|
{
|
|
100
127
|
displayName: 'Binary Property',
|
|
101
128
|
name: 'binaryProperty',
|
|
102
129
|
type: 'string',
|
|
103
130
|
default: 'data',
|
|
104
131
|
required: true,
|
|
105
|
-
displayOptions: { show: { resource: ['pin'], operation: ['create'] } },
|
|
132
|
+
displayOptions: { show: { resource: ['pin'], operation: ['create'], imageSource: ['uploadBinary'] } },
|
|
106
133
|
description: 'Name of the binary property containing the image',
|
|
107
134
|
},
|
|
135
|
+
{
|
|
136
|
+
displayName: 'Image URL',
|
|
137
|
+
name: 'imageUrl',
|
|
138
|
+
type: 'string',
|
|
139
|
+
default: '',
|
|
140
|
+
required: true,
|
|
141
|
+
displayOptions: { show: { resource: ['pin'], operation: ['create'], imageSource: ['scrapedUrl'] } },
|
|
142
|
+
description: 'Direct URL of the image when using the scraped method',
|
|
143
|
+
},
|
|
108
144
|
{
|
|
109
145
|
displayName: 'Title',
|
|
110
146
|
name: 'title',
|
|
@@ -243,7 +279,7 @@ class PinterestCookie {
|
|
|
243
279
|
};
|
|
244
280
|
}
|
|
245
281
|
async execute() {
|
|
246
|
-
var _a, _b, _c;
|
|
282
|
+
var _a, _b, _c, _d;
|
|
247
283
|
const items = this.getInputData();
|
|
248
284
|
const returnData = [];
|
|
249
285
|
const creds = (await this.getCredentials('pinterestCookieApi'));
|
|
@@ -307,135 +343,37 @@ class PinterestCookie {
|
|
|
307
343
|
const operation = this.getNodeParameter('operation', i);
|
|
308
344
|
if (operation === 'create') {
|
|
309
345
|
const boardId = this.getNodeParameter('boardId', i);
|
|
310
|
-
const
|
|
346
|
+
const imageSource = this.getNodeParameter('imageSource', i, 'uploadBinary');
|
|
311
347
|
const title = this.getNodeParameter('title', i, '');
|
|
312
348
|
const description = this.getNodeParameter('description', i, '');
|
|
313
349
|
const additionalFields = this.getNodeParameter('additionalFields', i, {});
|
|
314
|
-
const debugRaw = this.getNodeParameter('debugRaw', i, false);
|
|
315
350
|
const strictPluginMode = this.getNodeParameter('strictPluginMode', i, false);
|
|
316
|
-
const
|
|
317
|
-
if (!binary)
|
|
318
|
-
throw new Error(`Binary property "${binaryProperty}" is missing on item ${i}`);
|
|
319
|
-
// Step 1: request upload slot
|
|
320
|
-
const vipResp = (await this.helpers.request({
|
|
321
|
-
method: 'POST',
|
|
322
|
-
uri: `${base}/resource/VIPResource/create/`,
|
|
323
|
-
form: {
|
|
324
|
-
source_url: '/pin-builder/',
|
|
325
|
-
data: '{"options":{"type":"pinimage"},"context":{}}',
|
|
326
|
-
},
|
|
327
|
-
headers: strictPluginMode
|
|
328
|
-
? { ...buildCommonHeaders(csrf, true), Cookie: cookieHeader }
|
|
329
|
-
: {
|
|
330
|
-
...baseHeaders,
|
|
331
|
-
Cookie: cookieHeader,
|
|
332
|
-
Referer: `${base}/pin-builder/`,
|
|
333
|
-
Origin: base,
|
|
334
|
-
'X-Pinterest-Source-Url': '/pin-builder/',
|
|
335
|
-
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
336
|
-
},
|
|
337
|
-
proxy: proxy || undefined,
|
|
338
|
-
}));
|
|
339
|
-
const vip = JSON.parse(vipResp);
|
|
340
|
-
const res = vip.resource_response || {};
|
|
341
|
-
const data = res.data || {};
|
|
342
|
-
const uploadUrl = String(data.upload_url || '');
|
|
343
|
-
const uploadParams = data.upload_parameters || {};
|
|
344
|
-
if (!uploadUrl)
|
|
345
|
-
throw new Error('Failed to get upload URL');
|
|
346
|
-
// Step 2: upload image
|
|
347
|
-
const buffer = Buffer.from(binary.data, 'base64');
|
|
348
|
-
const contentType = binary.mimeType || 'image/jpeg';
|
|
349
|
-
const formData = {};
|
|
350
|
-
for (const [k, v] of Object.entries(uploadParams))
|
|
351
|
-
formData[k] = String(v);
|
|
352
|
-
formData['file'] = {
|
|
353
|
-
value: buffer,
|
|
354
|
-
options: {
|
|
355
|
-
filename: 'blob',
|
|
356
|
-
contentType,
|
|
357
|
-
},
|
|
358
|
-
};
|
|
359
|
-
const uploadResp = (await this.helpers.request({
|
|
360
|
-
method: 'POST',
|
|
361
|
-
uri: uploadUrl,
|
|
362
|
-
headers: {
|
|
363
|
-
Accept: '*/*',
|
|
364
|
-
'Accept-Encoding': 'gzip',
|
|
365
|
-
'User-Agent': strictPluginMode
|
|
366
|
-
? 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
|
|
367
|
-
: baseHeaders['User-Agent'],
|
|
368
|
-
Origin: strictPluginMode ? 'https://www.pinterest.com' : base,
|
|
369
|
-
Referer: strictPluginMode ? 'https://www.pinterest.com' : `${base}/pin-builder/`,
|
|
370
|
-
...(strictPluginMode
|
|
371
|
-
? {
|
|
372
|
-
'sec-ch-ua': '".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"',
|
|
373
|
-
'sec-ch-ua-mobile': '?0',
|
|
374
|
-
'sec-ch-ua-full': '?1',
|
|
375
|
-
'sec-ch-ua-platform': '"Windows"',
|
|
376
|
-
'Sec-Fetch-Dest': 'empty',
|
|
377
|
-
'Sec-Fetch-Mode': 'cors',
|
|
378
|
-
'Sec-Fetch-Site': 'same-origin',
|
|
379
|
-
Connection: 'keep-alive',
|
|
380
|
-
}
|
|
381
|
-
: {}),
|
|
382
|
-
},
|
|
383
|
-
formData,
|
|
384
|
-
proxy: proxy || undefined,
|
|
385
|
-
// get full response to read headers
|
|
386
|
-
resolveWithFullResponse: true,
|
|
387
|
-
simple: false,
|
|
388
|
-
}));
|
|
389
|
-
const etagRaw = uploadResp.headers['etag'] || uploadResp.headers['ETag'] || '';
|
|
390
|
-
const etag = String(etagRaw).replace(/"/g, '');
|
|
391
|
-
if (!etag)
|
|
392
|
-
throw new Error('Upload failed: missing ETag');
|
|
393
|
-
// compute imageUrl like plugin
|
|
394
|
-
const imageUrl = `https://i.pinimg.com/736x/${etag[0]}${etag[1]}/${etag[2]}${etag[3]}/${etag[4]}${etag[5]}/${etag}.jpg`;
|
|
395
|
-
// Step 3: create pin
|
|
351
|
+
const debugRaw = this.getNodeParameter('debugRaw', i, false);
|
|
396
352
|
const attachLink = (additionalFields.attachLink !== undefined) ? Boolean(additionalFields.attachLink) : true;
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
link: String(additionalFields.link || ''),
|
|
407
|
-
// Non-strict: allow disabling link entirely
|
|
408
|
-
...(strictPluginMode ? {} : (!attachLink ? { link: '' } : {})),
|
|
409
|
-
title: String(title || ''),
|
|
410
|
-
image_url: String(imageUrl),
|
|
411
|
-
method: 'uploaded',
|
|
412
|
-
upload_metric: { source: 'pinner_upload_standalone' },
|
|
413
|
-
user_mention_tags: [],
|
|
414
|
-
no_fetch_context_on_resource: false,
|
|
415
|
-
},
|
|
416
|
-
context: {},
|
|
417
|
-
};
|
|
418
|
-
const attemptCreate = async (payload) => {
|
|
419
|
-
var _a, _b;
|
|
353
|
+
const attemptCreate = async (payload, createOptions) => {
|
|
354
|
+
var _a, _b, _c, _d, _e;
|
|
355
|
+
const form = {
|
|
356
|
+
source_url: (_a = createOptions === null || createOptions === void 0 ? void 0 : createOptions.sourceUrl) !== null && _a !== void 0 ? _a : '/pin-builder/',
|
|
357
|
+
data: JSON.stringify(payload),
|
|
358
|
+
};
|
|
359
|
+
if (createOptions === null || createOptions === void 0 ? void 0 : createOptions.modulePath) {
|
|
360
|
+
form.module_path = createOptions.modulePath;
|
|
361
|
+
}
|
|
420
362
|
const createResp = (await this.helpers.request({
|
|
421
363
|
method: 'POST',
|
|
422
364
|
uri: `${base}/resource/PinResource/create/`,
|
|
423
|
-
form
|
|
424
|
-
source_url: '/pin-builder/',
|
|
425
|
-
data: JSON.stringify(payload),
|
|
426
|
-
},
|
|
365
|
+
form,
|
|
427
366
|
headers: strictPluginMode
|
|
428
367
|
? { ...buildCommonHeaders(csrf, true), Cookie: cookieHeader }
|
|
429
368
|
: {
|
|
430
369
|
...baseHeaders,
|
|
431
370
|
Cookie: cookieHeader,
|
|
432
|
-
Referer: `${base}/pin-builder/`,
|
|
371
|
+
Referer: ((_b = createOptions === null || createOptions === void 0 ? void 0 : createOptions.sourceUrl) === null || _b === void 0 ? void 0 : _b.startsWith('/pin/find')) ? `${base}/pin/find/` : `${base}/pin-builder/`,
|
|
433
372
|
Origin: base,
|
|
434
|
-
'X-Pinterest-Source-Url': '/pin-builder/',
|
|
373
|
+
'X-Pinterest-Source-Url': (_c = createOptions === null || createOptions === void 0 ? void 0 : createOptions.sourceUrl) !== null && _c !== void 0 ? _c : '/pin-builder/',
|
|
435
374
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
436
375
|
},
|
|
437
376
|
proxy: proxy || undefined,
|
|
438
|
-
// Get body even on 4xx
|
|
439
377
|
simple: false,
|
|
440
378
|
resolveWithFullResponse: true,
|
|
441
379
|
}));
|
|
@@ -448,52 +386,184 @@ class PinterestCookie {
|
|
|
448
386
|
const createdRes = created.resource_response || {};
|
|
449
387
|
const pinData = createdRes.data || {};
|
|
450
388
|
const id = String(pinData.id || '');
|
|
451
|
-
const msg = createdRes.message || ((
|
|
452
|
-
const msgDetail = ((
|
|
389
|
+
const msg = createdRes.message || ((_d = createdRes.error) === null || _d === void 0 ? void 0 : _d.message) || '';
|
|
390
|
+
const msgDetail = ((_e = createdRes.error) === null || _e === void 0 ? void 0 : _e.message_detail) || '';
|
|
453
391
|
return { ok: Boolean(id), id, msg: String(msg || msgDetail || ''), body, payload };
|
|
454
392
|
};
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
393
|
+
const isLinkError = (msg) => /doesn['’]?t allow you to save Pins|does not allow you to save Pins|please make sure the url is correct/i.test(msg || '');
|
|
394
|
+
const runStrategies = async (strategies) => {
|
|
395
|
+
let lastResult = { ok: false };
|
|
396
|
+
for (const strat of strategies) {
|
|
397
|
+
lastResult = await attemptCreate(strat.payload, strat.options);
|
|
398
|
+
if (lastResult.ok) {
|
|
399
|
+
return lastResult;
|
|
400
|
+
}
|
|
401
|
+
if (!strictPluginMode && isLinkError(lastResult.msg)) {
|
|
461
402
|
continue;
|
|
462
403
|
}
|
|
463
|
-
throw new Error(result.msg || result.body || 'Create failed');
|
|
464
404
|
}
|
|
405
|
+
return lastResult;
|
|
406
|
+
};
|
|
407
|
+
let result = { ok: false };
|
|
408
|
+
if (imageSource === 'scrapedUrl') {
|
|
409
|
+
const imageUrl = this.getNodeParameter('imageUrl', i, '');
|
|
410
|
+
if (!imageUrl) {
|
|
411
|
+
throw new Error('Image URL is required when using the scraped method.');
|
|
412
|
+
}
|
|
413
|
+
const sanitizedTitle = sanitizePinterestText(title, 100);
|
|
414
|
+
const sanitizedDesc = sanitizePinterestText(description, 500);
|
|
415
|
+
const linkValue = attachLink ? String((_c = additionalFields.link) !== null && _c !== void 0 ? _c : '') : '';
|
|
416
|
+
const scrapedPayload = {
|
|
417
|
+
options: {
|
|
418
|
+
method: 'scraped',
|
|
419
|
+
title: sanitizedTitle,
|
|
420
|
+
description: sanitizedDesc,
|
|
421
|
+
link: linkValue,
|
|
422
|
+
image_url: imageUrl,
|
|
423
|
+
share_facebook: false,
|
|
424
|
+
board_id: boardId,
|
|
425
|
+
scrape_metric: { source: 'www_url_scrape' },
|
|
426
|
+
},
|
|
427
|
+
context: {},
|
|
428
|
+
};
|
|
429
|
+
const scrapedOptions = {
|
|
430
|
+
sourceUrl: `/pin/find/?url=${doubleEncodeUrl(imageUrl)}`,
|
|
431
|
+
modulePath: 'App()%3EModalManager%3EModal%3EPinCreate%3EBoardPicker%3ESelectList(view_type%3DpinCreate%2C+selected_section_index%3Dundefined%2C+selected_item_index%3Dundefined%2C+highlight_matched_text%3Dtrue%2C+suppress_hover_events%3Dundefined%2C+scroll_selected_item_into_view%3Dtrue%2C+select_first_item_after_update%3Dfalse%2C+item_module%3D%5Bobject+Object%5D)',
|
|
432
|
+
};
|
|
433
|
+
const strategies = [
|
|
434
|
+
{ payload: scrapedPayload, options: scrapedOptions },
|
|
435
|
+
];
|
|
436
|
+
if (!strictPluginMode && linkValue) {
|
|
437
|
+
const withoutLink = JSON.parse(JSON.stringify(scrapedPayload));
|
|
438
|
+
if (withoutLink.options) {
|
|
439
|
+
withoutLink.options.link = '';
|
|
440
|
+
}
|
|
441
|
+
strategies.push({ payload: withoutLink, options: scrapedOptions });
|
|
442
|
+
}
|
|
443
|
+
result = await runStrategies(strategies);
|
|
465
444
|
}
|
|
466
445
|
else {
|
|
467
|
-
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
446
|
+
const binaryProperty = this.getNodeParameter('binaryProperty', i);
|
|
447
|
+
const binary = (_d = items[i].binary) === null || _d === void 0 ? void 0 : _d[binaryProperty];
|
|
448
|
+
if (!binary)
|
|
449
|
+
throw new Error(`Binary property "${binaryProperty}" is missing on item ${i}`);
|
|
450
|
+
const vipResp = (await this.helpers.request({
|
|
451
|
+
method: 'POST',
|
|
452
|
+
uri: `${base}/resource/VIPResource/create/`,
|
|
453
|
+
form: {
|
|
454
|
+
source_url: '/pin-builder/',
|
|
455
|
+
data: '{"options":{"type":"pinimage"},"context":{}}',
|
|
456
|
+
},
|
|
457
|
+
headers: strictPluginMode
|
|
458
|
+
? { ...buildCommonHeaders(csrf, true), Cookie: cookieHeader }
|
|
459
|
+
: {
|
|
460
|
+
...baseHeaders,
|
|
461
|
+
Cookie: cookieHeader,
|
|
462
|
+
Referer: `${base}/pin-builder/`,
|
|
463
|
+
Origin: base,
|
|
464
|
+
'X-Pinterest-Source-Url': '/pin-builder/',
|
|
465
|
+
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
466
|
+
},
|
|
467
|
+
proxy: proxy || undefined,
|
|
468
|
+
}));
|
|
469
|
+
const vip = JSON.parse(vipResp);
|
|
470
|
+
const res = vip.resource_response || {};
|
|
471
|
+
const data = res.data || {};
|
|
472
|
+
const uploadUrl = String(data.upload_url || '');
|
|
473
|
+
const uploadParams = data.upload_parameters || {};
|
|
474
|
+
if (!uploadUrl)
|
|
475
|
+
throw new Error('Failed to get upload URL');
|
|
476
|
+
const buffer = Buffer.from(binary.data, 'base64');
|
|
477
|
+
const contentType = binary.mimeType || 'image/jpeg';
|
|
478
|
+
const formData = {};
|
|
479
|
+
for (const [k, v] of Object.entries(uploadParams))
|
|
480
|
+
formData[k] = String(v);
|
|
481
|
+
formData['file'] = {
|
|
482
|
+
value: buffer,
|
|
475
483
|
options: {
|
|
476
|
-
|
|
484
|
+
filename: 'blob',
|
|
485
|
+
contentType,
|
|
486
|
+
},
|
|
487
|
+
};
|
|
488
|
+
const uploadResp = (await this.helpers.request({
|
|
489
|
+
method: 'POST',
|
|
490
|
+
uri: uploadUrl,
|
|
491
|
+
headers: {
|
|
492
|
+
Accept: '*/*',
|
|
493
|
+
'Accept-Encoding': 'gzip',
|
|
494
|
+
'User-Agent': strictPluginMode
|
|
495
|
+
? 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
|
|
496
|
+
: baseHeaders['User-Agent'],
|
|
497
|
+
Origin: strictPluginMode ? 'https://www.pinterest.com' : base,
|
|
498
|
+
Referer: strictPluginMode ? 'https://www.pinterest.com' : `${base}/pin-builder/`,
|
|
499
|
+
...(strictPluginMode
|
|
500
|
+
? {
|
|
501
|
+
'sec-ch-ua': '".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"',
|
|
502
|
+
'sec-ch-ua-mobile': '?0',
|
|
503
|
+
'sec-ch-ua-full': '?1',
|
|
504
|
+
'sec-ch-ua-platform': '"Windows"',
|
|
505
|
+
'Sec-Fetch-Dest': 'empty',
|
|
506
|
+
'Sec-Fetch-Mode': 'cors',
|
|
507
|
+
'Sec-Fetch-Site': 'same-origin',
|
|
508
|
+
Connection: 'keep-alive',
|
|
509
|
+
}
|
|
510
|
+
: {}),
|
|
511
|
+
},
|
|
512
|
+
formData,
|
|
513
|
+
proxy: proxy || undefined,
|
|
514
|
+
resolveWithFullResponse: true,
|
|
515
|
+
simple: false,
|
|
516
|
+
}));
|
|
517
|
+
const etagRaw = uploadResp.headers['etag'] || uploadResp.headers['ETag'] || '';
|
|
518
|
+
const etag = String(etagRaw).replace(/"/g, '');
|
|
519
|
+
if (!etag)
|
|
520
|
+
throw new Error('Upload failed: missing ETag');
|
|
521
|
+
const imageUrl = `https://i.pinimg.com/736x/${etag[0]}${etag[1]}/${etag[2]}${etag[3]}/${etag[4]}${etag[5]}/${etag}.jpg`;
|
|
522
|
+
const sendData = {
|
|
523
|
+
options: {
|
|
524
|
+
board_id: String(boardId),
|
|
477
525
|
field_set_key: 'create_success',
|
|
478
|
-
|
|
526
|
+
skip_pin_create_log: true,
|
|
527
|
+
description: String(description || ''),
|
|
528
|
+
alt_text: String(additionalFields.alt_text || ''),
|
|
529
|
+
link: attachLink && additionalFields.link ? String(additionalFields.link) : '',
|
|
530
|
+
title: String(title || ''),
|
|
531
|
+
image_url: String(imageUrl),
|
|
479
532
|
method: 'uploaded',
|
|
533
|
+
upload_metric: { source: 'pinner_upload_standalone' },
|
|
534
|
+
user_mention_tags: [],
|
|
535
|
+
no_fetch_context_on_resource: false,
|
|
480
536
|
},
|
|
481
537
|
context: {},
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
538
|
+
};
|
|
539
|
+
const strategies = [
|
|
540
|
+
{ payload: JSON.parse(JSON.stringify(sendData)) },
|
|
541
|
+
];
|
|
542
|
+
if (!strictPluginMode) {
|
|
543
|
+
const withoutLink = JSON.parse(JSON.stringify(sendData));
|
|
544
|
+
if (withoutLink.options)
|
|
545
|
+
delete withoutLink.options.link;
|
|
546
|
+
strategies.push({ payload: withoutLink });
|
|
547
|
+
strategies.push({
|
|
548
|
+
payload: {
|
|
549
|
+
options: {
|
|
550
|
+
board_id: boardId,
|
|
551
|
+
field_set_key: 'create_success',
|
|
552
|
+
image_url: imageUrl,
|
|
553
|
+
method: 'uploaded',
|
|
554
|
+
},
|
|
555
|
+
context: {},
|
|
556
|
+
},
|
|
557
|
+
});
|
|
489
558
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
}
|
|
495
|
-
|
|
559
|
+
result = await runStrategies(strategies);
|
|
560
|
+
}
|
|
561
|
+
if (!result.ok) {
|
|
562
|
+
if (debugRaw && this.continueOnFail()) {
|
|
563
|
+
returnData.push({ json: { error: result.msg || 'Create failed', response: result.body, request: result.payload } });
|
|
564
|
+
continue;
|
|
496
565
|
}
|
|
566
|
+
throw new Error(result.msg || result.body || 'Create failed');
|
|
497
567
|
}
|
|
498
568
|
returnData.push({ json: { id: result.id, link: `https://www.pinterest.com/pin/${result.id}` } });
|
|
499
569
|
}
|