n8n-nodes-upload-post 0.1.18 → 0.1.20

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.
@@ -2,6 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.UploadPost = void 0;
4
4
  const n8n_workflow_1 = require("n8n-workflow");
5
+ const MANUAL_USER_VALUE = '__manual_user__';
6
+ const MANUAL_FACEBOOK_VALUE = '__manual_facebook__';
7
+ const MANUAL_LINKEDIN_VALUE = '__manual_linkedin__';
8
+ const MANUAL_PINTEREST_VALUE = '__manual_pinterest__';
5
9
  class UploadPost {
6
10
  constructor() {
7
11
  this.description = {
@@ -81,12 +85,14 @@ class UploadPost {
81
85
  displayOptions: { show: { resource: ['users'] } },
82
86
  },
83
87
  {
84
- displayName: 'User Identifier',
88
+ displayName: 'User Identifier Name or ID',
85
89
  name: 'user',
86
- type: 'string',
90
+ type: 'options',
91
+ noDataExpression: true,
87
92
  required: true,
88
93
  default: '',
89
- description: 'The Profile Name you created in your upload-post.com account. You can find it in the \'Manage Users\' section (e.g., app.upload-post.com/manage-users).',
94
+ description: 'Choose from your created profiles, or specify a profile name using an <a href="https://docs.n8n.io/code/expressions/">expression</a>. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
95
+ typeOptions: { loadOptionsMethod: 'getUserProfiles' },
90
96
  displayOptions: {
91
97
  show: {
92
98
  resource: ['uploads', 'users'],
@@ -95,22 +101,28 @@ class UploadPost {
95
101
  },
96
102
  },
97
103
  {
98
- displayName: 'Platform(s)',
104
+ displayName: 'User Identifier (Manual Entry)',
105
+ name: 'userManual',
106
+ type: 'string',
107
+ required: true,
108
+ default: '',
109
+ description: 'Provide a profile name or ID when it does not appear in the list',
110
+ displayOptions: {
111
+ show: {
112
+ resource: ['uploads', 'users'],
113
+ operation: ['uploadPhotos', 'uploadVideo', 'uploadText', 'generateJwt'],
114
+ user: [MANUAL_USER_VALUE]
115
+ }
116
+ },
117
+ },
118
+ {
119
+ displayName: 'Platform Names or IDs',
99
120
  name: 'platform',
100
121
  type: 'multiOptions',
101
122
  required: true,
102
- options: [
103
- { name: 'Facebook', value: 'facebook' },
104
- { name: 'Instagram', value: 'instagram' },
105
- { name: 'LinkedIn', value: 'linkedin' },
106
- { name: 'Pinterest', value: 'pinterest' },
107
- { name: 'Threads', value: 'threads' },
108
- { name: 'TikTok', value: 'tiktok' },
109
- { name: 'X (Twitter)', value: 'x' },
110
- { name: 'YouTube', value: 'youtube' },
111
- ],
123
+ typeOptions: { loadOptionsMethod: 'getPlatforms' },
112
124
  default: [],
113
- description: 'Platform(s) to upload to. Supported platforms vary by operation.',
125
+ description: 'Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
114
126
  displayOptions: { show: { resource: ['uploads'], operation: ['uploadPhotos', 'uploadVideo', 'uploadText'] } },
115
127
  },
116
128
  {
@@ -178,6 +190,14 @@ class UploadPost {
178
190
  description: 'Optional override for Pinterest title',
179
191
  displayOptions: { show: { operation: ['uploadPhotos', 'uploadVideo', 'uploadText'], platform: ['pinterest'] } },
180
192
  },
193
+ {
194
+ displayName: 'Threads Title (Override)',
195
+ name: 'threadsTitle',
196
+ type: 'string',
197
+ default: '',
198
+ description: 'Optional override for Threads title',
199
+ displayOptions: { show: { operation: ['uploadText'], platform: ['threads'] } },
200
+ },
181
201
  {
182
202
  displayName: 'Description (Optional)',
183
203
  name: 'description',
@@ -487,9 +507,10 @@ class UploadPost {
487
507
  displayName: 'Target LinkedIn Page Name or ID',
488
508
  name: 'targetLinkedinPageId',
489
509
  type: 'options',
510
+ noDataExpression: true,
490
511
  default: '',
491
512
  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' },
513
+ typeOptions: { loadOptionsMethod: 'getLinkedinPages', loadOptionsDependsOn: ['user'] },
493
514
  displayOptions: {
494
515
  show: {
495
516
  operation: ['uploadPhotos', 'uploadVideo', 'uploadText'],
@@ -497,6 +518,21 @@ class UploadPost {
497
518
  }
498
519
  },
499
520
  },
521
+ {
522
+ displayName: 'LinkedIn Page Name or ID (Manual Entry)',
523
+ name: 'targetLinkedinPageIdManual',
524
+ type: 'string',
525
+ required: true,
526
+ default: '',
527
+ description: 'Provide the LinkedIn page identifier when it does not appear in the list',
528
+ displayOptions: {
529
+ show: {
530
+ operation: ['uploadPhotos', 'uploadVideo', 'uploadText'],
531
+ platform: ['linkedin'],
532
+ targetLinkedinPageId: [MANUAL_LINKEDIN_VALUE]
533
+ }
534
+ },
535
+ },
500
536
  {
501
537
  displayName: 'LinkedIn Video Description',
502
538
  name: 'linkedinDescription',
@@ -514,10 +550,11 @@ class UploadPost {
514
550
  displayName: 'Facebook Page Name or ID',
515
551
  name: 'facebookPageId',
516
552
  type: 'options',
553
+ noDataExpression: true,
517
554
  required: true,
518
555
  default: '',
519
556
  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' },
557
+ typeOptions: { loadOptionsMethod: 'getFacebookPages', loadOptionsDependsOn: ['user'] },
521
558
  displayOptions: {
522
559
  show: {
523
560
  operation: ['uploadPhotos', 'uploadVideo', 'uploadText'],
@@ -525,6 +562,21 @@ class UploadPost {
525
562
  }
526
563
  },
527
564
  },
565
+ {
566
+ displayName: 'Facebook Page Name or ID (Manual Entry)',
567
+ name: 'facebookPageIdManual',
568
+ type: 'string',
569
+ required: true,
570
+ default: '',
571
+ description: 'Provide the Facebook page identifier when it does not appear in the list',
572
+ displayOptions: {
573
+ show: {
574
+ operation: ['uploadPhotos', 'uploadVideo', 'uploadText'],
575
+ platform: ['facebook'],
576
+ facebookPageId: [MANUAL_FACEBOOK_VALUE]
577
+ }
578
+ },
579
+ },
528
580
  {
529
581
  displayName: 'Facebook Link (Text)',
530
582
  name: 'facebookLink',
@@ -558,10 +610,9 @@ class UploadPost {
558
610
  options: [
559
611
  { name: 'Published', value: 'PUBLISHED' },
560
612
  { name: 'Draft', value: 'DRAFT' },
561
- { name: 'Scheduled', value: 'SCHEDULED' },
562
613
  ],
563
614
  default: 'PUBLISHED',
564
- description: 'State for Facebook Video (DRAFT, PUBLISHED, SCHEDULED). Not for Photos/Text.',
615
+ description: 'State for Facebook Video (DRAFT or PUBLISHED). Use Scheduled Date field for scheduling.',
565
616
  displayOptions: {
566
617
  show: {
567
618
  operation: ['uploadVideo'],
@@ -613,11 +664,12 @@ class UploadPost {
613
664
  },
614
665
  },
615
666
  {
616
- displayName: 'TikTok Branded Content (Photo)',
617
- name: 'tiktokBrandedContentPhoto',
618
- type: 'boolean',
619
- default: false,
620
- description: 'Whether to indicate photo post is branded content (requires Disclose Commercial). Only for Upload Photos.',
667
+ displayName: 'TikTok Photo Cover Index',
668
+ name: 'tiktokPhotoCoverIndex',
669
+ type: 'number',
670
+ default: 0,
671
+ typeOptions: { minValue: 0 },
672
+ description: 'Index (0-based) of photo to use as cover for TikTok photo post. Only for Upload Photos.',
621
673
  displayOptions: {
622
674
  show: {
623
675
  operation: ['uploadPhotos'],
@@ -626,11 +678,11 @@ class UploadPost {
626
678
  },
627
679
  },
628
680
  {
629
- displayName: 'TikTok Disclose Commercial (Photo)',
630
- name: 'tiktokDiscloseCommercialPhoto',
631
- type: 'boolean',
632
- default: false,
633
- description: 'Whether to disclose commercial nature of photo post. Only for Upload Photos.',
681
+ displayName: 'TikTok Photo Description',
682
+ name: 'tiktokPhotoDescription',
683
+ type: 'string',
684
+ default: '',
685
+ description: 'Description for TikTok photo post. If not provided, Title is used. Only for Upload Photos.',
634
686
  displayOptions: {
635
687
  show: {
636
688
  operation: ['uploadPhotos'],
@@ -639,12 +691,11 @@ class UploadPost {
639
691
  },
640
692
  },
641
693
  {
642
- displayName: 'TikTok Photo Cover Index',
643
- name: 'tiktokPhotoCoverIndex',
644
- type: 'number',
645
- default: 0,
646
- typeOptions: { minValue: 0 },
647
- description: 'Index (0-based) of photo to use as cover for TikTok photo post. Only for Upload Photos.',
694
+ displayName: 'TikTok Brand Content Toggle (Photo)',
695
+ name: 'brand_content_toggle',
696
+ type: 'boolean',
697
+ default: false,
698
+ description: 'Whether to set as true for paid partnerships that promote third-party brands',
648
699
  displayOptions: {
649
700
  show: {
650
701
  operation: ['uploadPhotos'],
@@ -653,11 +704,11 @@ class UploadPost {
653
704
  },
654
705
  },
655
706
  {
656
- displayName: 'TikTok Photo Description',
657
- name: 'tiktokPhotoDescription',
658
- type: 'string',
659
- default: '',
660
- description: 'Description for TikTok photo post. If not provided, Title is used. Only for Upload Photos.',
707
+ displayName: 'TikTok Brand Organic Toggle (Photo)',
708
+ name: 'brand_organic_toggle',
709
+ type: 'boolean',
710
+ default: false,
711
+ description: 'Whether to set as true when promoting the creator\'s own business',
661
712
  displayOptions: {
662
713
  show: {
663
714
  operation: ['uploadPhotos'],
@@ -725,36 +776,10 @@ class UploadPost {
725
776
  },
726
777
  {
727
778
  displayName: 'TikTok Brand Content Toggle (Video)',
728
- name: 'tiktokBrandContentToggle',
779
+ name: 'brand_content_toggle',
729
780
  type: 'boolean',
730
781
  default: false,
731
- description: 'Whether to enable branded content for TikTok video. Only for Upload Video.',
732
- displayOptions: {
733
- show: {
734
- operation: ['uploadVideo'],
735
- platform: ['tiktok']
736
- },
737
- },
738
- },
739
- {
740
- displayName: 'TikTok Brand Organic (Video)',
741
- name: 'tiktokBrandOrganic',
742
- type: 'boolean',
743
- default: false,
744
- description: 'Whether to enable organic branded content for TikTok video. Only for Upload Video.',
745
- displayOptions: {
746
- show: {
747
- operation: ['uploadVideo'],
748
- platform: ['tiktok']
749
- },
750
- },
751
- },
752
- {
753
- displayName: 'TikTok Branded Content (Video)',
754
- name: 'tiktokBrandedContentVideo',
755
- type: 'boolean',
756
- default: false,
757
- description: 'Whether to enable branded content with disclosure for TikTok video. Only for Upload Video.',
782
+ description: 'Whether to enable brand content toggle for paid partnerships that promote third-party brands',
758
783
  displayOptions: {
759
784
  show: {
760
785
  operation: ['uploadVideo'],
@@ -764,10 +789,10 @@ class UploadPost {
764
789
  },
765
790
  {
766
791
  displayName: 'TikTok Brand Organic Toggle (Video)',
767
- name: 'tiktokBrandOrganicToggle',
792
+ name: 'brand_organic_toggle',
768
793
  type: 'boolean',
769
794
  default: false,
770
- description: 'Whether to enable organic branded content toggle for TikTok video. Only for Upload Video.',
795
+ description: 'Whether to enable brand organic toggle when promoting the creator\'s own business',
771
796
  displayOptions: {
772
797
  show: {
773
798
  operation: ['uploadVideo'],
@@ -914,6 +939,31 @@ class UploadPost {
914
939
  }
915
940
  },
916
941
  },
942
+ {
943
+ displayName: 'Threads Long Text as Single Post',
944
+ name: 'threadsLongTextAsPost',
945
+ type: 'boolean',
946
+ default: false,
947
+ description: 'Whether long text is published as a single post. If false (default), a thread is created if the text exceeds 500 characters.',
948
+ displayOptions: { show: { operation: ['uploadText'], platform: ['threads'] } },
949
+ },
950
+ {
951
+ displayName: 'Reddit Subreddit',
952
+ name: 'redditSubreddit',
953
+ type: 'string',
954
+ required: true,
955
+ default: '',
956
+ description: 'Destination subreddit, without r/ (e.g., python)',
957
+ displayOptions: { show: { operation: ['uploadText'], platform: ['reddit'] } },
958
+ },
959
+ {
960
+ displayName: 'Reddit Flair ID',
961
+ name: 'redditFlairId',
962
+ type: 'string',
963
+ default: '',
964
+ description: 'ID of the flair template to apply to the post',
965
+ displayOptions: { show: { operation: ['uploadText'], platform: ['reddit'] } },
966
+ },
917
967
  {
918
968
  displayName: 'YouTube Tags',
919
969
  name: 'youtubeTags',
@@ -1027,20 +1077,142 @@ class UploadPost {
1027
1077
  },
1028
1078
  },
1029
1079
  },
1080
+ {
1081
+ displayName: 'YouTube Self Declared Made For Kids',
1082
+ name: 'youtubeSelfDeclaredMadeForKids',
1083
+ type: 'boolean',
1084
+ default: false,
1085
+ description: 'Whether this is an explicit declaration for children content (COPPA compliance). Only for Upload Video.',
1086
+ displayOptions: {
1087
+ show: {
1088
+ operation: ['uploadVideo'],
1089
+ platform: ['youtube']
1090
+ },
1091
+ },
1092
+ },
1093
+ {
1094
+ displayName: 'YouTube Contains Synthetic Media',
1095
+ name: 'youtubeContainsSyntheticMedia',
1096
+ type: 'boolean',
1097
+ default: false,
1098
+ description: 'Whether this is a declaration for AI/synthetic content transparency. Only for Upload Video.',
1099
+ displayOptions: {
1100
+ show: {
1101
+ operation: ['uploadVideo'],
1102
+ platform: ['youtube']
1103
+ },
1104
+ },
1105
+ },
1106
+ {
1107
+ displayName: 'YouTube Default Language',
1108
+ name: 'youtubeDefaultLanguage',
1109
+ type: 'string',
1110
+ default: '',
1111
+ description: 'Title/description language (BCP-47 codes like "es", "en"). Only for Upload Video.',
1112
+ displayOptions: {
1113
+ show: {
1114
+ operation: ['uploadVideo'],
1115
+ platform: ['youtube']
1116
+ },
1117
+ },
1118
+ },
1119
+ {
1120
+ displayName: 'YouTube Default Audio Language',
1121
+ name: 'youtubeDefaultAudioLanguage',
1122
+ type: 'string',
1123
+ default: '',
1124
+ description: 'Video audio language (BCP-47 codes like "es-ES", "en-US"). Only for Upload Video.',
1125
+ displayOptions: {
1126
+ show: {
1127
+ operation: ['uploadVideo'],
1128
+ platform: ['youtube']
1129
+ },
1130
+ },
1131
+ },
1132
+ {
1133
+ displayName: 'YouTube Allowed Countries',
1134
+ name: 'youtubeAllowedCountries',
1135
+ type: 'string',
1136
+ default: '',
1137
+ description: 'Comma-separated country codes for allowed regions (ISO 3166-1 alpha-2). Cannot be used with blocked countries. Only for Upload Video.',
1138
+ displayOptions: {
1139
+ show: {
1140
+ operation: ['uploadVideo'],
1141
+ platform: ['youtube']
1142
+ },
1143
+ },
1144
+ },
1145
+ {
1146
+ displayName: 'YouTube Blocked Countries',
1147
+ name: 'youtubeBlockedCountries',
1148
+ type: 'string',
1149
+ default: '',
1150
+ description: 'Comma-separated country codes for blocked regions (ISO 3166-1 alpha-2). Cannot be used with allowed countries. Only for Upload Video.',
1151
+ displayOptions: {
1152
+ show: {
1153
+ operation: ['uploadVideo'],
1154
+ platform: ['youtube']
1155
+ },
1156
+ },
1157
+ },
1158
+ {
1159
+ displayName: 'YouTube Has Paid Product Placement',
1160
+ name: 'youtubeHasPaidProductPlacement',
1161
+ type: 'boolean',
1162
+ default: false,
1163
+ description: 'Whether this is a declaration for paid product placements (FTC compliance). Only for Upload Video.',
1164
+ displayOptions: {
1165
+ show: {
1166
+ operation: ['uploadVideo'],
1167
+ platform: ['youtube']
1168
+ },
1169
+ },
1170
+ },
1171
+ {
1172
+ displayName: 'YouTube Recording Date',
1173
+ name: 'youtubeRecordingDate',
1174
+ type: 'dateTime',
1175
+ default: '',
1176
+ description: 'Recording timestamp (ISO 8601 format). Only for Upload Video.',
1177
+ displayOptions: {
1178
+ show: {
1179
+ operation: ['uploadVideo'],
1180
+ platform: ['youtube']
1181
+ },
1182
+ },
1183
+ },
1030
1184
  {
1031
1185
  displayName: 'Pinterest Board Name or ID',
1032
1186
  name: 'pinterestBoardId',
1033
1187
  type: 'options',
1188
+ noDataExpression: true,
1034
1189
  default: '',
1035
1190
  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' },
1191
+ typeOptions: { loadOptionsMethod: 'getPinterestBoards', loadOptionsDependsOn: ['user'] },
1037
1192
  displayOptions: {
1038
1193
  show: {
1039
1194
  resource: ['uploads'],
1195
+ operation: ['uploadPhotos', 'uploadVideo'],
1040
1196
  platform: ['pinterest']
1041
1197
  },
1042
1198
  },
1043
1199
  },
1200
+ {
1201
+ displayName: 'Pinterest Board Name or ID (Manual Entry)',
1202
+ name: 'pinterestBoardIdManual',
1203
+ type: 'string',
1204
+ required: true,
1205
+ default: '',
1206
+ description: 'Provide the Pinterest board identifier when it does not appear in the list',
1207
+ displayOptions: {
1208
+ show: {
1209
+ resource: ['uploads'],
1210
+ operation: ['uploadPhotos', 'uploadVideo'],
1211
+ platform: ['pinterest'],
1212
+ pinterestBoardId: [MANUAL_PINTEREST_VALUE]
1213
+ },
1214
+ },
1215
+ },
1044
1216
  {
1045
1217
  displayName: 'Pinterest Link (Photo/Video)',
1046
1218
  name: 'pinterestLink',
@@ -1113,45 +1285,47 @@ class UploadPost {
1113
1285
  },
1114
1286
  },
1115
1287
  {
1116
- displayName: 'X Tagged User IDs (Video/Text)',
1288
+ displayName: 'X Tagged User IDs',
1117
1289
  name: 'xTaggedUserIds',
1118
1290
  type: 'string',
1119
1291
  default: '',
1120
- description: 'Comma-separated list of user IDs to tag for X (Twitter). Not for Photos.',
1292
+ description: 'Comma-separated list of user IDs to tag for X (Twitter)',
1121
1293
  displayOptions: {
1122
1294
  show: {
1123
- operation: ['uploadVideo', 'uploadText'],
1295
+ operation: ['uploadPhotos', 'uploadVideo'],
1124
1296
  platform: ['x']
1125
1297
  },
1126
1298
  },
1127
1299
  },
1128
1300
  {
1129
- displayName: 'X Reply Settings (Video/Text)',
1301
+ displayName: 'X Reply Settings',
1130
1302
  name: 'xReplySettings',
1131
1303
  type: 'options',
1132
1304
  options: [
1305
+ { name: 'Everyone', value: 'everyone' },
1133
1306
  { name: 'Following', value: 'following' },
1134
1307
  { name: 'Mentioned Users', value: 'mentionedUsers' },
1135
- { name: 'Everyone', value: 'everyone' },
1308
+ { name: 'Subscribers', value: 'subscribers' },
1309
+ { name: 'Verified', value: 'verified' },
1136
1310
  ],
1137
- default: 'following',
1138
- description: 'Who can reply to the post on X (Twitter) (following, mentionedUsers, everyone). Not for Photos.',
1311
+ default: 'everyone',
1312
+ description: 'Who can reply to the post on X (Twitter)',
1139
1313
  displayOptions: {
1140
1314
  show: {
1141
- operation: ['uploadVideo', 'uploadText'],
1315
+ operation: ['uploadPhotos', 'uploadVideo', 'uploadText'],
1142
1316
  platform: ['x']
1143
1317
  },
1144
1318
  },
1145
1319
  },
1146
1320
  {
1147
- displayName: 'X Nullcast (Video)',
1321
+ displayName: 'X Nullcast',
1148
1322
  name: 'xNullcastVideo',
1149
1323
  type: 'boolean',
1150
1324
  default: false,
1151
- description: 'Whether to publish X (Twitter) video without broadcasting. Not for Text/Photos.',
1325
+ description: 'Whether to publish X (Twitter) post without broadcasting (promoted-only posts)',
1152
1326
  displayOptions: {
1153
1327
  show: {
1154
- operation: ['uploadVideo'],
1328
+ operation: ['uploadPhotos', 'uploadVideo', 'uploadText'],
1155
1329
  platform: ['x']
1156
1330
  },
1157
1331
  },
@@ -1170,47 +1344,48 @@ class UploadPost {
1170
1344
  },
1171
1345
  },
1172
1346
  {
1173
- displayName: 'X Poll Duration (Minutes, Video)',
1174
- name: 'xPollDurationVideo',
1347
+ displayName: 'X Poll Duration (Minutes)',
1348
+ name: 'xPollDuration',
1175
1349
  type: 'number',
1176
1350
  default: 1440,
1177
- description: 'Poll duration in minutes for X (Twitter) video post (requires Poll Options). Not for Text/Photos.',
1351
+ description: 'Poll duration in minutes for X (Twitter) post (requires Poll Options)',
1178
1352
  displayOptions: {
1179
1353
  show: {
1180
- operation: ['uploadVideo'],
1354
+ operation: ['uploadText'],
1181
1355
  platform: ['x']
1182
- },
1356
+ }
1183
1357
  },
1184
1358
  },
1185
1359
  {
1186
- displayName: 'X Poll Options (Video)',
1187
- name: 'xPollOptionsVideo',
1360
+ displayName: 'X Poll Options',
1361
+ name: 'xPollOptions',
1188
1362
  type: 'string',
1189
1363
  default: '',
1190
- description: 'Comma-separated list of poll options for X (Twitter) video post. Will be sent as an array. Not for Text/Photos.',
1364
+ description: 'Comma-separated list of poll options for X (Twitter) post',
1191
1365
  displayOptions: {
1192
1366
  show: {
1193
- operation: ['uploadVideo'],
1367
+ operation: ['uploadText'],
1194
1368
  platform: ['x']
1195
- },
1369
+ }
1196
1370
  },
1197
1371
  },
1198
1372
  {
1199
- displayName: 'X Poll Reply Settings (Video)',
1200
- name: 'xPollReplySettingsVideo',
1373
+ displayName: 'X Poll Reply Settings',
1374
+ name: 'xPollReplySettings',
1201
1375
  type: 'options',
1202
1376
  options: [
1203
1377
  { name: 'Following', value: 'following' },
1204
1378
  { name: 'Mentioned Users', value: 'mentionedUsers' },
1205
1379
  { name: 'Everyone', value: 'everyone' },
1380
+ { name: 'Subscribers', value: 'subscribers' },
1206
1381
  ],
1207
1382
  default: 'following',
1208
- description: 'Who can reply to the poll in X (Twitter) video post (following, mentionedUsers, everyone). Not for Text/Photos.',
1383
+ description: 'Who can reply to the poll in X (Twitter) post',
1209
1384
  displayOptions: {
1210
1385
  show: {
1211
- operation: ['uploadVideo'],
1386
+ operation: ['uploadText'],
1212
1387
  platform: ['x']
1213
- },
1388
+ }
1214
1389
  },
1215
1390
  },
1216
1391
  {
@@ -1226,6 +1401,97 @@ class UploadPost {
1226
1401
  },
1227
1402
  },
1228
1403
  },
1404
+ {
1405
+ displayName: 'X Quote Tweet ID',
1406
+ name: 'xQuoteTweetId',
1407
+ type: 'string',
1408
+ default: '',
1409
+ description: 'ID of the tweet to quote in a quote tweet',
1410
+ displayOptions: {
1411
+ show: {
1412
+ operation: ['uploadText'],
1413
+ platform: ['x']
1414
+ }
1415
+ },
1416
+ },
1417
+ {
1418
+ displayName: 'X Geo Place ID',
1419
+ name: 'xGeoPlaceId',
1420
+ type: 'string',
1421
+ default: '',
1422
+ description: 'Geographic place ID to add location to the tweet',
1423
+ displayOptions: {
1424
+ show: {
1425
+ operation: ['uploadPhotos', 'uploadVideo', 'uploadText'],
1426
+ platform: ['x']
1427
+ },
1428
+ },
1429
+ },
1430
+ {
1431
+ displayName: 'X For Super Followers Only',
1432
+ name: 'xForSuperFollowersOnly',
1433
+ type: 'boolean',
1434
+ default: false,
1435
+ description: 'Whether the tweet is exclusive for super followers',
1436
+ displayOptions: {
1437
+ show: {
1438
+ operation: ['uploadPhotos', 'uploadVideo', 'uploadText'],
1439
+ platform: ['x']
1440
+ },
1441
+ },
1442
+ },
1443
+ {
1444
+ displayName: 'X Community ID',
1445
+ name: 'xCommunityId',
1446
+ type: 'string',
1447
+ default: '',
1448
+ description: 'Community ID for posting in specific communities',
1449
+ displayOptions: {
1450
+ show: {
1451
+ operation: ['uploadPhotos', 'uploadVideo', 'uploadText'],
1452
+ platform: ['x']
1453
+ },
1454
+ },
1455
+ },
1456
+ {
1457
+ displayName: 'X Share with Followers',
1458
+ name: 'xShareWithFollowers',
1459
+ type: 'boolean',
1460
+ default: false,
1461
+ description: 'Whether to share community post with followers',
1462
+ displayOptions: {
1463
+ show: {
1464
+ operation: ['uploadPhotos', 'uploadVideo', 'uploadText'],
1465
+ platform: ['x']
1466
+ },
1467
+ },
1468
+ },
1469
+ {
1470
+ displayName: 'X Direct Message Deep Link',
1471
+ name: 'xDirectMessageDeepLink',
1472
+ type: 'string',
1473
+ default: '',
1474
+ description: 'Link to take the conversation from public timeline to private Direct Message',
1475
+ displayOptions: {
1476
+ show: {
1477
+ operation: ['uploadPhotos', 'uploadVideo', 'uploadText'],
1478
+ platform: ['x']
1479
+ }
1480
+ },
1481
+ },
1482
+ {
1483
+ displayName: 'X Card URI',
1484
+ name: 'xCardUri',
1485
+ type: 'string',
1486
+ default: '',
1487
+ description: 'URI of card (for Twitter Cards/ads/promoted content)',
1488
+ displayOptions: {
1489
+ show: {
1490
+ operation: ['uploadText'],
1491
+ platform: ['x']
1492
+ }
1493
+ },
1494
+ },
1229
1495
  {
1230
1496
  displayName: 'X Long Text as Single Post',
1231
1497
  name: 'xLongTextAsPost',
@@ -1243,59 +1509,150 @@ class UploadPost {
1243
1509
  };
1244
1510
  this.methods = {
1245
1511
  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,
1512
+ async getPlatforms() {
1513
+ const operation = this.getCurrentNodeParameter('operation');
1514
+ const allPlatforms = [
1515
+ { name: 'Facebook', value: 'facebook' },
1516
+ { name: 'Instagram', value: 'instagram' },
1517
+ { name: 'LinkedIn', value: 'linkedin' },
1518
+ { name: 'Pinterest', value: 'pinterest' },
1519
+ { name: 'Reddit', value: 'reddit' },
1520
+ { name: 'Threads', value: 'threads' },
1521
+ { name: 'TikTok', value: 'tiktok' },
1522
+ { name: 'X (Twitter)', value: 'x' },
1523
+ { name: 'YouTube', value: 'youtube' },
1524
+ ];
1525
+ const platformSupport = {
1526
+ uploadPhotos: ['facebook', 'instagram', 'linkedin', 'pinterest', 'threads', 'tiktok', 'x'],
1527
+ uploadVideo: ['facebook', 'instagram', 'linkedin', 'pinterest', 'threads', 'tiktok', 'x', 'youtube'],
1528
+ uploadText: ['facebook', 'linkedin', 'reddit', 'threads', 'x'],
1259
1529
  };
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 }));
1530
+ const supportedPlatforms = platformSupport[operation] || [];
1531
+ return allPlatforms.filter(platform => supportedPlatforms.includes(platform.value));
1532
+ },
1533
+ async getFacebookPages() {
1534
+ try {
1535
+ const credentials = await this.getCredentials('uploadPostApi');
1536
+ const apiKey = credentials.apiKey;
1537
+ const profile = this.getCurrentNodeParameter('user') || '';
1538
+ const qs = {};
1539
+ if (profile)
1540
+ qs.profile = profile;
1541
+ const options = {
1542
+ uri: 'https://api.upload-post.com/api/uploadposts/facebook/pages',
1543
+ method: 'GET',
1544
+ headers: { Authorization: `ApiKey ${apiKey}` },
1545
+ qs,
1546
+ json: true,
1547
+ };
1548
+ const resp = await this.helpers.request(options);
1549
+ const pages = (resp && (resp.pages || resp.data || []));
1550
+ const pageOptions = (pages || []).map(p => ({ name: p.name ? `${p.name} (${p.id})` : p.id, value: p.id }));
1551
+ return [
1552
+ { name: 'Manual entry...', value: MANUAL_FACEBOOK_VALUE },
1553
+ ...pageOptions
1554
+ ];
1555
+ }
1556
+ catch (error) {
1557
+ return [
1558
+ { name: 'Manual entry...', value: MANUAL_FACEBOOK_VALUE }
1559
+ ];
1560
+ }
1263
1561
  },
1264
1562
  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 }));
1563
+ try {
1564
+ const credentials = await this.getCredentials('uploadPostApi');
1565
+ const apiKey = credentials.apiKey;
1566
+ const profile = this.getCurrentNodeParameter('user') || '';
1567
+ const qs = {};
1568
+ if (profile)
1569
+ qs.profile = profile;
1570
+ const options = {
1571
+ uri: 'https://api.upload-post.com/api/uploadposts/linkedin/pages',
1572
+ method: 'GET',
1573
+ headers: { Authorization: `ApiKey ${apiKey}` },
1574
+ qs,
1575
+ json: true,
1576
+ };
1577
+ const resp = await this.helpers.request(options);
1578
+ const pages = (resp && (resp.pages || resp.data || []));
1579
+ const pageOptions = (pages || []).map(p => ({ name: p.name ? `${p.name} (${p.id})` : p.id, value: p.id }));
1580
+ return [
1581
+ { name: 'Manual entry...', value: MANUAL_LINKEDIN_VALUE },
1582
+ { name: 'Me (Personal Profile)', value: 'me' },
1583
+ ...pageOptions
1584
+ ];
1585
+ }
1586
+ catch (error) {
1587
+ return [
1588
+ { name: 'Manual entry...', value: MANUAL_LINKEDIN_VALUE },
1589
+ { name: 'Me (Personal Profile)', value: 'me' }
1590
+ ];
1591
+ }
1281
1592
  },
1282
1593
  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 }));
1594
+ try {
1595
+ const credentials = await this.getCredentials('uploadPostApi');
1596
+ const apiKey = credentials.apiKey;
1597
+ const profile = this.getCurrentNodeParameter('user') || '';
1598
+ const qs = {};
1599
+ if (profile)
1600
+ qs.profile = profile;
1601
+ const options = {
1602
+ uri: 'https://api.upload-post.com/api/uploadposts/pinterest/boards',
1603
+ method: 'GET',
1604
+ headers: { Authorization: `ApiKey ${apiKey}` },
1605
+ qs,
1606
+ json: true,
1607
+ };
1608
+ const resp = await this.helpers.request(options);
1609
+ const boards = (resp && (resp.boards || resp.data || []));
1610
+ const boardOptions = (boards || []).map(b => ({ name: b.name ? `${b.name} (${b.id})` : b.id, value: b.id }));
1611
+ return [
1612
+ { name: 'Manual entry...', value: MANUAL_PINTEREST_VALUE },
1613
+ ...boardOptions
1614
+ ];
1615
+ }
1616
+ catch (error) {
1617
+ return [
1618
+ { name: 'Manual entry...', value: MANUAL_PINTEREST_VALUE }
1619
+ ];
1620
+ }
1621
+ },
1622
+ async getUserProfiles() {
1623
+ try {
1624
+ const credentials = await this.getCredentials('uploadPostApi');
1625
+ const apiKey = credentials.apiKey;
1626
+ const options = {
1627
+ uri: 'https://api.upload-post.com/api/uploadposts/users',
1628
+ method: 'GET',
1629
+ headers: { Authorization: `ApiKey ${apiKey}` },
1630
+ json: true,
1631
+ };
1632
+ const resp = await this.helpers.request(options);
1633
+ const profiles = (resp && resp.profiles);
1634
+ const profileOptions = (profiles || []).map(profile => {
1635
+ const connectedPlatforms = Object.keys(profile.social_accounts || {})
1636
+ .filter(platform => profile.social_accounts[platform] && typeof profile.social_accounts[platform] === 'object')
1637
+ .join(', ');
1638
+ const displayName = connectedPlatforms
1639
+ ? `${profile.username} (${connectedPlatforms})`
1640
+ : profile.username;
1641
+ return {
1642
+ name: displayName,
1643
+ value: profile.username
1644
+ };
1645
+ });
1646
+ return [
1647
+ { name: 'Manual entry...', value: MANUAL_USER_VALUE },
1648
+ ...profileOptions
1649
+ ];
1650
+ }
1651
+ catch (error) {
1652
+ return [
1653
+ { name: 'Manual entry...', value: MANUAL_USER_VALUE }
1654
+ ];
1655
+ }
1299
1656
  },
1300
1657
  },
1301
1658
  };
@@ -1308,7 +1665,13 @@ class UploadPost {
1308
1665
  const operation = this.getNodeParameter('operation', i);
1309
1666
  const isUploadOperation = ['uploadPhotos', 'uploadVideo', 'uploadText'].includes(operation);
1310
1667
  const needsUser = isUploadOperation || operation === 'generateJwt';
1311
- const user = needsUser ? this.getNodeParameter('user', i) : '';
1668
+ const userSelection = needsUser ? this.getNodeParameter('user', i) : '';
1669
+ const userManualValue = needsUser && userSelection === MANUAL_USER_VALUE
1670
+ ? this.getNodeParameter('userManual', i)
1671
+ : '';
1672
+ const user = needsUser
1673
+ ? (userSelection === MANUAL_USER_VALUE ? userManualValue : userSelection)
1674
+ : '';
1312
1675
  let platforms = isUploadOperation ? this.getNodeParameter('platform', i) : [];
1313
1676
  const title = isUploadOperation ? this.getNodeParameter('title', i) : '';
1314
1677
  let endpoint = '';
@@ -1514,7 +1877,7 @@ class UploadPost {
1514
1877
  break;
1515
1878
  case 'uploadText':
1516
1879
  endpoint = '/upload_text';
1517
- const allowedTextPlatforms = ['x', 'linkedin', 'facebook', 'threads'];
1880
+ const allowedTextPlatforms = ['x', 'linkedin', 'facebook', 'threads', 'reddit'];
1518
1881
  platforms = platforms.filter(p => allowedTextPlatforms.includes(p));
1519
1882
  formData['platform[]'] = platforms;
1520
1883
  break;
@@ -1606,7 +1969,11 @@ class UploadPost {
1606
1969
  break;
1607
1970
  }
1608
1971
  if (isUploadOperation && platforms.includes('pinterest')) {
1609
- const pinterestBoardId = this.getNodeParameter('pinterestBoardId', i);
1972
+ const pinterestSelection = this.getNodeParameter('pinterestBoardId', i);
1973
+ const pinterestManual = pinterestSelection === MANUAL_PINTEREST_VALUE
1974
+ ? this.getNodeParameter('pinterestBoardIdManual', i)
1975
+ : '';
1976
+ const pinterestBoardId = pinterestSelection === MANUAL_PINTEREST_VALUE ? pinterestManual : pinterestSelection;
1610
1977
  if (pinterestBoardId)
1611
1978
  formData.pinterest_board_id = pinterestBoardId;
1612
1979
  const pinterestLink = this.getNodeParameter('pinterestLink', i);
@@ -1635,8 +2002,12 @@ class UploadPost {
1635
2002
  }
1636
2003
  }
1637
2004
  if (isUploadOperation && platforms.includes('linkedin')) {
1638
- const targetLinkedinPageId = this.getNodeParameter('targetLinkedinPageId', i);
1639
- if (targetLinkedinPageId) {
2005
+ const targetLinkedinSelection = this.getNodeParameter('targetLinkedinPageId', i);
2006
+ const targetLinkedinManual = targetLinkedinSelection === MANUAL_LINKEDIN_VALUE
2007
+ ? this.getNodeParameter('targetLinkedinPageIdManual', i)
2008
+ : '';
2009
+ const targetLinkedinPageId = targetLinkedinSelection === MANUAL_LINKEDIN_VALUE ? targetLinkedinManual : targetLinkedinSelection;
2010
+ if (targetLinkedinPageId && targetLinkedinPageId !== 'me') {
1640
2011
  const match = targetLinkedinPageId.match(/(\d+)$/);
1641
2012
  if (match) {
1642
2013
  formData.target_linkedin_page_id = match[1];
@@ -1657,7 +2028,11 @@ class UploadPost {
1657
2028
  }
1658
2029
  }
1659
2030
  if (isUploadOperation && platforms.includes('facebook')) {
1660
- const facebookPageId = this.getNodeParameter('facebookPageId', i);
2031
+ const facebookPageSelection = this.getNodeParameter('facebookPageId', i);
2032
+ const facebookPageManual = facebookPageSelection === MANUAL_FACEBOOK_VALUE
2033
+ ? this.getNodeParameter('facebookPageIdManual', i)
2034
+ : '';
2035
+ const facebookPageId = facebookPageSelection === MANUAL_FACEBOOK_VALUE ? facebookPageManual : facebookPageSelection;
1661
2036
  formData.facebook_page_id = facebookPageId;
1662
2037
  if (operation === 'uploadVideo') {
1663
2038
  const facebookVideoState = this.getNodeParameter('facebookVideoState', i);
@@ -1673,25 +2048,25 @@ class UploadPost {
1673
2048
  else if (operation === 'uploadText') {
1674
2049
  const facebookLink = this.getNodeParameter('facebookLink', i);
1675
2050
  if (facebookLink)
1676
- formData.link = facebookLink;
2051
+ formData.facebook_link_url = facebookLink;
1677
2052
  }
1678
2053
  }
1679
2054
  if (isUploadOperation && platforms.includes('tiktok')) {
1680
2055
  if (operation === 'uploadPhotos') {
1681
2056
  const tiktokAutoAddMusic = this.getNodeParameter('tiktokAutoAddMusic', i);
1682
2057
  const tiktokDisableComment = this.getNodeParameter('tiktokDisableComment', i);
1683
- const tiktokBrandedContentPhoto = this.getNodeParameter('tiktokBrandedContentPhoto', i);
1684
- const tiktokDiscloseCommercialPhoto = this.getNodeParameter('tiktokDiscloseCommercialPhoto', i);
2058
+ const brandContentToggle = this.getNodeParameter('brand_content_toggle', i);
2059
+ const brandOrganicToggle = this.getNodeParameter('brand_organic_toggle', i);
1685
2060
  const tiktokPhotoCoverIndex = this.getNodeParameter('tiktokPhotoCoverIndex', i);
1686
2061
  const tiktokPhotoDescription = this.getNodeParameter('tiktokPhotoDescription', i);
1687
2062
  if (tiktokAutoAddMusic !== undefined)
1688
2063
  formData.auto_add_music = String(tiktokAutoAddMusic);
1689
2064
  if (tiktokDisableComment !== undefined)
1690
2065
  formData.disable_comment = String(tiktokDisableComment);
1691
- if (tiktokBrandedContentPhoto !== undefined)
1692
- formData.branded_content = String(tiktokBrandedContentPhoto);
1693
- if (tiktokDiscloseCommercialPhoto !== undefined)
1694
- formData.disclose_commercial = String(tiktokDiscloseCommercialPhoto);
2066
+ if (brandContentToggle !== undefined)
2067
+ formData.brand_content_toggle = String(brandContentToggle);
2068
+ if (brandOrganicToggle !== undefined)
2069
+ formData.brand_organic_toggle = String(brandOrganicToggle);
1695
2070
  if (tiktokPhotoCoverIndex !== undefined)
1696
2071
  formData.photo_cover_index = tiktokPhotoCoverIndex;
1697
2072
  if (tiktokPhotoDescription && formData.description === undefined)
@@ -1703,10 +2078,8 @@ class UploadPost {
1703
2078
  const tiktokDisableComment = this.getNodeParameter('tiktokDisableComment', i);
1704
2079
  const tiktokDisableStitch = this.getNodeParameter('tiktokDisableStitch', i);
1705
2080
  const tiktokCoverTimestamp = this.getNodeParameter('tiktokCoverTimestamp', i);
1706
- const tiktokBrandContentToggle = this.getNodeParameter('tiktokBrandContentToggle', i);
1707
- const tiktokBrandOrganic = this.getNodeParameter('tiktokBrandOrganic', i);
1708
- const tiktokBrandedContentVideo = this.getNodeParameter('tiktokBrandedContentVideo', i);
1709
- const tiktokBrandOrganicToggle = this.getNodeParameter('tiktokBrandOrganicToggle', i);
2081
+ const brandContentToggle = this.getNodeParameter('brand_content_toggle', i);
2082
+ const brandOrganicToggle = this.getNodeParameter('brand_organic_toggle', i);
1710
2083
  const tiktokIsAigc = this.getNodeParameter('tiktokIsAigc', i);
1711
2084
  const tiktokPostMode = this.getNodeParameter('tiktokPostMode', i);
1712
2085
  if (tiktokPrivacyLevel)
@@ -1719,14 +2092,10 @@ class UploadPost {
1719
2092
  formData.disable_stitch = String(tiktokDisableStitch);
1720
2093
  if (tiktokCoverTimestamp !== undefined)
1721
2094
  formData.cover_timestamp = tiktokCoverTimestamp;
1722
- if (tiktokBrandContentToggle !== undefined)
1723
- formData.brand_content_toggle = String(tiktokBrandContentToggle);
1724
- if (tiktokBrandOrganic !== undefined)
1725
- formData.brand_organic = String(tiktokBrandOrganic);
1726
- if (tiktokBrandedContentVideo !== undefined)
1727
- formData.branded_content = String(tiktokBrandedContentVideo);
1728
- if (tiktokBrandOrganicToggle !== undefined)
1729
- formData.brand_organic_toggle = String(tiktokBrandOrganicToggle);
2095
+ if (brandContentToggle !== undefined)
2096
+ formData.brand_content_toggle = String(brandContentToggle);
2097
+ if (brandOrganicToggle !== undefined)
2098
+ formData.brand_organic_toggle = String(brandOrganicToggle);
1730
2099
  if (tiktokIsAigc !== undefined)
1731
2100
  formData.is_aigc = String(tiktokIsAigc);
1732
2101
  if (tiktokPostMode)
@@ -1782,7 +2151,7 @@ class UploadPost {
1782
2151
  const youtubeMadeForKids = this.getNodeParameter('youtubeMadeForKids', i);
1783
2152
  const youtubeThumbnail = this.getNodeParameter('youtubeThumbnail', i);
1784
2153
  if (youtubeTagsRaw)
1785
- formData.tags = youtubeTagsRaw.split(',').map(tag => tag.trim());
2154
+ formData['tags[]'] = youtubeTagsRaw.split(',').map(tag => tag.trim());
1786
2155
  if (youtubeCategoryId)
1787
2156
  formData.categoryId = youtubeCategoryId;
1788
2157
  if (youtubePrivacyStatus)
@@ -1817,18 +2186,96 @@ class UploadPost {
1817
2186
  }
1818
2187
  }
1819
2188
  }
2189
+ const youtubeSelfDeclaredMadeForKids = this.getNodeParameter('youtubeSelfDeclaredMadeForKids', i);
2190
+ const youtubeContainsSyntheticMedia = this.getNodeParameter('youtubeContainsSyntheticMedia', i);
2191
+ const youtubeDefaultLanguage = this.getNodeParameter('youtubeDefaultLanguage', i);
2192
+ const youtubeDefaultAudioLanguage = this.getNodeParameter('youtubeDefaultAudioLanguage', i);
2193
+ const youtubeAllowedCountries = this.getNodeParameter('youtubeAllowedCountries', i);
2194
+ const youtubeBlockedCountries = this.getNodeParameter('youtubeBlockedCountries', i);
2195
+ const youtubeHasPaidProductPlacement = this.getNodeParameter('youtubeHasPaidProductPlacement', i);
2196
+ const youtubeRecordingDate = this.getNodeParameter('youtubeRecordingDate', i);
2197
+ if (youtubeSelfDeclaredMadeForKids !== undefined)
2198
+ formData.selfDeclaredMadeForKids = String(youtubeSelfDeclaredMadeForKids);
2199
+ if (youtubeContainsSyntheticMedia !== undefined)
2200
+ formData.containsSyntheticMedia = String(youtubeContainsSyntheticMedia);
2201
+ if (youtubeDefaultLanguage)
2202
+ formData.defaultLanguage = youtubeDefaultLanguage;
2203
+ if (youtubeDefaultAudioLanguage)
2204
+ formData.defaultAudioLanguage = youtubeDefaultAudioLanguage;
2205
+ if (youtubeAllowedCountries)
2206
+ formData.allowedCountries = youtubeAllowedCountries;
2207
+ if (youtubeBlockedCountries)
2208
+ formData.blockedCountries = youtubeBlockedCountries;
2209
+ if (youtubeHasPaidProductPlacement !== undefined)
2210
+ formData.hasPaidProductPlacement = String(youtubeHasPaidProductPlacement);
2211
+ if (youtubeRecordingDate)
2212
+ formData.recordingDate = youtubeRecordingDate;
1820
2213
  }
1821
2214
  if (isUploadOperation && platforms.includes('x')) {
2215
+ const xQuoteTweetId = this.getNodeParameter('xQuoteTweetId', i, '');
2216
+ const xGeoPlaceId = this.getNodeParameter('xGeoPlaceId', i, '');
2217
+ const xForSuperFollowersOnly = this.getNodeParameter('xForSuperFollowersOnly', i, false);
2218
+ const xCommunityId = this.getNodeParameter('xCommunityId', i, '');
2219
+ const xShareWithFollowers = this.getNodeParameter('xShareWithFollowers', i, false);
2220
+ const xDirectMessageDeepLink = this.getNodeParameter('xDirectMessageDeepLink', i, '');
2221
+ const xCardUri = this.getNodeParameter('xCardUri', i, '');
2222
+ if (xQuoteTweetId)
2223
+ formData.quote_tweet_id = xQuoteTweetId;
2224
+ if (xGeoPlaceId)
2225
+ formData.geo_place_id = xGeoPlaceId;
2226
+ if (xForSuperFollowersOnly)
2227
+ formData.for_super_followers_only = String(xForSuperFollowersOnly);
2228
+ if (xCommunityId)
2229
+ formData.community_id = xCommunityId;
2230
+ if (xShareWithFollowers)
2231
+ formData.share_with_followers = String(xShareWithFollowers);
2232
+ if (xDirectMessageDeepLink)
2233
+ formData.direct_message_deep_link = xDirectMessageDeepLink;
2234
+ if (xCardUri)
2235
+ formData.card_uri = xCardUri;
1822
2236
  if (operation === 'uploadText') {
1823
2237
  const xPostUrlText = this.getNodeParameter('xPostUrlText', i);
1824
2238
  if (xPostUrlText)
1825
2239
  formData.post_url = xPostUrlText;
1826
- const xTaggedUserIdsText = this.getNodeParameter('xTaggedUserIds', i);
1827
2240
  const xReplySettingsText = this.getNodeParameter('xReplySettings', i);
1828
- if (xTaggedUserIdsText)
1829
- formData.tagged_user_ids = xTaggedUserIdsText.split(',').map(id => id.trim());
1830
- if (xReplySettingsText)
2241
+ if (xReplySettingsText && xReplySettingsText !== 'everyone')
1831
2242
  formData.reply_settings = xReplySettingsText;
2243
+ const xPollDuration = this.getNodeParameter('xPollDuration', i, 1440);
2244
+ const xPollOptionsRaw = this.getNodeParameter('xPollOptions', i, '');
2245
+ const xPollReplySettings = this.getNodeParameter('xPollReplySettings', i, 'following');
2246
+ const xCardUri = this.getNodeParameter('xCardUri', i, '');
2247
+ const xQuoteTweetId = this.getNodeParameter('xQuoteTweetId', i, '');
2248
+ const xDirectMessageDeepLink = this.getNodeParameter('xDirectMessageDeepLink', i, '');
2249
+ const hasPollOptions = xPollOptionsRaw && xPollOptionsRaw.trim();
2250
+ const hasCardUri = xCardUri && xCardUri.trim();
2251
+ const hasQuoteTweetId = xQuoteTweetId && xQuoteTweetId.trim();
2252
+ const hasDirectMessageDeepLink = xDirectMessageDeepLink && xDirectMessageDeepLink.trim();
2253
+ if (hasPollOptions && (hasCardUri || hasQuoteTweetId || hasDirectMessageDeepLink)) {
2254
+ const conflictingFields = [];
2255
+ if (hasCardUri)
2256
+ conflictingFields.push('X Card URI');
2257
+ if (hasQuoteTweetId)
2258
+ conflictingFields.push('X Quote Tweet ID');
2259
+ if (hasDirectMessageDeepLink)
2260
+ conflictingFields.push('X Direct Message Deep Link');
2261
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `X Poll Options cannot be used with: ${conflictingFields.join(', ')}. These fields are mutually exclusive.`);
2262
+ }
2263
+ if (hasPollOptions) {
2264
+ const pollOptions = xPollOptionsRaw.split(',').map(opt => opt.trim()).filter(opt => opt.length > 0);
2265
+ if (pollOptions.length < 2 || pollOptions.length > 4) {
2266
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `X Poll Options must contain between 2 and 4 non-empty options. Found: ${pollOptions.length}`);
2267
+ }
2268
+ const invalidOptions = pollOptions.filter(opt => opt.length > 25);
2269
+ if (invalidOptions.length > 0) {
2270
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `X Poll Options cannot exceed 25 characters each. Invalid options: ${invalidOptions.join(', ')}`);
2271
+ }
2272
+ if (xPollDuration < 5 || xPollDuration > 10080) {
2273
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `X Poll Duration must be between 5 and 10080 minutes (5 minutes to 7 days). Provided: ${xPollDuration}`);
2274
+ }
2275
+ formData['poll_options[]'] = pollOptions;
2276
+ formData.poll_duration = xPollDuration;
2277
+ formData.poll_reply_settings = xPollReplySettings;
2278
+ }
1832
2279
  try {
1833
2280
  const xLongTextAsPostText = this.getNodeParameter('xLongTextAsPost', i, false);
1834
2281
  if (xLongTextAsPostText)
@@ -1837,35 +2284,44 @@ class UploadPost {
1837
2284
  catch { }
1838
2285
  delete formData.nullcast;
1839
2286
  delete formData.place_id;
1840
- delete formData.poll_duration;
1841
- delete formData.poll_options;
1842
- delete formData.poll_reply_settings;
1843
2287
  }
1844
- else if (operation === 'uploadVideo') {
2288
+ else if (operation === 'uploadVideo' || operation === 'uploadPhotos') {
1845
2289
  const xTaggedUserIds = this.getNodeParameter('xTaggedUserIds', i);
1846
2290
  const xReplySettings = this.getNodeParameter('xReplySettings', i);
1847
2291
  const xNullcastVideo = this.getNodeParameter('xNullcastVideo', i);
1848
- const xPlaceIdVideo = this.getNodeParameter('xPlaceIdVideo', i);
1849
- const xPollDurationVideo = this.getNodeParameter('xPollDurationVideo', i);
1850
- const xPollOptionsVideoRaw = this.getNodeParameter('xPollOptionsVideo', i);
1851
- const xPollReplySettingsVideo = this.getNodeParameter('xPollReplySettingsVideo', i);
1852
- const xLongTextAsPost = this.getNodeParameter('xLongTextAsPost', i, false);
1853
2292
  if (xTaggedUserIds)
1854
- formData.tagged_user_ids = xTaggedUserIds.split(',').map(id => id.trim());
1855
- if (xReplySettings)
2293
+ formData['tagged_user_ids[]'] = xTaggedUserIds.split(',').map(id => id.trim());
2294
+ if (xReplySettings && xReplySettings !== 'everyone')
1856
2295
  formData.reply_settings = xReplySettings;
1857
2296
  if (xNullcastVideo !== undefined)
1858
2297
  formData.nullcast = String(xNullcastVideo);
1859
- if (xPlaceIdVideo)
1860
- formData.place_id = xPlaceIdVideo;
1861
- if (xPollDurationVideo !== undefined)
1862
- formData.poll_duration = xPollDurationVideo;
1863
- if (xPollOptionsVideoRaw)
1864
- formData.poll_options = xPollOptionsVideoRaw.split(',').map(opt => opt.trim());
1865
- if (xPollReplySettingsVideo)
1866
- formData.poll_reply_settings = xPollReplySettingsVideo;
1867
- if (xLongTextAsPost)
1868
- formData.x_long_text_as_post = String(xLongTextAsPost);
2298
+ if (operation === 'uploadVideo') {
2299
+ try {
2300
+ const xLongTextAsPost = this.getNodeParameter('xLongTextAsPost', i, false);
2301
+ if (xLongTextAsPost)
2302
+ formData.x_long_text_as_post = String(xLongTextAsPost);
2303
+ }
2304
+ catch { }
2305
+ }
2306
+ }
2307
+ }
2308
+ if (isUploadOperation && platforms.includes('threads')) {
2309
+ if (operation === 'uploadText') {
2310
+ const threadsTitle = this.getNodeParameter('threadsTitle', i, '');
2311
+ if (threadsTitle)
2312
+ formData.threads_title = threadsTitle;
2313
+ const threadsLongTextAsPost = this.getNodeParameter('threadsLongTextAsPost', i, false);
2314
+ if (threadsLongTextAsPost)
2315
+ formData.threads_long_text_as_post = String(threadsLongTextAsPost);
2316
+ }
2317
+ }
2318
+ if (isUploadOperation && platforms.includes('reddit')) {
2319
+ if (operation === 'uploadText') {
2320
+ const redditSubreddit = this.getNodeParameter('redditSubreddit', i);
2321
+ formData.subreddit = redditSubreddit;
2322
+ const redditFlairId = this.getNodeParameter('redditFlairId', i, '');
2323
+ if (redditFlairId)
2324
+ formData.flair_id = redditFlairId;
1869
2325
  }
1870
2326
  }
1871
2327
  const credentials = await this.getCredentials('uploadPostApi');
@@ -1899,6 +2355,8 @@ class UploadPost {
1899
2355
  options.qs = qs;
1900
2356
  }
1901
2357
  }
2358
+ this.logger.info(`Operation: ${operation}, Is Upload Operation: ${isUploadOperation}`);
2359
+ this.logger.info('Complete Form Data being sent (JSON): ' + JSON.stringify(formData, null, 2));
1902
2360
  const responseData = await this.helpers.request(options);
1903
2361
  const shouldConsiderPolling = operation === 'uploadPhotos' || operation === 'uploadVideo' || operation === 'uploadText';
1904
2362
  const waitForCompletion = shouldConsiderPolling ? this.getNodeParameter('waitForCompletion', i, false) : false;