n8n-nodes-tornado-api 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,467 @@
1
+ # 🌪️ Tornado API for n8n
2
+
3
+ Download YouTube videos & Spotify podcasts directly in your n8n workflows.
4
+
5
+ ## Installation
6
+
7
+ ### Via npm (recommended)
8
+ ```bash
9
+ cd ~/.n8n/custom
10
+ npm install n8n-nodes-tornado-api
11
+ ```
12
+
13
+ ### Via Docker
14
+ ```bash
15
+ docker run -it --rm -p 5678:5678 \
16
+ -v n8n_data:/home/node/.n8n \
17
+ -v /path/to/n8n-nodes-tornado-api:/home/node/.n8n/custom/n8n-nodes-tornado-api \
18
+ n8nio/n8n
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Credentials Setup
24
+
25
+ 1. Go to **Credentials** → **New**
26
+ 2. Search for **Tornado API**
27
+ 3. Enter your API Key (starts with `sk_`)
28
+ 4. Base URL: `https://tornado.velys.software` (default)
29
+
30
+ ---
31
+
32
+ ## Operations
33
+
34
+ ### Job → Create
35
+
36
+ Creates a download job. Returns immediately with a `job_id`.
37
+
38
+ **Input:**
39
+ | Field | Type | Required | Description |
40
+ |-------|------|----------|-------------|
41
+ | URL | string | ✅ | YouTube or Spotify URL |
42
+ | Format | select | ❌ | mp4, mkv, webm, mov |
43
+ | Video Codec | select | ❌ | copy, h264, h265, vp9 |
44
+ | Audio Codec | select | ❌ | copy, aac, opus, mp3 |
45
+ | Audio Bitrate | select | ❌ | 64k to 320k |
46
+ | Video Quality | number | ❌ | CRF 0-51 (lower = better) |
47
+ | Filename | string | ❌ | Custom filename |
48
+ | Folder | string | ❌ | S3 folder prefix |
49
+ | Webhook URL | string | ❌ | Notification URL |
50
+
51
+ **Output (YouTube):**
52
+ ```json
53
+ {
54
+ "job_id": "550e8400-e29b-41d4-a716-446655440000"
55
+ }
56
+ ```
57
+
58
+ **Output (Spotify Show):**
59
+ ```json
60
+ {
61
+ "batch_id": "550e8400-e29b-41d4-a716-446655440001",
62
+ "total_episodes": 142,
63
+ "episode_jobs": ["job-1", "job-2", "..."]
64
+ }
65
+ ```
66
+
67
+ ---
68
+
69
+ ### Job → Get Status
70
+
71
+ Check the current status of a job.
72
+
73
+ **Input:**
74
+ | Field | Type | Required |
75
+ |-------|------|----------|
76
+ | Job ID | string | ✅ |
77
+
78
+ **Output:**
79
+ ```json
80
+ {
81
+ "id": "550e8400-e29b-41d4-a716-446655440000",
82
+ "url": "https://youtube.com/watch?v=...",
83
+ "status": "Completed",
84
+ "s3_url": "https://your-bucket.s3.amazonaws.com/videos/video.mp4?X-Amz-Algorithm=...",
85
+ "step": "Finished",
86
+ "error": null
87
+ }
88
+ ```
89
+
90
+ ---
91
+
92
+ ### Job → Wait for Completion
93
+
94
+ Polls until the job is complete or fails. **Best for simple workflows.**
95
+
96
+ **Input:**
97
+ | Field | Type | Default | Description |
98
+ |-------|------|---------|-------------|
99
+ | Job ID | string | - | The job UUID |
100
+ | Timeout | number | 600 | Max wait time (seconds) |
101
+ | Poll Interval | number | 5 | Time between checks (seconds) |
102
+
103
+ **Output:** Same as Get Status (when completed)
104
+
105
+ ---
106
+
107
+ ## 📦 S3 Storage - How It Works
108
+
109
+ ### Default Flow
110
+
111
+ ```
112
+ 1. You create a job → Tornado downloads the video
113
+ 2. Video is uploaded to S3 → Tornado generates a presigned URL
114
+ 3. You get the s3_url → Valid for 24 hours
115
+ ```
116
+
117
+ ### The `s3_url` Field
118
+
119
+ When a job completes, you receive a **presigned S3 URL**:
120
+
121
+ ```json
122
+ {
123
+ "s3_url": "https://bucket.s3.region.amazonaws.com/videos/my-video.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Signature=..."
124
+ }
125
+ ```
126
+
127
+ This URL:
128
+ - ✅ Can be downloaded directly (no auth needed)
129
+ - ✅ Works in browsers, wget, curl
130
+ - ✅ Valid for **24 hours**
131
+ - ❌ Expires after 24h (request new URL via Get Status)
132
+
133
+ ### Using s3_url in n8n
134
+
135
+ **Download the file:**
136
+ ```
137
+ HTTP Request node:
138
+ - Method: GET
139
+ - URL: {{ $json.s3_url }}
140
+ - Response: File
141
+ ```
142
+
143
+ **Send to user:**
144
+ ```
145
+ Telegram/Slack/Discord node:
146
+ - File URL: {{ $json.s3_url }}
147
+ ```
148
+
149
+ **Save to Google Drive:**
150
+ ```
151
+ Google Drive node:
152
+ - Upload from URL: {{ $json.s3_url }}
153
+ ```
154
+
155
+ ---
156
+
157
+ ## 🔄 Workflow Examples
158
+
159
+ ### Example 1: Simple YouTube Download
160
+
161
+ ```
162
+ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
163
+ │ Trigger │ → │ Tornado API │ → │ Tornado API │
164
+ │ (Manual) │ │ Job:Create │ │ Job:Wait │
165
+ └─────────────┘ └─────────────┘ └─────────────┘
166
+ │ │
167
+ ▼ ▼
168
+ { job_id: ... } { s3_url: ... }
169
+ ```
170
+
171
+ **Node 1 - Tornado API (Create):**
172
+ - Resource: Job
173
+ - Operation: Create
174
+ - URL: `https://youtube.com/watch?v=dQw4w9WgXcQ`
175
+
176
+ **Node 2 - Tornado API (Wait):**
177
+ - Resource: Job
178
+ - Operation: Wait for Completion
179
+ - Job ID: `{{ $json.job_id }}`
180
+
181
+ ---
182
+
183
+ ### Example 2: Download + Send to Telegram
184
+
185
+ ```
186
+ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
187
+ │ Telegram │ → │ Tornado API │ → │ Tornado API │ → │ Telegram │
188
+ │ Trigger │ │ Job:Create │ │ Job:Wait │ │ Send Video │
189
+ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
190
+ ```
191
+
192
+ **Telegram Trigger:** Receives YouTube URL from user
193
+ **Tornado Create:** URL = `{{ $json.message.text }}`
194
+ **Tornado Wait:** Job ID = `{{ $json.job_id }}`
195
+ **Telegram Send:** Video URL = `{{ $json.s3_url }}`
196
+
197
+ ---
198
+
199
+ ### Example 3: Batch Spotify Podcast
200
+
201
+ ```
202
+ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
203
+ │ Trigger │ → │ Tornado API │ → │ Split In │ → │ Tornado API │
204
+ │ │ │ Job:Create │ │ Batches │ │ Job:Wait │
205
+ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
206
+
207
+
208
+ { batch_id: ...,
209
+ episode_jobs: [...] }
210
+ ```
211
+
212
+ **Tornado Create:**
213
+ - URL: `https://open.spotify.com/show/...`
214
+ - Folder: `my-podcast`
215
+
216
+ **Split In Batches:**
217
+ - Input: `{{ $json.episode_jobs }}`
218
+ - Batch Size: 10
219
+
220
+ **Tornado Wait (for each):**
221
+ - Job ID: `{{ $json }}`
222
+
223
+ ---
224
+
225
+ ### Example 4: Configure Custom S3 Bucket + Download
226
+
227
+ Use your own S3 bucket (AWS, Cloudflare R2, MinIO):
228
+
229
+ ```
230
+ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
231
+ │ Manual │ → │ Tornado API │ → │ Tornado API │ → │ Tornado API │
232
+ │ Trigger │ │ Storage: │ │ Job:Create │ │ Job:Wait │
233
+ │ │ │ Configure │ │ │ │ │
234
+ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
235
+ │ │
236
+ ▼ ▼
237
+ Bucket linked! s3_url points to
238
+ YOUR bucket!
239
+ ```
240
+
241
+ **Node 1 - Tornado API (Configure Bucket):**
242
+ - Resource: **Storage**
243
+ - Operation: **Configure Bucket**
244
+ - Provider: Amazon S3 / Cloudflare R2 / MinIO
245
+ - Endpoint URL: `https://s3.us-east-1.amazonaws.com`
246
+ - Bucket Name: `my-videos-bucket`
247
+ - Region: `us-east-1`
248
+ - Access Key ID: `AKIA...`
249
+ - Secret Access Key: `********`
250
+
251
+ **Node 2 - Tornado API (Create Job):**
252
+ - Resource: Job
253
+ - Operation: Create
254
+ - URL: `https://youtube.com/watch?v=...`
255
+
256
+ **Node 3 - Tornado API (Wait):**
257
+ - Resource: Job
258
+ - Operation: Wait for Completion
259
+ - Job ID: `{{ $json.job_id }}`
260
+
261
+ **Output:**
262
+ ```json
263
+ {
264
+ "status": "Completed",
265
+ "s3_url": "https://my-videos-bucket.s3.us-east-1.amazonaws.com/videos/video.mp4?X-Amz-..."
266
+ }
267
+ ```
268
+
269
+ ---
270
+
271
+ ### Example 5: Cloudflare R2 Setup
272
+
273
+ ```
274
+ ┌─────────────┐
275
+ │ Tornado API │
276
+ │ Storage: │
277
+ │ Configure │
278
+ └─────────────┘
279
+ ```
280
+
281
+ **Settings for R2:**
282
+ - Provider: **Cloudflare R2**
283
+ - Endpoint URL: `https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com`
284
+ - Bucket Name: `my-r2-bucket`
285
+ - Region: `auto`
286
+ - Access Key ID: (from R2 API Tokens)
287
+ - Secret Access Key: (from R2 API Tokens)
288
+
289
+ ---
290
+
291
+ ### Example 6: Reset to Default Storage
292
+
293
+ If you want to stop using your custom bucket:
294
+
295
+ ```
296
+ ┌─────────────┐
297
+ │ Tornado API │
298
+ │ Storage: │
299
+ │ Reset │
300
+ └─────────────┘
301
+ ```
302
+
303
+ - Resource: **Storage**
304
+ - Operation: **Reset to Default**
305
+
306
+ Files will be uploaded to Tornado's default storage again.
307
+
308
+ ---
309
+
310
+ ## 📊 Status Values
311
+
312
+ | Status | Description | s3_url |
313
+ |--------|-------------|--------|
314
+ | `Pending` | In queue | ❌ |
315
+ | `Processing` | Downloading/encoding | ❌ |
316
+ | `Completed` | Done! | ✅ |
317
+ | `Failed` | Error occurred | ❌ |
318
+
319
+ ## 📍 Processing Steps
320
+
321
+ | Step | Description |
322
+ |------|-------------|
323
+ | `Queued` | Waiting in queue |
324
+ | `Downloading` | Fetching video/audio |
325
+ | `Muxing` | Combining with FFmpeg |
326
+ | `Uploading` | Sending to S3 |
327
+ | `Finished` | Complete |
328
+
329
+ ---
330
+
331
+ ## ⚠️ Error Handling
332
+
333
+ Use the **IF** node to handle errors:
334
+
335
+ ```
336
+ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
337
+ │ Tornado API │ → │ IF │ → │ Success │
338
+ │ Job:Wait │ │status=Done? │ │ Path │
339
+ └─────────────┘ └─────────────┘ └─────────────┘
340
+
341
+ ▼ (else)
342
+ ┌─────────────┐
343
+ │ Error │
344
+ │ Handler │
345
+ └─────────────┘
346
+ ```
347
+
348
+ **IF Condition:**
349
+ ```
350
+ {{ $json.status }} == "Completed"
351
+ ```
352
+
353
+ ---
354
+
355
+ ## 🛠️ Development
356
+
357
+ ### Build from Source
358
+
359
+ ```bash
360
+ git clone https://github.com/velys-software/n8n-nodes-tornado-api
361
+ cd n8n-nodes-tornado-api
362
+ npm install
363
+ npm run build
364
+ ```
365
+
366
+ ### Local Testing
367
+
368
+ 1. Build the node: `npm run build`
369
+ 2. Link to n8n:
370
+ ```bash
371
+ # Windows
372
+ mklink /D "%USERPROFILE%\.n8n\custom\n8n-nodes-tornado-api" "D:\path\to\n8n-nodes-tornado-api"
373
+
374
+ # Linux/Mac
375
+ ln -s /path/to/n8n-nodes-tornado-api ~/.n8n/custom/n8n-nodes-tornado-api
376
+ ```
377
+ 3. Start n8n: `n8n start`
378
+
379
+ ### Watch Mode
380
+
381
+ ```bash
382
+ npm run dev
383
+ ```
384
+
385
+ Automatically rebuilds on file changes.
386
+
387
+ ---
388
+
389
+ ## 📦 Publishing to npm
390
+
391
+ To publish this node so others can install it via `npm install`:
392
+
393
+ ### 1. Prerequisites
394
+
395
+ - npm account ([npmjs.com](https://www.npmjs.com/))
396
+ - Node.js 18+ installed
397
+
398
+ ### 2. Login to npm
399
+
400
+ ```bash
401
+ npm login
402
+ ```
403
+
404
+ ### 3. Update package.json
405
+
406
+ Ensure these fields are correct:
407
+ ```json
408
+ {
409
+ "name": "n8n-nodes-tornado-api",
410
+ "version": "1.0.0",
411
+ "description": "n8n node for Tornado API - Download YouTube videos & Spotify podcasts",
412
+ "author": "Velys Software",
413
+ "license": "MIT"
414
+ }
415
+ ```
416
+
417
+ ### 4. Build & Publish
418
+
419
+ ```bash
420
+ npm run build
421
+ npm publish
422
+ ```
423
+
424
+ ### 5. Versioning
425
+
426
+ For updates:
427
+ ```bash
428
+ npm version patch # 1.0.0 → 1.0.1 (bug fixes)
429
+ npm version minor # 1.0.0 → 1.1.0 (new features)
430
+ npm version major # 1.0.0 → 2.0.0 (breaking changes)
431
+ npm publish
432
+ ```
433
+
434
+ ---
435
+
436
+ ## ✅ n8n Community Verification (Optional)
437
+
438
+ To get your node listed in the official n8n integrations:
439
+
440
+ ### 1. Requirements
441
+
442
+ - Node must be published on npm
443
+ - Must follow n8n node naming: `n8n-nodes-*`
444
+ - Include proper documentation
445
+ - Include icon (SVG or PNG)
446
+ - Pass basic functionality tests
447
+
448
+ ### 2. Submit for Review
449
+
450
+ 1. Go to [n8n Community Nodes](https://docs.n8n.io/integrations/community-nodes/)
451
+ 2. Submit your node for review via their process
452
+ 3. n8n team will review and potentially feature it
453
+
454
+ ### 3. Benefits of Verification
455
+
456
+ - Listed in n8n's official integrations
457
+ - Discoverable in n8n node search
458
+ - Increased trust and visibility
459
+
460
+ ---
461
+
462
+ ## 🔗 Links
463
+
464
+ - [Tornado API](https://tornado.velys.software)
465
+ - [API Documentation](https://docs.tornado.velys.software)
466
+ - [Get API Key](https://tornado.velys.software/dashboard)
467
+ - [Support](mailto:support@velys.software)
@@ -0,0 +1,10 @@
1
+ import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class TornadoApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ icon: string;
6
+ documentationUrl: string;
7
+ properties: INodeProperties[];
8
+ authenticate: IAuthenticateGeneric;
9
+ test: ICredentialTestRequest;
10
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TornadoApi = void 0;
4
+ class TornadoApi {
5
+ constructor() {
6
+ this.name = 'tornadoApi';
7
+ this.displayName = 'Tornado API';
8
+ this.icon = 'file:tornado.svg';
9
+ this.documentationUrl = 'https://docs.tornado.velys.software/authentication';
10
+ this.properties = [
11
+ {
12
+ displayName: 'API Key',
13
+ name: 'apiKey',
14
+ type: 'string',
15
+ typeOptions: { password: true },
16
+ default: '',
17
+ required: true,
18
+ description: 'Your Tornado API key (starts with sk_)',
19
+ },
20
+ {
21
+ displayName: 'Base URL',
22
+ name: 'baseUrl',
23
+ type: 'string',
24
+ default: 'https://tornado.velys.software',
25
+ description: 'The base URL of the Tornado API',
26
+ },
27
+ ];
28
+ this.authenticate = {
29
+ type: 'generic',
30
+ properties: {
31
+ headers: {
32
+ 'x-api-key': '={{$credentials.apiKey}}',
33
+ },
34
+ },
35
+ };
36
+ this.test = {
37
+ request: {
38
+ baseURL: '={{$credentials.baseUrl}}',
39
+ url: '/usage',
40
+ method: 'GET',
41
+ },
42
+ };
43
+ }
44
+ }
45
+ exports.TornadoApi = TornadoApi;
46
+ //# sourceMappingURL=TornadoApi.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TornadoApi.credentials.js","sourceRoot":"","sources":["../../credentials/TornadoApi.credentials.ts"],"names":[],"mappings":";;;AAOA,MAAa,UAAU;IAAvB;QACC,SAAI,GAAG,YAAY,CAAC;QACpB,gBAAW,GAAG,aAAa,CAAC;QAC5B,SAAI,GAAG,kBAAkB,CAAC;QAC1B,qBAAgB,GAAG,oDAAoD,CAAC;QACxE,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,SAAS;gBACtB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,wCAAwC;aACrD;YACD;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,gCAAgC;gBACzC,WAAW,EAAE,iCAAiC;aAC9C;SACD,CAAC;QAEF,iBAAY,GAAyB;YACpC,IAAI,EAAE,SAAS;YACf,UAAU,EAAE;gBACX,OAAO,EAAE;oBACR,WAAW,EAAE,0BAA0B;iBACvC;aACD;SACD,CAAC;QAEF,SAAI,GAA2B;YAC9B,OAAO,EAAE;gBACR,OAAO,EAAE,2BAA2B;gBACpC,GAAG,EAAE,QAAQ;gBACb,MAAM,EAAE,KAAK;aACb;SACD,CAAC;IACH,CAAC;CAAA;AAxCD,gCAwCC"}
@@ -0,0 +1,5 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class TornadoApi implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,591 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TornadoApi = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ class TornadoApi {
6
+ constructor() {
7
+ this.description = {
8
+ displayName: 'Tornado API',
9
+ name: 'tornadoApi',
10
+ icon: 'file:tornado.svg',
11
+ group: ['transform'],
12
+ version: 1,
13
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
14
+ description: 'Download videos and podcasts from YouTube, Spotify and more with Tornado API',
15
+ defaults: {
16
+ name: 'Tornado API',
17
+ },
18
+ inputs: ['main'],
19
+ outputs: ['main'],
20
+ credentials: [
21
+ {
22
+ name: 'tornadoApi',
23
+ required: true,
24
+ },
25
+ ],
26
+ properties: [
27
+ // Resource
28
+ {
29
+ displayName: 'Resource',
30
+ name: 'resource',
31
+ type: 'options',
32
+ noDataExpression: true,
33
+ options: [
34
+ {
35
+ name: 'Job',
36
+ value: 'job',
37
+ },
38
+ {
39
+ name: 'Batch',
40
+ value: 'batch',
41
+ },
42
+ {
43
+ name: 'Storage',
44
+ value: 'storage',
45
+ },
46
+ {
47
+ name: 'Account',
48
+ value: 'account',
49
+ },
50
+ ],
51
+ default: 'job',
52
+ },
53
+ // ==================== JOB OPERATIONS ====================
54
+ {
55
+ displayName: 'Operation',
56
+ name: 'operation',
57
+ type: 'options',
58
+ noDataExpression: true,
59
+ displayOptions: {
60
+ show: {
61
+ resource: ['job'],
62
+ },
63
+ },
64
+ options: [
65
+ {
66
+ name: 'Create',
67
+ value: 'create',
68
+ description: 'Create a new download job',
69
+ action: 'Create a download job',
70
+ },
71
+ {
72
+ name: 'Get Status',
73
+ value: 'getStatus',
74
+ description: 'Get the status of a job',
75
+ action: 'Get job status',
76
+ },
77
+ {
78
+ name: 'Wait for Completion',
79
+ value: 'waitForCompletion',
80
+ description: 'Wait for a job to complete',
81
+ action: 'Wait for job completion',
82
+ },
83
+ ],
84
+ default: 'create',
85
+ },
86
+ // Job Create Fields
87
+ {
88
+ displayName: 'URL',
89
+ name: 'url',
90
+ type: 'string',
91
+ required: true,
92
+ displayOptions: {
93
+ show: {
94
+ resource: ['job'],
95
+ operation: ['create'],
96
+ },
97
+ },
98
+ default: '',
99
+ placeholder: 'https://www.youtube.com/watch?v=...',
100
+ description: 'The video or Spotify show URL to download',
101
+ },
102
+ {
103
+ displayName: 'Additional Options',
104
+ name: 'additionalOptions',
105
+ type: 'collection',
106
+ placeholder: 'Add Option',
107
+ default: {},
108
+ displayOptions: {
109
+ show: {
110
+ resource: ['job'],
111
+ operation: ['create'],
112
+ },
113
+ },
114
+ options: [
115
+ {
116
+ displayName: 'Format',
117
+ name: 'format',
118
+ type: 'options',
119
+ options: [
120
+ { name: 'MP4', value: 'mp4' },
121
+ { name: 'MKV', value: 'mkv' },
122
+ { name: 'WebM', value: 'webm' },
123
+ { name: 'MOV', value: 'mov' },
124
+ ],
125
+ default: 'mp4',
126
+ description: 'Output container format',
127
+ },
128
+ {
129
+ displayName: 'Video Codec',
130
+ name: 'video_codec',
131
+ type: 'options',
132
+ options: [
133
+ { name: 'Copy (No Re-encode)', value: 'copy' },
134
+ { name: 'H.264', value: 'h264' },
135
+ { name: 'H.265 (HEVC)', value: 'h265' },
136
+ { name: 'VP9', value: 'vp9' },
137
+ ],
138
+ default: 'copy',
139
+ description: 'Video codec to use',
140
+ },
141
+ {
142
+ displayName: 'Audio Codec',
143
+ name: 'audio_codec',
144
+ type: 'options',
145
+ options: [
146
+ { name: 'Copy (No Re-encode)', value: 'copy' },
147
+ { name: 'AAC', value: 'aac' },
148
+ { name: 'Opus', value: 'opus' },
149
+ { name: 'MP3', value: 'mp3' },
150
+ ],
151
+ default: 'copy',
152
+ description: 'Audio codec to use',
153
+ },
154
+ {
155
+ displayName: 'Audio Bitrate',
156
+ name: 'audio_bitrate',
157
+ type: 'options',
158
+ options: [
159
+ { name: '64 kbps', value: '64k' },
160
+ { name: '128 kbps', value: '128k' },
161
+ { name: '192 kbps', value: '192k' },
162
+ { name: '256 kbps', value: '256k' },
163
+ { name: '320 kbps', value: '320k' },
164
+ ],
165
+ default: '192k',
166
+ description: 'Audio bitrate when transcoding',
167
+ },
168
+ {
169
+ displayName: 'Video Quality (CRF)',
170
+ name: 'video_quality',
171
+ type: 'number',
172
+ typeOptions: {
173
+ minValue: 0,
174
+ maxValue: 51,
175
+ },
176
+ default: 23,
177
+ description: 'Video quality CRF (0-51, lower = better). Only used when video codec is not "copy".',
178
+ },
179
+ {
180
+ displayName: 'Custom Filename',
181
+ name: 'filename',
182
+ type: 'string',
183
+ default: '',
184
+ description: 'Custom filename (without extension)',
185
+ },
186
+ {
187
+ displayName: 'Folder',
188
+ name: 'folder',
189
+ type: 'string',
190
+ default: '',
191
+ description: 'S3 folder prefix for organizing files',
192
+ },
193
+ {
194
+ displayName: 'Webhook URL',
195
+ name: 'webhook_url',
196
+ type: 'string',
197
+ default: '',
198
+ description: 'URL to receive completion notification',
199
+ },
200
+ ],
201
+ },
202
+ // Job Get Status Fields
203
+ {
204
+ displayName: 'Job ID',
205
+ name: 'jobId',
206
+ type: 'string',
207
+ required: true,
208
+ displayOptions: {
209
+ show: {
210
+ resource: ['job'],
211
+ operation: ['getStatus', 'waitForCompletion'],
212
+ },
213
+ },
214
+ default: '',
215
+ description: 'The UUID of the job',
216
+ },
217
+ // Wait for Completion Options
218
+ {
219
+ displayName: 'Timeout (Seconds)',
220
+ name: 'timeout',
221
+ type: 'number',
222
+ displayOptions: {
223
+ show: {
224
+ resource: ['job'],
225
+ operation: ['waitForCompletion'],
226
+ },
227
+ },
228
+ default: 600,
229
+ description: 'Maximum time to wait for completion',
230
+ },
231
+ {
232
+ displayName: 'Poll Interval (Seconds)',
233
+ name: 'pollInterval',
234
+ type: 'number',
235
+ displayOptions: {
236
+ show: {
237
+ resource: ['job'],
238
+ operation: ['waitForCompletion'],
239
+ },
240
+ },
241
+ default: 5,
242
+ description: 'Time between status checks',
243
+ },
244
+ // ==================== BATCH OPERATIONS ====================
245
+ {
246
+ displayName: 'Operation',
247
+ name: 'operation',
248
+ type: 'options',
249
+ noDataExpression: true,
250
+ displayOptions: {
251
+ show: {
252
+ resource: ['batch'],
253
+ },
254
+ },
255
+ options: [
256
+ {
257
+ name: 'Get Status',
258
+ value: 'getStatus',
259
+ description: 'Get the status of a batch',
260
+ action: 'Get batch status',
261
+ },
262
+ ],
263
+ default: 'getStatus',
264
+ },
265
+ // Batch Get Status Fields
266
+ {
267
+ displayName: 'Batch ID',
268
+ name: 'batchId',
269
+ type: 'string',
270
+ required: true,
271
+ displayOptions: {
272
+ show: {
273
+ resource: ['batch'],
274
+ operation: ['getStatus'],
275
+ },
276
+ },
277
+ default: '',
278
+ description: 'The UUID of the batch',
279
+ },
280
+ // ==================== STORAGE OPERATIONS ====================
281
+ {
282
+ displayName: 'Operation',
283
+ name: 'operation',
284
+ type: 'options',
285
+ noDataExpression: true,
286
+ displayOptions: {
287
+ show: {
288
+ resource: ['storage'],
289
+ },
290
+ },
291
+ options: [
292
+ {
293
+ name: 'Configure Bucket',
294
+ value: 'configureBucket',
295
+ description: 'Configure your own S3/R2 bucket for file uploads',
296
+ action: 'Configure S3 bucket',
297
+ },
298
+ {
299
+ name: 'Reset to Default',
300
+ value: 'resetBucket',
301
+ description: 'Reset to use Tornado default storage',
302
+ action: 'Reset to default storage',
303
+ },
304
+ ],
305
+ default: 'configureBucket',
306
+ },
307
+ // Storage Provider
308
+ {
309
+ displayName: 'Provider',
310
+ name: 'provider',
311
+ type: 'options',
312
+ displayOptions: {
313
+ show: {
314
+ resource: ['storage'],
315
+ operation: ['configureBucket'],
316
+ },
317
+ },
318
+ options: [
319
+ { name: 'Amazon S3', value: 'aws' },
320
+ { name: 'Cloudflare R2', value: 'r2' },
321
+ { name: 'MinIO', value: 'minio' },
322
+ { name: 'Other S3-Compatible', value: 'other' },
323
+ ],
324
+ default: 'aws',
325
+ description: 'Your storage provider',
326
+ },
327
+ // S3 Endpoint
328
+ {
329
+ displayName: 'Endpoint URL',
330
+ name: 'endpoint',
331
+ type: 'string',
332
+ required: true,
333
+ displayOptions: {
334
+ show: {
335
+ resource: ['storage'],
336
+ operation: ['configureBucket'],
337
+ },
338
+ },
339
+ default: '',
340
+ placeholder: 'https://s3.us-east-1.amazonaws.com',
341
+ description: 'S3 endpoint URL. For AWS use https://s3.REGION.amazonaws.com, for R2 use https://ACCOUNT_ID.r2.cloudflarestorage.com',
342
+ },
343
+ // Bucket Name
344
+ {
345
+ displayName: 'Bucket Name',
346
+ name: 'bucket',
347
+ type: 'string',
348
+ required: true,
349
+ displayOptions: {
350
+ show: {
351
+ resource: ['storage'],
352
+ operation: ['configureBucket'],
353
+ },
354
+ },
355
+ default: '',
356
+ placeholder: 'my-videos-bucket',
357
+ description: 'The name of your S3 bucket',
358
+ },
359
+ // Region
360
+ {
361
+ displayName: 'Region',
362
+ name: 'region',
363
+ type: 'string',
364
+ required: true,
365
+ displayOptions: {
366
+ show: {
367
+ resource: ['storage'],
368
+ operation: ['configureBucket'],
369
+ },
370
+ },
371
+ default: 'us-east-1',
372
+ placeholder: 'us-east-1',
373
+ description: 'AWS region (use "auto" for Cloudflare R2)',
374
+ },
375
+ // Access Key
376
+ {
377
+ displayName: 'Access Key ID',
378
+ name: 'accessKey',
379
+ type: 'string',
380
+ required: true,
381
+ displayOptions: {
382
+ show: {
383
+ resource: ['storage'],
384
+ operation: ['configureBucket'],
385
+ },
386
+ },
387
+ default: '',
388
+ description: 'Your S3 Access Key ID',
389
+ },
390
+ // Secret Key
391
+ {
392
+ displayName: 'Secret Access Key',
393
+ name: 'secretKey',
394
+ type: 'string',
395
+ typeOptions: { password: true },
396
+ required: true,
397
+ displayOptions: {
398
+ show: {
399
+ resource: ['storage'],
400
+ operation: ['configureBucket'],
401
+ },
402
+ },
403
+ default: '',
404
+ description: 'Your S3 Secret Access Key',
405
+ },
406
+ // ==================== ACCOUNT OPERATIONS ====================
407
+ {
408
+ displayName: 'Operation',
409
+ name: 'operation',
410
+ type: 'options',
411
+ noDataExpression: true,
412
+ displayOptions: {
413
+ show: {
414
+ resource: ['account'],
415
+ },
416
+ },
417
+ options: [
418
+ {
419
+ name: 'Get Usage',
420
+ value: 'getUsage',
421
+ description: 'Get account usage statistics',
422
+ action: 'Get usage statistics',
423
+ },
424
+ ],
425
+ default: 'getUsage',
426
+ },
427
+ ],
428
+ };
429
+ }
430
+ async execute() {
431
+ const items = this.getInputData();
432
+ const returnData = [];
433
+ const credentials = await this.getCredentials('tornadoApi');
434
+ const baseUrl = credentials.baseUrl;
435
+ for (let i = 0; i < items.length; i++) {
436
+ try {
437
+ const resource = this.getNodeParameter('resource', i);
438
+ const operation = this.getNodeParameter('operation', i);
439
+ let responseData;
440
+ // ==================== JOB ====================
441
+ if (resource === 'job') {
442
+ if (operation === 'create') {
443
+ const url = this.getNodeParameter('url', i);
444
+ const additionalOptions = this.getNodeParameter('additionalOptions', i);
445
+ const body = { url };
446
+ // Add optional fields
447
+ if (additionalOptions.format)
448
+ body.format = additionalOptions.format;
449
+ if (additionalOptions.video_codec)
450
+ body.video_codec = additionalOptions.video_codec;
451
+ if (additionalOptions.audio_codec)
452
+ body.audio_codec = additionalOptions.audio_codec;
453
+ if (additionalOptions.audio_bitrate)
454
+ body.audio_bitrate = additionalOptions.audio_bitrate;
455
+ if (additionalOptions.video_quality !== undefined)
456
+ body.video_quality = additionalOptions.video_quality;
457
+ if (additionalOptions.filename)
458
+ body.filename = additionalOptions.filename;
459
+ if (additionalOptions.folder)
460
+ body.folder = additionalOptions.folder;
461
+ if (additionalOptions.webhook_url)
462
+ body.webhook_url = additionalOptions.webhook_url;
463
+ responseData = await this.helpers.httpRequest({
464
+ method: 'POST',
465
+ url: `${baseUrl}/jobs`,
466
+ body,
467
+ json: true,
468
+ headers: {
469
+ 'x-api-key': credentials.apiKey,
470
+ 'Content-Type': 'application/json',
471
+ },
472
+ });
473
+ }
474
+ if (operation === 'getStatus') {
475
+ const jobId = this.getNodeParameter('jobId', i);
476
+ responseData = await this.helpers.httpRequest({
477
+ method: 'GET',
478
+ url: `${baseUrl}/jobs/${jobId}`,
479
+ json: true,
480
+ headers: {
481
+ 'x-api-key': credentials.apiKey,
482
+ },
483
+ });
484
+ }
485
+ if (operation === 'waitForCompletion') {
486
+ const jobId = this.getNodeParameter('jobId', i);
487
+ const timeout = this.getNodeParameter('timeout', i);
488
+ const pollInterval = this.getNodeParameter('pollInterval', i);
489
+ const startTime = Date.now();
490
+ const timeoutMs = timeout * 1000;
491
+ while (Date.now() - startTime < timeoutMs) {
492
+ responseData = await this.helpers.httpRequest({
493
+ method: 'GET',
494
+ url: `${baseUrl}/jobs/${jobId}`,
495
+ json: true,
496
+ headers: {
497
+ 'x-api-key': credentials.apiKey,
498
+ },
499
+ });
500
+ if (responseData.status === 'Completed') {
501
+ break;
502
+ }
503
+ if (responseData.status === 'Failed') {
504
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Job failed: ${responseData.error || 'Unknown error'}`, { itemIndex: i });
505
+ }
506
+ // Wait before next poll
507
+ await new Promise((resolve) => setTimeout(resolve, pollInterval * 1000));
508
+ }
509
+ if (responseData.status !== 'Completed' && responseData.status !== 'Failed') {
510
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Timeout waiting for job completion. Last status: ${responseData.status}`, { itemIndex: i });
511
+ }
512
+ }
513
+ }
514
+ // ==================== BATCH ====================
515
+ if (resource === 'batch') {
516
+ if (operation === 'getStatus') {
517
+ const batchId = this.getNodeParameter('batchId', i);
518
+ responseData = await this.helpers.httpRequest({
519
+ method: 'GET',
520
+ url: `${baseUrl}/batch/${batchId}`,
521
+ json: true,
522
+ headers: {
523
+ 'x-api-key': credentials.apiKey,
524
+ },
525
+ });
526
+ }
527
+ }
528
+ // ==================== STORAGE ====================
529
+ if (resource === 'storage') {
530
+ if (operation === 'configureBucket') {
531
+ const endpoint = this.getNodeParameter('endpoint', i);
532
+ const bucket = this.getNodeParameter('bucket', i);
533
+ const region = this.getNodeParameter('region', i);
534
+ const accessKey = this.getNodeParameter('accessKey', i);
535
+ const secretKey = this.getNodeParameter('secretKey', i);
536
+ responseData = await this.helpers.httpRequest({
537
+ method: 'POST',
538
+ url: `${baseUrl}/user/bucket`,
539
+ body: {
540
+ endpoint,
541
+ bucket,
542
+ region,
543
+ access_key: accessKey,
544
+ secret_key: secretKey,
545
+ },
546
+ json: true,
547
+ headers: {
548
+ 'x-api-key': credentials.apiKey,
549
+ 'Content-Type': 'application/json',
550
+ },
551
+ });
552
+ }
553
+ if (operation === 'resetBucket') {
554
+ responseData = await this.helpers.httpRequest({
555
+ method: 'DELETE',
556
+ url: `${baseUrl}/user/bucket`,
557
+ json: true,
558
+ headers: {
559
+ 'x-api-key': credentials.apiKey,
560
+ },
561
+ });
562
+ }
563
+ }
564
+ // ==================== ACCOUNT ====================
565
+ if (resource === 'account') {
566
+ if (operation === 'getUsage') {
567
+ responseData = await this.helpers.httpRequest({
568
+ method: 'GET',
569
+ url: `${baseUrl}/usage`,
570
+ json: true,
571
+ headers: {
572
+ 'x-api-key': credentials.apiKey,
573
+ },
574
+ });
575
+ }
576
+ }
577
+ returnData.push({ json: responseData });
578
+ }
579
+ catch (error) {
580
+ if (this.continueOnFail()) {
581
+ returnData.push({ json: { error: error.message } });
582
+ continue;
583
+ }
584
+ throw error;
585
+ }
586
+ }
587
+ return [returnData];
588
+ }
589
+ }
590
+ exports.TornadoApi = TornadoApi;
591
+ //# sourceMappingURL=TornadoApi.node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TornadoApi.node.js","sourceRoot":"","sources":["../../../nodes/TornadoApi/TornadoApi.node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
2
+ <text x="50%" y="50%" dominant-baseline="central" text-anchor="middle" font-size="48">🌪️</text>
3
+ </svg>
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "n8n-nodes-tornado-api",
3
+ "version": "0.1.0",
4
+ "description": "n8n node for Tornado API - Download YouTube videos & Spotify podcasts",
5
+ "keywords": [
6
+ "n8n-community-node-package",
7
+ "n8n",
8
+ "tornado",
9
+ "video",
10
+ "downloader",
11
+ "youtube",
12
+ "spotify",
13
+ "podcast",
14
+ "mp4",
15
+ "mp3"
16
+ ],
17
+ "license": "MIT",
18
+ "homepage": "https://tornado.velys.software",
19
+ "author": {
20
+ "name": "Velys Software",
21
+ "email": "support@velys.software"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/velys/n8n-nodes-tornado-api.git"
26
+ },
27
+ "main": "index.js",
28
+ "scripts": {
29
+ "build": "tsc && gulp build:icons",
30
+ "dev": "tsc --watch",
31
+ "format": "prettier nodes credentials --write",
32
+ "lint": "eslint nodes credentials package.json",
33
+ "lintfix": "eslint nodes credentials package.json --fix",
34
+ "prepublishOnly": "npm run build"
35
+ },
36
+ "files": [
37
+ "dist"
38
+ ],
39
+ "n8n": {
40
+ "n8nNodesApiVersion": 1,
41
+ "credentials": [
42
+ "dist/credentials/TornadoApi.credentials.js"
43
+ ],
44
+ "nodes": [
45
+ "dist/nodes/TornadoApi/TornadoApi.node.js"
46
+ ]
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^20.10.0",
50
+ "@typescript-eslint/parser": "^6.0.0",
51
+ "eslint": "^8.56.0",
52
+ "gulp": "^5.0.1",
53
+ "n8n-workflow": "^1.17.0",
54
+ "prettier": "^3.1.0",
55
+ "typescript": "^5.3.0"
56
+ },
57
+ "peerDependencies": {
58
+ "n8n-workflow": "*"
59
+ }
60
+ }