n8n-nodes-shortgenius 0.1.0

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.
@@ -0,0 +1,1071 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ShortGenius = void 0;
4
+ const vendor_1 = require("../../src/vendor");
5
+ const n8n_workflow_1 = require("n8n-workflow");
6
+ /**
7
+ * Action node for the ShortGenius API.
8
+ *
9
+ * Mirrors the @shortgenius/sdk surface: Videos, Series, Images, Audio, Voices,
10
+ * Music, Connections, Credits, Webhooks. Each operation maps directly to a
11
+ * method on the SDK client so the shape stays in sync when new endpoints land.
12
+ *
13
+ * ShortGenius is single-user — the API key authenticates and there is NO
14
+ * organization parameter anywhere.
15
+ */
16
+ class ShortGenius {
17
+ description = {
18
+ displayName: 'ShortGenius',
19
+ name: 'shortGenius',
20
+ icon: 'file:ShortGenius.svg',
21
+ group: ['transform'],
22
+ version: 1,
23
+ usableAsTool: true,
24
+ subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
25
+ description: 'Generate AI videos, images, and audio, and manage your ShortGenius account',
26
+ defaults: { name: 'ShortGenius' },
27
+ inputs: ['main'],
28
+ outputs: ['main'],
29
+ credentials: [{ name: 'shortGeniusApi', required: true }],
30
+ properties: [
31
+ {
32
+ displayName: 'Resource',
33
+ name: 'resource',
34
+ type: 'options',
35
+ noDataExpression: true,
36
+ default: 'video',
37
+ options: [
38
+ { name: 'Video', value: 'video' },
39
+ { name: 'Series', value: 'series' },
40
+ { name: 'Image', value: 'image' },
41
+ { name: 'Audio', value: 'audio' },
42
+ { name: 'Voice', value: 'voice' },
43
+ { name: 'Music', value: 'music' },
44
+ { name: 'Connection', value: 'connection' },
45
+ { name: 'Credits', value: 'credits' },
46
+ { name: 'Webhook', value: 'webhook' }
47
+ ]
48
+ },
49
+ // ---------------------------------------------------------------------
50
+ // Video operations
51
+ // ---------------------------------------------------------------------
52
+ {
53
+ displayName: 'Operation',
54
+ name: 'operation',
55
+ type: 'options',
56
+ noDataExpression: true,
57
+ default: 'list',
58
+ displayOptions: { show: { resource: ['video'] } },
59
+ options: [
60
+ { name: 'List', value: 'list', action: 'List videos', description: 'List the videos in your account' },
61
+ { name: 'Get', value: 'get', action: 'Get a video', description: 'Get a single video by ID' },
62
+ { name: 'Create', value: 'create', action: 'Create a video', description: 'Render a video from drafted content' },
63
+ { name: 'Draft', value: 'draft', action: 'Draft a video', description: 'Write a video script from a topic' },
64
+ { name: 'Draft From URL', value: 'draftUrl', action: 'Draft a video from a URL', description: 'Summarize a webpage into a video script' },
65
+ { name: 'Draft From Script', value: 'draftScript', action: 'Draft a video from a script', description: 'Build scenes from your own script' },
66
+ { name: 'Draft Quiz', value: 'draftQuiz', action: 'Draft a quiz video', description: 'Write a quiz video from a topic' },
67
+ { name: 'Draft News', value: 'draftNews', action: 'Draft a news video', description: 'Write a news video from a topic' },
68
+ { name: 'Generate Topics', value: 'topics', action: 'Generate video topics', description: 'Brainstorm video topic ideas' }
69
+ ]
70
+ },
71
+ // ---------------------------------------------------------------------
72
+ // Series operations
73
+ // ---------------------------------------------------------------------
74
+ {
75
+ displayName: 'Operation',
76
+ name: 'operation',
77
+ type: 'options',
78
+ noDataExpression: true,
79
+ default: 'list',
80
+ displayOptions: { show: { resource: ['series'] } },
81
+ options: [
82
+ { name: 'List', value: 'list', action: 'List series', description: 'List the series in your account' },
83
+ { name: 'Get', value: 'get', action: 'Get a series', description: 'Get a single series and its episodes by ID' },
84
+ { name: 'Create', value: 'create', action: 'Create a series', description: 'Start a recurring, scheduled video series' }
85
+ ]
86
+ },
87
+ // ---------------------------------------------------------------------
88
+ // Image operations
89
+ // ---------------------------------------------------------------------
90
+ {
91
+ displayName: 'Operation',
92
+ name: 'operation',
93
+ type: 'options',
94
+ noDataExpression: true,
95
+ default: 'list',
96
+ displayOptions: { show: { resource: ['image'] } },
97
+ options: [
98
+ { name: 'List', value: 'list', action: 'List images', description: 'List the images in your account' },
99
+ { name: 'Get', value: 'get', action: 'Get an image', description: 'Get a single image by ID' },
100
+ { name: 'Create', value: 'create', action: 'Create an image', description: 'Generate an image from a prompt' },
101
+ { name: 'List Styles', value: 'styles', action: 'List image styles', description: 'List the available image styles' }
102
+ ]
103
+ },
104
+ // ---------------------------------------------------------------------
105
+ // Audio operations
106
+ // ---------------------------------------------------------------------
107
+ {
108
+ displayName: 'Operation',
109
+ name: 'operation',
110
+ type: 'options',
111
+ noDataExpression: true,
112
+ default: 'list',
113
+ displayOptions: { show: { resource: ['audio'] } },
114
+ options: [
115
+ { name: 'List', value: 'list', action: 'List audio', description: 'List the audio clips in your account' },
116
+ { name: 'Get', value: 'get', action: 'Get an audio clip', description: 'Get a single audio clip by ID' },
117
+ { name: 'Create Speech', value: 'speech', action: 'Create speech audio', description: 'Generate text-to-speech audio' }
118
+ ]
119
+ },
120
+ // ---------------------------------------------------------------------
121
+ // Voice operations
122
+ // ---------------------------------------------------------------------
123
+ {
124
+ displayName: 'Operation',
125
+ name: 'operation',
126
+ type: 'options',
127
+ noDataExpression: true,
128
+ default: 'list',
129
+ displayOptions: { show: { resource: ['voice'] } },
130
+ options: [
131
+ { name: 'List', value: 'list', action: 'List voices', description: 'List the available text-to-speech voices' },
132
+ { name: 'Get', value: 'get', action: 'Get a voice', description: 'Get a single voice by ID' }
133
+ ]
134
+ },
135
+ // ---------------------------------------------------------------------
136
+ // Music operations
137
+ // ---------------------------------------------------------------------
138
+ {
139
+ displayName: 'Operation',
140
+ name: 'operation',
141
+ type: 'options',
142
+ noDataExpression: true,
143
+ default: 'genres',
144
+ displayOptions: { show: { resource: ['music'] } },
145
+ options: [
146
+ { name: 'List Genres', value: 'genres', action: 'List music genres', description: 'List the available soundtrack genres' },
147
+ { name: 'List Tracks', value: 'tracks', action: 'List tracks in a genre', description: 'List the soundtracks in a genre' }
148
+ ]
149
+ },
150
+ // ---------------------------------------------------------------------
151
+ // Connection operations
152
+ // ---------------------------------------------------------------------
153
+ {
154
+ displayName: 'Operation',
155
+ name: 'operation',
156
+ type: 'options',
157
+ noDataExpression: true,
158
+ default: 'list',
159
+ displayOptions: { show: { resource: ['connection'] } },
160
+ options: [
161
+ { name: 'List', value: 'list', action: 'List connections', description: 'List your publishing connections' }
162
+ ]
163
+ },
164
+ // ---------------------------------------------------------------------
165
+ // Credits operations
166
+ // ---------------------------------------------------------------------
167
+ {
168
+ displayName: 'Operation',
169
+ name: 'operation',
170
+ type: 'options',
171
+ noDataExpression: true,
172
+ default: 'get',
173
+ displayOptions: { show: { resource: ['credits'] } },
174
+ options: [
175
+ { name: 'Get Balance', value: 'get', action: 'Get the credit balance', description: 'Get your current credit balance' }
176
+ ]
177
+ },
178
+ // ---------------------------------------------------------------------
179
+ // Webhook operations
180
+ // ---------------------------------------------------------------------
181
+ {
182
+ displayName: 'Operation',
183
+ name: 'operation',
184
+ type: 'options',
185
+ noDataExpression: true,
186
+ default: 'list',
187
+ displayOptions: { show: { resource: ['webhook'] } },
188
+ options: [
189
+ { name: 'List Endpoints', value: 'list', action: 'List webhook endpoints' },
190
+ { name: 'Create Endpoint', value: 'create', action: 'Create a webhook endpoint' },
191
+ { name: 'Delete Endpoint', value: 'delete', action: 'Delete a webhook endpoint' }
192
+ ]
193
+ },
194
+ // =====================================================================
195
+ // Shared pagination (List operations)
196
+ // =====================================================================
197
+ {
198
+ displayName: 'Page',
199
+ name: 'page',
200
+ type: 'number',
201
+ default: 0,
202
+ typeOptions: { minValue: 0 },
203
+ description: 'Page number to fetch (0 = first page)',
204
+ displayOptions: {
205
+ show: {
206
+ resource: ['video', 'series', 'image', 'audio', 'voice'],
207
+ operation: ['list']
208
+ }
209
+ }
210
+ },
211
+ {
212
+ displayName: 'Limit',
213
+ name: 'limit',
214
+ type: 'number',
215
+ default: 20,
216
+ typeOptions: { minValue: 1, maxValue: 100 },
217
+ description: 'Max number of items per page',
218
+ displayOptions: {
219
+ show: {
220
+ resource: ['video', 'series', 'image', 'audio', 'voice'],
221
+ operation: ['list']
222
+ }
223
+ }
224
+ },
225
+ // =====================================================================
226
+ // Video: Get — ID
227
+ // =====================================================================
228
+ {
229
+ displayName: 'Video ID',
230
+ name: 'videoId',
231
+ type: 'string',
232
+ default: '',
233
+ required: true,
234
+ description: 'ShortGenius video ID (UUID)',
235
+ displayOptions: { show: { resource: ['video'], operation: ['get'] } }
236
+ },
237
+ // Video: Draft — topic + duration + locale
238
+ {
239
+ displayName: 'Topic',
240
+ name: 'topic',
241
+ type: 'string',
242
+ default: '',
243
+ required: true,
244
+ placeholder: 'The history of the Roman Empire',
245
+ description: 'The topic to write a video about',
246
+ displayOptions: { show: { resource: ['video'], operation: ['draft', 'draftQuiz', 'draftNews'] } }
247
+ },
248
+ {
249
+ displayName: 'Duration (Seconds)',
250
+ name: 'duration',
251
+ type: 'options',
252
+ default: '60',
253
+ required: true,
254
+ description: 'Desired length of the final script in seconds (best-effort)',
255
+ options: [
256
+ { name: '60', value: '60' },
257
+ { name: '90', value: '90' },
258
+ { name: '120', value: '120' },
259
+ { name: '180', value: '180' },
260
+ { name: '240', value: '240' },
261
+ { name: '300', value: '300' },
262
+ { name: '360', value: '360' },
263
+ { name: '420', value: '420' },
264
+ { name: '480', value: '480' },
265
+ { name: '540', value: '540' },
266
+ { name: '600', value: '600' },
267
+ { name: '660', value: '660' },
268
+ { name: '720', value: '720' },
269
+ { name: '780', value: '780' },
270
+ { name: '840', value: '840' },
271
+ { name: '900', value: '900' }
272
+ ],
273
+ displayOptions: { show: { resource: ['video'], operation: ['draft'] } }
274
+ },
275
+ {
276
+ displayName: 'Locale',
277
+ name: 'locale',
278
+ type: 'string',
279
+ default: '',
280
+ placeholder: 'en-US',
281
+ description: 'Optional locale for the generated video (e.g. en-US). Leave blank to auto-detect.',
282
+ displayOptions: { show: { resource: ['video'], operation: ['draft', 'draftUrl', 'draftQuiz', 'draftNews'] } }
283
+ },
284
+ // Video: Draft From URL — url + prompt
285
+ {
286
+ displayName: 'URL',
287
+ name: 'url',
288
+ type: 'string',
289
+ default: '',
290
+ required: true,
291
+ placeholder: 'https://example.com/article',
292
+ description: 'URL of a webpage to summarize into a video',
293
+ displayOptions: { show: { resource: ['video'], operation: ['draftUrl'] } }
294
+ },
295
+ {
296
+ displayName: 'Prompt',
297
+ name: 'prompt',
298
+ type: 'string',
299
+ default: '',
300
+ description: 'Optional instructions for the AI reading the webpage',
301
+ displayOptions: { show: { resource: ['video'], operation: ['draftUrl'] } }
302
+ },
303
+ // Video: Draft From Script — script
304
+ {
305
+ displayName: 'Script',
306
+ name: 'script',
307
+ type: 'string',
308
+ typeOptions: { rows: 4 },
309
+ default: '',
310
+ required: true,
311
+ description: 'The content you want the AI to narrate',
312
+ displayOptions: { show: { resource: ['video'], operation: ['draftScript'] } }
313
+ },
314
+ // Video: Generate Topics
315
+ {
316
+ displayName: 'Parent Topic',
317
+ name: 'parentTopic',
318
+ type: 'string',
319
+ default: '',
320
+ description: 'Optional base idea or theme for generating topics',
321
+ displayOptions: { show: { resource: ['video'], operation: ['topics'] } }
322
+ },
323
+ {
324
+ displayName: 'Number of Topics',
325
+ name: 'numberOfTopics',
326
+ type: 'number',
327
+ default: 10,
328
+ typeOptions: { minValue: 1, maxValue: 100 },
329
+ description: 'Approximate number of topics to generate (max 100)',
330
+ displayOptions: { show: { resource: ['video'], operation: ['topics'] } }
331
+ },
332
+ {
333
+ displayName: 'Locale',
334
+ name: 'locale',
335
+ type: 'string',
336
+ default: '',
337
+ placeholder: 'en-US',
338
+ description: 'Optional locale for the generated topics',
339
+ displayOptions: { show: { resource: ['video'], operation: ['topics'] } }
340
+ },
341
+ {
342
+ displayName: 'Content Type',
343
+ name: 'contentType',
344
+ type: 'options',
345
+ default: 'Custom',
346
+ description: 'Optional content type to bias the topics',
347
+ options: [
348
+ { name: 'Custom', value: 'Custom' },
349
+ { name: 'News', value: 'News' },
350
+ { name: 'Quiz', value: 'Quiz' },
351
+ { name: 'History', value: 'History' },
352
+ { name: 'Scary', value: 'Scary' },
353
+ { name: 'Motivational', value: 'Motivational' },
354
+ { name: 'Bedtime', value: 'Bedtime' },
355
+ { name: 'Fun Facts', value: 'FunFacts' },
356
+ { name: 'Life Tips', value: 'LifeTips' },
357
+ { name: 'ELI5', value: 'ELI5' },
358
+ { name: 'Philosophy', value: 'Philosophy' },
359
+ { name: 'Blog', value: 'Blog' },
360
+ { name: 'Ad', value: 'Ad' }
361
+ ],
362
+ displayOptions: { show: { resource: ['video'], operation: ['topics'] } }
363
+ },
364
+ // Video: Create — connection IDs, title, caption + optional fields
365
+ {
366
+ displayName: 'Connection IDs',
367
+ name: 'connectionIds',
368
+ type: 'string',
369
+ default: '',
370
+ required: true,
371
+ placeholder: 'uuid-1, uuid-2',
372
+ description: 'Comma-separated publishing connection IDs. Use the Connection: List operation to find them.',
373
+ displayOptions: { show: { resource: ['video'], operation: ['create'] } }
374
+ },
375
+ {
376
+ displayName: 'Title',
377
+ name: 'title',
378
+ type: 'string',
379
+ default: '',
380
+ required: true,
381
+ description: 'The title of the video',
382
+ displayOptions: { show: { resource: ['video'], operation: ['create'] } }
383
+ },
384
+ {
385
+ displayName: 'Caption',
386
+ name: 'caption',
387
+ type: 'string',
388
+ default: '',
389
+ required: true,
390
+ description: 'The description shown beside the video when posted to social media',
391
+ displayOptions: { show: { resource: ['video'], operation: ['create'] } }
392
+ },
393
+ {
394
+ displayName: 'Scenes (JSON)',
395
+ name: 'scenes',
396
+ type: 'json',
397
+ default: '',
398
+ description: 'Optional array of scenes from a draft. Not required for Quiz videos.',
399
+ displayOptions: { show: { resource: ['video'], operation: ['create'] } }
400
+ },
401
+ {
402
+ displayName: 'Content Type',
403
+ name: 'contentType',
404
+ type: 'options',
405
+ default: 'Custom',
406
+ options: [
407
+ { name: 'Custom', value: 'Custom' },
408
+ { name: 'News', value: 'News' },
409
+ { name: 'Quiz', value: 'Quiz' },
410
+ { name: 'History', value: 'History' },
411
+ { name: 'Scary', value: 'Scary' },
412
+ { name: 'Motivational', value: 'Motivational' },
413
+ { name: 'Bedtime', value: 'Bedtime' },
414
+ { name: 'Fun Facts', value: 'FunFacts' },
415
+ { name: 'Life Tips', value: 'LifeTips' },
416
+ { name: 'ELI5', value: 'ELI5' },
417
+ { name: 'Philosophy', value: 'Philosophy' },
418
+ { name: 'Blog', value: 'Blog' },
419
+ { name: 'Ad', value: 'Ad' }
420
+ ],
421
+ displayOptions: { show: { resource: ['video'], operation: ['create'] } }
422
+ },
423
+ {
424
+ displayName: 'Aspect Ratio',
425
+ name: 'aspectRatio',
426
+ type: 'options',
427
+ default: '9:16',
428
+ options: [
429
+ { name: 'Vertical (9:16)', value: '9:16' },
430
+ { name: 'Horizontal (16:9)', value: '16:9' },
431
+ { name: 'Square (1:1)', value: '1:1' }
432
+ ],
433
+ displayOptions: { show: { resource: ['video'], operation: ['create'] } }
434
+ },
435
+ {
436
+ displayName: 'Image Style ID',
437
+ name: 'imageStyleId',
438
+ type: 'string',
439
+ default: '',
440
+ description: 'Optional image style ID. Use the Image: List Styles operation to find them. Blank lets the AI choose.',
441
+ displayOptions: { show: { resource: ['video'], operation: ['create'] } }
442
+ },
443
+ {
444
+ displayName: 'Voice ID',
445
+ name: 'voiceId',
446
+ type: 'string',
447
+ default: '',
448
+ description: 'Optional voice ID. Use the Voice: List operation to find them.',
449
+ displayOptions: { show: { resource: ['video'], operation: ['create'] } }
450
+ },
451
+ {
452
+ displayName: 'Soundtrack ID',
453
+ name: 'soundtrackId',
454
+ type: 'string',
455
+ default: '',
456
+ description: 'Optional soundtrack ID. Use the Music: List Tracks operation to find them.',
457
+ displayOptions: { show: { resource: ['video'], operation: ['create'] } }
458
+ },
459
+ {
460
+ displayName: 'Publish At',
461
+ name: 'publishAt',
462
+ type: 'string',
463
+ default: '',
464
+ placeholder: '2026-01-01T12:00:00Z',
465
+ description: 'Optional ISO 8601 timestamp to schedule publishing',
466
+ displayOptions: { show: { resource: ['video'], operation: ['create'] } }
467
+ },
468
+ // =====================================================================
469
+ // Series: Get — ID
470
+ // =====================================================================
471
+ {
472
+ displayName: 'Series ID',
473
+ name: 'seriesId',
474
+ type: 'string',
475
+ default: '',
476
+ required: true,
477
+ description: 'ShortGenius series ID',
478
+ displayOptions: { show: { resource: ['series'], operation: ['get'] } }
479
+ },
480
+ // Series: Create
481
+ {
482
+ displayName: 'Connection IDs',
483
+ name: 'connectionIds',
484
+ type: 'string',
485
+ default: '',
486
+ required: true,
487
+ placeholder: 'uuid-1, uuid-2',
488
+ description: 'Comma-separated publishing connection IDs. Use the Connection: List operation to find them.',
489
+ displayOptions: { show: { resource: ['series'], operation: ['create'] } }
490
+ },
491
+ {
492
+ displayName: 'Parent Topic',
493
+ name: 'parentTopic',
494
+ type: 'string',
495
+ default: '',
496
+ description: 'Optional theme for auto-generating episode topics',
497
+ displayOptions: { show: { resource: ['series'], operation: ['create'] } }
498
+ },
499
+ {
500
+ displayName: 'Topics',
501
+ name: 'topics',
502
+ type: 'string',
503
+ default: '',
504
+ description: 'Optional comma-separated list of explicit episode topics',
505
+ displayOptions: { show: { resource: ['series'], operation: ['create'] } }
506
+ },
507
+ {
508
+ displayName: 'Content Type',
509
+ name: 'contentType',
510
+ type: 'options',
511
+ default: 'Custom',
512
+ options: [
513
+ { name: 'Custom', value: 'Custom' },
514
+ { name: 'News', value: 'News' },
515
+ { name: 'Quiz', value: 'Quiz' },
516
+ { name: 'History', value: 'History' },
517
+ { name: 'Scary', value: 'Scary' },
518
+ { name: 'Motivational', value: 'Motivational' },
519
+ { name: 'Bedtime', value: 'Bedtime' },
520
+ { name: 'Fun Facts', value: 'FunFacts' },
521
+ { name: 'Life Tips', value: 'LifeTips' },
522
+ { name: 'ELI5', value: 'ELI5' },
523
+ { name: 'Philosophy', value: 'Philosophy' },
524
+ { name: 'Blog', value: 'Blog' },
525
+ { name: 'Ad', value: 'Ad' }
526
+ ],
527
+ displayOptions: { show: { resource: ['series'], operation: ['create'] } }
528
+ },
529
+ {
530
+ displayName: 'Aspect Ratio',
531
+ name: 'aspectRatio',
532
+ type: 'options',
533
+ default: '9:16',
534
+ options: [
535
+ { name: 'Vertical (9:16)', value: '9:16' },
536
+ { name: 'Horizontal (16:9)', value: '16:9' },
537
+ { name: 'Square (1:1)', value: '1:1' }
538
+ ],
539
+ displayOptions: { show: { resource: ['series'], operation: ['create'] } }
540
+ },
541
+ {
542
+ displayName: 'Duration (Seconds)',
543
+ name: 'duration',
544
+ type: 'options',
545
+ default: '60',
546
+ description: 'Desired length of each episode in seconds (best-effort)',
547
+ options: [
548
+ { name: '60', value: '60' },
549
+ { name: '90', value: '90' },
550
+ { name: '120', value: '120' },
551
+ { name: '180', value: '180' },
552
+ { name: '240', value: '240' },
553
+ { name: '300', value: '300' },
554
+ { name: '360', value: '360' },
555
+ { name: '420', value: '420' },
556
+ { name: '480', value: '480' },
557
+ { name: '540', value: '540' },
558
+ { name: '600', value: '600' },
559
+ { name: '660', value: '660' },
560
+ { name: '720', value: '720' },
561
+ { name: '780', value: '780' },
562
+ { name: '840', value: '840' },
563
+ { name: '900', value: '900' }
564
+ ],
565
+ displayOptions: { show: { resource: ['series'], operation: ['create'] } }
566
+ },
567
+ {
568
+ displayName: 'Voice IDs',
569
+ name: 'voiceIds',
570
+ type: 'string',
571
+ default: '',
572
+ description: 'Optional comma-separated voice IDs to rotate through',
573
+ displayOptions: { show: { resource: ['series'], operation: ['create'] } }
574
+ },
575
+ {
576
+ displayName: 'Schedule (JSON)',
577
+ name: 'schedule',
578
+ type: 'json',
579
+ default: '',
580
+ description: 'Optional posting schedule object',
581
+ displayOptions: { show: { resource: ['series'], operation: ['create'] } }
582
+ },
583
+ // =====================================================================
584
+ // Image: Get — ID
585
+ // =====================================================================
586
+ {
587
+ displayName: 'Image ID',
588
+ name: 'imageId',
589
+ type: 'string',
590
+ default: '',
591
+ required: true,
592
+ description: 'ShortGenius image ID',
593
+ displayOptions: { show: { resource: ['image'], operation: ['get'] } }
594
+ },
595
+ // Image: Create
596
+ {
597
+ displayName: 'Prompt',
598
+ name: 'prompt',
599
+ type: 'string',
600
+ typeOptions: { rows: 3 },
601
+ default: '',
602
+ required: true,
603
+ placeholder: 'A neon city skyline at dusk',
604
+ description: 'Text prompt describing the image to generate',
605
+ displayOptions: { show: { resource: ['image'], operation: ['create'] } }
606
+ },
607
+ {
608
+ displayName: 'Aspect Ratio',
609
+ name: 'aspectRatio',
610
+ type: 'options',
611
+ default: '9:16',
612
+ required: true,
613
+ options: [
614
+ { name: 'Vertical (9:16)', value: '9:16' },
615
+ { name: 'Horizontal (16:9)', value: '16:9' },
616
+ { name: 'Square (1:1)', value: '1:1' }
617
+ ],
618
+ displayOptions: { show: { resource: ['image'], operation: ['create'] } }
619
+ },
620
+ {
621
+ displayName: 'Image Style ID',
622
+ name: 'imageStyleId',
623
+ type: 'string',
624
+ default: '',
625
+ description: 'Optional image style ID. Use the Image: List Styles operation to find them.',
626
+ displayOptions: { show: { resource: ['image'], operation: ['create'] } }
627
+ },
628
+ {
629
+ displayName: 'Quality',
630
+ name: 'quality',
631
+ type: 'options',
632
+ default: 'high',
633
+ options: [
634
+ { name: 'Low', value: 'low' },
635
+ { name: 'Medium', value: 'medium' },
636
+ { name: 'High', value: 'high' }
637
+ ],
638
+ description: 'Generation quality',
639
+ displayOptions: { show: { resource: ['image'], operation: ['create'] } }
640
+ },
641
+ {
642
+ displayName: 'Wait for Generation',
643
+ name: 'waitForGeneration',
644
+ type: 'boolean',
645
+ default: false,
646
+ description: 'Whether to block until the image finishes generating before returning',
647
+ displayOptions: { show: { resource: ['image'], operation: ['create'] } }
648
+ },
649
+ // =====================================================================
650
+ // Audio: Get — ID
651
+ // =====================================================================
652
+ {
653
+ displayName: 'Audio ID',
654
+ name: 'audioId',
655
+ type: 'string',
656
+ default: '',
657
+ required: true,
658
+ description: 'ShortGenius audio ID',
659
+ displayOptions: { show: { resource: ['audio'], operation: ['get'] } }
660
+ },
661
+ // Audio: Create Speech
662
+ {
663
+ displayName: 'Text',
664
+ name: 'text',
665
+ type: 'string',
666
+ typeOptions: { rows: 3 },
667
+ default: '',
668
+ required: true,
669
+ description: 'The text to synthesize into speech',
670
+ displayOptions: { show: { resource: ['audio'], operation: ['speech'] } }
671
+ },
672
+ {
673
+ displayName: 'Voice ID',
674
+ name: 'voiceId',
675
+ type: 'string',
676
+ default: '',
677
+ required: true,
678
+ description: 'Voice to narrate with. Use the Voice: List operation to find them.',
679
+ displayOptions: { show: { resource: ['audio'], operation: ['speech'] } }
680
+ },
681
+ {
682
+ displayName: 'Locale',
683
+ name: 'locale',
684
+ type: 'string',
685
+ default: '',
686
+ placeholder: 'en-US',
687
+ description: 'Optional locale for the speech',
688
+ displayOptions: { show: { resource: ['audio'], operation: ['speech'] } }
689
+ },
690
+ {
691
+ displayName: 'Wait for Generation',
692
+ name: 'waitForGeneration',
693
+ type: 'boolean',
694
+ default: false,
695
+ description: 'Whether to block until the audio finishes generating before returning',
696
+ displayOptions: { show: { resource: ['audio'], operation: ['speech'] } }
697
+ },
698
+ // =====================================================================
699
+ // Voice: List filter + Get — ID
700
+ // =====================================================================
701
+ {
702
+ displayName: 'Locale',
703
+ name: 'locale',
704
+ type: 'string',
705
+ default: '',
706
+ placeholder: 'en-US',
707
+ description: 'Optional locale filter for the voices',
708
+ displayOptions: { show: { resource: ['voice'], operation: ['list'] } }
709
+ },
710
+ {
711
+ displayName: 'Voice ID',
712
+ name: 'voiceId',
713
+ type: 'string',
714
+ default: '',
715
+ required: true,
716
+ description: 'ShortGenius voice ID',
717
+ displayOptions: { show: { resource: ['voice'], operation: ['get'] } }
718
+ },
719
+ // =====================================================================
720
+ // Music: List Tracks — genre ID
721
+ // =====================================================================
722
+ {
723
+ displayName: 'Genre ID',
724
+ name: 'genreId',
725
+ type: 'string',
726
+ default: '',
727
+ required: true,
728
+ description: 'Music genre ID. Use the Music: List Genres operation to find them.',
729
+ displayOptions: { show: { resource: ['music'], operation: ['tracks'] } }
730
+ },
731
+ // =====================================================================
732
+ // Webhook: Create — url + events + description
733
+ // =====================================================================
734
+ {
735
+ displayName: 'Endpoint URL',
736
+ name: 'url',
737
+ type: 'string',
738
+ default: '',
739
+ required: true,
740
+ placeholder: 'https://example.com/webhooks/shortgenius',
741
+ displayOptions: { show: { resource: ['webhook'], operation: ['create'] } }
742
+ },
743
+ {
744
+ displayName: 'Events',
745
+ name: 'events',
746
+ type: 'multiOptions',
747
+ default: ['video.completed'],
748
+ options: [
749
+ { name: 'Video Created', value: 'video.created' },
750
+ { name: 'Video Completed', value: 'video.completed' },
751
+ { name: 'Video Published', value: 'video.published' },
752
+ { name: 'Series Created', value: 'series.created' },
753
+ { name: 'Image Completed', value: 'image.completed' },
754
+ { name: 'Audio Completed', value: 'audio.completed' },
755
+ { name: 'All Events', value: '*' }
756
+ ],
757
+ required: true,
758
+ displayOptions: { show: { resource: ['webhook'], operation: ['create'] } }
759
+ },
760
+ {
761
+ displayName: 'Description',
762
+ name: 'description',
763
+ type: 'string',
764
+ default: '',
765
+ displayOptions: { show: { resource: ['webhook'], operation: ['create'] } }
766
+ },
767
+ // Webhook: Delete — endpoint ID
768
+ {
769
+ displayName: 'Endpoint ID',
770
+ name: 'endpointId',
771
+ type: 'string',
772
+ default: '',
773
+ required: true,
774
+ displayOptions: { show: { resource: ['webhook'], operation: ['delete'] } }
775
+ }
776
+ ]
777
+ };
778
+ async execute() {
779
+ const credentials = await this.getCredentials('shortGeniusApi');
780
+ const client = new vendor_1.ShortGenius({
781
+ apiKey: credentials.apiKey,
782
+ baseUrl: credentials.baseUrl || undefined
783
+ });
784
+ // Split a comma-separated string field into a trimmed, non-empty array.
785
+ const splitCsv = (raw) => raw
786
+ .split(',')
787
+ .map(s => s.trim())
788
+ .filter(Boolean);
789
+ // Parse an n8n `json` field that may be a parsed value or a raw string.
790
+ const parseJson = (raw) => {
791
+ if (typeof raw !== 'string')
792
+ return raw;
793
+ const trimmed = raw.trim();
794
+ return trimmed ? JSON.parse(trimmed) : undefined;
795
+ };
796
+ const items = this.getInputData();
797
+ const out = [];
798
+ for (let i = 0; i < items.length; i++) {
799
+ const resource = this.getNodeParameter('resource', i);
800
+ const operation = this.getNodeParameter('operation', i);
801
+ try {
802
+ let result;
803
+ if (resource === 'video') {
804
+ if (operation === 'list') {
805
+ const page = this.getNodeParameter('page', i, 0);
806
+ const limit = this.getNodeParameter('limit', i, 20);
807
+ result = await client.videos.list({ page, limit });
808
+ }
809
+ else if (operation === 'get') {
810
+ const videoId = this.getNodeParameter('videoId', i);
811
+ result = await client.videos.get(videoId);
812
+ }
813
+ else if (operation === 'draft') {
814
+ const topic = this.getNodeParameter('topic', i);
815
+ const duration = this.getNodeParameter('duration', i);
816
+ const locale = this.getNodeParameter('locale', i, '');
817
+ result = await client.videos.draft({ topic, duration, locale: locale || undefined });
818
+ }
819
+ else if (operation === 'draftUrl') {
820
+ const url = this.getNodeParameter('url', i);
821
+ const prompt = this.getNodeParameter('prompt', i, '');
822
+ const locale = this.getNodeParameter('locale', i, '');
823
+ result = await client.videos.draftFromUrl({ url, prompt: prompt || undefined, locale: locale || undefined });
824
+ }
825
+ else if (operation === 'draftScript') {
826
+ const script = this.getNodeParameter('script', i);
827
+ result = await client.videos.draftFromScript({ script });
828
+ }
829
+ else if (operation === 'draftQuiz') {
830
+ const topic = this.getNodeParameter('topic', i);
831
+ const locale = this.getNodeParameter('locale', i, '');
832
+ result = await client.videos.draftQuiz({ topic, locale: locale || undefined });
833
+ }
834
+ else if (operation === 'draftNews') {
835
+ const topic = this.getNodeParameter('topic', i);
836
+ const locale = this.getNodeParameter('locale', i, '');
837
+ result = await client.videos.draftNews({ topic, locale: locale || undefined });
838
+ }
839
+ else if (operation === 'topics') {
840
+ const parentTopic = this.getNodeParameter('parentTopic', i, '');
841
+ const numberOfTopics = this.getNodeParameter('numberOfTopics', i, 10);
842
+ const locale = this.getNodeParameter('locale', i, '');
843
+ const contentType = this.getNodeParameter('contentType', i, 'Custom');
844
+ result = await client.videos.generateTopics({
845
+ parent_topic: parentTopic || undefined,
846
+ number_of_topics: numberOfTopics || undefined,
847
+ locale: locale || undefined,
848
+ content_type: contentType || undefined
849
+ });
850
+ }
851
+ else if (operation === 'create') {
852
+ const connectionIds = splitCsv(this.getNodeParameter('connectionIds', i, ''));
853
+ const title = this.getNodeParameter('title', i);
854
+ const caption = this.getNodeParameter('caption', i);
855
+ const scenes = parseJson(this.getNodeParameter('scenes', i, ''));
856
+ const contentType = this.getNodeParameter('contentType', i, 'Custom');
857
+ const aspectRatio = this.getNodeParameter('aspectRatio', i, '9:16');
858
+ const imageStyleId = this.getNodeParameter('imageStyleId', i, '');
859
+ const voiceId = this.getNodeParameter('voiceId', i, '');
860
+ const soundtrackId = this.getNodeParameter('soundtrackId', i, '');
861
+ const publishAt = this.getNodeParameter('publishAt', i, '');
862
+ result = await client.videos.create({
863
+ connection_ids: connectionIds,
864
+ title,
865
+ caption,
866
+ scenes,
867
+ content_type: contentType || undefined,
868
+ aspect_ratio: aspectRatio || undefined,
869
+ image_style_id: imageStyleId || undefined,
870
+ voice_id: voiceId || undefined,
871
+ soundtrack_id: soundtrackId || undefined,
872
+ publish_at: publishAt || undefined
873
+ });
874
+ }
875
+ }
876
+ else if (resource === 'series') {
877
+ if (operation === 'list') {
878
+ const page = this.getNodeParameter('page', i, 0);
879
+ const limit = this.getNodeParameter('limit', i, 20);
880
+ result = await client.series.list({ page, limit });
881
+ }
882
+ else if (operation === 'get') {
883
+ const seriesId = this.getNodeParameter('seriesId', i);
884
+ result = await client.series.get(seriesId);
885
+ }
886
+ else if (operation === 'create') {
887
+ const connectionIds = splitCsv(this.getNodeParameter('connectionIds', i, ''));
888
+ const parentTopic = this.getNodeParameter('parentTopic', i, '');
889
+ const topicsCsv = splitCsv(this.getNodeParameter('topics', i, ''));
890
+ const contentType = this.getNodeParameter('contentType', i, 'Custom');
891
+ const aspectRatio = this.getNodeParameter('aspectRatio', i, '9:16');
892
+ const duration = this.getNodeParameter('duration', i, '60');
893
+ const voiceIds = splitCsv(this.getNodeParameter('voiceIds', i, ''));
894
+ const schedule = parseJson(this.getNodeParameter('schedule', i, ''));
895
+ result = await client.series.create({
896
+ connection_ids: connectionIds,
897
+ parent_topic: parentTopic || undefined,
898
+ // /series expects topics as objects ({ topic }) and duration as a number.
899
+ topics: topicsCsv.length > 0 ? topicsCsv.map(topic => ({ topic })) : undefined,
900
+ content_type: contentType || undefined,
901
+ aspect_ratio: aspectRatio || undefined,
902
+ duration: duration ? Number(duration) : undefined,
903
+ voice_ids: voiceIds.length > 0 ? voiceIds : undefined,
904
+ schedule: schedule || undefined
905
+ });
906
+ }
907
+ }
908
+ else if (resource === 'image') {
909
+ if (operation === 'list') {
910
+ const page = this.getNodeParameter('page', i, 0);
911
+ const limit = this.getNodeParameter('limit', i, 20);
912
+ result = await client.images.list({ page, limit });
913
+ }
914
+ else if (operation === 'get') {
915
+ const imageId = this.getNodeParameter('imageId', i);
916
+ result = await client.images.get(imageId);
917
+ }
918
+ else if (operation === 'styles') {
919
+ result = await client.images.styles();
920
+ }
921
+ else if (operation === 'create') {
922
+ const prompt = this.getNodeParameter('prompt', i);
923
+ const aspectRatio = this.getNodeParameter('aspectRatio', i);
924
+ const imageStyleId = this.getNodeParameter('imageStyleId', i, '');
925
+ const quality = this.getNodeParameter('quality', i, 'high');
926
+ const waitForGeneration = this.getNodeParameter('waitForGeneration', i, false);
927
+ result = await client.images.create({
928
+ prompt,
929
+ aspect_ratio: aspectRatio,
930
+ image_style_id: imageStyleId || undefined,
931
+ quality: quality || undefined,
932
+ wait_for_generation: waitForGeneration || undefined
933
+ });
934
+ }
935
+ }
936
+ else if (resource === 'audio') {
937
+ if (operation === 'list') {
938
+ const page = this.getNodeParameter('page', i, 0);
939
+ const limit = this.getNodeParameter('limit', i, 20);
940
+ result = await client.audio.list({ page, limit });
941
+ }
942
+ else if (operation === 'get') {
943
+ const audioId = this.getNodeParameter('audioId', i);
944
+ result = await client.audio.get(audioId);
945
+ }
946
+ else if (operation === 'speech') {
947
+ const text = this.getNodeParameter('text', i);
948
+ const voiceId = this.getNodeParameter('voiceId', i);
949
+ const locale = this.getNodeParameter('locale', i, '');
950
+ const waitForGeneration = this.getNodeParameter('waitForGeneration', i, false);
951
+ result = await client.audio.createSpeech({
952
+ text,
953
+ voice_id: voiceId,
954
+ locale: locale || undefined,
955
+ wait_for_generation: waitForGeneration || undefined
956
+ });
957
+ }
958
+ }
959
+ else if (resource === 'voice') {
960
+ if (operation === 'list') {
961
+ const locale = this.getNodeParameter('locale', i, '');
962
+ const page = this.getNodeParameter('page', i, 0);
963
+ const limit = this.getNodeParameter('limit', i, 20);
964
+ result = await client.audio.voices({ locale: locale || undefined, page, limit });
965
+ }
966
+ else if (operation === 'get') {
967
+ const voiceId = this.getNodeParameter('voiceId', i);
968
+ result = await client.audio.voice(voiceId);
969
+ }
970
+ }
971
+ else if (resource === 'music') {
972
+ if (operation === 'genres') {
973
+ result = await client.music.genres();
974
+ }
975
+ else if (operation === 'tracks') {
976
+ const genreId = this.getNodeParameter('genreId', i);
977
+ result = await client.music.tracks(genreId);
978
+ }
979
+ }
980
+ else if (resource === 'connection') {
981
+ if (operation === 'list') {
982
+ result = await client.connections.list();
983
+ }
984
+ }
985
+ else if (resource === 'credits') {
986
+ if (operation === 'get') {
987
+ result = await client.credits.get();
988
+ }
989
+ }
990
+ else if (resource === 'webhook') {
991
+ if (operation === 'list') {
992
+ result = await client.webhooks.listEndpoints();
993
+ }
994
+ else if (operation === 'create') {
995
+ const url = this.getNodeParameter('url', i);
996
+ const events = this.getNodeParameter('events', i);
997
+ const description = this.getNodeParameter('description', i, '');
998
+ result = await client.webhooks.createEndpoint({
999
+ url,
1000
+ events,
1001
+ description: description || undefined
1002
+ });
1003
+ }
1004
+ else if (operation === 'delete') {
1005
+ const endpointId = this.getNodeParameter('endpointId', i);
1006
+ await client.webhooks.deleteEndpoint(endpointId);
1007
+ result = { ok: true, deleted: endpointId };
1008
+ }
1009
+ }
1010
+ if (result === undefined) {
1011
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported resource/operation combination: ${resource}.${operation}`, { itemIndex: i });
1012
+ }
1013
+ // n8n expects each output item to be wrapped in { json: ... }.
1014
+ // Array-returning operations (and the paginated { videos } / { series }
1015
+ // / { images } / { audio } envelopes) are split into one item per row so
1016
+ // downstream nodes can iterate naturally. Cast through unknown because
1017
+ // n8n's IDataObject is stricter than Record<string, unknown> — upstream
1018
+ // SDK return types are intentionally loose.
1019
+ const push = (row) => {
1020
+ out.push({ json: row, pairedItem: { item: i } });
1021
+ };
1022
+ const envelopeKey = ['videos', 'series', 'images', 'audio'].find(k => result && typeof result === 'object' && Array.isArray(result[k]));
1023
+ if (Array.isArray(result)) {
1024
+ for (const row of result)
1025
+ push(row);
1026
+ }
1027
+ else if (envelopeKey) {
1028
+ for (const row of result[envelopeKey])
1029
+ push(row);
1030
+ }
1031
+ else {
1032
+ push(result);
1033
+ }
1034
+ }
1035
+ catch (err) {
1036
+ const message = err instanceof vendor_1.ShortGeniusError
1037
+ ? err.detail || err.message
1038
+ : err instanceof Error
1039
+ ? err.message
1040
+ : String(err);
1041
+ // Per-item Continue On Fail: emit the error as this item's output and
1042
+ // keep going, so prior items' results survive and later items still run.
1043
+ if (this.continueOnFail()) {
1044
+ out.push({
1045
+ json: { error: message, ...(err instanceof vendor_1.ShortGeniusError ? { status: err.status } : {}) },
1046
+ pairedItem: { item: i }
1047
+ });
1048
+ continue;
1049
+ }
1050
+ if (err instanceof vendor_1.ShortGeniusError) {
1051
+ // Vendored SDK HTTP error → n8n's convention is NodeApiError. Extract
1052
+ // the error fields into a JsonObject so workflow branches can read
1053
+ // status / message / request_id.
1054
+ throw new n8n_workflow_1.NodeApiError(this.getNode(), {
1055
+ status: err.status,
1056
+ message: err.detail || err.message,
1057
+ request_id: err.requestId
1058
+ }, {
1059
+ itemIndex: i,
1060
+ message,
1061
+ httpCode: err.status ? String(err.status) : undefined,
1062
+ description: `HTTP ${err.status}${err.requestId ? ` (request ${err.requestId})` : ''}`
1063
+ });
1064
+ }
1065
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), err, { itemIndex: i });
1066
+ }
1067
+ }
1068
+ return [out];
1069
+ }
1070
+ }
1071
+ exports.ShortGenius = ShortGenius;