n8n-nodes-upload-post 0.1.16 → 0.1.18

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.
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.UploadPost = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
4
5
  class UploadPost {
5
6
  constructor() {
6
7
  this.description = {
@@ -54,8 +55,12 @@ class UploadPost {
54
55
  type: 'options',
55
56
  noDataExpression: true,
56
57
  options: [
58
+ { name: 'Cancel Scheduled Post', value: 'cancelScheduled', action: 'Cancel scheduled post', description: 'Cancel a scheduled post by its job ID' },
59
+ { name: 'Edit Scheduled Post', value: 'editScheduled', action: 'Edit scheduled post', description: 'Edit schedule details (like date/time) by job ID' },
60
+ { name: 'Get Analytics', value: 'getAnalytics', action: 'Get analytics', description: 'Retrieve aggregated analytics for uploads' },
57
61
  { name: 'Get Upload History', value: 'getHistory', action: 'Get upload history', description: 'List past uploads with optional filters' },
58
62
  { name: 'Get Upload Status', value: 'getStatus', action: 'Get upload status', description: 'Check the status of an upload using the request_id' },
63
+ { name: 'List Scheduled Posts', value: 'listScheduled', action: 'List scheduled posts', description: 'List your scheduled (future) posts' },
59
64
  ],
60
65
  default: 'getStatus',
61
66
  displayOptions: { show: { resource: ['monitoring'] } },
@@ -109,26 +114,148 @@ class UploadPost {
109
114
  displayOptions: { show: { resource: ['uploads'], operation: ['uploadPhotos', 'uploadVideo', 'uploadText'] } },
110
115
  },
111
116
  {
112
- displayName: 'Pinterest Board ID',
113
- name: 'pinterestBoardId',
117
+ displayName: 'Title / Main Content',
118
+ name: 'title',
119
+ type: 'string',
120
+ required: true,
121
+ default: '',
122
+ description: 'Title of the post. For Upload Text, this is the main text content. For some video platforms, this acts as a fallback for description if a specific description is not provided.',
123
+ displayOptions: { show: { resource: ['uploads'], operation: ['uploadPhotos', 'uploadVideo', 'uploadText'] } },
124
+ },
125
+ {
126
+ displayName: 'Instagram Title (Override)',
127
+ name: 'instagramTitle',
128
+ type: 'string',
129
+ default: '',
130
+ description: 'Optional override for Instagram title',
131
+ displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo', 'uploadText'], platform: ['instagram'] } },
132
+ },
133
+ {
134
+ displayName: 'Facebook Title (Override)',
135
+ name: 'facebookTitle',
136
+ type: 'string',
137
+ default: '',
138
+ description: 'Optional override for Facebook title',
139
+ displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo', 'uploadText'], platform: ['facebook'] } },
140
+ },
141
+ {
142
+ displayName: 'TikTok Title (Override)',
143
+ name: 'tiktokTitle',
144
+ type: 'string',
145
+ default: '',
146
+ description: 'Optional override for TikTok title',
147
+ displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo', 'uploadText'], platform: ['tiktok'] } },
148
+ },
149
+ {
150
+ displayName: 'LinkedIn Title (Override)',
151
+ name: 'linkedinTitle',
152
+ type: 'string',
153
+ default: '',
154
+ description: 'Optional override for LinkedIn title',
155
+ displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo', 'uploadText'], platform: ['linkedin'] } },
156
+ },
157
+ {
158
+ displayName: 'X Title (Override)',
159
+ name: 'xTitle',
160
+ type: 'string',
161
+ default: '',
162
+ description: 'Optional override for X title',
163
+ displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo', 'uploadText'], platform: ['x'] } },
164
+ },
165
+ {
166
+ displayName: 'YouTube Title (Override)',
167
+ name: 'youtubeTitle',
114
168
  type: 'string',
115
169
  default: '',
116
- description: 'Target Pinterest board ID. Required when Pinterest is selected.',
170
+ description: 'Optional override for YouTube title',
171
+ displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo', 'uploadText'], platform: ['youtube'] } },
172
+ },
173
+ {
174
+ displayName: 'Pinterest Title (Override)',
175
+ name: 'pinterestTitle',
176
+ type: 'string',
177
+ default: '',
178
+ description: 'Optional override for Pinterest title',
179
+ displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo', 'uploadText'], platform: ['pinterest'] } },
180
+ },
181
+ {
182
+ displayName: 'Description (Optional)',
183
+ name: 'description',
184
+ type: 'string',
185
+ default: '',
186
+ description: 'Optional extended description used for LinkedIn commentary, Facebook description/message, YouTube video description, Pinterest description, and TikTok photo captions. Other platforms ignore it. When empty we fall back to the main title where a description is required. Platform-specific overrides below take precedence.',
117
187
  displayOptions: {
118
188
  show: {
119
- resource: ['uploads'],
120
- platform: ['pinterest']
189
+ operation: ['uploadPhotos', 'uploadVideo'],
190
+ platform: ['linkedin', 'facebook', 'youtube', 'pinterest', 'tiktok']
191
+ }
192
+ },
193
+ },
194
+ {
195
+ displayName: 'Facebook Description (Override)',
196
+ name: 'facebookDescription',
197
+ type: 'string',
198
+ default: '',
199
+ description: 'Override for Facebook description/message when supported (Reels/feed, albums). Falls back to the main title when empty.',
200
+ displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo'], platform: ['facebook'] } },
201
+ },
202
+ {
203
+ displayName: 'TikTok Description (Override)',
204
+ name: 'tiktokDescription',
205
+ type: 'string',
206
+ default: '',
207
+ description: 'Override for TikTok photo post description. Video uploads ignore this value.',
208
+ displayOptions: { show: { operation: ['uploadPhotos'], platform: ['tiktok'] } },
209
+ },
210
+ {
211
+ displayName: 'LinkedIn Description (Override)',
212
+ name: 'linkedinDescription',
213
+ type: 'string',
214
+ default: '',
215
+ description: 'Override for LinkedIn post commentary. When empty we repeat the main title.',
216
+ displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo'], platform: ['linkedin'] } },
217
+ },
218
+ {
219
+ displayName: 'YouTube Description (Override)',
220
+ name: 'youtubeDescription',
221
+ type: 'string',
222
+ default: '',
223
+ description: 'Override for YouTube video description. When empty we default to the main title.',
224
+ displayOptions: { show: { operation: ['uploadVideo'], platform: ['youtube'] } },
225
+ },
226
+ {
227
+ displayName: 'Pinterest Description (Override)',
228
+ name: 'pinterestDescription',
229
+ type: 'string',
230
+ default: '',
231
+ description: 'Override for Pinterest pin description (and alt text fallback). When empty we re-use the main title.',
232
+ displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo'], platform: ['pinterest'] } },
233
+ },
234
+ {
235
+ displayName: 'Photos (Files or URLs)',
236
+ name: 'photos',
237
+ type: 'string',
238
+ required: true,
239
+ default: '',
240
+ description: 'Provide photo files or URLs as a comma-separated list (e.g., data,https://example.com/image.jpg,otherImage). For files, enter the binary property name (e.g., data, myImage). For URLs, provide direct HTTP/HTTPS URLs.',
241
+ displayOptions: {
242
+ show: {
243
+ operation: ['uploadPhotos'],
121
244
  },
122
245
  },
123
246
  },
124
247
  {
125
- displayName: 'Title / Main Content',
126
- name: 'title',
248
+ displayName: 'Video (File or URL)',
249
+ name: 'video',
127
250
  type: 'string',
128
251
  required: true,
129
252
  default: '',
130
- description: 'Title of the post. For Upload Text, this is the main text content. For some video platforms, this acts as a fallback for description if a specific description is not provided.',
131
- displayOptions: { show: { resource: ['uploads'], operation: ['uploadPhotos', 'uploadVideo', 'uploadText'] } },
253
+ description: 'The video file to upload or a video URL. For files, enter the binary property name (e.g., data).',
254
+ displayOptions: {
255
+ show: {
256
+ operation: ['uploadVideo'],
257
+ },
258
+ },
132
259
  },
133
260
  {
134
261
  displayName: 'Scheduled Date',
@@ -155,7 +282,7 @@ class UploadPost {
155
282
  name: 'waitForCompletion',
156
283
  type: 'boolean',
157
284
  default: true,
158
- description: 'Whether to poll the status endpoint until completion or timeout when the upload is processed asynchronously',
285
+ description: 'Whether to perform best-effort sleeping between status checks within this node. Not guaranteed to finish; for reliable long polling use a separate Wait node plus Get Upload Status.',
159
286
  displayOptions: {
160
287
  show: {
161
288
  operation: ['uploadPhotos', 'uploadVideo', 'uploadText']
@@ -167,7 +294,7 @@ class UploadPost {
167
294
  name: 'pollInterval',
168
295
  type: 'number',
169
296
  default: 10,
170
- description: 'How often to poll the status endpoint when waiting for completion',
297
+ description: 'Sleep interval between status checks when waiting for completion',
171
298
  displayOptions: {
172
299
  show: {
173
300
  operation: ['uploadPhotos', 'uploadVideo', 'uploadText'],
@@ -180,7 +307,7 @@ class UploadPost {
180
307
  name: 'pollTimeout',
181
308
  type: 'number',
182
309
  default: 600,
183
- description: 'Maximum time to wait before giving up polling',
310
+ description: 'Maximum time to sleep-and-check before giving up inside this node',
184
311
  displayOptions: {
185
312
  show: {
186
313
  operation: ['uploadPhotos', 'uploadVideo', 'uploadText'],
@@ -225,6 +352,45 @@ class UploadPost {
225
352
  }
226
353
  },
227
354
  },
355
+ {
356
+ displayName: 'Job ID',
357
+ name: 'scheduleJobId',
358
+ type: 'string',
359
+ default: '',
360
+ description: 'Scheduled job identifier',
361
+ displayOptions: { show: { operation: ['cancelScheduled', 'editScheduled'] } },
362
+ },
363
+ {
364
+ displayName: 'Profile Username',
365
+ name: 'analyticsProfileUsername',
366
+ type: 'string',
367
+ required: true,
368
+ default: '',
369
+ description: 'Profile username to fetch analytics for',
370
+ displayOptions: { show: { operation: ['getAnalytics'] } },
371
+ },
372
+ {
373
+ displayName: 'New Scheduled Date',
374
+ name: 'newScheduledDate',
375
+ type: 'dateTime',
376
+ default: '',
377
+ description: 'New scheduled date/time for the post',
378
+ displayOptions: { show: { operation: ['editScheduled'] } },
379
+ },
380
+ {
381
+ displayName: 'Platforms (Optional)',
382
+ name: 'analyticsPlatforms',
383
+ type: 'multiOptions',
384
+ options: [
385
+ { name: 'Instagram', value: 'instagram' },
386
+ { name: 'LinkedIn', value: 'linkedin' },
387
+ { name: 'Facebook', value: 'facebook' },
388
+ { name: 'X (Twitter)', value: 'x' },
389
+ ],
390
+ default: [],
391
+ description: 'Platforms to fetch analytics for (comma-joined in request)',
392
+ displayOptions: { show: { operation: ['getAnalytics'] } },
393
+ },
228
394
  {
229
395
  displayName: 'New User Identifier',
230
396
  name: 'newUser',
@@ -298,44 +464,6 @@ class UploadPost {
298
464
  description: 'JWT to validate',
299
465
  displayOptions: { show: { operation: ['validateJwt'] } },
300
466
  },
301
- {
302
- displayName: 'Photos (Files or URLs)',
303
- name: 'photos',
304
- type: 'string',
305
- required: true,
306
- default: '',
307
- description: 'Provide photo files or URLs as a comma-separated list (e.g., data,https://example.com/image.jpg,otherImage). For files, enter the binary property name (e.g., data, myImage). For URLs, provide direct HTTP/HTTPS URLs.',
308
- displayOptions: {
309
- show: {
310
- operation: ['uploadPhotos'],
311
- },
312
- },
313
- },
314
- {
315
- displayName: 'Caption (Photo/Video Commentary)',
316
- name: 'caption',
317
- type: 'string',
318
- default: '',
319
- description: 'General caption/commentary for Photo or Video posts. For some video platforms, a specific \'Description\' field might be used instead (see platform-specific options). Not used for Upload Text.',
320
- displayOptions: {
321
- show: {
322
- operation: ['uploadPhotos', 'uploadVideo'],
323
- },
324
- },
325
- },
326
- {
327
- displayName: 'Video (File or URL)',
328
- name: 'video',
329
- type: 'string',
330
- required: true,
331
- default: '',
332
- description: 'The video file to upload or a video URL. For files, enter the binary property name (e.g., data).',
333
- displayOptions: {
334
- show: {
335
- operation: ['uploadVideo'],
336
- },
337
- },
338
- },
339
467
  {
340
468
  displayName: 'LinkedIn Visibility',
341
469
  name: 'linkedinVisibility',
@@ -356,16 +484,17 @@ class UploadPost {
356
484
  },
357
485
  },
358
486
  {
359
- displayName: 'Target LinkedIn Page ID',
487
+ displayName: 'Target LinkedIn Page Name or ID',
360
488
  name: 'targetLinkedinPageId',
361
- type: 'string',
489
+ type: 'options',
362
490
  default: '',
363
- description: 'LinkedIn page ID to upload to an organization (optional). Used for Photos, Video, and Text operations.',
491
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
492
+ typeOptions: { loadOptionsMethod: 'getLinkedinPages' },
364
493
  displayOptions: {
365
494
  show: {
366
495
  operation: ['uploadPhotos', 'uploadVideo', 'uploadText'],
367
496
  platform: ['linkedin']
368
- },
497
+ }
369
498
  },
370
499
  },
371
500
  {
@@ -382,17 +511,18 @@ class UploadPost {
382
511
  },
383
512
  },
384
513
  {
385
- displayName: 'Facebook Page ID',
514
+ displayName: 'Facebook Page Name or ID',
386
515
  name: 'facebookPageId',
387
- type: 'string',
516
+ type: 'options',
388
517
  required: true,
389
518
  default: '',
390
- description: 'Facebook Page ID. Required for Photos, Video, and Text operations.',
519
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
520
+ typeOptions: { loadOptionsMethod: 'getFacebookPages' },
391
521
  displayOptions: {
392
522
  show: {
393
523
  operation: ['uploadPhotos', 'uploadVideo', 'uploadText'],
394
524
  platform: ['facebook']
395
- },
525
+ }
396
526
  },
397
527
  },
398
528
  {
@@ -439,6 +569,23 @@ class UploadPost {
439
569
  },
440
570
  },
441
571
  },
572
+ {
573
+ displayName: 'Facebook Media Type (Video)',
574
+ name: 'facebookMediaType',
575
+ type: 'options',
576
+ options: [
577
+ { name: 'Reels', value: 'REELS' },
578
+ { name: 'Stories', value: 'STORIES' },
579
+ ],
580
+ default: 'REELS',
581
+ description: 'Choose whether to post as Reels or Stories for Facebook video',
582
+ displayOptions: {
583
+ show: {
584
+ operation: ['uploadVideo'],
585
+ platform: ['facebook']
586
+ },
587
+ },
588
+ },
442
589
  {
443
590
  displayName: 'TikTok Auto Add Music (Photo)',
444
591
  name: 'tiktokAutoAddMusic',
@@ -638,7 +785,24 @@ class UploadPost {
638
785
  show: {
639
786
  operation: ['uploadVideo'],
640
787
  platform: ['tiktok']
641
- },
788
+ }
789
+ },
790
+ },
791
+ {
792
+ displayName: 'TikTok Post Mode (Video)',
793
+ name: 'tiktokPostMode',
794
+ type: 'options',
795
+ options: [
796
+ { name: 'Direct Post', value: 'DIRECT_POST' },
797
+ { name: 'Media Upload (Inbox)', value: 'MEDIA_UPLOAD' },
798
+ ],
799
+ default: 'DIRECT_POST',
800
+ description: 'Choose TikTok posting mode for video',
801
+ displayOptions: {
802
+ show: {
803
+ operation: ['uploadVideo'],
804
+ platform: ['tiktok']
805
+ }
642
806
  },
643
807
  },
644
808
  {
@@ -747,20 +911,7 @@ class UploadPost {
747
911
  show: {
748
912
  operation: ['uploadVideo'],
749
913
  platform: ['instagram']
750
- },
751
- },
752
- },
753
- {
754
- displayName: 'YouTube Video Description',
755
- name: 'youtubeDescription',
756
- type: 'string',
757
- default: '',
758
- description: 'Description of the video for YouTube. If not provided, Title is used. Only for Upload Video.',
759
- displayOptions: {
760
- show: {
761
- operation: ['uploadVideo'],
762
- platform: ['youtube']
763
- },
914
+ }
764
915
  },
765
916
  },
766
917
  {
@@ -877,16 +1028,88 @@ class UploadPost {
877
1028
  },
878
1029
  },
879
1030
  {
880
- displayName: 'Threads Video Description',
881
- name: 'threadsDescription',
1031
+ displayName: 'Pinterest Board Name or ID',
1032
+ name: 'pinterestBoardId',
1033
+ type: 'options',
1034
+ default: '',
1035
+ description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
1036
+ typeOptions: { loadOptionsMethod: 'getPinterestBoards' },
1037
+ displayOptions: {
1038
+ show: {
1039
+ resource: ['uploads'],
1040
+ platform: ['pinterest']
1041
+ },
1042
+ },
1043
+ },
1044
+ {
1045
+ displayName: 'Pinterest Link (Photo/Video)',
1046
+ name: 'pinterestLink',
1047
+ type: 'string',
1048
+ default: '',
1049
+ description: 'Optional link to attach to the Pinterest pin',
1050
+ displayOptions: {
1051
+ show: {
1052
+ operation: ['uploadPhotos', 'uploadVideo'],
1053
+ platform: ['pinterest']
1054
+ }
1055
+ },
1056
+ },
1057
+ {
1058
+ displayName: 'Pinterest Cover Image URL (Video)',
1059
+ name: 'pinterestCoverImageUrl',
882
1060
  type: 'string',
883
1061
  default: '',
884
- description: 'User commentary for Threads video. If not provided, Title is used. Not for Photos/Text.',
1062
+ description: 'Optional cover image URL for Pinterest video. If provided, overrides other cover options.',
885
1063
  displayOptions: {
886
1064
  show: {
887
1065
  operation: ['uploadVideo'],
888
- platform: ['threads']
889
- },
1066
+ platform: ['pinterest']
1067
+ }
1068
+ },
1069
+ },
1070
+ {
1071
+ displayName: 'Pinterest Cover Image Content Type (Video)',
1072
+ name: 'pinterestCoverImageContentType',
1073
+ type: 'options',
1074
+ options: [
1075
+ { name: 'JPEG', value: 'image/jpeg' },
1076
+ { name: 'PNG', value: 'image/png' },
1077
+ { name: 'GIF', value: 'image/gif' },
1078
+ { name: 'BMP', value: 'image/bmp' },
1079
+ ],
1080
+ default: 'image/jpeg',
1081
+ description: 'MIME type for the cover image when providing raw base64 data',
1082
+ displayOptions: {
1083
+ show: {
1084
+ operation: ['uploadVideo'],
1085
+ platform: ['pinterest']
1086
+ }
1087
+ },
1088
+ },
1089
+ {
1090
+ displayName: 'Pinterest Cover Image Data (Base64, Video)',
1091
+ name: 'pinterestCoverImageData',
1092
+ type: 'string',
1093
+ default: '',
1094
+ description: 'Base64-encoded image bytes for the cover image. Used if URL is not provided.',
1095
+ displayOptions: {
1096
+ show: {
1097
+ operation: ['uploadVideo'],
1098
+ platform: ['pinterest']
1099
+ }
1100
+ },
1101
+ },
1102
+ {
1103
+ displayName: 'Pinterest Cover Image Key Frame Time (MS, Video)',
1104
+ name: 'pinterestCoverImageKeyFrameTime',
1105
+ type: 'number',
1106
+ default: 0,
1107
+ description: 'Key frame time to use as the cover image if no image is provided',
1108
+ displayOptions: {
1109
+ show: {
1110
+ operation: ['uploadVideo'],
1111
+ platform: ['pinterest']
1112
+ }
890
1113
  },
891
1114
  },
892
1115
  {
@@ -1003,8 +1226,79 @@ class UploadPost {
1003
1226
  },
1004
1227
  },
1005
1228
  },
1229
+ {
1230
+ displayName: 'X Long Text as Single Post',
1231
+ name: 'xLongTextAsPost',
1232
+ type: 'boolean',
1233
+ default: false,
1234
+ description: 'Whether to post long text as a single post instead of splitting into a thread (if supported)',
1235
+ displayOptions: {
1236
+ show: {
1237
+ operation: ['uploadVideo', 'uploadText'],
1238
+ platform: ['x']
1239
+ },
1240
+ },
1241
+ },
1006
1242
  ],
1007
1243
  };
1244
+ this.methods = {
1245
+ loadOptions: {
1246
+ async getFacebookPages() {
1247
+ const credentials = await this.getCredentials('uploadPostApi');
1248
+ const apiKey = credentials.apiKey;
1249
+ const profile = this.getCurrentNodeParameter('user') || '';
1250
+ const qs = {};
1251
+ if (profile)
1252
+ qs.profile = profile;
1253
+ const options = {
1254
+ uri: 'https://api.upload-post.com/api/uploadposts/facebook/pages',
1255
+ method: 'GET',
1256
+ headers: { Authorization: `ApiKey ${apiKey}` },
1257
+ qs,
1258
+ json: true,
1259
+ };
1260
+ const resp = await this.helpers.request(options);
1261
+ const pages = (resp && (resp.pages || resp.data || []));
1262
+ return (pages || []).map(p => ({ name: p.name ? `${p.name} (${p.id})` : p.id, value: p.id }));
1263
+ },
1264
+ async getLinkedinPages() {
1265
+ const credentials = await this.getCredentials('uploadPostApi');
1266
+ const apiKey = credentials.apiKey;
1267
+ const profile = this.getCurrentNodeParameter('user') || '';
1268
+ const qs = {};
1269
+ if (profile)
1270
+ qs.profile = profile;
1271
+ const options = {
1272
+ uri: 'https://api.upload-post.com/api/uploadposts/linkedin/pages',
1273
+ method: 'GET',
1274
+ headers: { Authorization: `ApiKey ${apiKey}` },
1275
+ qs,
1276
+ json: true,
1277
+ };
1278
+ const resp = await this.helpers.request(options);
1279
+ const pages = (resp && (resp.pages || resp.data || []));
1280
+ return (pages || []).map(p => ({ name: p.name ? `${p.name} (${p.id})` : p.id, value: p.id }));
1281
+ },
1282
+ async getPinterestBoards() {
1283
+ const credentials = await this.getCredentials('uploadPostApi');
1284
+ const apiKey = credentials.apiKey;
1285
+ const profile = this.getCurrentNodeParameter('user') || '';
1286
+ const qs = {};
1287
+ if (profile)
1288
+ qs.profile = profile;
1289
+ const options = {
1290
+ uri: 'https://api.upload-post.com/api/uploadposts/pinterest/boards',
1291
+ method: 'GET',
1292
+ headers: { Authorization: `ApiKey ${apiKey}` },
1293
+ qs,
1294
+ json: true,
1295
+ };
1296
+ const resp = await this.helpers.request(options);
1297
+ const boards = (resp && (resp.boards || resp.data || []));
1298
+ return (boards || []).map(b => ({ name: b.name ? `${b.name} (${b.id})` : b.id, value: b.id }));
1299
+ },
1300
+ },
1301
+ };
1008
1302
  }
1009
1303
  async execute() {
1010
1304
  var _a, _b, _c;
@@ -1039,10 +1333,114 @@ class UploadPost {
1039
1333
  formData.async_upload = String(uploadAsync);
1040
1334
  }
1041
1335
  }
1336
+ if (isUploadOperation) {
1337
+ try {
1338
+ if (platforms.includes('instagram')) {
1339
+ const instagramTitle = this.getNodeParameter('instagramTitle', i, '');
1340
+ if (instagramTitle)
1341
+ formData.instagram_title = instagramTitle;
1342
+ }
1343
+ }
1344
+ catch { }
1345
+ try {
1346
+ if (platforms.includes('facebook')) {
1347
+ const facebookTitle = this.getNodeParameter('facebookTitle', i, '');
1348
+ if (facebookTitle)
1349
+ formData.facebook_title = facebookTitle;
1350
+ }
1351
+ }
1352
+ catch { }
1353
+ try {
1354
+ if (platforms.includes('tiktok')) {
1355
+ const tiktokTitle = this.getNodeParameter('tiktokTitle', i, '');
1356
+ if (tiktokTitle)
1357
+ formData.tiktok_title = tiktokTitle;
1358
+ }
1359
+ }
1360
+ catch { }
1361
+ try {
1362
+ if (platforms.includes('linkedin')) {
1363
+ const linkedinTitle = this.getNodeParameter('linkedinTitle', i, '');
1364
+ if (linkedinTitle)
1365
+ formData.linkedin_title = linkedinTitle;
1366
+ }
1367
+ }
1368
+ catch { }
1369
+ try {
1370
+ if (platforms.includes('x')) {
1371
+ const xTitle = this.getNodeParameter('xTitle', i, '');
1372
+ if (xTitle)
1373
+ formData.x_title = xTitle;
1374
+ }
1375
+ }
1376
+ catch { }
1377
+ try {
1378
+ if (platforms.includes('youtube')) {
1379
+ const youtubeTitle = this.getNodeParameter('youtubeTitle', i, '');
1380
+ if (youtubeTitle)
1381
+ formData.youtube_title = youtubeTitle;
1382
+ }
1383
+ }
1384
+ catch { }
1385
+ try {
1386
+ if (platforms.includes('pinterest')) {
1387
+ const pinterestTitle = this.getNodeParameter('pinterestTitle', i, '');
1388
+ if (pinterestTitle)
1389
+ formData.pinterest_title = pinterestTitle;
1390
+ }
1391
+ }
1392
+ catch { }
1393
+ }
1394
+ if (isUploadOperation) {
1395
+ const genericDescription = this.getNodeParameter('description', i, '');
1396
+ const descriptionPlatforms = new Set(['linkedin', 'facebook', 'youtube', 'pinterest', 'tiktok']);
1397
+ if (genericDescription && platforms.some(p => descriptionPlatforms.has(p))) {
1398
+ formData.description = genericDescription;
1399
+ }
1400
+ try {
1401
+ if (platforms.includes('linkedin')) {
1402
+ const linkedinDescription = this.getNodeParameter('linkedinDescription', i, '');
1403
+ if (linkedinDescription)
1404
+ formData.linkedin_description = linkedinDescription;
1405
+ }
1406
+ }
1407
+ catch { }
1408
+ try {
1409
+ if (platforms.includes('youtube')) {
1410
+ const youtubeDescription = this.getNodeParameter('youtubeDescription', i, '');
1411
+ if (youtubeDescription)
1412
+ formData.youtube_description = youtubeDescription;
1413
+ }
1414
+ }
1415
+ catch { }
1416
+ try {
1417
+ if (platforms.includes('facebook')) {
1418
+ const facebookDescription = this.getNodeParameter('facebookDescription', i, '');
1419
+ if (facebookDescription)
1420
+ formData.facebook_description = facebookDescription;
1421
+ }
1422
+ }
1423
+ catch { }
1424
+ try {
1425
+ if (platforms.includes('tiktok')) {
1426
+ const tiktokDescription = this.getNodeParameter('tiktokDescription', i, '');
1427
+ if (tiktokDescription)
1428
+ formData.tiktok_description = tiktokDescription;
1429
+ }
1430
+ }
1431
+ catch { }
1432
+ try {
1433
+ if (platforms.includes('pinterest')) {
1434
+ const pinterestDescription = this.getNodeParameter('pinterestDescription', i, '');
1435
+ if (pinterestDescription)
1436
+ formData.pinterest_description = pinterestDescription;
1437
+ }
1438
+ }
1439
+ catch { }
1440
+ }
1042
1441
  switch (operation) {
1043
1442
  case 'uploadPhotos':
1044
1443
  endpoint = '/upload_photos';
1045
- const photoCaption = this.getNodeParameter('caption', i);
1046
1444
  let photosInput = this.getNodeParameter('photos', i, []);
1047
1445
  let photosToProcess;
1048
1446
  if (typeof photosInput === 'string') {
@@ -1051,7 +1449,7 @@ class UploadPost {
1051
1449
  else {
1052
1450
  photosToProcess = photosInput.filter(item => typeof item === 'string' && item.trim() !== '');
1053
1451
  }
1054
- const allowedPhotoPlatforms = ['tiktok', 'instagram', 'linkedin', 'facebook', 'x', 'threads'];
1452
+ const allowedPhotoPlatforms = ['tiktok', 'instagram', 'linkedin', 'facebook', 'x', 'threads', 'pinterest'];
1055
1453
  platforms = platforms.filter(p => allowedPhotoPlatforms.includes(p));
1056
1454
  formData['platform[]'] = platforms;
1057
1455
  if (photosToProcess.length > 0) {
@@ -1084,8 +1482,6 @@ class UploadPost {
1084
1482
  formData['photos[]'] = photoArray;
1085
1483
  }
1086
1484
  }
1087
- if (photoCaption)
1088
- formData.caption = photoCaption;
1089
1485
  break;
1090
1486
  case 'uploadVideo':
1091
1487
  endpoint = '/upload';
@@ -1135,6 +1531,43 @@ class UploadPost {
1135
1531
  const historyLimit = this.getNodeParameter('historyLimit', i);
1136
1532
  qs.limit = historyLimit !== null && historyLimit !== void 0 ? historyLimit : 20;
1137
1533
  break;
1534
+ case 'getAnalytics':
1535
+ method = 'GET';
1536
+ {
1537
+ const analyticsPlatforms = this.getNodeParameter('analyticsPlatforms', i, []);
1538
+ const profileUsername = this.getNodeParameter('analyticsProfileUsername', i);
1539
+ endpoint = `/analytics/${encodeURIComponent(profileUsername)}`;
1540
+ if (Array.isArray(analyticsPlatforms) && analyticsPlatforms.length > 0) {
1541
+ qs.platforms = analyticsPlatforms.join(',');
1542
+ }
1543
+ }
1544
+ break;
1545
+ case 'listScheduled':
1546
+ method = 'GET';
1547
+ endpoint = '/uploadposts/schedule';
1548
+ break;
1549
+ case 'cancelScheduled':
1550
+ method = 'DELETE';
1551
+ {
1552
+ const jobId = this.getNodeParameter('scheduleJobId', i);
1553
+ endpoint = `/uploadposts/schedule/${jobId}`;
1554
+ }
1555
+ break;
1556
+ case 'editScheduled':
1557
+ method = 'POST';
1558
+ {
1559
+ const jobId = this.getNodeParameter('scheduleJobId', i);
1560
+ endpoint = `/uploadposts/schedule/${jobId}`;
1561
+ const newScheduledDate = this.getNodeParameter('newScheduledDate', i, '');
1562
+ if (newScheduledDate) {
1563
+ let normalizedDate = newScheduledDate;
1564
+ const hasTimezone = /[zZ]$|[+-]\d{2}:?\d{2}$/.test(normalizedDate);
1565
+ if (!hasTimezone)
1566
+ normalizedDate = `${normalizedDate}Z`;
1567
+ body.scheduled_date = normalizedDate;
1568
+ }
1569
+ }
1570
+ break;
1138
1571
  case 'listUsers':
1139
1572
  method = 'GET';
1140
1573
  endpoint = '/uploadposts/users';
@@ -1176,11 +1609,42 @@ class UploadPost {
1176
1609
  const pinterestBoardId = this.getNodeParameter('pinterestBoardId', i);
1177
1610
  if (pinterestBoardId)
1178
1611
  formData.pinterest_board_id = pinterestBoardId;
1612
+ const pinterestLink = this.getNodeParameter('pinterestLink', i);
1613
+ if (pinterestLink)
1614
+ formData.pinterest_link = pinterestLink;
1615
+ if (operation === 'uploadVideo') {
1616
+ const pinterestCoverImageUrl = this.getNodeParameter('pinterestCoverImageUrl', i);
1617
+ const pinterestCoverImageContentType = this.getNodeParameter('pinterestCoverImageContentType', i);
1618
+ const pinterestCoverImageData = this.getNodeParameter('pinterestCoverImageData', i);
1619
+ const pinterestCoverImageKeyFrameTime = this.getNodeParameter('pinterestCoverImageKeyFrameTime', i);
1620
+ const pinterestLink = this.getNodeParameter('pinterestLink', i);
1621
+ if (pinterestCoverImageUrl) {
1622
+ formData.pinterest_cover_image_url = pinterestCoverImageUrl;
1623
+ }
1624
+ else {
1625
+ if (pinterestCoverImageContentType && pinterestCoverImageData) {
1626
+ formData.pinterest_cover_image_content_type = pinterestCoverImageContentType;
1627
+ formData.pinterest_cover_image_data = pinterestCoverImageData;
1628
+ }
1629
+ else if (pinterestCoverImageKeyFrameTime !== undefined) {
1630
+ formData.pinterest_cover_image_key_frame_time = pinterestCoverImageKeyFrameTime;
1631
+ }
1632
+ }
1633
+ if (pinterestLink)
1634
+ formData.pinterest_link = pinterestLink;
1635
+ }
1179
1636
  }
1180
1637
  if (isUploadOperation && platforms.includes('linkedin')) {
1181
1638
  const targetLinkedinPageId = this.getNodeParameter('targetLinkedinPageId', i);
1182
- if (targetLinkedinPageId)
1183
- formData.target_linkedin_page_id = targetLinkedinPageId;
1639
+ if (targetLinkedinPageId) {
1640
+ const match = targetLinkedinPageId.match(/(\d+)$/);
1641
+ if (match) {
1642
+ formData.target_linkedin_page_id = match[1];
1643
+ }
1644
+ else {
1645
+ formData.target_linkedin_page_id = targetLinkedinPageId;
1646
+ }
1647
+ }
1184
1648
  if (operation === 'uploadPhotos') {
1185
1649
  const linkedinVisibility = this.getNodeParameter('linkedinVisibility', i);
1186
1650
  if (linkedinVisibility === 'PUBLIC') {
@@ -1189,22 +1653,22 @@ class UploadPost {
1189
1653
  }
1190
1654
  else if (operation === 'uploadVideo') {
1191
1655
  const linkedinVisibility = this.getNodeParameter('linkedinVisibility', i);
1192
- const linkedinDescription = this.getNodeParameter('linkedinDescription', i);
1193
1656
  formData.visibility = linkedinVisibility;
1194
- if (linkedinDescription)
1195
- formData.description = linkedinDescription;
1196
1657
  }
1197
1658
  }
1198
1659
  if (isUploadOperation && platforms.includes('facebook')) {
1199
1660
  const facebookPageId = this.getNodeParameter('facebookPageId', i);
1200
1661
  formData.facebook_page_id = facebookPageId;
1201
1662
  if (operation === 'uploadVideo') {
1202
- const facebookVideoDescription = this.getNodeParameter('facebookVideoDescription', i);
1203
1663
  const facebookVideoState = this.getNodeParameter('facebookVideoState', i);
1204
- if (facebookVideoDescription)
1205
- formData.description = facebookVideoDescription;
1206
1664
  if (facebookVideoState)
1207
1665
  formData.video_state = facebookVideoState;
1666
+ try {
1667
+ const facebookMediaType = this.getNodeParameter('facebookMediaType', i);
1668
+ if (facebookMediaType)
1669
+ formData.facebook_media_type = facebookMediaType;
1670
+ }
1671
+ catch { }
1208
1672
  }
1209
1673
  else if (operation === 'uploadText') {
1210
1674
  const facebookLink = this.getNodeParameter('facebookLink', i);
@@ -1230,7 +1694,7 @@ class UploadPost {
1230
1694
  formData.disclose_commercial = String(tiktokDiscloseCommercialPhoto);
1231
1695
  if (tiktokPhotoCoverIndex !== undefined)
1232
1696
  formData.photo_cover_index = tiktokPhotoCoverIndex;
1233
- if (tiktokPhotoDescription)
1697
+ if (tiktokPhotoDescription && formData.description === undefined)
1234
1698
  formData.description = tiktokPhotoDescription;
1235
1699
  }
1236
1700
  else if (operation === 'uploadVideo') {
@@ -1244,6 +1708,7 @@ class UploadPost {
1244
1708
  const tiktokBrandedContentVideo = this.getNodeParameter('tiktokBrandedContentVideo', i);
1245
1709
  const tiktokBrandOrganicToggle = this.getNodeParameter('tiktokBrandOrganicToggle', i);
1246
1710
  const tiktokIsAigc = this.getNodeParameter('tiktokIsAigc', i);
1711
+ const tiktokPostMode = this.getNodeParameter('tiktokPostMode', i);
1247
1712
  if (tiktokPrivacyLevel)
1248
1713
  formData.privacy_level = tiktokPrivacyLevel;
1249
1714
  if (tiktokDisableDuet !== undefined)
@@ -1264,6 +1729,8 @@ class UploadPost {
1264
1729
  formData.brand_organic_toggle = String(tiktokBrandOrganicToggle);
1265
1730
  if (tiktokIsAigc !== undefined)
1266
1731
  formData.is_aigc = String(tiktokIsAigc);
1732
+ if (tiktokPostMode)
1733
+ formData.post_mode = tiktokPostMode;
1267
1734
  }
1268
1735
  }
1269
1736
  if (isUploadOperation && platforms.includes('instagram')) {
@@ -1306,7 +1773,6 @@ class UploadPost {
1306
1773
  }
1307
1774
  }
1308
1775
  if (isUploadOperation && platforms.includes('youtube') && operation === 'uploadVideo') {
1309
- const youtubeDescription = this.getNodeParameter('youtubeDescription', i);
1310
1776
  const youtubeTagsRaw = this.getNodeParameter('youtubeTags', i);
1311
1777
  const youtubeCategoryId = this.getNodeParameter('youtubeCategoryId', i);
1312
1778
  const youtubePrivacyStatus = this.getNodeParameter('youtubePrivacyStatus', i);
@@ -1315,8 +1781,6 @@ class UploadPost {
1315
1781
  const youtubePublicStatsViewable = this.getNodeParameter('youtubePublicStatsViewable', i);
1316
1782
  const youtubeMadeForKids = this.getNodeParameter('youtubeMadeForKids', i);
1317
1783
  const youtubeThumbnail = this.getNodeParameter('youtubeThumbnail', i);
1318
- if (youtubeDescription)
1319
- formData.description = youtubeDescription;
1320
1784
  if (youtubeTagsRaw)
1321
1785
  formData.tags = youtubeTagsRaw.split(',').map(tag => tag.trim());
1322
1786
  if (youtubeCategoryId)
@@ -1354,13 +1818,6 @@ class UploadPost {
1354
1818
  }
1355
1819
  }
1356
1820
  }
1357
- if (isUploadOperation && platforms.includes('threads')) {
1358
- if (operation === 'uploadVideo') {
1359
- const threadsDescription = this.getNodeParameter('threadsDescription', i);
1360
- if (threadsDescription)
1361
- formData.description = threadsDescription;
1362
- }
1363
- }
1364
1821
  if (isUploadOperation && platforms.includes('x')) {
1365
1822
  if (operation === 'uploadText') {
1366
1823
  const xPostUrlText = this.getNodeParameter('xPostUrlText', i);
@@ -1372,6 +1829,12 @@ class UploadPost {
1372
1829
  formData.tagged_user_ids = xTaggedUserIdsText.split(',').map(id => id.trim());
1373
1830
  if (xReplySettingsText)
1374
1831
  formData.reply_settings = xReplySettingsText;
1832
+ try {
1833
+ const xLongTextAsPostText = this.getNodeParameter('xLongTextAsPost', i, false);
1834
+ if (xLongTextAsPostText)
1835
+ formData.x_long_text_as_post = String(xLongTextAsPostText);
1836
+ }
1837
+ catch { }
1375
1838
  delete formData.nullcast;
1376
1839
  delete formData.place_id;
1377
1840
  delete formData.poll_duration;
@@ -1386,6 +1849,7 @@ class UploadPost {
1386
1849
  const xPollDurationVideo = this.getNodeParameter('xPollDurationVideo', i);
1387
1850
  const xPollOptionsVideoRaw = this.getNodeParameter('xPollOptionsVideo', i);
1388
1851
  const xPollReplySettingsVideo = this.getNodeParameter('xPollReplySettingsVideo', i);
1852
+ const xLongTextAsPost = this.getNodeParameter('xLongTextAsPost', i, false);
1389
1853
  if (xTaggedUserIds)
1390
1854
  formData.tagged_user_ids = xTaggedUserIds.split(',').map(id => id.trim());
1391
1855
  if (xReplySettings)
@@ -1400,6 +1864,8 @@ class UploadPost {
1400
1864
  formData.poll_options = xPollOptionsVideoRaw.split(',').map(opt => opt.trim());
1401
1865
  if (xPollReplySettingsVideo)
1402
1866
  formData.poll_reply_settings = xPollReplySettingsVideo;
1867
+ if (xLongTextAsPost)
1868
+ formData.x_long_text_as_post = String(xLongTextAsPost);
1403
1869
  }
1404
1870
  }
1405
1871
  const credentials = await this.getCredentials('uploadPostApi');
@@ -1445,7 +1911,7 @@ class UploadPost {
1445
1911
  const pollTimeoutSec = this.getNodeParameter('pollTimeout', i, 600);
1446
1912
  const start = Date.now();
1447
1913
  while (true) {
1448
- await new Promise(res => globalThis.setTimeout(res, Math.max(1, pollIntervalSec) * 1000));
1914
+ await (0, n8n_workflow_1.sleep)(Math.max(1, pollIntervalSec) * 1000);
1449
1915
  if (Date.now() - start > Math.max(5, pollTimeoutSec) * 1000) {
1450
1916
  finalData = { success: false, message: 'Polling timed out', request_id: requestId };
1451
1917
  break;