n8n-nodes-userapi 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/dist/credentials/UserApi.credentials.d.ts +4 -0
- package/dist/credentials/UserApi.credentials.js +2 -1
- package/dist/credentials/userapi.dark.svg +9 -0
- package/dist/credentials/userapi.svg +9 -0
- package/dist/nodes/UserApi/UserApi.node.js +412 -424
- package/dist/nodes/UserApi/UserApi.node.json +27 -0
- package/dist/nodes/UserApi/userapi.dark.svg +9 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -14,14 +14,14 @@ Official resources:
|
|
|
14
14
|
## Features
|
|
15
15
|
|
|
16
16
|
- Supports all currently available UserApi.Ai methods in this node package.
|
|
17
|
-
- ✨ [Imagine](https://userapi.ai/
|
|
18
|
-
- ⬆️ [Upscale](https://userapi.ai/
|
|
19
|
-
- 🔄 [Variation](https://userapi.ai/
|
|
20
|
-
- 🔀 [Blend](https://userapi.ai/
|
|
21
|
-
- 📝 [Describe](https://userapi.ai/
|
|
22
|
-
- 🎬 [Animate](https://userapi.ai/
|
|
23
|
-
- 🖌️ [Inpaint](https://userapi.ai/
|
|
24
|
-
- And more: [See full docs](https://userapi.ai/
|
|
17
|
+
- ✨ [Imagine](https://userapi.ai/docs#imagine)
|
|
18
|
+
- ⬆️ [Upscale](https://userapi.ai/docs#upscale)
|
|
19
|
+
- 🔄 [Variation](https://userapi.ai/docs#variation)
|
|
20
|
+
- 🔀 [Blend](https://userapi.ai/docs#blend)
|
|
21
|
+
- 📝 [Describe](https://userapi.ai/docs#describe)
|
|
22
|
+
- 🎬 [Animate](https://userapi.ai/docs#animate)
|
|
23
|
+
- 🖌️ [Inpaint](https://userapi.ai/docs#inpaint)
|
|
24
|
+
- And more: [See full docs](https://userapi.ai/docs)
|
|
25
25
|
|
|
26
26
|
## Installation
|
|
27
27
|
|
|
@@ -2,6 +2,10 @@ import type { ICredentialType, INodeProperties, IAuthenticateGeneric, ICredentia
|
|
|
2
2
|
export declare class UserApi implements ICredentialType {
|
|
3
3
|
name: string;
|
|
4
4
|
displayName: string;
|
|
5
|
+
icon: {
|
|
6
|
+
readonly light: "file:userapi.svg";
|
|
7
|
+
readonly dark: "file:userapi.dark.svg";
|
|
8
|
+
};
|
|
5
9
|
documentationUrl: string;
|
|
6
10
|
authenticate: IAuthenticateGeneric;
|
|
7
11
|
properties: INodeProperties[];
|
|
@@ -4,7 +4,8 @@ exports.UserApi = void 0;
|
|
|
4
4
|
class UserApi {
|
|
5
5
|
constructor() {
|
|
6
6
|
this.name = 'userApi';
|
|
7
|
-
this.displayName = 'UserAPI';
|
|
7
|
+
this.displayName = 'UserAPI API';
|
|
8
|
+
this.icon = { light: 'file:userapi.svg', dark: 'file:userapi.dark.svg' };
|
|
8
9
|
this.documentationUrl = 'https://userapi.ai/first-steps';
|
|
9
10
|
this.authenticate = {
|
|
10
11
|
type: 'generic',
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<svg version="1.0" width="248.000000pt" height="248.000000pt" viewBox="0 0 248.000000 248.000000" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<g transform="translate(0.000000,248.000000) scale(0.100000,-0.100000)" fill="#000000" stroke="none">
|
|
4
|
+
<path d="M1967 1973 c-15 -2 -43 -20 -62 -39 -91 -90 -30 -236 99 -236 90 0 141 49 141 137 0 97 -74 155 -178 138z" style="paint-order: fill; fill: rgb(244, 114, 182);"/>
|
|
5
|
+
<path d="M 1065 1585 C 989 1568 946 1550 893 1511 C 848 1478 785 1393 775 1350 C 769 1326 773 1323 857 1296 C 905 1280 950 1265 956 1263 C 962 1261 970 1272 973 1289 C 982 1334 1047 1396 1100 1410 C 1193 1435 1308 1404 1342 1345 C 1352 1327 1361 1288 1362 1255 L 1365 1195 L 1180 1189 C 986 1184 920 1172 859 1134 C 771 1079 731 1006 731 895 C 730 714 865 600 1081 600 C 1210 600 1293 638 1345 721 C 1374 767 1388 764 1415 707 C 1446 643 1482 627 1603 626 L 1705 625 L 1708 713 L 1711 800 L 1661 800 C 1580 800 1580 800 1580 1038 C 1580 1152 1575 1274 1569 1310 C 1558 1380 1522 1454 1481 1494 C 1436 1537 1341.502 1588.421 1258.502 1599.421 C 1162.502 1612.421 1141 1602 1065 1585 Z M 1363 990 C 1357 914 1349 893 1304 848 C 1200 742 1008 748 954 858 C 922 925 964 1000 1045 1019 C 1067 1024 1148 1028 1225 1027 L 1365 1025 L 1363 990 Z" style="paint-order: fill; fill: rgb(244, 114, 182);"/>
|
|
6
|
+
<path d="M1902 1098 l3 -473 105 0 105 0 0 470 0 470 -108 3 -107 3 2 -473z" style="paint-order: fill; fill: rgb(244, 114, 182);"/>
|
|
7
|
+
<path d="M 292 913 C 241 888 205.626 845 201.626 773 C 197.626 719 211 704 231 674 C 276 607 381 579 450 615 C 549 666 568 816 484 889 C 465 906 445 918 440 915 C 435 912 429 914 425 920 C 415 936 329 931 292 913 Z" style="paint-order: fill; fill: rgb(244, 114, 182);"/>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<svg version="1.0" width="248.000000pt" height="248.000000pt" viewBox="0 0 248.000000 248.000000" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<g transform="translate(0.000000,248.000000) scale(0.100000,-0.100000)" fill="#000000" stroke="none">
|
|
4
|
+
<path d="M1967 1973 c-15 -2 -43 -20 -62 -39 -91 -90 -30 -236 99 -236 90 0 141 49 141 137 0 97 -74 155 -178 138z" style="paint-order: fill; fill: rgb(236, 72, 153);"/>
|
|
5
|
+
<path d="M 1065 1585 C 989 1568 946 1550 893 1511 C 848 1478 785 1393 775 1350 C 769 1326 773 1323 857 1296 C 905 1280 950 1265 956 1263 C 962 1261 970 1272 973 1289 C 982 1334 1047 1396 1100 1410 C 1193 1435 1308 1404 1342 1345 C 1352 1327 1361 1288 1362 1255 L 1365 1195 L 1180 1189 C 986 1184 920 1172 859 1134 C 771 1079 731 1006 731 895 C 730 714 865 600 1081 600 C 1210 600 1293 638 1345 721 C 1374 767 1388 764 1415 707 C 1446 643 1482 627 1603 626 L 1705 625 L 1708 713 L 1711 800 L 1661 800 C 1580 800 1580 800 1580 1038 C 1580 1152 1575 1274 1569 1310 C 1558 1380 1522 1454 1481 1494 C 1436 1537 1341.502 1588.421 1258.502 1599.421 C 1162.502 1612.421 1141 1602 1065 1585 Z M 1363 990 C 1357 914 1349 893 1304 848 C 1200 742 1008 748 954 858 C 922 925 964 1000 1045 1019 C 1067 1024 1148 1028 1225 1027 L 1365 1025 L 1363 990 Z" style="paint-order: fill; fill: rgb(236, 72, 153);"/>
|
|
6
|
+
<path d="M1902 1098 l3 -473 105 0 105 0 0 470 0 470 -108 3 -107 3 2 -473z" style="paint-order: fill; fill: rgb(236, 72, 153);"/>
|
|
7
|
+
<path d="M 292 913 C 241 888 205.626 845 201.626 773 C 197.626 719 211 704 231 674 C 276 607 381 579 450 615 C 549 666 568 816 484 889 C 465 906 445 918 440 915 C 435 912 429 914 425 920 C 415 936 329 931 292 913 Z" style="paint-order: fill; fill: rgb(236, 72, 153);"/>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
|
@@ -2,469 +2,446 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.UserApi = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
-
const webhookTypeOptions = [
|
|
6
|
-
{ name: 'Progress', value: 'progress' },
|
|
7
|
-
{ name: 'Result', value: 'result' },
|
|
8
|
-
];
|
|
9
5
|
const API_BASE_URL = 'https://api.userapi.ai';
|
|
10
6
|
const operationOptions = [
|
|
11
|
-
{ name: '
|
|
12
|
-
{ name: '
|
|
13
|
-
{ name: 'Describe', value: 'describe', description: '
|
|
14
|
-
{ name: '
|
|
15
|
-
{ name: '
|
|
16
|
-
{ name: '
|
|
17
|
-
{ name: '
|
|
18
|
-
{ name: '
|
|
19
|
-
{ name: '
|
|
20
|
-
{ name: '
|
|
21
|
-
{ name: '
|
|
22
|
-
{ name: '
|
|
23
|
-
{ name: '
|
|
24
|
-
{ name: '
|
|
25
|
-
{ name: '
|
|
26
|
-
{ name: '
|
|
27
|
-
{ name: '
|
|
28
|
-
{ name: '
|
|
29
|
-
{ name: '
|
|
30
|
-
{ name: '
|
|
7
|
+
{ name: 'Animate', value: 'animate', description: 'Turn an image into a short video', action: 'Animate an image' },
|
|
8
|
+
{ name: 'Blend', value: 'blend', description: 'Blend 2 to 5 images into one', action: 'Blend images' },
|
|
9
|
+
{ name: 'Describe', value: 'describe', description: 'Get prompt suggestions that describe an image', action: 'Describe an image' },
|
|
10
|
+
{ name: 'Imagine', value: 'imagine', description: 'Generate four images from a text prompt', action: 'Generate images from a prompt' },
|
|
11
|
+
{ name: 'Imagine Instant', value: 'imagineInstant', description: 'Generate images from a prompt and pick the result in one call', action: 'Generate and pick an image' },
|
|
12
|
+
{ name: 'Info', value: 'info', description: 'Get the subscription and usage details of a Midjourney account', action: 'Get account info' },
|
|
13
|
+
{ name: 'Inpaint', value: 'inpaint', description: 'Redraw a masked region of an image', action: 'Inpaint an image' },
|
|
14
|
+
{ name: 'Pan', value: 'pan', description: 'Extend an image in one direction', action: 'Pan an image' },
|
|
15
|
+
{ name: 'Reroll', value: 'reroll', description: 'Run the same prompt again', action: 'Reroll a prompt' },
|
|
16
|
+
{ name: 'Seed', value: 'seed', description: 'Get the seed of a generation', action: 'Get the seed of a generation' },
|
|
17
|
+
{ name: 'Speed', value: 'speed', description: 'Switch an account between Relax, Fast and Turbo modes', action: 'Set account speed' },
|
|
18
|
+
{ name: 'Status', value: 'status', description: 'Get the status and result of a task', action: 'Get task status' },
|
|
19
|
+
{ name: 'Toggle Remix Mode', value: 'prefer', description: 'Turn Remix mode on or off for an account', action: 'Toggle remix mode' },
|
|
20
|
+
{ name: 'Upload', value: 'upload', description: 'Upload an image to get a Midjourney link for it', action: 'Upload an image' },
|
|
21
|
+
{ name: 'Upsample', value: 'upsample', description: 'Upscale an image with a chosen upscaler', action: 'Upsample an image' },
|
|
22
|
+
{ name: 'Upscale', value: 'upscale', description: 'Upscale one image from a grid', action: 'Upscale an image' },
|
|
23
|
+
{ name: 'Variation', value: 'variation', description: 'Create variations of one image from a grid', action: 'Create variations' },
|
|
24
|
+
{ name: 'Vary Strong', value: 'varyStrong', description: 'Create strong variations of an upscaled image', action: 'Create strong variations' },
|
|
25
|
+
{ name: 'Vary Subtle', value: 'varySubtle', description: 'Create subtle variations of an upscaled image', action: 'Create subtle variations' },
|
|
26
|
+
{ name: 'Zoom', value: 'zoom', description: 'Zoom out of an image', action: 'Zoom out of an image' },
|
|
31
27
|
];
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
28
|
+
// Request path of each operation. Every operation is a POST except Status.
|
|
29
|
+
const operationPaths = {
|
|
30
|
+
animate: '/midjourney/v2/animate',
|
|
31
|
+
blend: '/midjourney/v2/blend',
|
|
32
|
+
describe: '/midjourney/v2/describe',
|
|
33
|
+
imagine: '/midjourney/v2/imagine',
|
|
34
|
+
imagineInstant: '/midjourney/v2/imagine-instant',
|
|
35
|
+
info: '/midjourney/v2/info',
|
|
36
|
+
inpaint: '/midjourney/v2/inpaint',
|
|
37
|
+
pan: '/midjourney/v2/pan',
|
|
38
|
+
prefer: '/midjourney/v2/prefer',
|
|
39
|
+
reroll: '/midjourney/v2/reroll',
|
|
40
|
+
seed: '/midjourney/v2/seed',
|
|
41
|
+
speed: '/midjourney/v2/speed',
|
|
42
|
+
status: '/midjourney/v2/status',
|
|
43
|
+
upload: '/midjourney/v2/upload',
|
|
44
|
+
upsample: '/midjourney/v2/upsample',
|
|
45
|
+
upscale: '/midjourney/v2/upscale',
|
|
46
|
+
variation: '/midjourney/v2/variation',
|
|
47
|
+
varyStrong: '/midjourney/v2/vary-strong',
|
|
48
|
+
varySubtle: '/midjourney/v2/vary-subtle',
|
|
49
|
+
zoom: '/midjourney/v2/zoom',
|
|
50
|
+
};
|
|
51
|
+
// Operations that take the hash of an earlier task.
|
|
52
|
+
const hashOperations = ['animate', 'inpaint', 'pan', 'reroll', 'seed', 'status', 'upsample', 'upscale', 'variation', 'varyStrong', 'varySubtle', 'zoom'];
|
|
53
|
+
function show(operations, extra = {}) {
|
|
54
|
+
return { show: { operation: operations, ...extra } };
|
|
45
55
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
// ---------------------------------------------------------------- optional fields
|
|
57
|
+
const accountHashField = {
|
|
58
|
+
displayName: 'Account Hash',
|
|
59
|
+
name: 'account_hash',
|
|
60
|
+
type: 'string',
|
|
61
|
+
default: '',
|
|
62
|
+
description: 'Discord account to run the task on. If empty, the accounts linked to your API key take turns.',
|
|
63
|
+
};
|
|
64
|
+
const callbackIdField = {
|
|
65
|
+
displayName: 'Callback ID',
|
|
66
|
+
name: 'callback_id',
|
|
67
|
+
type: 'string',
|
|
68
|
+
default: '',
|
|
69
|
+
description: 'Your own identifier, returned with the task (up to 72 characters)',
|
|
70
|
+
};
|
|
71
|
+
const isAsyncField = {
|
|
72
|
+
displayName: 'Is Async',
|
|
73
|
+
name: 'is_async',
|
|
74
|
+
type: 'boolean',
|
|
75
|
+
default: false,
|
|
76
|
+
description: 'Whether to return the task hash at once instead of waiting for the final result',
|
|
77
|
+
};
|
|
78
|
+
const prefilterField = {
|
|
79
|
+
displayName: 'Disable Prefilter',
|
|
80
|
+
name: 'is_disable_prefilter',
|
|
81
|
+
type: 'boolean',
|
|
82
|
+
default: false,
|
|
83
|
+
description: 'Whether to skip the prompt check that runs before the prompt is sent to Midjourney',
|
|
84
|
+
};
|
|
85
|
+
const remixPromptField = {
|
|
86
|
+
displayName: 'Prompt',
|
|
87
|
+
name: 'prompt',
|
|
60
88
|
type: 'string',
|
|
61
|
-
required: true,
|
|
62
89
|
default: '',
|
|
63
|
-
description: '
|
|
90
|
+
description: 'New prompt for the result. Used when Remix mode is on.',
|
|
64
91
|
};
|
|
65
|
-
const
|
|
66
|
-
|
|
92
|
+
const webhookTypeField = {
|
|
93
|
+
displayName: 'Webhook Type',
|
|
94
|
+
name: 'webhook_type',
|
|
95
|
+
type: 'options',
|
|
96
|
+
default: 'result',
|
|
97
|
+
description: 'Which updates are sent to the webhook',
|
|
98
|
+
options: [
|
|
99
|
+
{ name: 'Progress', value: 'progress', description: 'Every progress update and the result' },
|
|
100
|
+
{ name: 'Result', value: 'result', description: 'Only the final result' },
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
const webhookUrlField = {
|
|
104
|
+
displayName: 'Webhook URL',
|
|
105
|
+
name: 'webhook_url',
|
|
106
|
+
type: 'string',
|
|
107
|
+
default: '',
|
|
108
|
+
description: 'URL that receives the task result as a POST request',
|
|
109
|
+
};
|
|
110
|
+
const byName = (a, b) => a.displayName.localeCompare(b.displayName);
|
|
111
|
+
function additionalFields(operations, fields) {
|
|
112
|
+
return {
|
|
113
|
+
displayName: 'Additional Fields',
|
|
114
|
+
name: 'additionalFields',
|
|
115
|
+
type: 'collection',
|
|
116
|
+
placeholder: 'Add Field',
|
|
117
|
+
default: {},
|
|
118
|
+
displayOptions: show(operations),
|
|
119
|
+
options: [...fields].sort(byName),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const webhook = [webhookUrlField, webhookTypeField];
|
|
123
|
+
const choiceOneToFour = [
|
|
124
|
+
{ name: '1', value: 1 },
|
|
125
|
+
{ name: '2', value: 2 },
|
|
126
|
+
{ name: '3', value: 3 },
|
|
127
|
+
{ name: '4', value: 4 },
|
|
128
|
+
];
|
|
129
|
+
// ---------------------------------------------------------------- node
|
|
67
130
|
class UserApi {
|
|
68
131
|
constructor() {
|
|
69
132
|
this.description = {
|
|
70
133
|
displayName: 'UserAPI',
|
|
71
134
|
name: 'userApi',
|
|
72
|
-
icon: 'file:userapi.svg',
|
|
135
|
+
icon: { light: 'file:userapi.svg', dark: 'file:userapi.dark.svg' },
|
|
73
136
|
group: ['transform'],
|
|
74
137
|
version: 1,
|
|
75
138
|
subtitle: '={{$parameter["operation"]}}',
|
|
76
|
-
description: '
|
|
139
|
+
description: 'Generate Midjourney images and videos through the UserAPI.ai REST API',
|
|
77
140
|
defaults: { name: 'UserAPI' },
|
|
78
|
-
|
|
79
|
-
|
|
141
|
+
usableAsTool: true,
|
|
142
|
+
inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
143
|
+
outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
80
144
|
credentials: [{ name: 'userApi', required: true }],
|
|
81
145
|
properties: [
|
|
82
|
-
{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
name: '
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
146
|
+
{
|
|
147
|
+
displayName: 'Operation',
|
|
148
|
+
name: 'operation',
|
|
149
|
+
type: 'options',
|
|
150
|
+
noDataExpression: true,
|
|
151
|
+
default: 'imagine',
|
|
152
|
+
options: operationOptions,
|
|
153
|
+
},
|
|
154
|
+
// Required inputs.
|
|
155
|
+
{
|
|
156
|
+
displayName: 'Prompt',
|
|
157
|
+
name: 'prompt',
|
|
158
|
+
type: 'string',
|
|
159
|
+
required: true,
|
|
160
|
+
default: '',
|
|
161
|
+
description: 'Text prompt. Midjourney parameters such as --ar 3:2 go at the end.',
|
|
162
|
+
displayOptions: show(['imagine', 'imagineInstant', 'inpaint']),
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
displayName: 'Hash',
|
|
166
|
+
name: 'hash',
|
|
167
|
+
type: 'string',
|
|
168
|
+
required: true,
|
|
169
|
+
default: '',
|
|
170
|
+
description: 'Hash of the task to work with, returned when the task was created',
|
|
171
|
+
displayOptions: show(hashOperations),
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
displayName: 'Account Hash',
|
|
175
|
+
name: 'account_hash',
|
|
176
|
+
type: 'string',
|
|
177
|
+
required: true,
|
|
178
|
+
default: '',
|
|
179
|
+
description: 'Discord account hash from your UserAPI dashboard',
|
|
180
|
+
displayOptions: show(['info', 'prefer', 'speed']),
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
displayName: 'Image URL',
|
|
184
|
+
name: 'url',
|
|
185
|
+
type: 'string',
|
|
186
|
+
required: true,
|
|
187
|
+
default: '',
|
|
188
|
+
description: 'Public http or https link to the image',
|
|
189
|
+
displayOptions: show(['describe', 'upload']),
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
displayName: 'Choice',
|
|
193
|
+
name: 'choice',
|
|
194
|
+
type: 'options',
|
|
195
|
+
required: true,
|
|
196
|
+
default: 'nothing',
|
|
197
|
+
description: 'Which result to return after the grid is ready',
|
|
198
|
+
options: [
|
|
199
|
+
{ name: '1', value: '1' },
|
|
200
|
+
{ name: '2', value: '2' },
|
|
201
|
+
{ name: '3', value: '3' },
|
|
202
|
+
{ name: '4', value: '4' },
|
|
203
|
+
{ name: 'All (Seed)', value: 'all' },
|
|
204
|
+
{ name: 'Nothing', value: 'nothing' },
|
|
205
|
+
{ name: 'Random', value: 'random' },
|
|
206
|
+
],
|
|
207
|
+
displayOptions: show(['imagineInstant']),
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
displayName: 'Choice',
|
|
211
|
+
name: 'choice',
|
|
212
|
+
type: 'options',
|
|
213
|
+
required: true,
|
|
214
|
+
default: 1,
|
|
215
|
+
description: 'Image of the grid, counted from the top left',
|
|
216
|
+
options: choiceOneToFour,
|
|
217
|
+
displayOptions: show(['upscale', 'variation']),
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
displayName: 'Direction',
|
|
221
|
+
name: 'choice',
|
|
222
|
+
type: 'options',
|
|
223
|
+
required: true,
|
|
224
|
+
default: 'right',
|
|
225
|
+
options: [
|
|
226
|
+
{ name: 'Down', value: 'down' },
|
|
227
|
+
{ name: 'Left', value: 'left' },
|
|
228
|
+
{ name: 'Right', value: 'right' },
|
|
229
|
+
{ name: 'Up', value: 'up' },
|
|
230
|
+
],
|
|
231
|
+
displayOptions: show(['pan']),
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
displayName: 'Upscaler',
|
|
235
|
+
name: 'choice',
|
|
236
|
+
type: 'options',
|
|
237
|
+
required: true,
|
|
238
|
+
default: 'v6_2x_subtle',
|
|
239
|
+
options: [
|
|
240
|
+
{ name: 'V5 2x', value: 'v5_2x' },
|
|
241
|
+
{ name: 'V5 4x', value: 'v5_4x' },
|
|
242
|
+
{ name: 'V6 2x Creative', value: 'v6_2x_creative' },
|
|
243
|
+
{ name: 'V6 2x Subtle', value: 'v6_2x_subtle' },
|
|
244
|
+
{ name: 'V6r1 2x Creative', value: 'v6r1_2x_creative' },
|
|
245
|
+
{ name: 'V6r1 2x Subtle', value: 'v6r1_2x_subtle' },
|
|
246
|
+
{ name: 'V7 2x Creative', value: 'v7_2x_creative' },
|
|
247
|
+
{ name: 'V7 2x Subtle', value: 'v7_2x_subtle' },
|
|
248
|
+
],
|
|
249
|
+
displayOptions: show(['upsample']),
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
displayName: 'Motion',
|
|
253
|
+
name: 'choice',
|
|
254
|
+
type: 'options',
|
|
255
|
+
required: true,
|
|
256
|
+
default: 'high',
|
|
257
|
+
options: [
|
|
258
|
+
{ name: 'High', value: 'high' },
|
|
259
|
+
{ name: 'Low', value: 'low' },
|
|
260
|
+
],
|
|
261
|
+
displayOptions: show(['animate']),
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
displayName: 'Mask (Base64)',
|
|
265
|
+
name: 'mask',
|
|
266
|
+
type: 'string',
|
|
267
|
+
required: true,
|
|
268
|
+
default: '',
|
|
269
|
+
description: 'Region to redraw, as a Base64-encoded image',
|
|
270
|
+
displayOptions: show(['inpaint']),
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
displayName: 'Zoom Mode',
|
|
274
|
+
name: 'zoom_mode',
|
|
275
|
+
type: 'options',
|
|
276
|
+
default: 'choice',
|
|
277
|
+
options: [
|
|
278
|
+
{ name: 'By Choice', value: 'choice' },
|
|
279
|
+
{ name: 'By Prompt', value: 'prompt' },
|
|
280
|
+
],
|
|
281
|
+
displayOptions: show(['zoom']),
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
displayName: 'Zoom',
|
|
285
|
+
name: 'choice',
|
|
286
|
+
type: 'options',
|
|
287
|
+
required: true,
|
|
288
|
+
default: 50,
|
|
289
|
+
options: [
|
|
290
|
+
{ name: '50', value: 50 },
|
|
291
|
+
{ name: '75', value: 75 },
|
|
292
|
+
],
|
|
293
|
+
displayOptions: show(['zoom'], { zoom_mode: ['choice'] }),
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
displayName: 'Zoom Prompt',
|
|
297
|
+
name: 'prompt',
|
|
298
|
+
type: 'string',
|
|
299
|
+
required: true,
|
|
300
|
+
default: '',
|
|
301
|
+
description: 'Custom zoom, for example "--zoom 1.8" with an optional new prompt',
|
|
302
|
+
displayOptions: show(['zoom'], { zoom_mode: ['prompt'] }),
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
displayName: 'Image URLs',
|
|
306
|
+
name: 'urls',
|
|
307
|
+
type: 'fixedCollection',
|
|
308
|
+
typeOptions: { multipleValues: true },
|
|
309
|
+
default: { values: [{ url: '' }, { url: '' }] },
|
|
310
|
+
description: 'From 2 to 5 image URLs',
|
|
311
|
+
options: [
|
|
312
|
+
{
|
|
313
|
+
displayName: 'Image',
|
|
314
|
+
name: 'values',
|
|
315
|
+
values: [{ displayName: 'URL', name: 'url', type: 'string', required: true, default: '' }],
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
displayOptions: show(['blend']),
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
displayName: 'Speed',
|
|
322
|
+
name: 'choice',
|
|
323
|
+
type: 'options',
|
|
324
|
+
required: true,
|
|
325
|
+
default: 'relax',
|
|
326
|
+
options: [
|
|
327
|
+
{ name: 'Fast', value: 'fast' },
|
|
328
|
+
{ name: 'Relax', value: 'relax' },
|
|
329
|
+
{ name: 'Turbo', value: 'turbo' },
|
|
330
|
+
],
|
|
331
|
+
displayOptions: show(['speed']),
|
|
332
|
+
},
|
|
333
|
+
// Optional inputs, one collection per group of operations with the same fields.
|
|
334
|
+
additionalFields(['imagine'], [...webhook, callbackIdField, accountHashField, prefilterField]),
|
|
335
|
+
additionalFields(['imagineInstant', 'upload'], [accountHashField]),
|
|
336
|
+
additionalFields(['describe', 'blend'], [...webhook, accountHashField]),
|
|
337
|
+
additionalFields(['info'], [webhookUrlField, callbackIdField]),
|
|
338
|
+
additionalFields(['prefer', 'speed', 'seed'], [webhookUrlField, callbackIdField, isAsyncField]),
|
|
339
|
+
additionalFields(['upscale', 'upsample', 'animate', 'inpaint', 'zoom'], webhook),
|
|
340
|
+
additionalFields(['pan', 'variation', 'varySubtle', 'varyStrong', 'reroll'], [remixPromptField, ...webhook]),
|
|
224
341
|
],
|
|
225
342
|
};
|
|
226
343
|
}
|
|
227
344
|
async execute() {
|
|
228
|
-
var _a;
|
|
229
345
|
const items = this.getInputData();
|
|
230
346
|
const returnData = [];
|
|
231
347
|
for (let i = 0; i < items.length; i++) {
|
|
232
348
|
try {
|
|
233
349
|
const operation = this.getNodeParameter('operation', i);
|
|
234
|
-
const
|
|
350
|
+
const path = operationPaths[operation];
|
|
351
|
+
if (!path) {
|
|
352
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported operation: ${operation}`, { itemIndex: i });
|
|
353
|
+
}
|
|
354
|
+
const param = (name) => this.getNodeParameter(name, i);
|
|
355
|
+
const fail = (message) => new n8n_workflow_1.NodeOperationError(this.getNode(), message, { itemIndex: i });
|
|
235
356
|
let body = {};
|
|
236
|
-
let
|
|
357
|
+
let qs = {};
|
|
358
|
+
if (hashOperations.includes(operation)) {
|
|
359
|
+
const hash = param('hash').trim();
|
|
360
|
+
if (!isUuid(hash))
|
|
361
|
+
throw fail('Hash must be a valid UUID');
|
|
362
|
+
if (operation === 'status')
|
|
363
|
+
qs = { hash };
|
|
364
|
+
else
|
|
365
|
+
body.hash = hash;
|
|
366
|
+
}
|
|
237
367
|
switch (operation) {
|
|
238
368
|
case 'imagine':
|
|
239
|
-
|
|
240
|
-
requestOptions.url = '/midjourney/v2/imagine';
|
|
241
|
-
body = { prompt: this.getNodeParameter('prompt', i), is_disable_prefilter: this.getNodeParameter('is_disable_prefilter', i) };
|
|
242
|
-
addWebhookFields(this, i, body);
|
|
243
|
-
addOptionalString(body, 'callback_id', this.getNodeParameter('callback_id', i));
|
|
244
|
-
addOptionalString(body, 'account_hash', this.getNodeParameter('account_hash', i));
|
|
369
|
+
body.prompt = param('prompt');
|
|
245
370
|
break;
|
|
246
371
|
case 'imagineInstant':
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
body = { prompt: this.getNodeParameter('prompt', i), choice: this.getNodeParameter('choice', i) };
|
|
250
|
-
addOptionalString(body, 'account_hash', this.getNodeParameter('account_hash', i));
|
|
372
|
+
body.prompt = param('prompt');
|
|
373
|
+
body.choice = param('choice');
|
|
251
374
|
break;
|
|
252
375
|
case 'describe':
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
addWebhookFields(this, i, body);
|
|
260
|
-
addOptionalString(body, 'account_hash', this.getNodeParameter('account_hash', i));
|
|
376
|
+
case 'upload': {
|
|
377
|
+
const url = param('url');
|
|
378
|
+
if (!isValidHttpUrl(url))
|
|
379
|
+
throw fail('Image URL must be a valid http or https URL');
|
|
380
|
+
body.url = url;
|
|
261
381
|
break;
|
|
382
|
+
}
|
|
262
383
|
case 'info':
|
|
263
|
-
|
|
264
|
-
requestOptions.url = '/midjourney/v2/info';
|
|
265
|
-
body = { account_hash: this.getNodeParameter('account_hash', i) };
|
|
266
|
-
{
|
|
267
|
-
const webhookUrl = this.getNodeParameter('webhook_url', i);
|
|
268
|
-
if (webhookUrl !== '') {
|
|
269
|
-
if (!isValidHttpUrl(webhookUrl)) {
|
|
270
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Webhook URL must be a valid http/https URL' });
|
|
271
|
-
}
|
|
272
|
-
body.webhook_url = webhookUrl;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
addOptionalString(body, 'callback_id', this.getNodeParameter('callback_id', i));
|
|
384
|
+
body.account_hash = param('account_hash');
|
|
276
385
|
break;
|
|
277
|
-
case '
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
body =
|
|
281
|
-
if (!isUuidV4Like(body.hash))
|
|
282
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Hash must be a valid UUID' });
|
|
283
|
-
addWebhookFields(this, i, body);
|
|
284
|
-
break;
|
|
285
|
-
case 'pan':
|
|
286
|
-
requestOptions.method = 'POST';
|
|
287
|
-
requestOptions.url = '/midjourney/v2/pan';
|
|
288
|
-
body = { hash: this.getNodeParameter('hash', i), choice: this.getNodeParameter('choice', i) };
|
|
289
|
-
if (!isUuidV4Like(body.hash))
|
|
290
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Hash must be a valid UUID' });
|
|
291
|
-
addOptionalString(body, 'prompt', this.getNodeParameter('prompt', i));
|
|
292
|
-
addWebhookFields(this, i, body);
|
|
293
|
-
break;
|
|
294
|
-
case 'variation':
|
|
295
|
-
requestOptions.method = 'POST';
|
|
296
|
-
requestOptions.url = '/midjourney/v2/variation';
|
|
297
|
-
body = { hash: this.getNodeParameter('hash', i), choice: this.getNodeParameter('choice', i) };
|
|
298
|
-
if (!isUuidV4Like(body.hash))
|
|
299
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Hash must be a valid UUID' });
|
|
300
|
-
addOptionalString(body, 'prompt', this.getNodeParameter('prompt', i));
|
|
301
|
-
addWebhookFields(this, i, body);
|
|
302
|
-
break;
|
|
303
|
-
case 'varySubtle':
|
|
304
|
-
requestOptions.method = 'POST';
|
|
305
|
-
requestOptions.url = '/midjourney/v2/vary-subtle';
|
|
306
|
-
body = { hash: this.getNodeParameter('hash', i) };
|
|
307
|
-
if (!isUuidV4Like(body.hash))
|
|
308
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Hash must be a valid UUID' });
|
|
309
|
-
addOptionalString(body, 'prompt', this.getNodeParameter('prompt', i));
|
|
310
|
-
addWebhookFields(this, i, body);
|
|
386
|
+
case 'prefer':
|
|
387
|
+
body.account_hash = param('account_hash');
|
|
388
|
+
body.choice = 'remix';
|
|
389
|
+
body.is_async = false;
|
|
311
390
|
break;
|
|
312
|
-
case '
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
body =
|
|
316
|
-
if (!isUuidV4Like(body.hash))
|
|
317
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Hash must be a valid UUID' });
|
|
318
|
-
addOptionalString(body, 'prompt', this.getNodeParameter('prompt', i));
|
|
319
|
-
addWebhookFields(this, i, body);
|
|
391
|
+
case 'speed':
|
|
392
|
+
body.account_hash = param('account_hash');
|
|
393
|
+
body.choice = param('choice');
|
|
394
|
+
body.is_async = false;
|
|
320
395
|
break;
|
|
321
|
-
case '
|
|
322
|
-
|
|
323
|
-
requestOptions.url = '/midjourney/v2/reroll';
|
|
324
|
-
body = { hash: this.getNodeParameter('hash', i) };
|
|
325
|
-
if (!isUuidV4Like(body.hash))
|
|
326
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Hash must be a valid UUID' });
|
|
327
|
-
addOptionalString(body, 'prompt', this.getNodeParameter('prompt', i));
|
|
328
|
-
addWebhookFields(this, i, body);
|
|
396
|
+
case 'seed':
|
|
397
|
+
body.is_async = false;
|
|
329
398
|
break;
|
|
399
|
+
case 'upscale':
|
|
400
|
+
case 'variation':
|
|
401
|
+
case 'pan':
|
|
330
402
|
case 'upsample':
|
|
331
|
-
requestOptions.method = 'POST';
|
|
332
|
-
requestOptions.url = '/midjourney/v2/upsample';
|
|
333
|
-
body = { hash: this.getNodeParameter('hash', i), choice: this.getNodeParameter('choice', i) };
|
|
334
|
-
if (!isUuidV4Like(body.hash))
|
|
335
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Hash must be a valid UUID' });
|
|
336
|
-
addWebhookFields(this, i, body);
|
|
337
|
-
break;
|
|
338
403
|
case 'animate':
|
|
339
|
-
|
|
340
|
-
requestOptions.url = '/midjourney/v2/animate';
|
|
341
|
-
body = { hash: this.getNodeParameter('hash', i), choice: this.getNodeParameter('choice', i) };
|
|
342
|
-
if (!isUuidV4Like(body.hash))
|
|
343
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Hash must be a valid UUID' });
|
|
344
|
-
addWebhookFields(this, i, body);
|
|
404
|
+
body.choice = param('choice');
|
|
345
405
|
break;
|
|
346
406
|
case 'inpaint':
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
body = { hash: this.getNodeParameter('hash', i), mask: this.getNodeParameter('mask', i), prompt: this.getNodeParameter('prompt', i) };
|
|
350
|
-
if (!isUuidV4Like(body.hash))
|
|
351
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Hash must be a valid UUID' });
|
|
352
|
-
addWebhookFields(this, i, body);
|
|
407
|
+
body.mask = param('mask');
|
|
408
|
+
body.prompt = param('prompt');
|
|
353
409
|
break;
|
|
354
|
-
case 'zoom':
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
body = { hash: this.getNodeParameter('hash', i) };
|
|
358
|
-
if (!isUuidV4Like(body.hash))
|
|
359
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Hash must be a valid UUID' });
|
|
360
|
-
const mode = this.getNodeParameter('zoom_mode', i);
|
|
361
|
-
if (mode === 'choice')
|
|
362
|
-
body.choice = this.getNodeParameter('choice', i);
|
|
410
|
+
case 'zoom':
|
|
411
|
+
if (param('zoom_mode') === 'choice')
|
|
412
|
+
body.choice = param('choice');
|
|
363
413
|
else
|
|
364
|
-
body.prompt =
|
|
365
|
-
addWebhookFields(this, i, body);
|
|
414
|
+
body.prompt = param('prompt');
|
|
366
415
|
break;
|
|
367
|
-
}
|
|
368
416
|
case 'blend': {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
if (
|
|
373
|
-
throw
|
|
374
|
-
|
|
375
|
-
if (!isValidHttpUrl(entry.url)) {
|
|
376
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Each Blend URL must be a valid http/https URL' });
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
body = { urls: urlsRaw.map((u) => ({ url: u.url })) };
|
|
380
|
-
addWebhookFields(this, i, body);
|
|
381
|
-
addOptionalString(body, 'account_hash', this.getNodeParameter('account_hash', i));
|
|
417
|
+
const urls = this.getNodeParameter('urls.values', i, []);
|
|
418
|
+
if (urls.length < 2 || urls.length > 5)
|
|
419
|
+
throw fail('Blend needs 2 to 5 image URLs');
|
|
420
|
+
if (urls.some((u) => !isValidHttpUrl(u.url)))
|
|
421
|
+
throw fail('Each image URL must be a valid http or https URL');
|
|
422
|
+
body.urls = urls.map((u) => ({ url: u.url }));
|
|
382
423
|
break;
|
|
383
424
|
}
|
|
384
|
-
case 'prefer':
|
|
385
|
-
requestOptions.method = 'POST';
|
|
386
|
-
requestOptions.url = '/midjourney/v2/prefer';
|
|
387
|
-
body = { account_hash: this.getNodeParameter('account_hash', i), choice: 'remix', is_async: this.getNodeParameter('is_async', i) };
|
|
388
|
-
{
|
|
389
|
-
const webhookUrl = this.getNodeParameter('webhook_url', i);
|
|
390
|
-
if (webhookUrl !== '') {
|
|
391
|
-
if (!isValidHttpUrl(webhookUrl)) {
|
|
392
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Webhook URL must be a valid http/https URL' });
|
|
393
|
-
}
|
|
394
|
-
body.webhook_url = webhookUrl;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
addOptionalString(body, 'callback_id', this.getNodeParameter('callback_id', i));
|
|
398
|
-
break;
|
|
399
|
-
case 'speed':
|
|
400
|
-
requestOptions.method = 'POST';
|
|
401
|
-
requestOptions.url = '/midjourney/v2/speed';
|
|
402
|
-
body = {
|
|
403
|
-
account_hash: this.getNodeParameter('account_hash', i),
|
|
404
|
-
choice: this.getNodeParameter('choice', i),
|
|
405
|
-
is_async: this.getNodeParameter('is_async', i),
|
|
406
|
-
};
|
|
407
|
-
{
|
|
408
|
-
const webhookUrl = this.getNodeParameter('webhook_url', i);
|
|
409
|
-
if (webhookUrl !== '') {
|
|
410
|
-
if (!isValidHttpUrl(webhookUrl)) {
|
|
411
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Webhook URL must be a valid http/https URL' });
|
|
412
|
-
}
|
|
413
|
-
body.webhook_url = webhookUrl;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
addOptionalString(body, 'callback_id', this.getNodeParameter('callback_id', i));
|
|
417
|
-
break;
|
|
418
|
-
case 'upload':
|
|
419
|
-
requestOptions.method = 'POST';
|
|
420
|
-
requestOptions.url = '/midjourney/v2/upload';
|
|
421
|
-
{
|
|
422
|
-
const url = this.getNodeParameter('url', i);
|
|
423
|
-
if (!isValidHttpUrl(url)) {
|
|
424
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Image URL must be a valid http/https URL' });
|
|
425
|
-
}
|
|
426
|
-
body = { url };
|
|
427
|
-
addOptionalString(body, 'account_hash', this.getNodeParameter('account_hash', i));
|
|
428
|
-
}
|
|
429
|
-
break;
|
|
430
|
-
case 'seed':
|
|
431
|
-
requestOptions.method = 'POST';
|
|
432
|
-
requestOptions.url = '/midjourney/v2/seed';
|
|
433
|
-
body = { hash: this.getNodeParameter('hash', i), is_async: this.getNodeParameter('is_async', i) };
|
|
434
|
-
if (!isUuidV4Like(body.hash))
|
|
435
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Hash must be a valid UUID' });
|
|
436
|
-
{
|
|
437
|
-
const webhookUrl = this.getNodeParameter('webhook_url', i);
|
|
438
|
-
if (webhookUrl !== '') {
|
|
439
|
-
if (!isValidHttpUrl(webhookUrl)) {
|
|
440
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Webhook URL must be a valid http/https URL' });
|
|
441
|
-
}
|
|
442
|
-
body.webhook_url = webhookUrl;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
break;
|
|
446
|
-
case 'status':
|
|
447
|
-
requestOptions.method = 'GET';
|
|
448
|
-
requestOptions.url = '/midjourney/v2/status';
|
|
449
|
-
query = { hash: this.getNodeParameter('hash', i) };
|
|
450
|
-
if (!isUuidV4Like(query.hash))
|
|
451
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: 'Hash must be a valid UUID' });
|
|
452
|
-
break;
|
|
453
|
-
default:
|
|
454
|
-
throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: `Unsupported operation: ${operation}` });
|
|
455
425
|
}
|
|
456
|
-
if (
|
|
457
|
-
|
|
426
|
+
if (operation !== 'status') {
|
|
427
|
+
body = { ...body, ...optionalFields(this.getNodeParameter('additionalFields', i, {}), fail) };
|
|
458
428
|
}
|
|
459
|
-
|
|
460
|
-
|
|
429
|
+
const method = operation === 'status' ? 'GET' : 'POST';
|
|
430
|
+
const options = { method, url: `${API_BASE_URL}${path}`, json: true };
|
|
431
|
+
if (method === 'POST')
|
|
432
|
+
options.body = body;
|
|
433
|
+
if (Object.keys(qs).length > 0)
|
|
434
|
+
options.qs = qs;
|
|
435
|
+
let response;
|
|
436
|
+
try {
|
|
437
|
+
response = await this.helpers.httpRequestWithAuthentication.call(this, 'userApi', options);
|
|
461
438
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
439
|
+
catch (error) {
|
|
440
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), error, { itemIndex: i });
|
|
441
|
+
}
|
|
442
|
+
const json = typeof response === 'object' && response !== null
|
|
443
|
+
? response
|
|
444
|
+
: { value: response };
|
|
468
445
|
returnData.push({ json, pairedItem: { item: i } });
|
|
469
446
|
}
|
|
470
447
|
catch (error) {
|
|
@@ -472,26 +449,37 @@ class UserApi {
|
|
|
472
449
|
returnData.push({ json: { error: error.message }, pairedItem: { item: i } });
|
|
473
450
|
continue;
|
|
474
451
|
}
|
|
475
|
-
|
|
452
|
+
// Both constructors return the error unchanged when it is already of their type.
|
|
453
|
+
if (error instanceof n8n_workflow_1.NodeApiError)
|
|
454
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
|
|
455
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i });
|
|
476
456
|
}
|
|
477
457
|
}
|
|
478
458
|
return [returnData];
|
|
479
459
|
}
|
|
480
460
|
}
|
|
481
461
|
exports.UserApi = UserApi;
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
462
|
+
// Copies the filled optional fields into the request body. The webhook type is sent only with a webhook URL.
|
|
463
|
+
function optionalFields(fields, fail) {
|
|
464
|
+
const out = {};
|
|
465
|
+
for (const key of ['account_hash', 'callback_id', 'prompt']) {
|
|
466
|
+
const value = typeof fields[key] === 'string' ? fields[key].trim() : '';
|
|
467
|
+
if (value !== '')
|
|
468
|
+
out[key] = value;
|
|
469
|
+
}
|
|
470
|
+
for (const key of ['is_async', 'is_disable_prefilter']) {
|
|
471
|
+
if (typeof fields[key] === 'boolean')
|
|
472
|
+
out[key] = fields[key];
|
|
473
|
+
}
|
|
474
|
+
const webhookUrl = typeof fields.webhook_url === 'string' ? fields.webhook_url.trim() : '';
|
|
475
|
+
if (webhookUrl !== '') {
|
|
476
|
+
if (!isValidHttpUrl(webhookUrl))
|
|
477
|
+
throw fail('Webhook URL must be a valid http or https URL');
|
|
478
|
+
out.webhook_url = webhookUrl;
|
|
479
|
+
if (fields.webhook_type)
|
|
480
|
+
out.webhook_type = fields.webhook_type;
|
|
492
481
|
}
|
|
493
|
-
|
|
494
|
-
target.webhook_type = context.getNodeParameter('webhook_type', itemIndex);
|
|
482
|
+
return out;
|
|
495
483
|
}
|
|
496
484
|
function isValidHttpUrl(value) {
|
|
497
485
|
try {
|
|
@@ -502,6 +490,6 @@ function isValidHttpUrl(value) {
|
|
|
502
490
|
return false;
|
|
503
491
|
}
|
|
504
492
|
}
|
|
505
|
-
function
|
|
493
|
+
function isUuid(value) {
|
|
506
494
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
507
495
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"node": "n8n-nodes-userapi.userApi",
|
|
3
|
+
"nodeVersion": "1.0",
|
|
4
|
+
"codexVersion": "1.0",
|
|
5
|
+
"categories": [
|
|
6
|
+
"Marketing & Content",
|
|
7
|
+
"Development"
|
|
8
|
+
],
|
|
9
|
+
"resources": {
|
|
10
|
+
"credentialDocumentation": [
|
|
11
|
+
{
|
|
12
|
+
"url": "https://userapi.ai/first-steps"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"primaryDocumentation": [
|
|
16
|
+
{
|
|
17
|
+
"url": "https://userapi.ai/docs"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"alias": [
|
|
22
|
+
"Midjourney",
|
|
23
|
+
"Image",
|
|
24
|
+
"Text to Image",
|
|
25
|
+
"Video"
|
|
26
|
+
]
|
|
27
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<svg version="1.0" width="248.000000pt" height="248.000000pt" viewBox="0 0 248.000000 248.000000" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<g transform="translate(0.000000,248.000000) scale(0.100000,-0.100000)" fill="#000000" stroke="none">
|
|
4
|
+
<path d="M1967 1973 c-15 -2 -43 -20 -62 -39 -91 -90 -30 -236 99 -236 90 0 141 49 141 137 0 97 -74 155 -178 138z" style="paint-order: fill; fill: rgb(244, 114, 182);"/>
|
|
5
|
+
<path d="M 1065 1585 C 989 1568 946 1550 893 1511 C 848 1478 785 1393 775 1350 C 769 1326 773 1323 857 1296 C 905 1280 950 1265 956 1263 C 962 1261 970 1272 973 1289 C 982 1334 1047 1396 1100 1410 C 1193 1435 1308 1404 1342 1345 C 1352 1327 1361 1288 1362 1255 L 1365 1195 L 1180 1189 C 986 1184 920 1172 859 1134 C 771 1079 731 1006 731 895 C 730 714 865 600 1081 600 C 1210 600 1293 638 1345 721 C 1374 767 1388 764 1415 707 C 1446 643 1482 627 1603 626 L 1705 625 L 1708 713 L 1711 800 L 1661 800 C 1580 800 1580 800 1580 1038 C 1580 1152 1575 1274 1569 1310 C 1558 1380 1522 1454 1481 1494 C 1436 1537 1341.502 1588.421 1258.502 1599.421 C 1162.502 1612.421 1141 1602 1065 1585 Z M 1363 990 C 1357 914 1349 893 1304 848 C 1200 742 1008 748 954 858 C 922 925 964 1000 1045 1019 C 1067 1024 1148 1028 1225 1027 L 1365 1025 L 1363 990 Z" style="paint-order: fill; fill: rgb(244, 114, 182);"/>
|
|
6
|
+
<path d="M1902 1098 l3 -473 105 0 105 0 0 470 0 470 -108 3 -107 3 2 -473z" style="paint-order: fill; fill: rgb(244, 114, 182);"/>
|
|
7
|
+
<path d="M 292 913 C 241 888 205.626 845 201.626 773 C 197.626 719 211 704 231 674 C 276 607 381 579 450 615 C 549 666 568 816 484 889 C 465 906 445 918 440 915 C 435 912 429 914 425 920 C 415 936 329 931 292 913 Z" style="paint-order: fill; fill: rgb(244, 114, 182);"/>
|
|
8
|
+
</g>
|
|
9
|
+
</svg>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-userapi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "n8n community nodes for UserApi.Ai endpoints",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://userapi.ai",
|
|
@@ -26,13 +26,16 @@
|
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "tsc -p tsconfig.json && node scripts/copy-assets.js",
|
|
28
28
|
"dev": "tsc -w -p tsconfig.json",
|
|
29
|
-
"lint": "
|
|
29
|
+
"lint": "n8n-node lint"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"n8n-workflow": "*"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
+
"@n8n/node-cli": "^0.56.3",
|
|
35
36
|
"@types/node": "^22.13.4",
|
|
37
|
+
"eslint": "^9.29.0",
|
|
38
|
+
"n8n-workflow": "^1.120.9",
|
|
36
39
|
"typescript": "^5.7.3"
|
|
37
40
|
},
|
|
38
41
|
"n8n": {
|