n8n-nodes-commandos-image 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/nodes/CommandosImage.node.js +140 -24
- package/nodes/CommandosImage.node.ts +152 -23
- package/package.json +1 -1
|
@@ -15,10 +15,36 @@ const resolveGenerationUrl = (resolvedModel) => {
|
|
|
15
15
|
}
|
|
16
16
|
return GENERATION_URL_DEFAULT;
|
|
17
17
|
};
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Maps standard ratios to specific model requirements
|
|
20
|
+
*/
|
|
21
|
+
const mapRatio = (model, ratio) => {
|
|
22
|
+
if (model.includes('seedream')) {
|
|
23
|
+
if (ratio === '2:3')
|
|
24
|
+
return 'portrait_3_2';
|
|
25
|
+
if (ratio === '3:2')
|
|
26
|
+
return 'landscape_2_3';
|
|
27
|
+
}
|
|
28
|
+
return ratio;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Maps standard formats to specific model requirements (jpeg vs jpg)
|
|
32
|
+
*/
|
|
33
|
+
const mapFormat = (model, format) => {
|
|
34
|
+
const f = String(format || 'png').toLowerCase().trim();
|
|
35
|
+
if (model === 'nano-banana-pro') {
|
|
36
|
+
return (f === 'jpeg' || f === 'jpg') ? 'jpg' : f;
|
|
37
|
+
}
|
|
38
|
+
if (model.includes('google/nano-banana')) {
|
|
39
|
+
return (f === 'jpeg' || f === 'jpg') ? 'jpeg' : f;
|
|
40
|
+
}
|
|
41
|
+
return f;
|
|
42
|
+
};
|
|
43
|
+
const buildGenerationRequest = ({ model, prompt, ratio, references, outputFormat = 'png', quality = 'basic', resolution = '1K', }) => {
|
|
19
44
|
const hasReferences = references.length > 0;
|
|
20
45
|
const requestedModel = model;
|
|
21
46
|
let resolvedModel = model;
|
|
47
|
+
// Model Resolution
|
|
22
48
|
if (requestedModel === 'flux-pro') {
|
|
23
49
|
resolvedModel = hasReferences ? 'flux-2/pro-image-to-image' : 'flux-2/pro-text-to-image';
|
|
24
50
|
}
|
|
@@ -29,11 +55,15 @@ const buildGenerationRequest = ({ model, prompt, ratio, references }) => {
|
|
|
29
55
|
resolvedModel = 'nano-banana-pro';
|
|
30
56
|
}
|
|
31
57
|
else if (requestedModel === 'seedream') {
|
|
32
|
-
resolvedModel = '
|
|
58
|
+
resolvedModel = hasReferences ? 'seedream/4.5-edit' : 'seedream/4.5';
|
|
33
59
|
}
|
|
60
|
+
// Fallback for MJ or generic seedream
|
|
34
61
|
if (!hasReferences && (requestedModel === 'seedream' || requestedModel === 'midjourney')) {
|
|
35
|
-
if (requestedModel
|
|
36
|
-
|
|
62
|
+
if (requestedModel === 'seedream') {
|
|
63
|
+
// Handled above for 4.5
|
|
64
|
+
}
|
|
65
|
+
else if (requestedModel === 'midjourney') {
|
|
66
|
+
resolvedModel = 'midjourney';
|
|
37
67
|
}
|
|
38
68
|
}
|
|
39
69
|
let body = {};
|
|
@@ -42,7 +72,7 @@ const buildGenerationRequest = ({ model, prompt, ratio, references }) => {
|
|
|
42
72
|
filesUrl: references,
|
|
43
73
|
prompt,
|
|
44
74
|
size: ratio,
|
|
45
|
-
callBackUrl: 'https://
|
|
75
|
+
callBackUrl: 'https://api.comandos.ai/internal/kie/callback', // Fixed to internal
|
|
46
76
|
fallbackModel: 'FLUX_MAX',
|
|
47
77
|
};
|
|
48
78
|
}
|
|
@@ -52,19 +82,30 @@ const buildGenerationRequest = ({ model, prompt, ratio, references }) => {
|
|
|
52
82
|
input: {
|
|
53
83
|
prompt,
|
|
54
84
|
aspect_ratio: ratio,
|
|
55
|
-
resolution
|
|
85
|
+
resolution,
|
|
56
86
|
...(hasReferences ? { input_urls: references } : {}),
|
|
57
87
|
},
|
|
58
88
|
};
|
|
59
89
|
}
|
|
90
|
+
else if (resolvedModel === 'seedream/4.5' || resolvedModel === 'seedream/4.5-edit') {
|
|
91
|
+
body = {
|
|
92
|
+
model: resolvedModel,
|
|
93
|
+
input: {
|
|
94
|
+
prompt,
|
|
95
|
+
aspect_ratio: mapRatio(resolvedModel, ratio),
|
|
96
|
+
quality,
|
|
97
|
+
...(hasReferences ? { image_urls: references } : {}),
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
60
101
|
else if (resolvedModel === 'nano-banana-pro') {
|
|
61
102
|
body = {
|
|
62
103
|
model: resolvedModel,
|
|
63
104
|
input: {
|
|
64
105
|
prompt,
|
|
65
106
|
aspect_ratio: ratio,
|
|
66
|
-
resolution
|
|
67
|
-
output_format:
|
|
107
|
+
resolution,
|
|
108
|
+
output_format: mapFormat(resolvedModel, outputFormat),
|
|
68
109
|
...(hasReferences ? { image_input: references } : {}),
|
|
69
110
|
},
|
|
70
111
|
};
|
|
@@ -75,7 +116,7 @@ const buildGenerationRequest = ({ model, prompt, ratio, references }) => {
|
|
|
75
116
|
input: {
|
|
76
117
|
prompt,
|
|
77
118
|
image_size: ratio,
|
|
78
|
-
output_format:
|
|
119
|
+
output_format: mapFormat(resolvedModel, outputFormat),
|
|
79
120
|
...(hasReferences ? { image_urls: references } : {}),
|
|
80
121
|
},
|
|
81
122
|
};
|
|
@@ -91,13 +132,13 @@ const buildGenerationRequest = ({ model, prompt, ratio, references }) => {
|
|
|
91
132
|
};
|
|
92
133
|
}
|
|
93
134
|
else {
|
|
135
|
+
// Default fallback (v4 edit style)
|
|
94
136
|
body = {
|
|
95
|
-
model: 'bytedance/seedream-v4-edit',
|
|
137
|
+
model: requestedModel === 'gpt4o-image' ? 'gpt-4o' : 'bytedance/seedream-v4-edit',
|
|
96
138
|
input: {
|
|
97
139
|
image_urls: references,
|
|
98
140
|
prompt,
|
|
99
|
-
image_size:
|
|
100
|
-
image_resolution: '1K',
|
|
141
|
+
image_size: mapRatio('seedream', ratio),
|
|
101
142
|
},
|
|
102
143
|
};
|
|
103
144
|
}
|
|
@@ -111,16 +152,24 @@ const buildGenerationRequest = ({ model, prompt, ratio, references }) => {
|
|
|
111
152
|
hasReferences,
|
|
112
153
|
};
|
|
113
154
|
};
|
|
114
|
-
const extractReferences = (raw) => {
|
|
155
|
+
const extractReferences = (raw, model) => {
|
|
115
156
|
if (!raw || typeof raw !== 'object') {
|
|
116
157
|
return [];
|
|
117
158
|
}
|
|
118
159
|
const collection = raw;
|
|
119
160
|
const items = Array.isArray(collection.reference) ? collection.reference : [];
|
|
161
|
+
// Dynamic limit based on model docs
|
|
162
|
+
let limit = 8;
|
|
163
|
+
if (model === 'seedream')
|
|
164
|
+
limit = 14;
|
|
165
|
+
if (model === 'gpt4o-image')
|
|
166
|
+
limit = 16;
|
|
167
|
+
if (model.includes('nano-banana'))
|
|
168
|
+
limit = 10;
|
|
120
169
|
return items
|
|
121
170
|
.map((entry) => String((entry === null || entry === void 0 ? void 0 : entry.url) || '').trim())
|
|
122
171
|
.filter((value) => value.length > 0 && /^https?:\/\//i.test(value))
|
|
123
|
-
.slice(0,
|
|
172
|
+
.slice(0, limit);
|
|
124
173
|
};
|
|
125
174
|
class CommandosImage {
|
|
126
175
|
constructor() {
|
|
@@ -165,14 +214,14 @@ class CommandosImage {
|
|
|
165
214
|
name: 'model',
|
|
166
215
|
type: 'options',
|
|
167
216
|
options: [
|
|
217
|
+
{ name: 'Seedream 4.5', value: 'seedream' },
|
|
168
218
|
{ name: 'Flux Pro', value: 'flux-pro' },
|
|
169
219
|
{ name: 'GPT-4o Image', value: 'gpt4o-image' },
|
|
170
220
|
{ name: 'Nano Banana', value: 'nano-banana' },
|
|
171
221
|
{ name: 'Nano Banana Pro', value: 'nano-banana-pro' },
|
|
172
|
-
{ name: 'Seedream', value: 'seedream' },
|
|
173
222
|
{ name: 'Midjourney', value: 'midjourney' },
|
|
174
223
|
],
|
|
175
|
-
default: '
|
|
224
|
+
default: 'seedream',
|
|
176
225
|
displayOptions: {
|
|
177
226
|
show: {
|
|
178
227
|
operation: ['create'],
|
|
@@ -204,6 +253,9 @@ class CommandosImage {
|
|
|
204
253
|
{ name: '4:5', value: '4:5' },
|
|
205
254
|
{ name: '16:9', value: '16:9' },
|
|
206
255
|
{ name: '9:16', value: '9:16' },
|
|
256
|
+
{ name: '4:3', value: '4:3' },
|
|
257
|
+
{ name: '3:4', value: '3:4' },
|
|
258
|
+
{ name: '21:9', value: '21:9' },
|
|
207
259
|
],
|
|
208
260
|
default: '2:3',
|
|
209
261
|
displayOptions: {
|
|
@@ -212,6 +264,56 @@ class CommandosImage {
|
|
|
212
264
|
},
|
|
213
265
|
},
|
|
214
266
|
},
|
|
267
|
+
{
|
|
268
|
+
displayName: 'Output Format',
|
|
269
|
+
name: 'outputFormat',
|
|
270
|
+
type: 'options',
|
|
271
|
+
options: [
|
|
272
|
+
{ name: 'PNG', value: 'png' },
|
|
273
|
+
{ name: 'JPEG', value: 'jpeg' },
|
|
274
|
+
],
|
|
275
|
+
default: 'png',
|
|
276
|
+
displayOptions: {
|
|
277
|
+
show: {
|
|
278
|
+
operation: ['create'],
|
|
279
|
+
model: ['nano-banana', 'nano-banana-pro'],
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
displayName: 'Quality',
|
|
285
|
+
name: 'quality',
|
|
286
|
+
type: 'options',
|
|
287
|
+
options: [
|
|
288
|
+
{ name: 'Basic (2K)', value: 'basic' },
|
|
289
|
+
{ name: 'High (4K)', value: 'high' },
|
|
290
|
+
{ name: 'Medium (Balanced)', value: 'medium' },
|
|
291
|
+
],
|
|
292
|
+
default: 'basic',
|
|
293
|
+
displayOptions: {
|
|
294
|
+
show: {
|
|
295
|
+
operation: ['create'],
|
|
296
|
+
model: ['seedream', 'gpt4o-image', 'flux-pro'],
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
displayName: 'Resolution',
|
|
302
|
+
name: 'resolution',
|
|
303
|
+
type: 'options',
|
|
304
|
+
options: [
|
|
305
|
+
{ name: '1K', value: '1K' },
|
|
306
|
+
{ name: '2K', value: '2K' },
|
|
307
|
+
{ name: '4K', value: '4K' },
|
|
308
|
+
],
|
|
309
|
+
default: '1K',
|
|
310
|
+
displayOptions: {
|
|
311
|
+
show: {
|
|
312
|
+
operation: ['create'],
|
|
313
|
+
model: ['flux-pro', 'nano-banana-pro'],
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
},
|
|
215
317
|
{
|
|
216
318
|
displayName: 'References',
|
|
217
319
|
name: 'references',
|
|
@@ -269,11 +371,29 @@ class CommandosImage {
|
|
|
269
371
|
const model = String(this.getNodeParameter('model', i));
|
|
270
372
|
const prompt = String(this.getNodeParameter('prompt', i) || '').trim();
|
|
271
373
|
const ratio = String(this.getNodeParameter('ratio', i));
|
|
272
|
-
const references = extractReferences(this.getNodeParameter('references', i, {}));
|
|
374
|
+
const references = extractReferences(this.getNodeParameter('references', i, {}), model);
|
|
375
|
+
// Get optional parameters safely
|
|
376
|
+
const outputFormat = ['nano-banana', 'nano-banana-pro'].includes(model)
|
|
377
|
+
? String(this.getNodeParameter('outputFormat', i, 'png'))
|
|
378
|
+
: 'png';
|
|
379
|
+
const quality = ['seedream', 'gpt4o-image', 'flux-pro'].includes(model)
|
|
380
|
+
? String(this.getNodeParameter('quality', i, 'basic'))
|
|
381
|
+
: 'basic';
|
|
382
|
+
const resolution = ['flux-pro', 'nano-banana-pro'].includes(model)
|
|
383
|
+
? String(this.getNodeParameter('resolution', i, '1K'))
|
|
384
|
+
: '1K';
|
|
273
385
|
if (!prompt) {
|
|
274
386
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Prompt is required', { itemIndex: i });
|
|
275
387
|
}
|
|
276
|
-
const request = buildGenerationRequest({
|
|
388
|
+
const request = buildGenerationRequest({
|
|
389
|
+
model,
|
|
390
|
+
prompt,
|
|
391
|
+
ratio,
|
|
392
|
+
references,
|
|
393
|
+
outputFormat,
|
|
394
|
+
quality,
|
|
395
|
+
resolution
|
|
396
|
+
});
|
|
277
397
|
if (!request.url) {
|
|
278
398
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'URL генерации не настроен. Проверь переменные окружения COMMANDOS_IMAGE_URL_DEFAULT/COMMANDOS_IMAGE_URL_GPT4O/COMMANDOS_IMAGE_URL_MJ', { itemIndex: i });
|
|
279
399
|
}
|
|
@@ -316,17 +436,13 @@ class CommandosImage {
|
|
|
316
436
|
});
|
|
317
437
|
results.push({ json: response });
|
|
318
438
|
}
|
|
319
|
-
else {
|
|
320
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation: ${operation}`, {
|
|
321
|
-
itemIndex: i,
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
439
|
}
|
|
325
440
|
catch (error) {
|
|
326
441
|
if (error instanceof n8n_workflow_1.NodeOperationError) {
|
|
327
442
|
throw error;
|
|
328
443
|
}
|
|
329
|
-
|
|
444
|
+
const message = error instanceof Error ? error.message : 'Request failed';
|
|
445
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), message, { itemIndex: i });
|
|
330
446
|
}
|
|
331
447
|
}
|
|
332
448
|
return [results];
|
|
@@ -17,6 +17,9 @@ type BuildParams = {
|
|
|
17
17
|
prompt: string;
|
|
18
18
|
ratio: string;
|
|
19
19
|
references: string[];
|
|
20
|
+
outputFormat?: string;
|
|
21
|
+
quality?: string;
|
|
22
|
+
resolution?: string;
|
|
20
23
|
};
|
|
21
24
|
|
|
22
25
|
type BuildResult = {
|
|
@@ -39,11 +42,45 @@ const resolveGenerationUrl = (resolvedModel: string): string => {
|
|
|
39
42
|
return GENERATION_URL_DEFAULT;
|
|
40
43
|
};
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Maps standard ratios to specific model requirements
|
|
47
|
+
*/
|
|
48
|
+
const mapRatio = (model: string, ratio: string): string => {
|
|
49
|
+
if (model.includes('seedream')) {
|
|
50
|
+
if (ratio === '2:3') return 'portrait_3_2';
|
|
51
|
+
if (ratio === '3:2') return 'landscape_2_3';
|
|
52
|
+
}
|
|
53
|
+
return ratio;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Maps standard formats to specific model requirements (jpeg vs jpg)
|
|
58
|
+
*/
|
|
59
|
+
const mapFormat = (model: string, format: string): string => {
|
|
60
|
+
const f = String(format || 'png').toLowerCase().trim();
|
|
61
|
+
if (model === 'nano-banana-pro') {
|
|
62
|
+
return (f === 'jpeg' || f === 'jpg') ? 'jpg' : f;
|
|
63
|
+
}
|
|
64
|
+
if (model.includes('google/nano-banana')) {
|
|
65
|
+
return (f === 'jpeg' || f === 'jpg') ? 'jpeg' : f;
|
|
66
|
+
}
|
|
67
|
+
return f;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const buildGenerationRequest = ({
|
|
71
|
+
model,
|
|
72
|
+
prompt,
|
|
73
|
+
ratio,
|
|
74
|
+
references,
|
|
75
|
+
outputFormat = 'png',
|
|
76
|
+
quality = 'basic',
|
|
77
|
+
resolution = '1K',
|
|
78
|
+
}: BuildParams): BuildResult => {
|
|
43
79
|
const hasReferences = references.length > 0;
|
|
44
80
|
const requestedModel = model;
|
|
45
81
|
let resolvedModel = model;
|
|
46
82
|
|
|
83
|
+
// Model Resolution
|
|
47
84
|
if (requestedModel === 'flux-pro') {
|
|
48
85
|
resolvedModel = hasReferences ? 'flux-2/pro-image-to-image' : 'flux-2/pro-text-to-image';
|
|
49
86
|
} else if (requestedModel === 'nano-banana') {
|
|
@@ -51,12 +88,15 @@ const buildGenerationRequest = ({ model, prompt, ratio, references }: BuildParam
|
|
|
51
88
|
} else if (requestedModel === 'nano-banana-pro') {
|
|
52
89
|
resolvedModel = 'nano-banana-pro';
|
|
53
90
|
} else if (requestedModel === 'seedream') {
|
|
54
|
-
resolvedModel = '
|
|
91
|
+
resolvedModel = hasReferences ? 'seedream/4.5-edit' : 'seedream/4.5';
|
|
55
92
|
}
|
|
56
93
|
|
|
94
|
+
// Fallback for MJ or generic seedream
|
|
57
95
|
if (!hasReferences && (requestedModel === 'seedream' || requestedModel === 'midjourney')) {
|
|
58
|
-
if (requestedModel
|
|
59
|
-
|
|
96
|
+
if (requestedModel === 'seedream') {
|
|
97
|
+
// Handled above for 4.5
|
|
98
|
+
} else if (requestedModel === 'midjourney') {
|
|
99
|
+
resolvedModel = 'midjourney';
|
|
60
100
|
}
|
|
61
101
|
}
|
|
62
102
|
|
|
@@ -67,7 +107,7 @@ const buildGenerationRequest = ({ model, prompt, ratio, references }: BuildParam
|
|
|
67
107
|
filesUrl: references,
|
|
68
108
|
prompt,
|
|
69
109
|
size: ratio,
|
|
70
|
-
callBackUrl: 'https://
|
|
110
|
+
callBackUrl: 'https://api.comandos.ai/internal/kie/callback', // Fixed to internal
|
|
71
111
|
fallbackModel: 'FLUX_MAX',
|
|
72
112
|
};
|
|
73
113
|
} else if (resolvedModel.includes('flux-2/')) {
|
|
@@ -76,18 +116,28 @@ const buildGenerationRequest = ({ model, prompt, ratio, references }: BuildParam
|
|
|
76
116
|
input: {
|
|
77
117
|
prompt,
|
|
78
118
|
aspect_ratio: ratio,
|
|
79
|
-
resolution
|
|
119
|
+
resolution,
|
|
80
120
|
...(hasReferences ? { input_urls: references } : {}),
|
|
81
121
|
},
|
|
82
122
|
};
|
|
123
|
+
} else if (resolvedModel === 'seedream/4.5' || resolvedModel === 'seedream/4.5-edit') {
|
|
124
|
+
body = {
|
|
125
|
+
model: resolvedModel,
|
|
126
|
+
input: {
|
|
127
|
+
prompt,
|
|
128
|
+
aspect_ratio: mapRatio(resolvedModel, ratio),
|
|
129
|
+
quality,
|
|
130
|
+
...(hasReferences ? { image_urls: references } : {}),
|
|
131
|
+
},
|
|
132
|
+
};
|
|
83
133
|
} else if (resolvedModel === 'nano-banana-pro') {
|
|
84
134
|
body = {
|
|
85
135
|
model: resolvedModel,
|
|
86
136
|
input: {
|
|
87
137
|
prompt,
|
|
88
138
|
aspect_ratio: ratio,
|
|
89
|
-
resolution
|
|
90
|
-
output_format:
|
|
139
|
+
resolution,
|
|
140
|
+
output_format: mapFormat(resolvedModel, outputFormat),
|
|
91
141
|
...(hasReferences ? { image_input: references } : {}),
|
|
92
142
|
},
|
|
93
143
|
};
|
|
@@ -97,7 +147,7 @@ const buildGenerationRequest = ({ model, prompt, ratio, references }: BuildParam
|
|
|
97
147
|
input: {
|
|
98
148
|
prompt,
|
|
99
149
|
image_size: ratio,
|
|
100
|
-
output_format:
|
|
150
|
+
output_format: mapFormat(resolvedModel, outputFormat),
|
|
101
151
|
...(hasReferences ? { image_urls: references } : {}),
|
|
102
152
|
},
|
|
103
153
|
};
|
|
@@ -111,13 +161,13 @@ const buildGenerationRequest = ({ model, prompt, ratio, references }: BuildParam
|
|
|
111
161
|
enableTranslation: true,
|
|
112
162
|
};
|
|
113
163
|
} else {
|
|
164
|
+
// Default fallback (v4 edit style)
|
|
114
165
|
body = {
|
|
115
|
-
model: 'bytedance/seedream-v4-edit',
|
|
166
|
+
model: requestedModel === 'gpt4o-image' ? 'gpt-4o' : 'bytedance/seedream-v4-edit',
|
|
116
167
|
input: {
|
|
117
168
|
image_urls: references,
|
|
118
169
|
prompt,
|
|
119
|
-
image_size:
|
|
120
|
-
image_resolution: '1K',
|
|
170
|
+
image_size: mapRatio('seedream', ratio),
|
|
121
171
|
},
|
|
122
172
|
};
|
|
123
173
|
}
|
|
@@ -133,16 +183,23 @@ const buildGenerationRequest = ({ model, prompt, ratio, references }: BuildParam
|
|
|
133
183
|
};
|
|
134
184
|
};
|
|
135
185
|
|
|
136
|
-
const extractReferences = (raw: unknown): string[] => {
|
|
186
|
+
const extractReferences = (raw: unknown, model: string): string[] => {
|
|
137
187
|
if (!raw || typeof raw !== 'object') {
|
|
138
188
|
return [];
|
|
139
189
|
}
|
|
140
190
|
const collection = raw as { reference?: Array<{ url?: string }> };
|
|
141
191
|
const items = Array.isArray(collection.reference) ? collection.reference : [];
|
|
192
|
+
|
|
193
|
+
// Dynamic limit based on model docs
|
|
194
|
+
let limit = 8;
|
|
195
|
+
if (model === 'seedream') limit = 14;
|
|
196
|
+
if (model === 'gpt4o-image') limit = 16;
|
|
197
|
+
if (model.includes('nano-banana')) limit = 10;
|
|
198
|
+
|
|
142
199
|
return items
|
|
143
200
|
.map((entry) => String(entry?.url || '').trim())
|
|
144
201
|
.filter((value) => value.length > 0 && /^https?:\/\//i.test(value))
|
|
145
|
-
.slice(0,
|
|
202
|
+
.slice(0, limit);
|
|
146
203
|
};
|
|
147
204
|
|
|
148
205
|
export class CommandosImage implements INodeType {
|
|
@@ -187,14 +244,14 @@ export class CommandosImage implements INodeType {
|
|
|
187
244
|
name: 'model',
|
|
188
245
|
type: 'options',
|
|
189
246
|
options: [
|
|
247
|
+
{ name: 'Seedream 4.5', value: 'seedream' },
|
|
190
248
|
{ name: 'Flux Pro', value: 'flux-pro' },
|
|
191
249
|
{ name: 'GPT-4o Image', value: 'gpt4o-image' },
|
|
192
250
|
{ name: 'Nano Banana', value: 'nano-banana' },
|
|
193
251
|
{ name: 'Nano Banana Pro', value: 'nano-banana-pro' },
|
|
194
|
-
{ name: 'Seedream', value: 'seedream' },
|
|
195
252
|
{ name: 'Midjourney', value: 'midjourney' },
|
|
196
253
|
],
|
|
197
|
-
default: '
|
|
254
|
+
default: 'seedream',
|
|
198
255
|
displayOptions: {
|
|
199
256
|
show: {
|
|
200
257
|
operation: ['create'],
|
|
@@ -226,6 +283,9 @@ export class CommandosImage implements INodeType {
|
|
|
226
283
|
{ name: '4:5', value: '4:5' },
|
|
227
284
|
{ name: '16:9', value: '16:9' },
|
|
228
285
|
{ name: '9:16', value: '9:16' },
|
|
286
|
+
{ name: '4:3', value: '4:3' },
|
|
287
|
+
{ name: '3:4', value: '3:4' },
|
|
288
|
+
{ name: '21:9', value: '21:9' },
|
|
229
289
|
],
|
|
230
290
|
default: '2:3',
|
|
231
291
|
displayOptions: {
|
|
@@ -234,6 +294,56 @@ export class CommandosImage implements INodeType {
|
|
|
234
294
|
},
|
|
235
295
|
},
|
|
236
296
|
},
|
|
297
|
+
{
|
|
298
|
+
displayName: 'Output Format',
|
|
299
|
+
name: 'outputFormat',
|
|
300
|
+
type: 'options',
|
|
301
|
+
options: [
|
|
302
|
+
{ name: 'PNG', value: 'png' },
|
|
303
|
+
{ name: 'JPEG', value: 'jpeg' },
|
|
304
|
+
],
|
|
305
|
+
default: 'png',
|
|
306
|
+
displayOptions: {
|
|
307
|
+
show: {
|
|
308
|
+
operation: ['create'],
|
|
309
|
+
model: ['nano-banana', 'nano-banana-pro'],
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
displayName: 'Quality',
|
|
315
|
+
name: 'quality',
|
|
316
|
+
type: 'options',
|
|
317
|
+
options: [
|
|
318
|
+
{ name: 'Basic (2K)', value: 'basic' },
|
|
319
|
+
{ name: 'High (4K)', value: 'high' },
|
|
320
|
+
{ name: 'Medium (Balanced)', value: 'medium' },
|
|
321
|
+
],
|
|
322
|
+
default: 'basic',
|
|
323
|
+
displayOptions: {
|
|
324
|
+
show: {
|
|
325
|
+
operation: ['create'],
|
|
326
|
+
model: ['seedream', 'gpt4o-image', 'flux-pro'],
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
displayName: 'Resolution',
|
|
332
|
+
name: 'resolution',
|
|
333
|
+
type: 'options',
|
|
334
|
+
options: [
|
|
335
|
+
{ name: '1K', value: '1K' },
|
|
336
|
+
{ name: '2K', value: '2K' },
|
|
337
|
+
{ name: '4K', value: '4K' },
|
|
338
|
+
],
|
|
339
|
+
default: '1K',
|
|
340
|
+
displayOptions: {
|
|
341
|
+
show: {
|
|
342
|
+
operation: ['create'],
|
|
343
|
+
model: ['flux-pro', 'nano-banana-pro'],
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
},
|
|
237
347
|
{
|
|
238
348
|
displayName: 'References',
|
|
239
349
|
name: 'references',
|
|
@@ -294,13 +404,35 @@ export class CommandosImage implements INodeType {
|
|
|
294
404
|
const model = String(this.getNodeParameter('model', i));
|
|
295
405
|
const prompt = String(this.getNodeParameter('prompt', i) || '').trim();
|
|
296
406
|
const ratio = String(this.getNodeParameter('ratio', i));
|
|
297
|
-
const references = extractReferences(this.getNodeParameter('references', i, {}));
|
|
407
|
+
const references = extractReferences(this.getNodeParameter('references', i, {}), model);
|
|
408
|
+
|
|
409
|
+
// Get optional parameters safely
|
|
410
|
+
const outputFormat = ['nano-banana', 'nano-banana-pro'].includes(model)
|
|
411
|
+
? String(this.getNodeParameter('outputFormat', i, 'png'))
|
|
412
|
+
: 'png';
|
|
413
|
+
|
|
414
|
+
const quality = ['seedream', 'gpt4o-image', 'flux-pro'].includes(model)
|
|
415
|
+
? String(this.getNodeParameter('quality', i, 'basic'))
|
|
416
|
+
: 'basic';
|
|
417
|
+
|
|
418
|
+
const resolution = ['flux-pro', 'nano-banana-pro'].includes(model)
|
|
419
|
+
? String(this.getNodeParameter('resolution', i, '1K'))
|
|
420
|
+
: '1K';
|
|
298
421
|
|
|
299
422
|
if (!prompt) {
|
|
300
423
|
throw new NodeOperationError(this.getNode(), 'Prompt is required', { itemIndex: i });
|
|
301
424
|
}
|
|
302
425
|
|
|
303
|
-
const request = buildGenerationRequest({
|
|
426
|
+
const request = buildGenerationRequest({
|
|
427
|
+
model,
|
|
428
|
+
prompt,
|
|
429
|
+
ratio,
|
|
430
|
+
references,
|
|
431
|
+
outputFormat,
|
|
432
|
+
quality,
|
|
433
|
+
resolution
|
|
434
|
+
});
|
|
435
|
+
|
|
304
436
|
if (!request.url) {
|
|
305
437
|
throw new NodeOperationError(
|
|
306
438
|
this.getNode(),
|
|
@@ -350,16 +482,13 @@ export class CommandosImage implements INodeType {
|
|
|
350
482
|
});
|
|
351
483
|
|
|
352
484
|
results.push({ json: response as IDataObject });
|
|
353
|
-
} else {
|
|
354
|
-
throw new NodeOperationError(this.getNode(), `Unknown operation: ${operation}`, {
|
|
355
|
-
itemIndex: i,
|
|
356
|
-
});
|
|
357
485
|
}
|
|
358
486
|
} catch (error) {
|
|
359
487
|
if (error instanceof NodeOperationError) {
|
|
360
488
|
throw error;
|
|
361
489
|
}
|
|
362
|
-
|
|
490
|
+
const message = error instanceof Error ? error.message : 'Request failed';
|
|
491
|
+
throw new NodeOperationError(this.getNode(), message, { itemIndex: i });
|
|
363
492
|
}
|
|
364
493
|
}
|
|
365
494
|
|