n8n-nodes-eranol 0.2.9 → 0.3.1
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/credentials/EranolApi.credentials.d.ts +2 -3
- package/dist/credentials/EranolApi.credentials.js +7 -9
- package/dist/nodes/Eranol/Eranol.node.js +202 -294
- package/dist/nodes/Eranol/operations.d.ts +30 -0
- package/dist/nodes/Eranol/operations.js +543 -0
- package/package.json +1 -1
- package/dist/nodes/Eranol/resources/audio/denoise.d.ts +0 -2
- package/dist/nodes/Eranol/resources/audio/denoise.js +0 -83
- package/dist/nodes/Eranol/resources/audio/highlights.d.ts +0 -2
- package/dist/nodes/Eranol/resources/audio/highlights.js +0 -79
- package/dist/nodes/Eranol/resources/audio/index.d.ts +0 -2
- package/dist/nodes/Eranol/resources/audio/index.js +0 -43
- package/dist/nodes/Eranol/resources/audio/removeSilence.d.ts +0 -2
- package/dist/nodes/Eranol/resources/audio/removeSilence.js +0 -88
- package/dist/nodes/Eranol/resources/compose/composeVideo.d.ts +0 -2
- package/dist/nodes/Eranol/resources/compose/composeVideo.js +0 -110
- package/dist/nodes/Eranol/resources/compose/concat.d.ts +0 -2
- package/dist/nodes/Eranol/resources/compose/concat.js +0 -76
- package/dist/nodes/Eranol/resources/compose/index.d.ts +0 -2
- package/dist/nodes/Eranol/resources/compose/index.js +0 -43
- package/dist/nodes/Eranol/resources/compose/merge.d.ts +0 -2
- package/dist/nodes/Eranol/resources/compose/merge.js +0 -185
- package/dist/nodes/Eranol/resources/convert/audioToMp3.d.ts +0 -2
- package/dist/nodes/Eranol/resources/convert/audioToMp3.js +0 -48
- package/dist/nodes/Eranol/resources/convert/audioToWav.d.ts +0 -2
- package/dist/nodes/Eranol/resources/convert/audioToWav.js +0 -48
- package/dist/nodes/Eranol/resources/convert/imageToJpg.d.ts +0 -2
- package/dist/nodes/Eranol/resources/convert/imageToJpg.js +0 -48
- package/dist/nodes/Eranol/resources/convert/imageToWebp.d.ts +0 -2
- package/dist/nodes/Eranol/resources/convert/imageToWebp.js +0 -48
- package/dist/nodes/Eranol/resources/convert/index.d.ts +0 -2
- package/dist/nodes/Eranol/resources/convert/index.js +0 -67
- package/dist/nodes/Eranol/resources/convert/videoToMp4.d.ts +0 -2
- package/dist/nodes/Eranol/resources/convert/videoToMp4.js +0 -48
- package/dist/nodes/Eranol/resources/convert/videoToWebm.d.ts +0 -2
- package/dist/nodes/Eranol/resources/convert/videoToWebm.js +0 -48
- package/dist/nodes/Eranol/resources/image/generateImage.d.ts +0 -2
- package/dist/nodes/Eranol/resources/image/generateImage.js +0 -93
- package/dist/nodes/Eranol/resources/image/imageStatus.d.ts +0 -2
- package/dist/nodes/Eranol/resources/image/imageStatus.js +0 -20
- package/dist/nodes/Eranol/resources/image/index.d.ts +0 -2
- package/dist/nodes/Eranol/resources/image/index.js +0 -35
- package/dist/nodes/Eranol/resources/job/deleteJob.d.ts +0 -2
- package/dist/nodes/Eranol/resources/job/deleteJob.js +0 -20
- package/dist/nodes/Eranol/resources/job/getResult.d.ts +0 -2
- package/dist/nodes/Eranol/resources/job/getResult.js +0 -20
- package/dist/nodes/Eranol/resources/job/getStatus.d.ts +0 -2
- package/dist/nodes/Eranol/resources/job/getStatus.js +0 -20
- package/dist/nodes/Eranol/resources/job/index.d.ts +0 -2
- package/dist/nodes/Eranol/resources/job/index.js +0 -51
- package/dist/nodes/Eranol/resources/job/verify.d.ts +0 -2
- package/dist/nodes/Eranol/resources/job/verify.js +0 -4
- package/dist/nodes/Eranol/resources/notify/index.d.ts +0 -2
- package/dist/nodes/Eranol/resources/notify/index.js +0 -27
- package/dist/nodes/Eranol/resources/notify/sendEmail.d.ts +0 -2
- package/dist/nodes/Eranol/resources/notify/sendEmail.js +0 -82
- package/dist/nodes/Eranol/resources/video/addIntro.d.ts +0 -2
- package/dist/nodes/Eranol/resources/video/addIntro.js +0 -64
- package/dist/nodes/Eranol/resources/video/addOutro.d.ts +0 -2
- package/dist/nodes/Eranol/resources/video/addOutro.js +0 -64
- package/dist/nodes/Eranol/resources/video/caption.d.ts +0 -2
- package/dist/nodes/Eranol/resources/video/caption.js +0 -120
- package/dist/nodes/Eranol/resources/video/extractAudio.d.ts +0 -2
- package/dist/nodes/Eranol/resources/video/extractAudio.js +0 -72
- package/dist/nodes/Eranol/resources/video/extractImages.d.ts +0 -2
- package/dist/nodes/Eranol/resources/video/extractImages.js +0 -102
- package/dist/nodes/Eranol/resources/video/generateGif.d.ts +0 -2
- package/dist/nodes/Eranol/resources/video/generateGif.js +0 -109
- package/dist/nodes/Eranol/resources/video/index.d.ts +0 -2
- package/dist/nodes/Eranol/resources/video/index.js +0 -115
- package/dist/nodes/Eranol/resources/video/overlay.d.ts +0 -2
- package/dist/nodes/Eranol/resources/video/overlay.js +0 -149
- package/dist/nodes/Eranol/resources/video/progressBar.d.ts +0 -2
- package/dist/nodes/Eranol/resources/video/progressBar.js +0 -109
- package/dist/nodes/Eranol/resources/video/reframe.d.ts +0 -2
- package/dist/nodes/Eranol/resources/video/reframe.js +0 -102
- package/dist/nodes/Eranol/resources/video/thumbnail.d.ts +0 -2
- package/dist/nodes/Eranol/resources/video/thumbnail.js +0 -126
- package/dist/nodes/Eranol/resources/video/trim.d.ts +0 -2
- package/dist/nodes/Eranol/resources/video/trim.js +0 -79
- package/dist/nodes/Eranol/resources/video/watermark.d.ts +0 -2
- package/dist/nodes/Eranol/resources/video/watermark.js +0 -117
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ICredentialType, INodeProperties, Icon, ICredentialTestRequest } from 'n8n-workflow';
|
|
2
2
|
export declare class EranolApi implements ICredentialType {
|
|
3
3
|
name: string;
|
|
4
4
|
displayName: string;
|
|
5
5
|
documentationUrl: string;
|
|
6
6
|
icon: Icon;
|
|
7
|
-
test: ICredentialTestRequest;
|
|
8
7
|
properties: INodeProperties[];
|
|
9
|
-
|
|
8
|
+
test: ICredentialTestRequest;
|
|
10
9
|
}
|
|
@@ -7,12 +7,6 @@ class EranolApi {
|
|
|
7
7
|
this.displayName = 'Eranol API';
|
|
8
8
|
this.documentationUrl = 'https://www.eranol.com/documentation';
|
|
9
9
|
this.icon = 'file:../nodes/Eranol/eranol.svg';
|
|
10
|
-
this.test = {
|
|
11
|
-
request: {
|
|
12
|
-
baseURL: 'https://eranol.com/api/v1',
|
|
13
|
-
url: '/me',
|
|
14
|
-
},
|
|
15
|
-
};
|
|
16
10
|
this.properties = [
|
|
17
11
|
{
|
|
18
12
|
displayName: 'API Key',
|
|
@@ -24,9 +18,13 @@ class EranolApi {
|
|
|
24
18
|
description: 'Your Eranol API key',
|
|
25
19
|
},
|
|
26
20
|
];
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
// No generic `authenticate` block: the node attaches the x-api-key header
|
|
22
|
+
// itself in execute(). Omitting it also stops n8n from injecting a
|
|
23
|
+
// "Custom API Call" operation into the node's Operation dropdown.
|
|
24
|
+
this.test = {
|
|
25
|
+
request: {
|
|
26
|
+
baseURL: 'https://eranol.com/api/v1',
|
|
27
|
+
url: '/verify',
|
|
30
28
|
headers: {
|
|
31
29
|
'x-api-key': '={{$credentials.apiKey}}',
|
|
32
30
|
},
|
|
@@ -2,240 +2,69 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Eranol = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
-
const
|
|
6
|
-
const audio_1 = require("./resources/audio");
|
|
7
|
-
const convert_1 = require("./resources/convert");
|
|
8
|
-
const compose_1 = require("./resources/compose");
|
|
9
|
-
const job_1 = require("./resources/job");
|
|
10
|
-
const image_1 = require("./resources/image");
|
|
11
|
-
const notify_1 = require("./resources/notify");
|
|
5
|
+
const operations_1 = require("./operations");
|
|
12
6
|
const BASE_URL = 'https://eranol.com/api/v1';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
removeSilence: { method: 'POST', url: '/ffmpeg/audio/remove-silence' },
|
|
31
|
-
// convert
|
|
32
|
-
audioToMp3: { method: 'POST', url: '/ffmpeg/convert/audio/to/mp3' },
|
|
33
|
-
audioToWav: { method: 'POST', url: '/ffmpeg/convert/audio/to/wav' },
|
|
34
|
-
imageToJpg: { method: 'POST', url: '/ffmpeg/convert/image/to/jpg' },
|
|
35
|
-
imageToWebp: { method: 'POST', url: '/ffmpeg/convert/image/to/webp' },
|
|
36
|
-
videoToMp4: { method: 'POST', url: '/ffmpeg/convert/video/to/mp4' },
|
|
37
|
-
videoToWebm: { method: 'POST', url: '/ffmpeg/convert/video/to/webm' },
|
|
38
|
-
// compose
|
|
39
|
-
concat: { method: 'POST', url: '/ffmpeg/video/concat' },
|
|
40
|
-
composeVideo: { method: 'POST', url: '/ffmpeg/video/compose' },
|
|
41
|
-
merge: { method: 'POST', url: '/ffmpeg/merge' },
|
|
42
|
-
// image
|
|
43
|
-
generateImage: { method: 'POST', url: '/image' },
|
|
44
|
-
// notify
|
|
45
|
-
sendEmail: { method: 'POST', url: '/notifications/email' },
|
|
46
|
-
};
|
|
47
|
-
function buildBody(operation, p) {
|
|
48
|
-
var _a, _b, _c, _d;
|
|
49
|
-
const additional = p('additionalFields', {});
|
|
50
|
-
switch (operation) {
|
|
51
|
-
// ── video ──────────────────────────────────────────────────────────
|
|
52
|
-
case 'trim':
|
|
53
|
-
return { url: p('url'), start_sec: p('startSec'), end_sec: p('endSec') };
|
|
54
|
-
case 'addIntro':
|
|
55
|
-
return { url: p('url'), intro_url: p('introUrl') };
|
|
56
|
-
case 'addOutro':
|
|
57
|
-
return { url: p('url'), outro_url: p('outroUrl') };
|
|
58
|
-
case 'caption': {
|
|
59
|
-
const body = { url: p('url') };
|
|
60
|
-
if (additional.fontColor !== undefined)
|
|
61
|
-
body.font_color = additional.fontColor;
|
|
62
|
-
if (additional.fontSize !== undefined)
|
|
63
|
-
body.font_size = additional.fontSize;
|
|
64
|
-
if (additional.maxSegmentDuration !== undefined)
|
|
65
|
-
body.max_segment_duration = additional.maxSegmentDuration;
|
|
66
|
-
if (additional.maxWordsPerLine !== undefined)
|
|
67
|
-
body.max_words_per_line = additional.maxWordsPerLine;
|
|
68
|
-
if (additional.outlineColor !== undefined)
|
|
69
|
-
body.outline_color = additional.outlineColor;
|
|
70
|
-
if (additional.outlineWidth !== undefined)
|
|
71
|
-
body.outline_width = additional.outlineWidth;
|
|
72
|
-
if (additional.position !== undefined)
|
|
73
|
-
body.position = additional.position;
|
|
74
|
-
return body;
|
|
75
|
-
}
|
|
76
|
-
case 'extractAudio': {
|
|
77
|
-
const body = { url: p('url') };
|
|
78
|
-
if (additional.mono !== undefined)
|
|
79
|
-
body.mono = additional.mono;
|
|
80
|
-
return body;
|
|
81
|
-
}
|
|
82
|
-
case 'extractImages': {
|
|
83
|
-
const body = { url: p('url'), start_sec: p('startSec'), end_sec: p('endSec') };
|
|
84
|
-
if (additional.fps !== undefined)
|
|
85
|
-
body.fps = additional.fps;
|
|
86
|
-
return body;
|
|
87
|
-
}
|
|
88
|
-
case 'generateGif': {
|
|
89
|
-
const body = { url: p('url'), start_sec: p('startSec'), end_sec: p('endSec') };
|
|
90
|
-
if (additional.fps !== undefined)
|
|
91
|
-
body.fps = additional.fps;
|
|
92
|
-
if (additional.width !== undefined)
|
|
93
|
-
body.width = additional.width;
|
|
94
|
-
return body;
|
|
95
|
-
}
|
|
96
|
-
case 'overlay': {
|
|
97
|
-
const raw = p('overlays', { overlayValues: [] });
|
|
98
|
-
return { url: p('url'), overlays: (_a = raw.overlayValues) !== null && _a !== void 0 ? _a : [] };
|
|
99
|
-
}
|
|
100
|
-
case 'progressBar': {
|
|
101
|
-
const body = { url: p('url') };
|
|
102
|
-
if (additional.color !== undefined)
|
|
103
|
-
body.color = additional.color;
|
|
104
|
-
if (additional.height !== undefined)
|
|
105
|
-
body.height = additional.height;
|
|
106
|
-
if (additional.opacity !== undefined)
|
|
107
|
-
body.opacity = additional.opacity;
|
|
108
|
-
if (additional.position !== undefined)
|
|
109
|
-
body.position = additional.position;
|
|
110
|
-
if (additional.style !== undefined)
|
|
111
|
-
body.style = additional.style;
|
|
112
|
-
return body;
|
|
113
|
-
}
|
|
114
|
-
case 'reframe': {
|
|
115
|
-
const body = { url: p('url'), width: p('width'), height: p('height') };
|
|
116
|
-
if (additional.bgColor !== undefined)
|
|
117
|
-
body.bg_color = additional.bgColor;
|
|
118
|
-
return body;
|
|
119
|
-
}
|
|
120
|
-
case 'thumbnail': {
|
|
121
|
-
const body = { url: p('url') };
|
|
122
|
-
if (additional.bgColor !== undefined)
|
|
123
|
-
body.bg_color = additional.bgColor;
|
|
124
|
-
if (additional.fontColor !== undefined)
|
|
125
|
-
body.font_color = additional.fontColor;
|
|
126
|
-
if (additional.fontSize !== undefined)
|
|
127
|
-
body.font_size = additional.fontSize;
|
|
128
|
-
if (additional.height !== undefined)
|
|
129
|
-
body.height = additional.height;
|
|
130
|
-
if (additional.position !== undefined)
|
|
131
|
-
body.position = additional.position;
|
|
132
|
-
if (additional.text !== undefined)
|
|
133
|
-
body.text = additional.text;
|
|
134
|
-
if (additional.timeSec !== undefined)
|
|
135
|
-
body.time_sec = additional.timeSec;
|
|
136
|
-
if (additional.width !== undefined)
|
|
137
|
-
body.width = additional.width;
|
|
138
|
-
return body;
|
|
139
|
-
}
|
|
140
|
-
case 'watermark': {
|
|
141
|
-
const body = { url: p('url'), watermark_url: p('watermarkUrl') };
|
|
142
|
-
if (additional.position !== undefined)
|
|
143
|
-
body.position = additional.position;
|
|
144
|
-
if (additional.scale !== undefined)
|
|
145
|
-
body.scale = additional.scale;
|
|
146
|
-
if (additional.opacity !== undefined)
|
|
147
|
-
body.opacity = additional.opacity;
|
|
148
|
-
if (additional.margin !== undefined)
|
|
149
|
-
body.margin = additional.margin;
|
|
150
|
-
return body;
|
|
151
|
-
}
|
|
152
|
-
// ── audio ──────────────────────────────────────────────────────────
|
|
153
|
-
case 'denoise': {
|
|
154
|
-
const body = { url: p('url') };
|
|
155
|
-
if (additional.method !== undefined)
|
|
156
|
-
body.method = additional.method;
|
|
157
|
-
if (additional.noiseReduction !== undefined)
|
|
158
|
-
body.noise_reduction = additional.noiseReduction;
|
|
159
|
-
return body;
|
|
160
|
-
}
|
|
161
|
-
case 'highlights': {
|
|
162
|
-
const body = { url: p('url') };
|
|
163
|
-
if (additional.topN !== undefined)
|
|
164
|
-
body.top_n = additional.topN;
|
|
165
|
-
if (additional.clipDuration !== undefined)
|
|
166
|
-
body.clip_duration = additional.clipDuration;
|
|
167
|
-
return body;
|
|
168
|
-
}
|
|
169
|
-
case 'removeSilence': {
|
|
170
|
-
const body = { url: p('url') };
|
|
171
|
-
if (additional.silenceThreshDb !== undefined)
|
|
172
|
-
body.silence_thresh_db = additional.silenceThreshDb;
|
|
173
|
-
if (additional.minSilenceDuration !== undefined)
|
|
174
|
-
body.min_silence_duration = additional.minSilenceDuration;
|
|
175
|
-
if (additional.padding !== undefined)
|
|
176
|
-
body.padding = additional.padding;
|
|
177
|
-
return body;
|
|
178
|
-
}
|
|
179
|
-
// ── convert ────────────────────────────────────────────────────────
|
|
180
|
-
case 'audioToMp3':
|
|
181
|
-
case 'audioToWav':
|
|
182
|
-
case 'imageToJpg':
|
|
183
|
-
case 'imageToWebp':
|
|
184
|
-
case 'videoToMp4':
|
|
185
|
-
case 'videoToWebm':
|
|
186
|
-
return { url: p('url') };
|
|
187
|
-
// ── compose ────────────────────────────────────────────────────────
|
|
188
|
-
case 'concat': {
|
|
189
|
-
const raw = p('clips', { clipValues: [] });
|
|
190
|
-
return { clips: (_b = raw.clipValues) !== null && _b !== void 0 ? _b : [] };
|
|
191
|
-
}
|
|
192
|
-
case 'composeVideo': {
|
|
193
|
-
const raw = p('overlays', { overlayValues: [] });
|
|
194
|
-
return { main_video_url: p('mainVideoUrl'), overlays: (_c = raw.overlayValues) !== null && _c !== void 0 ? _c : [] };
|
|
7
|
+
/**
|
|
8
|
+
* Coerce an HTTP response into a JSON-safe object for n8n output.
|
|
9
|
+
*
|
|
10
|
+
* `httpRequest` normally returns the parsed body, but if the response is a full
|
|
11
|
+
* wrapper (status/headers/body with circular socket refs) we extract `body`,
|
|
12
|
+
* and otherwise fall back to a plain string/value wrapper. This prevents
|
|
13
|
+
* "Converting circular structure to JSON" errors.
|
|
14
|
+
*/
|
|
15
|
+
function toJsonSafe(data) {
|
|
16
|
+
if (data === null || data === undefined) {
|
|
17
|
+
return { success: true };
|
|
18
|
+
}
|
|
19
|
+
if (typeof data === 'object') {
|
|
20
|
+
const obj = data;
|
|
21
|
+
// Full-response wrapper: surface just the body.
|
|
22
|
+
if ('body' in obj && ('headers' in obj || 'statusCode' in obj)) {
|
|
23
|
+
return toJsonSafe(obj.body);
|
|
195
24
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (additional.audioMode !== undefined)
|
|
200
|
-
body.audio_mode = additional.audioMode;
|
|
201
|
-
if (additional.bgAudioUrl !== undefined)
|
|
202
|
-
body.bg_audio_url = additional.bgAudioUrl;
|
|
203
|
-
if (additional.bgAudioVolume !== undefined)
|
|
204
|
-
body.bg_audio_volume = additional.bgAudioVolume;
|
|
205
|
-
if (additional.fadeSecs !== undefined)
|
|
206
|
-
body.fade_secs = additional.fadeSecs;
|
|
207
|
-
if (additional.fit !== undefined)
|
|
208
|
-
body.fit = additional.fit;
|
|
209
|
-
if (additional.fps !== undefined)
|
|
210
|
-
body.fps = additional.fps;
|
|
211
|
-
if (additional.height !== undefined)
|
|
212
|
-
body.height = additional.height;
|
|
213
|
-
if (additional.transition !== undefined)
|
|
214
|
-
body.transition = additional.transition;
|
|
215
|
-
if (additional.width !== undefined)
|
|
216
|
-
body.width = additional.width;
|
|
217
|
-
return body;
|
|
25
|
+
try {
|
|
26
|
+
// Round-trip to drop any non-serialisable (circular) properties.
|
|
27
|
+
return JSON.parse(JSON.stringify(obj));
|
|
218
28
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const body = { prompt: p('prompt') };
|
|
222
|
-
if (additional.height !== undefined)
|
|
223
|
-
body.height = additional.height;
|
|
224
|
-
if (additional.negativePrompt !== undefined)
|
|
225
|
-
body.negative_prompt = additional.negativePrompt;
|
|
226
|
-
if (additional.seed !== undefined)
|
|
227
|
-
body.seed = additional.seed;
|
|
228
|
-
if (additional.width !== undefined)
|
|
229
|
-
body.width = additional.width;
|
|
230
|
-
return body;
|
|
29
|
+
catch {
|
|
30
|
+
return { result: String(data) };
|
|
231
31
|
}
|
|
232
|
-
// ── notify ─────────────────────────────────────────────────────────
|
|
233
|
-
case 'sendEmail':
|
|
234
|
-
return { to: p('to'), subject: p('subject'), message: p('message') };
|
|
235
|
-
default:
|
|
236
|
-
return {};
|
|
237
32
|
}
|
|
33
|
+
return { result: data };
|
|
238
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Universal body UI. For each operation, shown only when that operation is
|
|
37
|
+
* selected: a slim notice linking to the operation's documentation page (n8n
|
|
38
|
+
* auto-links the bare URL), then an empty "Body (JSON)" editor. The docs page
|
|
39
|
+
* lists the exact payload fields and examples to paste in.
|
|
40
|
+
*/
|
|
41
|
+
const universalBodyFields = operations_1.OPERATIONS.flatMap((op) => [
|
|
42
|
+
{
|
|
43
|
+
displayName: `See the <a href="${op.docs}" target="_blank">${op.name} payload reference</a>.`,
|
|
44
|
+
name: `docs_${op.value}`,
|
|
45
|
+
type: 'notice',
|
|
46
|
+
default: '',
|
|
47
|
+
displayOptions: {
|
|
48
|
+
show: {
|
|
49
|
+
resource: ['universal'],
|
|
50
|
+
eranolAction: [op.value],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
displayName: 'Body (JSON)',
|
|
56
|
+
name: `body_${op.value}`,
|
|
57
|
+
type: 'json',
|
|
58
|
+
default: '{}',
|
|
59
|
+
description: 'Request body sent to the Eranol API. See the payload reference link above.',
|
|
60
|
+
displayOptions: {
|
|
61
|
+
show: {
|
|
62
|
+
resource: ['universal'],
|
|
63
|
+
eranolAction: [op.value],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
]);
|
|
239
68
|
class Eranol {
|
|
240
69
|
constructor() {
|
|
241
70
|
this.description = {
|
|
@@ -244,8 +73,8 @@ class Eranol {
|
|
|
244
73
|
icon: 'file:eranol.svg',
|
|
245
74
|
group: ['transform'],
|
|
246
75
|
version: 1,
|
|
247
|
-
subtitle: '={{$parameter["
|
|
248
|
-
description: 'Interact with the Eranol
|
|
76
|
+
subtitle: '={{ $parameter["resource"] === "job" ? $parameter["operation"] : $parameter["eranolAction"] }}',
|
|
77
|
+
description: 'Interact with the Eranol media processing API',
|
|
249
78
|
defaults: {
|
|
250
79
|
name: 'Eranol',
|
|
251
80
|
},
|
|
@@ -265,125 +94,204 @@ class Eranol {
|
|
|
265
94
|
noDataExpression: true,
|
|
266
95
|
options: [
|
|
267
96
|
{
|
|
268
|
-
name: '
|
|
269
|
-
value: '
|
|
97
|
+
name: 'Universal',
|
|
98
|
+
value: 'universal',
|
|
99
|
+
description: 'Run any Eranol API operation with a JSON body',
|
|
270
100
|
},
|
|
271
101
|
{
|
|
272
|
-
name: '
|
|
273
|
-
value: '
|
|
102
|
+
name: 'Job',
|
|
103
|
+
value: 'job',
|
|
104
|
+
description: 'Check job status, fetch results, or verify your API key',
|
|
274
105
|
},
|
|
106
|
+
],
|
|
107
|
+
default: 'universal',
|
|
108
|
+
},
|
|
109
|
+
// ── Universal ──────────────────────────────────────────────────────
|
|
110
|
+
// A single-option "operation" so n8n renders exactly ONE "Universal"
|
|
111
|
+
// tile in the nodes panel (n8n generates one tile per option of the
|
|
112
|
+
// parameter literally named "operation"). It is hidden inside the node
|
|
113
|
+
// so users only see the real "Operation" picker (eranolAction) below.
|
|
114
|
+
{
|
|
115
|
+
displayName: 'Operation',
|
|
116
|
+
name: 'operation',
|
|
117
|
+
type: 'hidden',
|
|
118
|
+
noDataExpression: true,
|
|
119
|
+
options: [
|
|
275
120
|
{
|
|
276
|
-
name: '
|
|
277
|
-
value: '
|
|
121
|
+
name: 'Universal',
|
|
122
|
+
value: 'universal',
|
|
123
|
+
action: 'Universal',
|
|
124
|
+
description: 'Run any Eranol API operation with a JSON body',
|
|
278
125
|
},
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
126
|
+
],
|
|
127
|
+
default: 'universal',
|
|
128
|
+
displayOptions: {
|
|
129
|
+
show: {
|
|
130
|
+
resource: ['universal'],
|
|
282
131
|
},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
// The actual operation picker shown inside the Universal node.
|
|
135
|
+
{
|
|
136
|
+
displayName: 'Operation',
|
|
137
|
+
name: 'eranolAction',
|
|
138
|
+
type: 'options',
|
|
139
|
+
noDataExpression: true,
|
|
140
|
+
displayOptions: {
|
|
141
|
+
show: {
|
|
142
|
+
resource: ['universal'],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
options: operations_1.OPERATION_OPTIONS,
|
|
146
|
+
default: 'trim',
|
|
147
|
+
},
|
|
148
|
+
...universalBodyFields,
|
|
149
|
+
// ── Job ────────────────────────────────────────────────────────────
|
|
150
|
+
{
|
|
151
|
+
displayName: 'Operation',
|
|
152
|
+
name: 'operation',
|
|
153
|
+
type: 'options',
|
|
154
|
+
noDataExpression: true,
|
|
155
|
+
displayOptions: {
|
|
156
|
+
show: {
|
|
157
|
+
resource: ['job'],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
options: [
|
|
283
161
|
{
|
|
284
|
-
name: '
|
|
285
|
-
value: '
|
|
162
|
+
name: 'Get Status',
|
|
163
|
+
value: 'getStatus',
|
|
164
|
+
action: 'Get job status',
|
|
165
|
+
description: 'Retrieve current job status, progress, and completion data',
|
|
286
166
|
},
|
|
287
167
|
{
|
|
288
|
-
name: '
|
|
289
|
-
value: '
|
|
168
|
+
name: 'Get Result',
|
|
169
|
+
value: 'getResult',
|
|
170
|
+
action: 'Get job result',
|
|
171
|
+
description: 'Get the result of a completed job',
|
|
290
172
|
},
|
|
291
173
|
{
|
|
292
|
-
name: '
|
|
293
|
-
value: '
|
|
174
|
+
name: 'Delete',
|
|
175
|
+
value: 'deleteJob',
|
|
176
|
+
action: 'Delete a job',
|
|
177
|
+
description: 'Remove a job and its associated output file',
|
|
294
178
|
},
|
|
295
179
|
],
|
|
296
|
-
default: '
|
|
180
|
+
default: 'getStatus',
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
displayName: 'Job ID',
|
|
184
|
+
name: 'jobId',
|
|
185
|
+
type: 'string',
|
|
186
|
+
default: '',
|
|
187
|
+
required: true,
|
|
188
|
+
description: 'The ID of the job to act on',
|
|
189
|
+
displayOptions: {
|
|
190
|
+
show: {
|
|
191
|
+
resource: ['job'],
|
|
192
|
+
},
|
|
193
|
+
},
|
|
297
194
|
},
|
|
298
|
-
...video_1.videoDescription,
|
|
299
|
-
...audio_1.audioDescription,
|
|
300
|
-
...convert_1.convertDescription,
|
|
301
|
-
...compose_1.composeDescription,
|
|
302
|
-
...image_1.imageDescription,
|
|
303
|
-
...notify_1.notifyDescription,
|
|
304
|
-
...job_1.jobDescription,
|
|
305
195
|
],
|
|
306
196
|
usableAsTool: true,
|
|
307
197
|
};
|
|
308
198
|
}
|
|
309
199
|
async execute() {
|
|
310
|
-
const
|
|
200
|
+
const credentials = await this.getCredentials('eranolApi');
|
|
201
|
+
const apiKey = credentials.apiKey;
|
|
202
|
+
const authHeaders = { 'x-api-key': apiKey };
|
|
203
|
+
const httpRequest = this.helpers.httpRequest.bind(this);
|
|
204
|
+
const node = this.getNode();
|
|
205
|
+
// Wrap httpRequest so a failed request surfaces the API's status and body
|
|
206
|
+
// as a clean message instead of a circular response object.
|
|
207
|
+
const request = async (options) => {
|
|
208
|
+
var _a, _b, _c, _d, _e, _f;
|
|
209
|
+
try {
|
|
210
|
+
return await httpRequest(options);
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
const err = error;
|
|
214
|
+
const status = (_a = err.statusCode) !== null && _a !== void 0 ? _a : (_b = err.response) === null || _b === void 0 ? void 0 : _b.statusCode;
|
|
215
|
+
const bodyText = typeof ((_c = err.response) === null || _c === void 0 ? void 0 : _c.body) === 'string'
|
|
216
|
+
? err.response.body
|
|
217
|
+
: JSON.stringify((_e = (_d = err.response) === null || _d === void 0 ? void 0 : _d.body) !== null && _e !== void 0 ? _e : '');
|
|
218
|
+
const detail = bodyText && bodyText !== '""' ? ` — ${bodyText}` : '';
|
|
219
|
+
throw new n8n_workflow_1.NodeOperationError(node, `Eranol API request failed${status ? ` (HTTP ${status})` : ''}${detail || `: ${(_f = err.message) !== null && _f !== void 0 ? _f : 'Unknown error'}`}`);
|
|
220
|
+
}
|
|
221
|
+
};
|
|
311
222
|
const items = this.getInputData();
|
|
312
223
|
const returnData = [];
|
|
313
224
|
for (let i = 0; i < items.length; i++) {
|
|
314
225
|
const resource = this.getNodeParameter('resource', i);
|
|
315
|
-
const operation = this.getNodeParameter('operation', i);
|
|
316
|
-
const useJsonBody = this.getNodeParameter('useJsonBody', i, false);
|
|
317
|
-
const p = (name, fallback) => {
|
|
318
|
-
try {
|
|
319
|
-
return this.getNodeParameter(name, i);
|
|
320
|
-
}
|
|
321
|
-
catch {
|
|
322
|
-
return fallback;
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
226
|
let responseData;
|
|
326
|
-
// ── GET/DELETE operations (no body) ────────────────────────────
|
|
327
227
|
if (resource === 'job') {
|
|
328
|
-
const
|
|
228
|
+
const operation = this.getNodeParameter('operation', i);
|
|
229
|
+
const jobId = this.getNodeParameter('jobId', i);
|
|
329
230
|
if (operation === 'getStatus') {
|
|
330
|
-
responseData = await request(
|
|
231
|
+
responseData = await request({
|
|
331
232
|
method: 'GET',
|
|
332
233
|
url: `${BASE_URL}/ffmpeg/status/${jobId}`,
|
|
333
|
-
headers: { Accept: 'application/json' },
|
|
234
|
+
headers: { Accept: 'application/json', ...authHeaders },
|
|
235
|
+
json: true,
|
|
334
236
|
});
|
|
335
237
|
}
|
|
336
238
|
else if (operation === 'getResult') {
|
|
337
|
-
responseData = await request(
|
|
239
|
+
responseData = await request({
|
|
338
240
|
method: 'GET',
|
|
339
241
|
url: `${BASE_URL}/ffmpeg/result/${jobId}`,
|
|
340
|
-
headers: { Accept: 'application/json' },
|
|
242
|
+
headers: { Accept: 'application/json', ...authHeaders },
|
|
243
|
+
json: true,
|
|
341
244
|
});
|
|
342
245
|
}
|
|
343
246
|
else if (operation === 'deleteJob') {
|
|
344
|
-
responseData = await request(
|
|
247
|
+
responseData = await request({
|
|
345
248
|
method: 'DELETE',
|
|
346
249
|
url: `${BASE_URL}/ffmpeg/jobs/${jobId}`,
|
|
347
|
-
headers: { Accept: 'application/json' },
|
|
250
|
+
headers: { Accept: 'application/json', ...authHeaders },
|
|
251
|
+
json: true,
|
|
348
252
|
});
|
|
349
253
|
}
|
|
350
|
-
else
|
|
351
|
-
|
|
352
|
-
method: 'GET',
|
|
353
|
-
url: `${BASE_URL}/verify`,
|
|
354
|
-
headers: { Accept: 'application/json' },
|
|
355
|
-
});
|
|
254
|
+
else {
|
|
255
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown job operation: ${operation}`);
|
|
356
256
|
}
|
|
357
257
|
}
|
|
358
|
-
else if (resource === 'image' && operation === 'imageStatus') {
|
|
359
|
-
const jobId = p('jobId');
|
|
360
|
-
responseData = await request('eranolApi', {
|
|
361
|
-
method: 'GET',
|
|
362
|
-
url: `${BASE_URL}/image/status/${jobId}`,
|
|
363
|
-
headers: { Accept: 'application/json' },
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
258
|
else {
|
|
367
|
-
// ── POST
|
|
368
|
-
const
|
|
259
|
+
// ── Universal: POST with a user-supplied JSON body ───────────────
|
|
260
|
+
const eranolAction = this.getNodeParameter('eranolAction', i);
|
|
261
|
+
const route = operations_1.OPERATION_BY_VALUE[eranolAction];
|
|
369
262
|
if (!route) {
|
|
370
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation: ${
|
|
263
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown operation: ${eranolAction}`);
|
|
264
|
+
}
|
|
265
|
+
const rawBody = this.getNodeParameter(`body_${eranolAction}`, i, '{}');
|
|
266
|
+
let body;
|
|
267
|
+
if (typeof rawBody === 'string') {
|
|
268
|
+
try {
|
|
269
|
+
body = JSON.parse(rawBody || '{}');
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Body is not valid JSON: ${error.message}`, { itemIndex: i });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
body = rawBody;
|
|
371
277
|
}
|
|
372
|
-
|
|
373
|
-
? JSON.parse(p('jsonBody', '{}'))
|
|
374
|
-
: buildBody(operation, p);
|
|
375
|
-
responseData = await request('eranolApi', {
|
|
278
|
+
responseData = await request({
|
|
376
279
|
method: route.method,
|
|
377
280
|
url: `${BASE_URL}${route.url}`,
|
|
378
281
|
headers: {
|
|
379
282
|
Accept: 'application/json',
|
|
380
283
|
'Content-Type': 'application/json',
|
|
284
|
+
...authHeaders,
|
|
381
285
|
},
|
|
382
286
|
body,
|
|
383
287
|
json: true,
|
|
384
288
|
});
|
|
385
289
|
}
|
|
386
|
-
|
|
290
|
+
// Normalise to a JSON-safe value. httpRequest can hand back a full
|
|
291
|
+
// response wrapper (with circular socket refs) when the body is not
|
|
292
|
+
// JSON; strip it down to the parsed body or a plain wrapper.
|
|
293
|
+
const safeData = toJsonSafe(responseData);
|
|
294
|
+
const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(safeData), { itemData: { item: i } });
|
|
387
295
|
returnData.push(...executionData);
|
|
388
296
|
}
|
|
389
297
|
return [returnData];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { INodePropertyOptions } from 'n8n-workflow';
|
|
2
|
+
/**
|
|
3
|
+
* Authoritative table of every Eranol POST operation exposed by the Universal
|
|
4
|
+
* resource. Each entry carries the exact display name used in the Eranol
|
|
5
|
+
* documentation, the HTTP route, a link to its documentation page, and a set of
|
|
6
|
+
* copy-paste-ready payload examples taken verbatim from the Eranol docs.
|
|
7
|
+
*
|
|
8
|
+
* Source: https://www.eranol.com/documentation (per-endpoint pages).
|
|
9
|
+
*/
|
|
10
|
+
export interface EranolExample {
|
|
11
|
+
/** Short label shown in the Example dropdown, e.g. "Basic" or "Styled". */
|
|
12
|
+
label: string;
|
|
13
|
+
/** Copy-paste-ready request body for this use case. */
|
|
14
|
+
body: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
export interface EranolOperation {
|
|
17
|
+
name: string;
|
|
18
|
+
value: string;
|
|
19
|
+
description: string;
|
|
20
|
+
method: 'POST';
|
|
21
|
+
url: string;
|
|
22
|
+
/** Full documentation page for this operation. */
|
|
23
|
+
docs: string;
|
|
24
|
+
examples: EranolExample[];
|
|
25
|
+
}
|
|
26
|
+
export declare const OPERATIONS: EranolOperation[];
|
|
27
|
+
/** Map keyed by operation value for fast route lookup at execution time. */
|
|
28
|
+
export declare const OPERATION_BY_VALUE: Record<string, EranolOperation>;
|
|
29
|
+
/** Dropdown options for the Universal Operation field, sorted alphabetically. */
|
|
30
|
+
export declare const OPERATION_OPTIONS: INodePropertyOptions[];
|