n8n-nodes-sb-render 1.1.4 → 1.1.5
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/LICENSE +21 -21
- package/README.md +442 -442
- package/dist/nodes/SbRender/sbrender.svg +7 -7
- package/dist/nodes/SbRender/services/SubtitleEngine.js +9 -9
- package/index.js +3 -3
- package/package.json +73 -73
- package/scripts/fix-ffprobe-permissions.js +52 -52
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 sb-render contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 sb-render contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,442 +1,442 @@
|
|
|
1
|
-
# n8n-nodes-sb-render
|
|
2
|
-
|
|
3
|
-
[](https://badge.fury.io/js/n8n-nodes-sb-render)
|
|
4
|
-
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
|
|
6
|
-
This is an n8n community node for video rendering with customizable subtitles, background music (BGM), and narration using FFmpeg.
|
|
7
|
-
|
|
8
|
-
**sb-render** allows you to automate video composition workflows in n8n, combining video files with:
|
|
9
|
-
- 🎵 Background music with volume control and fade effects
|
|
10
|
-
- 🎙️ Narration audio with timing control
|
|
11
|
-
- 📝 Customizable subtitles with extensive styling options
|
|
12
|
-
- 🎨 Multiple output formats and quality presets
|
|
13
|
-
|
|
14
|
-
Inspired by [n8n-nodes-mediafx](https://github.com/dandacompany/n8n-nodes-mediafx).
|
|
15
|
-
|
|
16
|
-
## Table of Contents
|
|
17
|
-
|
|
18
|
-
- [Installation](#installation)
|
|
19
|
-
- [Prerequisites](#prerequisites)
|
|
20
|
-
- [Operations](#operations)
|
|
21
|
-
- [Configuration](#configuration)
|
|
22
|
-
- [Examples](#examples)
|
|
23
|
-
- [Development](#development)
|
|
24
|
-
- [Troubleshooting](#troubleshooting)
|
|
25
|
-
- [License](#license)
|
|
26
|
-
|
|
27
|
-
## Installation
|
|
28
|
-
|
|
29
|
-
### Community Nodes (Recommended)
|
|
30
|
-
|
|
31
|
-
1. Go to **Settings > Community Nodes** in n8n
|
|
32
|
-
2. Select **Install**
|
|
33
|
-
3. Enter `n8n-nodes-sb-render`
|
|
34
|
-
4. Agree to the risks and install
|
|
35
|
-
|
|
36
|
-
### Manual Installation
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
npm install n8n-nodes-sb-render
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
For n8n self-hosted installations:
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
cd ~/.n8n
|
|
46
|
-
npm install n8n-nodes-sb-render
|
|
47
|
-
# Restart n8n
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Prerequisites
|
|
51
|
-
|
|
52
|
-
- **n8n version**: 1.0.0 or higher
|
|
53
|
-
- **Node.js**: 16.0.0 or higher
|
|
54
|
-
- **FFmpeg**: Automatically installed via `@ffmpeg-installer/ffmpeg`
|
|
55
|
-
|
|
56
|
-
## Operations
|
|
57
|
-
|
|
58
|
-
### Video → Render
|
|
59
|
-
|
|
60
|
-
Compose a video with optional background music, narration, and subtitles.
|
|
61
|
-
|
|
62
|
-
**Features:**
|
|
63
|
-
- ✅ Multiple input sources (URL or binary data)
|
|
64
|
-
- ✅ Audio mixing with independent volume controls
|
|
65
|
-
- ✅ Fade in/out effects for BGM
|
|
66
|
-
- ✅ Customizable subtitle positioning and styling
|
|
67
|
-
- ✅ Multiple output formats (MP4, MOV, WebM)
|
|
68
|
-
- ✅ Quality presets (Low, Medium, High, Custom)
|
|
69
|
-
|
|
70
|
-
## Configuration
|
|
71
|
-
|
|
72
|
-
### Video Input
|
|
73
|
-
|
|
74
|
-
| Parameter | Type | Required | Description |
|
|
75
|
-
|-----------|------|----------|-------------|
|
|
76
|
-
| **Video Source** | Options | Yes | `url` or `binary` |
|
|
77
|
-
| **Video URL** | String | If URL | URL of the video file |
|
|
78
|
-
| **Video Binary Property** | String | If Binary | Name of binary property containing video |
|
|
79
|
-
|
|
80
|
-
### Background Music (BGM)
|
|
81
|
-
|
|
82
|
-
| Parameter | Type | Default | Description |
|
|
83
|
-
|-----------|------|---------|-------------|
|
|
84
|
-
| **Enable BGM** | Boolean | false | Add background music |
|
|
85
|
-
| **BGM Source** | Options | - | `url` or `binary` |
|
|
86
|
-
| **BGM URL** | String | - | URL of BGM file |
|
|
87
|
-
| **BGM Volume** | Number | 30 | Volume 0-100 |
|
|
88
|
-
| **BGM Fade In** | Number | 2 | Fade-in duration (seconds) |
|
|
89
|
-
| **BGM Fade Out** | Number | 2 | Fade-out duration (seconds) |
|
|
90
|
-
|
|
91
|
-
### Narration
|
|
92
|
-
|
|
93
|
-
| Parameter | Type | Default | Description |
|
|
94
|
-
|-----------|------|---------|-------------|
|
|
95
|
-
| **Enable Narration** | Boolean | false | Add narration audio |
|
|
96
|
-
| **Narration Source** | Options | - | `url` or `binary` |
|
|
97
|
-
| **Narration URL** | String | - | URL of narration file |
|
|
98
|
-
| **Narration Volume** | Number | 80 | Volume 0-100 |
|
|
99
|
-
| **Narration Delay** | Number | 0 | Delay before start (seconds) |
|
|
100
|
-
|
|
101
|
-
### Subtitles
|
|
102
|
-
|
|
103
|
-
| Parameter | Type | Default | Description |
|
|
104
|
-
|-----------|------|---------|-------------|
|
|
105
|
-
| **Enable Subtitles** | Boolean | false | Add subtitles |
|
|
106
|
-
| **Text** | String | - | Subtitle text content |
|
|
107
|
-
| **Start Time** | Number | - | Start time in seconds |
|
|
108
|
-
| **End Time** | Number | - | End time in seconds |
|
|
109
|
-
| **Position** | Options | bottom | `top`, `middle`, `bottom`, `custom` |
|
|
110
|
-
| **Font Size** | Number | 48 | Text size |
|
|
111
|
-
| **Font Color** | Color | #FFFFFF | Text color (hex) |
|
|
112
|
-
| **Font Family** | String | Arial | Font name |
|
|
113
|
-
| **Alignment** | Options | center | `left`, `center`, `right` |
|
|
114
|
-
| **Background Color** | Color | #000000 | Background color (hex) |
|
|
115
|
-
| **Background Opacity** | Number | 80 | Opacity 0-100 |
|
|
116
|
-
| **Border Color** | Color | #000000 | Border color (hex) |
|
|
117
|
-
| **Border Width** | Number | 2 | Border width (pixels) |
|
|
118
|
-
|
|
119
|
-
### Output Options
|
|
120
|
-
|
|
121
|
-
| Parameter | Type | Default | Description |
|
|
122
|
-
|-----------|------|---------|-------------|
|
|
123
|
-
| **Output Format** | Options | mp4 | `mp4`, `mov`, `webm` |
|
|
124
|
-
| **Video Codec** | Options | libx264 | `libx264`, `libx265`, `vp9` |
|
|
125
|
-
| **Quality** | Options | high | `low`, `medium`, `high`, `custom` |
|
|
126
|
-
| **Custom CRF** | Number | 18 | CRF value 0-51 (if custom quality) |
|
|
127
|
-
| **Output Binary Property** | String | data | Property name for output |
|
|
128
|
-
|
|
129
|
-
## Examples
|
|
130
|
-
|
|
131
|
-
### Example 1: Simple Video with Subtitles
|
|
132
|
-
|
|
133
|
-
```json
|
|
134
|
-
{
|
|
135
|
-
"videoSource": "url",
|
|
136
|
-
"videoUrl": "https://example.com/video.mp4",
|
|
137
|
-
"enableSubtitles": true,
|
|
138
|
-
"subtitles": {
|
|
139
|
-
"subtitle": [
|
|
140
|
-
{
|
|
141
|
-
"text": "Welcome to our video!",
|
|
142
|
-
"startTime": 0,
|
|
143
|
-
"endTime": 5,
|
|
144
|
-
"position": "bottom",
|
|
145
|
-
"fontSize": 48,
|
|
146
|
-
"fontColor": "#FFFFFF",
|
|
147
|
-
"alignment": "center"
|
|
148
|
-
}
|
|
149
|
-
]
|
|
150
|
-
},
|
|
151
|
-
"outputFormat": "mp4",
|
|
152
|
-
"quality": "high"
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
### Example 2: Video with BGM and Narration
|
|
157
|
-
|
|
158
|
-
```json
|
|
159
|
-
{
|
|
160
|
-
"videoSource": "url",
|
|
161
|
-
"videoUrl": "https://example.com/video.mp4",
|
|
162
|
-
"enableBGM": true,
|
|
163
|
-
"bgmSource": "url",
|
|
164
|
-
"bgmUrl": "https://example.com/music.mp3",
|
|
165
|
-
"bgmVolume": 20,
|
|
166
|
-
"bgmFadeIn": 3,
|
|
167
|
-
"bgmFadeOut": 3,
|
|
168
|
-
"enableNarration": true,
|
|
169
|
-
"narrationSource": "url",
|
|
170
|
-
"narrationUrl": "https://example.com/narration.mp3",
|
|
171
|
-
"narrationVolume": 90,
|
|
172
|
-
"narrationDelay": 2,
|
|
173
|
-
"outputFormat": "mp4"
|
|
174
|
-
}
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### Example 3: Custom Positioned Subtitles
|
|
178
|
-
|
|
179
|
-
```json
|
|
180
|
-
{
|
|
181
|
-
"videoSource": "url",
|
|
182
|
-
"videoUrl": "https://example.com/video.mp4",
|
|
183
|
-
"enableSubtitles": true,
|
|
184
|
-
"subtitles": {
|
|
185
|
-
"subtitle": [
|
|
186
|
-
{
|
|
187
|
-
"text": "Top-left subtitle",
|
|
188
|
-
"startTime": 0,
|
|
189
|
-
"endTime": 5,
|
|
190
|
-
"position": "custom",
|
|
191
|
-
"customX": 100,
|
|
192
|
-
"customY": 100,
|
|
193
|
-
"fontSize": 36,
|
|
194
|
-
"fontColor": "#FFFF00",
|
|
195
|
-
"alignment": "left",
|
|
196
|
-
"backgroundColor": "#000000",
|
|
197
|
-
"backgroundOpacity": 70,
|
|
198
|
-
"borderColor": "#FFFFFF",
|
|
199
|
-
"borderWidth": 3
|
|
200
|
-
}
|
|
201
|
-
]
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### Example 4: Workflow Integration
|
|
207
|
-
|
|
208
|
-
**Scenario**: Download video from URL, add BGM and subtitles, upload to cloud storage
|
|
209
|
-
|
|
210
|
-
```
|
|
211
|
-
HTTP Request → SB Render → Google Drive
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
1. **HTTP Request**: Download video file
|
|
215
|
-
2. **SB Render**: Add BGM and subtitles
|
|
216
|
-
3. **Google Drive**: Upload rendered video
|
|
217
|
-
|
|
218
|
-
## Development
|
|
219
|
-
|
|
220
|
-
### Build
|
|
221
|
-
|
|
222
|
-
```bash
|
|
223
|
-
npm install
|
|
224
|
-
npm run build
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
### Development Mode
|
|
228
|
-
|
|
229
|
-
```bash
|
|
230
|
-
npm run dev
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
### Linting
|
|
234
|
-
|
|
235
|
-
```bash
|
|
236
|
-
npm run lint
|
|
237
|
-
npm run lintfix
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
### Project Structure
|
|
241
|
-
|
|
242
|
-
```
|
|
243
|
-
sb-render/
|
|
244
|
-
├── nodes/
|
|
245
|
-
│ └── SbRender/
|
|
246
|
-
│ ├── SbRender.node.ts # Main node implementation
|
|
247
|
-
│ ├── SbRender.node.json # Node metadata
|
|
248
|
-
│ ├── services/
|
|
249
|
-
│ │ ├── FileManager.ts # File handling
|
|
250
|
-
│ │ ├── AudioMixer.ts # Audio composition
|
|
251
|
-
│ │ ├── SubtitleEngine.ts # Subtitle generation
|
|
252
|
-
│ │ └── VideoComposer.ts # Video rendering
|
|
253
|
-
│ ├── interfaces/
|
|
254
|
-
│ │ └── index.ts # TypeScript types
|
|
255
|
-
│ └── utils/
|
|
256
|
-
│ ├── ffmpeg.ts # FFmpeg helpers
|
|
257
|
-
│ └── validation.ts # Input validation
|
|
258
|
-
├── fonts/ # Custom fonts
|
|
259
|
-
├── package.json
|
|
260
|
-
├── tsconfig.json
|
|
261
|
-
└── README.md
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
## Troubleshooting
|
|
265
|
-
|
|
266
|
-
### FFmpeg Not Found
|
|
267
|
-
|
|
268
|
-
The node automatically installs FFmpeg via `@ffmpeg-installer/ffmpeg`. If you encounter issues:
|
|
269
|
-
|
|
270
|
-
```bash
|
|
271
|
-
npm install @ffmpeg-installer/ffmpeg --force
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
### File Download Errors
|
|
275
|
-
|
|
276
|
-
**Issue**: Video/audio URLs fail to download
|
|
277
|
-
|
|
278
|
-
**Solutions**:
|
|
279
|
-
- Verify URL accessibility
|
|
280
|
-
- Check for authentication requirements
|
|
281
|
-
- Ensure sufficient disk space
|
|
282
|
-
- Try using binary data input instead
|
|
283
|
-
|
|
284
|
-
### Subtitle Not Appearing
|
|
285
|
-
|
|
286
|
-
**Issue**: Subtitles don't show in output video
|
|
287
|
-
|
|
288
|
-
**Solutions**:
|
|
289
|
-
- Verify timing (start/end times within video duration)
|
|
290
|
-
- Check subtitle position and size
|
|
291
|
-
- Ensure font is available on system
|
|
292
|
-
- Try different output format (MP4 recommended)
|
|
293
|
-
|
|
294
|
-
### Memory Issues
|
|
295
|
-
|
|
296
|
-
**Issue**: Process crashes with large video files
|
|
297
|
-
|
|
298
|
-
**Solutions**:
|
|
299
|
-
- Reduce video resolution before processing
|
|
300
|
-
- Use lower quality preset
|
|
301
|
-
- Process videos in smaller batches
|
|
302
|
-
- Increase Node.js memory limit:
|
|
303
|
-
|
|
304
|
-
```bash
|
|
305
|
-
export NODE_OPTIONS="--max-old-space-size=4096"
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### Audio Sync Issues
|
|
309
|
-
|
|
310
|
-
**Issue**: Audio out of sync with video
|
|
311
|
-
|
|
312
|
-
**Solutions**:
|
|
313
|
-
- Ensure all audio files are in compatible formats
|
|
314
|
-
- Check narration delay settings
|
|
315
|
-
- Verify video frame rate compatibility
|
|
316
|
-
- Try re-encoding source files
|
|
317
|
-
|
|
318
|
-
## Technical Details
|
|
319
|
-
|
|
320
|
-
### FFmpeg Commands
|
|
321
|
-
|
|
322
|
-
The node uses **fluent-ffmpeg** to construct FFmpeg commands:
|
|
323
|
-
|
|
324
|
-
**Audio Mixing**:
|
|
325
|
-
```bash
|
|
326
|
-
-filter_complex "[0:a]volume=1.0[original];[1:a]volume=0.3,afade=t=in:st=0:d=2[bgm];[original][bgm]amix=inputs=2[mixed]"
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
**Subtitle Overlay**:
|
|
330
|
-
```bash
|
|
331
|
-
-vf "ass=subtitles.ass"
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
**Complete Command**:
|
|
335
|
-
```bash
|
|
336
|
-
ffmpeg -i video.mp4 -i bgm.mp3 -filter_complex "..." -vf "ass=..." -c:v libx264 -crf 18 output.mp4
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
### Subtitle Formats
|
|
340
|
-
|
|
341
|
-
- **SRT**: Simple subtitle format (limited styling)
|
|
342
|
-
- **ASS**: Advanced SubStation Alpha (full styling support) ← **Used by default**
|
|
343
|
-
|
|
344
|
-
ASS format provides:
|
|
345
|
-
- Custom fonts and colors
|
|
346
|
-
- Precise positioning
|
|
347
|
-
- Background colors and opacity
|
|
348
|
-
- Border/outline effects
|
|
349
|
-
- Multiple alignment options
|
|
350
|
-
|
|
351
|
-
## Performance
|
|
352
|
-
|
|
353
|
-
### Resource Usage
|
|
354
|
-
|
|
355
|
-
| Video Length | Memory | Processing Time |
|
|
356
|
-
|--------------|--------|-----------------|
|
|
357
|
-
| 1 minute | ~200MB | ~30 seconds |
|
|
358
|
-
| 5 minutes | ~500MB | ~2 minutes |
|
|
359
|
-
| 10 minutes | ~1GB | ~5 minutes |
|
|
360
|
-
|
|
361
|
-
*Times measured on standard VPS (2 CPU, 4GB RAM)*
|
|
362
|
-
|
|
363
|
-
### Optimization Tips
|
|
364
|
-
|
|
365
|
-
1. **Use appropriate quality**: High quality for final output, medium for testing
|
|
366
|
-
2. **Compress BGM**: Use lower bitrate audio files (128-192 kbps)
|
|
367
|
-
3. **Batch processing**: Process multiple videos in parallel workflows
|
|
368
|
-
4. **Cache audio files**: Reuse same BGM/narration across multiple videos
|
|
369
|
-
|
|
370
|
-
## Compatibility
|
|
371
|
-
|
|
372
|
-
### Supported Video Formats
|
|
373
|
-
|
|
374
|
-
**Input**: MP4, MOV, WebM, AVI, MKV
|
|
375
|
-
**Output**: MP4, MOV, WebM
|
|
376
|
-
|
|
377
|
-
### Supported Audio Formats
|
|
378
|
-
|
|
379
|
-
**Input**: MP3, WAV, AAC, OGG, M4A
|
|
380
|
-
**Output**: AAC (default)
|
|
381
|
-
|
|
382
|
-
### Codec Compatibility
|
|
383
|
-
|
|
384
|
-
| Codec | Quality | Speed | Browser Support |
|
|
385
|
-
|-------|---------|-------|-----------------|
|
|
386
|
-
| H.264 (libx264) | Good | Fast | Excellent |
|
|
387
|
-
| H.265 (libx265) | Better | Slower | Limited |
|
|
388
|
-
| VP9 | Good | Slow | Good (WebM) |
|
|
389
|
-
|
|
390
|
-
## Contributing
|
|
391
|
-
|
|
392
|
-
Contributions are welcome! Please:
|
|
393
|
-
|
|
394
|
-
1. Fork the repository
|
|
395
|
-
2. Create a feature branch
|
|
396
|
-
3. Make your changes
|
|
397
|
-
4. Add tests if applicable
|
|
398
|
-
5. Submit a pull request
|
|
399
|
-
|
|
400
|
-
### Development Guidelines
|
|
401
|
-
|
|
402
|
-
- Follow existing code style (ESLint configured)
|
|
403
|
-
- Add TypeScript types for new features
|
|
404
|
-
- Update documentation for new parameters
|
|
405
|
-
- Test with various video formats
|
|
406
|
-
|
|
407
|
-
## Roadmap
|
|
408
|
-
|
|
409
|
-
### v1.1 (Planned)
|
|
410
|
-
|
|
411
|
-
- [ ] Multiple video layers
|
|
412
|
-
- [ ] Video transitions
|
|
413
|
-
- [ ] Logo/watermark overlay
|
|
414
|
-
- [ ] Preset subtitle templates
|
|
415
|
-
- [ ] Batch processing mode
|
|
416
|
-
|
|
417
|
-
### v2.0 (Future)
|
|
418
|
-
|
|
419
|
-
- [ ] GPU acceleration (NVENC, VideoToolbox)
|
|
420
|
-
- [ ] Cloud storage integration
|
|
421
|
-
- [ ] Progress callbacks
|
|
422
|
-
- [ ] Advanced video effects
|
|
423
|
-
|
|
424
|
-
## Credits
|
|
425
|
-
|
|
426
|
-
- Inspired by [n8n-nodes-mediafx](https://github.com/dandacompany/n8n-nodes-mediafx)
|
|
427
|
-
- Uses [FFmpeg](https://ffmpeg.org/) for video processing
|
|
428
|
-
- Built with [fluent-ffmpeg](https://github.com/fluent-ffmpeg/node-fluent-ffmpeg)
|
|
429
|
-
|
|
430
|
-
## License
|
|
431
|
-
|
|
432
|
-
[MIT](LICENSE)
|
|
433
|
-
|
|
434
|
-
## Support
|
|
435
|
-
|
|
436
|
-
- **Issues**: [GitHub Issues](https://github.com/choisb87/sb-render/issues)
|
|
437
|
-
- **Documentation**: [GitHub Wiki](https://github.com/choisb87/sb-render/wiki)
|
|
438
|
-
- **n8n Community**: [n8n Community Forum](https://community.n8n.io/)
|
|
439
|
-
|
|
440
|
-
---
|
|
441
|
-
|
|
442
|
-
**Made with ❤️ for the n8n community**
|
|
1
|
+
# n8n-nodes-sb-render
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/n8n-nodes-sb-render)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
This is an n8n community node for video rendering with customizable subtitles, background music (BGM), and narration using FFmpeg.
|
|
7
|
+
|
|
8
|
+
**sb-render** allows you to automate video composition workflows in n8n, combining video files with:
|
|
9
|
+
- 🎵 Background music with volume control and fade effects
|
|
10
|
+
- 🎙️ Narration audio with timing control
|
|
11
|
+
- 📝 Customizable subtitles with extensive styling options
|
|
12
|
+
- 🎨 Multiple output formats and quality presets
|
|
13
|
+
|
|
14
|
+
Inspired by [n8n-nodes-mediafx](https://github.com/dandacompany/n8n-nodes-mediafx).
|
|
15
|
+
|
|
16
|
+
## Table of Contents
|
|
17
|
+
|
|
18
|
+
- [Installation](#installation)
|
|
19
|
+
- [Prerequisites](#prerequisites)
|
|
20
|
+
- [Operations](#operations)
|
|
21
|
+
- [Configuration](#configuration)
|
|
22
|
+
- [Examples](#examples)
|
|
23
|
+
- [Development](#development)
|
|
24
|
+
- [Troubleshooting](#troubleshooting)
|
|
25
|
+
- [License](#license)
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
### Community Nodes (Recommended)
|
|
30
|
+
|
|
31
|
+
1. Go to **Settings > Community Nodes** in n8n
|
|
32
|
+
2. Select **Install**
|
|
33
|
+
3. Enter `n8n-nodes-sb-render`
|
|
34
|
+
4. Agree to the risks and install
|
|
35
|
+
|
|
36
|
+
### Manual Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install n8n-nodes-sb-render
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
For n8n self-hosted installations:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
cd ~/.n8n
|
|
46
|
+
npm install n8n-nodes-sb-render
|
|
47
|
+
# Restart n8n
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Prerequisites
|
|
51
|
+
|
|
52
|
+
- **n8n version**: 1.0.0 or higher
|
|
53
|
+
- **Node.js**: 16.0.0 or higher
|
|
54
|
+
- **FFmpeg**: Automatically installed via `@ffmpeg-installer/ffmpeg`
|
|
55
|
+
|
|
56
|
+
## Operations
|
|
57
|
+
|
|
58
|
+
### Video → Render
|
|
59
|
+
|
|
60
|
+
Compose a video with optional background music, narration, and subtitles.
|
|
61
|
+
|
|
62
|
+
**Features:**
|
|
63
|
+
- ✅ Multiple input sources (URL or binary data)
|
|
64
|
+
- ✅ Audio mixing with independent volume controls
|
|
65
|
+
- ✅ Fade in/out effects for BGM
|
|
66
|
+
- ✅ Customizable subtitle positioning and styling
|
|
67
|
+
- ✅ Multiple output formats (MP4, MOV, WebM)
|
|
68
|
+
- ✅ Quality presets (Low, Medium, High, Custom)
|
|
69
|
+
|
|
70
|
+
## Configuration
|
|
71
|
+
|
|
72
|
+
### Video Input
|
|
73
|
+
|
|
74
|
+
| Parameter | Type | Required | Description |
|
|
75
|
+
|-----------|------|----------|-------------|
|
|
76
|
+
| **Video Source** | Options | Yes | `url` or `binary` |
|
|
77
|
+
| **Video URL** | String | If URL | URL of the video file |
|
|
78
|
+
| **Video Binary Property** | String | If Binary | Name of binary property containing video |
|
|
79
|
+
|
|
80
|
+
### Background Music (BGM)
|
|
81
|
+
|
|
82
|
+
| Parameter | Type | Default | Description |
|
|
83
|
+
|-----------|------|---------|-------------|
|
|
84
|
+
| **Enable BGM** | Boolean | false | Add background music |
|
|
85
|
+
| **BGM Source** | Options | - | `url` or `binary` |
|
|
86
|
+
| **BGM URL** | String | - | URL of BGM file |
|
|
87
|
+
| **BGM Volume** | Number | 30 | Volume 0-100 |
|
|
88
|
+
| **BGM Fade In** | Number | 2 | Fade-in duration (seconds) |
|
|
89
|
+
| **BGM Fade Out** | Number | 2 | Fade-out duration (seconds) |
|
|
90
|
+
|
|
91
|
+
### Narration
|
|
92
|
+
|
|
93
|
+
| Parameter | Type | Default | Description |
|
|
94
|
+
|-----------|------|---------|-------------|
|
|
95
|
+
| **Enable Narration** | Boolean | false | Add narration audio |
|
|
96
|
+
| **Narration Source** | Options | - | `url` or `binary` |
|
|
97
|
+
| **Narration URL** | String | - | URL of narration file |
|
|
98
|
+
| **Narration Volume** | Number | 80 | Volume 0-100 |
|
|
99
|
+
| **Narration Delay** | Number | 0 | Delay before start (seconds) |
|
|
100
|
+
|
|
101
|
+
### Subtitles
|
|
102
|
+
|
|
103
|
+
| Parameter | Type | Default | Description |
|
|
104
|
+
|-----------|------|---------|-------------|
|
|
105
|
+
| **Enable Subtitles** | Boolean | false | Add subtitles |
|
|
106
|
+
| **Text** | String | - | Subtitle text content |
|
|
107
|
+
| **Start Time** | Number | - | Start time in seconds |
|
|
108
|
+
| **End Time** | Number | - | End time in seconds |
|
|
109
|
+
| **Position** | Options | bottom | `top`, `middle`, `bottom`, `custom` |
|
|
110
|
+
| **Font Size** | Number | 48 | Text size |
|
|
111
|
+
| **Font Color** | Color | #FFFFFF | Text color (hex) |
|
|
112
|
+
| **Font Family** | String | Arial | Font name |
|
|
113
|
+
| **Alignment** | Options | center | `left`, `center`, `right` |
|
|
114
|
+
| **Background Color** | Color | #000000 | Background color (hex) |
|
|
115
|
+
| **Background Opacity** | Number | 80 | Opacity 0-100 |
|
|
116
|
+
| **Border Color** | Color | #000000 | Border color (hex) |
|
|
117
|
+
| **Border Width** | Number | 2 | Border width (pixels) |
|
|
118
|
+
|
|
119
|
+
### Output Options
|
|
120
|
+
|
|
121
|
+
| Parameter | Type | Default | Description |
|
|
122
|
+
|-----------|------|---------|-------------|
|
|
123
|
+
| **Output Format** | Options | mp4 | `mp4`, `mov`, `webm` |
|
|
124
|
+
| **Video Codec** | Options | libx264 | `libx264`, `libx265`, `vp9` |
|
|
125
|
+
| **Quality** | Options | high | `low`, `medium`, `high`, `custom` |
|
|
126
|
+
| **Custom CRF** | Number | 18 | CRF value 0-51 (if custom quality) |
|
|
127
|
+
| **Output Binary Property** | String | data | Property name for output |
|
|
128
|
+
|
|
129
|
+
## Examples
|
|
130
|
+
|
|
131
|
+
### Example 1: Simple Video with Subtitles
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"videoSource": "url",
|
|
136
|
+
"videoUrl": "https://example.com/video.mp4",
|
|
137
|
+
"enableSubtitles": true,
|
|
138
|
+
"subtitles": {
|
|
139
|
+
"subtitle": [
|
|
140
|
+
{
|
|
141
|
+
"text": "Welcome to our video!",
|
|
142
|
+
"startTime": 0,
|
|
143
|
+
"endTime": 5,
|
|
144
|
+
"position": "bottom",
|
|
145
|
+
"fontSize": 48,
|
|
146
|
+
"fontColor": "#FFFFFF",
|
|
147
|
+
"alignment": "center"
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
},
|
|
151
|
+
"outputFormat": "mp4",
|
|
152
|
+
"quality": "high"
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Example 2: Video with BGM and Narration
|
|
157
|
+
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"videoSource": "url",
|
|
161
|
+
"videoUrl": "https://example.com/video.mp4",
|
|
162
|
+
"enableBGM": true,
|
|
163
|
+
"bgmSource": "url",
|
|
164
|
+
"bgmUrl": "https://example.com/music.mp3",
|
|
165
|
+
"bgmVolume": 20,
|
|
166
|
+
"bgmFadeIn": 3,
|
|
167
|
+
"bgmFadeOut": 3,
|
|
168
|
+
"enableNarration": true,
|
|
169
|
+
"narrationSource": "url",
|
|
170
|
+
"narrationUrl": "https://example.com/narration.mp3",
|
|
171
|
+
"narrationVolume": 90,
|
|
172
|
+
"narrationDelay": 2,
|
|
173
|
+
"outputFormat": "mp4"
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Example 3: Custom Positioned Subtitles
|
|
178
|
+
|
|
179
|
+
```json
|
|
180
|
+
{
|
|
181
|
+
"videoSource": "url",
|
|
182
|
+
"videoUrl": "https://example.com/video.mp4",
|
|
183
|
+
"enableSubtitles": true,
|
|
184
|
+
"subtitles": {
|
|
185
|
+
"subtitle": [
|
|
186
|
+
{
|
|
187
|
+
"text": "Top-left subtitle",
|
|
188
|
+
"startTime": 0,
|
|
189
|
+
"endTime": 5,
|
|
190
|
+
"position": "custom",
|
|
191
|
+
"customX": 100,
|
|
192
|
+
"customY": 100,
|
|
193
|
+
"fontSize": 36,
|
|
194
|
+
"fontColor": "#FFFF00",
|
|
195
|
+
"alignment": "left",
|
|
196
|
+
"backgroundColor": "#000000",
|
|
197
|
+
"backgroundOpacity": 70,
|
|
198
|
+
"borderColor": "#FFFFFF",
|
|
199
|
+
"borderWidth": 3
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Example 4: Workflow Integration
|
|
207
|
+
|
|
208
|
+
**Scenario**: Download video from URL, add BGM and subtitles, upload to cloud storage
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
HTTP Request → SB Render → Google Drive
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
1. **HTTP Request**: Download video file
|
|
215
|
+
2. **SB Render**: Add BGM and subtitles
|
|
216
|
+
3. **Google Drive**: Upload rendered video
|
|
217
|
+
|
|
218
|
+
## Development
|
|
219
|
+
|
|
220
|
+
### Build
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
npm install
|
|
224
|
+
npm run build
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Development Mode
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
npm run dev
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Linting
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
npm run lint
|
|
237
|
+
npm run lintfix
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Project Structure
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
sb-render/
|
|
244
|
+
├── nodes/
|
|
245
|
+
│ └── SbRender/
|
|
246
|
+
│ ├── SbRender.node.ts # Main node implementation
|
|
247
|
+
│ ├── SbRender.node.json # Node metadata
|
|
248
|
+
│ ├── services/
|
|
249
|
+
│ │ ├── FileManager.ts # File handling
|
|
250
|
+
│ │ ├── AudioMixer.ts # Audio composition
|
|
251
|
+
│ │ ├── SubtitleEngine.ts # Subtitle generation
|
|
252
|
+
│ │ └── VideoComposer.ts # Video rendering
|
|
253
|
+
│ ├── interfaces/
|
|
254
|
+
│ │ └── index.ts # TypeScript types
|
|
255
|
+
│ └── utils/
|
|
256
|
+
│ ├── ffmpeg.ts # FFmpeg helpers
|
|
257
|
+
│ └── validation.ts # Input validation
|
|
258
|
+
├── fonts/ # Custom fonts
|
|
259
|
+
├── package.json
|
|
260
|
+
├── tsconfig.json
|
|
261
|
+
└── README.md
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Troubleshooting
|
|
265
|
+
|
|
266
|
+
### FFmpeg Not Found
|
|
267
|
+
|
|
268
|
+
The node automatically installs FFmpeg via `@ffmpeg-installer/ffmpeg`. If you encounter issues:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
npm install @ffmpeg-installer/ffmpeg --force
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### File Download Errors
|
|
275
|
+
|
|
276
|
+
**Issue**: Video/audio URLs fail to download
|
|
277
|
+
|
|
278
|
+
**Solutions**:
|
|
279
|
+
- Verify URL accessibility
|
|
280
|
+
- Check for authentication requirements
|
|
281
|
+
- Ensure sufficient disk space
|
|
282
|
+
- Try using binary data input instead
|
|
283
|
+
|
|
284
|
+
### Subtitle Not Appearing
|
|
285
|
+
|
|
286
|
+
**Issue**: Subtitles don't show in output video
|
|
287
|
+
|
|
288
|
+
**Solutions**:
|
|
289
|
+
- Verify timing (start/end times within video duration)
|
|
290
|
+
- Check subtitle position and size
|
|
291
|
+
- Ensure font is available on system
|
|
292
|
+
- Try different output format (MP4 recommended)
|
|
293
|
+
|
|
294
|
+
### Memory Issues
|
|
295
|
+
|
|
296
|
+
**Issue**: Process crashes with large video files
|
|
297
|
+
|
|
298
|
+
**Solutions**:
|
|
299
|
+
- Reduce video resolution before processing
|
|
300
|
+
- Use lower quality preset
|
|
301
|
+
- Process videos in smaller batches
|
|
302
|
+
- Increase Node.js memory limit:
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
export NODE_OPTIONS="--max-old-space-size=4096"
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Audio Sync Issues
|
|
309
|
+
|
|
310
|
+
**Issue**: Audio out of sync with video
|
|
311
|
+
|
|
312
|
+
**Solutions**:
|
|
313
|
+
- Ensure all audio files are in compatible formats
|
|
314
|
+
- Check narration delay settings
|
|
315
|
+
- Verify video frame rate compatibility
|
|
316
|
+
- Try re-encoding source files
|
|
317
|
+
|
|
318
|
+
## Technical Details
|
|
319
|
+
|
|
320
|
+
### FFmpeg Commands
|
|
321
|
+
|
|
322
|
+
The node uses **fluent-ffmpeg** to construct FFmpeg commands:
|
|
323
|
+
|
|
324
|
+
**Audio Mixing**:
|
|
325
|
+
```bash
|
|
326
|
+
-filter_complex "[0:a]volume=1.0[original];[1:a]volume=0.3,afade=t=in:st=0:d=2[bgm];[original][bgm]amix=inputs=2[mixed]"
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Subtitle Overlay**:
|
|
330
|
+
```bash
|
|
331
|
+
-vf "ass=subtitles.ass"
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Complete Command**:
|
|
335
|
+
```bash
|
|
336
|
+
ffmpeg -i video.mp4 -i bgm.mp3 -filter_complex "..." -vf "ass=..." -c:v libx264 -crf 18 output.mp4
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Subtitle Formats
|
|
340
|
+
|
|
341
|
+
- **SRT**: Simple subtitle format (limited styling)
|
|
342
|
+
- **ASS**: Advanced SubStation Alpha (full styling support) ← **Used by default**
|
|
343
|
+
|
|
344
|
+
ASS format provides:
|
|
345
|
+
- Custom fonts and colors
|
|
346
|
+
- Precise positioning
|
|
347
|
+
- Background colors and opacity
|
|
348
|
+
- Border/outline effects
|
|
349
|
+
- Multiple alignment options
|
|
350
|
+
|
|
351
|
+
## Performance
|
|
352
|
+
|
|
353
|
+
### Resource Usage
|
|
354
|
+
|
|
355
|
+
| Video Length | Memory | Processing Time |
|
|
356
|
+
|--------------|--------|-----------------|
|
|
357
|
+
| 1 minute | ~200MB | ~30 seconds |
|
|
358
|
+
| 5 minutes | ~500MB | ~2 minutes |
|
|
359
|
+
| 10 minutes | ~1GB | ~5 minutes |
|
|
360
|
+
|
|
361
|
+
*Times measured on standard VPS (2 CPU, 4GB RAM)*
|
|
362
|
+
|
|
363
|
+
### Optimization Tips
|
|
364
|
+
|
|
365
|
+
1. **Use appropriate quality**: High quality for final output, medium for testing
|
|
366
|
+
2. **Compress BGM**: Use lower bitrate audio files (128-192 kbps)
|
|
367
|
+
3. **Batch processing**: Process multiple videos in parallel workflows
|
|
368
|
+
4. **Cache audio files**: Reuse same BGM/narration across multiple videos
|
|
369
|
+
|
|
370
|
+
## Compatibility
|
|
371
|
+
|
|
372
|
+
### Supported Video Formats
|
|
373
|
+
|
|
374
|
+
**Input**: MP4, MOV, WebM, AVI, MKV
|
|
375
|
+
**Output**: MP4, MOV, WebM
|
|
376
|
+
|
|
377
|
+
### Supported Audio Formats
|
|
378
|
+
|
|
379
|
+
**Input**: MP3, WAV, AAC, OGG, M4A
|
|
380
|
+
**Output**: AAC (default)
|
|
381
|
+
|
|
382
|
+
### Codec Compatibility
|
|
383
|
+
|
|
384
|
+
| Codec | Quality | Speed | Browser Support |
|
|
385
|
+
|-------|---------|-------|-----------------|
|
|
386
|
+
| H.264 (libx264) | Good | Fast | Excellent |
|
|
387
|
+
| H.265 (libx265) | Better | Slower | Limited |
|
|
388
|
+
| VP9 | Good | Slow | Good (WebM) |
|
|
389
|
+
|
|
390
|
+
## Contributing
|
|
391
|
+
|
|
392
|
+
Contributions are welcome! Please:
|
|
393
|
+
|
|
394
|
+
1. Fork the repository
|
|
395
|
+
2. Create a feature branch
|
|
396
|
+
3. Make your changes
|
|
397
|
+
4. Add tests if applicable
|
|
398
|
+
5. Submit a pull request
|
|
399
|
+
|
|
400
|
+
### Development Guidelines
|
|
401
|
+
|
|
402
|
+
- Follow existing code style (ESLint configured)
|
|
403
|
+
- Add TypeScript types for new features
|
|
404
|
+
- Update documentation for new parameters
|
|
405
|
+
- Test with various video formats
|
|
406
|
+
|
|
407
|
+
## Roadmap
|
|
408
|
+
|
|
409
|
+
### v1.1 (Planned)
|
|
410
|
+
|
|
411
|
+
- [ ] Multiple video layers
|
|
412
|
+
- [ ] Video transitions
|
|
413
|
+
- [ ] Logo/watermark overlay
|
|
414
|
+
- [ ] Preset subtitle templates
|
|
415
|
+
- [ ] Batch processing mode
|
|
416
|
+
|
|
417
|
+
### v2.0 (Future)
|
|
418
|
+
|
|
419
|
+
- [ ] GPU acceleration (NVENC, VideoToolbox)
|
|
420
|
+
- [ ] Cloud storage integration
|
|
421
|
+
- [ ] Progress callbacks
|
|
422
|
+
- [ ] Advanced video effects
|
|
423
|
+
|
|
424
|
+
## Credits
|
|
425
|
+
|
|
426
|
+
- Inspired by [n8n-nodes-mediafx](https://github.com/dandacompany/n8n-nodes-mediafx)
|
|
427
|
+
- Uses [FFmpeg](https://ffmpeg.org/) for video processing
|
|
428
|
+
- Built with [fluent-ffmpeg](https://github.com/fluent-ffmpeg/node-fluent-ffmpeg)
|
|
429
|
+
|
|
430
|
+
## License
|
|
431
|
+
|
|
432
|
+
[MIT](LICENSE)
|
|
433
|
+
|
|
434
|
+
## Support
|
|
435
|
+
|
|
436
|
+
- **Issues**: [GitHub Issues](https://github.com/choisb87/sb-render/issues)
|
|
437
|
+
- **Documentation**: [GitHub Wiki](https://github.com/choisb87/sb-render/wiki)
|
|
438
|
+
- **n8n Community**: [n8n Community Forum](https://community.n8n.io/)
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
**Made with ❤️ for the n8n community**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
|
2
|
-
<rect width="100" height="100" fill="#4A90E2" rx="15"/>
|
|
3
|
-
<path d="M30 35 L30 65 L60 50 Z" fill="white"/>
|
|
4
|
-
<rect x="65" y="60" width="15" height="8" fill="white" rx="2"/>
|
|
5
|
-
<rect x="65" y="45" width="15" height="8" fill="white" rx="2"/>
|
|
6
|
-
<rect x="65" y="30" width="15" height="8" fill="white" rx="2"/>
|
|
7
|
-
</svg>
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
|
|
2
|
+
<rect width="100" height="100" fill="#4A90E2" rx="15"/>
|
|
3
|
+
<path d="M30 35 L30 65 L60 50 Z" fill="white"/>
|
|
4
|
+
<rect x="65" y="60" width="15" height="8" fill="white" rx="2"/>
|
|
5
|
+
<rect x="65" y="45" width="15" height="8" fill="white" rx="2"/>
|
|
6
|
+
<rect x="65" y="30" width="15" height="8" fill="white" rx="2"/>
|
|
7
|
+
</svg>
|
|
@@ -65,12 +65,12 @@ class SubtitleEngine {
|
|
|
65
65
|
* Generate ASS file header
|
|
66
66
|
*/
|
|
67
67
|
generateASSHeader(videoWidth, videoHeight) {
|
|
68
|
-
return `[Script Info]
|
|
69
|
-
Title: sb-render subtitles
|
|
70
|
-
ScriptType: v4.00+
|
|
71
|
-
WrapStyle: 0
|
|
72
|
-
PlayResX: ${videoWidth}
|
|
73
|
-
PlayResY: ${videoHeight}
|
|
68
|
+
return `[Script Info]
|
|
69
|
+
Title: sb-render subtitles
|
|
70
|
+
ScriptType: v4.00+
|
|
71
|
+
WrapStyle: 0
|
|
72
|
+
PlayResX: ${videoWidth}
|
|
73
|
+
PlayResY: ${videoHeight}
|
|
74
74
|
ScaledBorderAndShadow: yes`;
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
@@ -86,7 +86,7 @@ ScaledBorderAndShadow: yes`;
|
|
|
86
86
|
* Generate ASS styles section
|
|
87
87
|
*/
|
|
88
88
|
generateASSStyles(subtitles) {
|
|
89
|
-
const stylesHeader = `[V4+ Styles]
|
|
89
|
+
const stylesHeader = `[V4+ Styles]
|
|
90
90
|
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding`;
|
|
91
91
|
const uniqueStyles = new Map();
|
|
92
92
|
const fontName = this.getFontName();
|
|
@@ -145,7 +145,7 @@ Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour,
|
|
|
145
145
|
* Generate ASS events section
|
|
146
146
|
*/
|
|
147
147
|
generateASSEvents(subtitles, videoWidth, videoHeight) {
|
|
148
|
-
const eventsHeader = `[Events]
|
|
148
|
+
const eventsHeader = `[Events]
|
|
149
149
|
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text`;
|
|
150
150
|
const eventLines = [];
|
|
151
151
|
subtitles.forEach((subtitle, index) => {
|
|
@@ -263,7 +263,7 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text`
|
|
|
263
263
|
// Default configuration for SRT-based subtitles
|
|
264
264
|
const config = {
|
|
265
265
|
position: 'bottom',
|
|
266
|
-
fontSize:
|
|
266
|
+
fontSize: 90,
|
|
267
267
|
fontColor: '#FFFFFF',
|
|
268
268
|
fontFamily: 'NanumGothic',
|
|
269
269
|
alignment: 'center',
|
package/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
// Entry point for n8n community node
|
|
2
|
-
// This file is generated by the TypeScript compiler
|
|
3
|
-
module.exports = require('./dist/nodes/SbRender/SbRender.node');
|
|
1
|
+
// Entry point for n8n community node
|
|
2
|
+
// This file is generated by the TypeScript compiler
|
|
3
|
+
module.exports = require('./dist/nodes/SbRender/SbRender.node');
|
package/package.json
CHANGED
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "n8n-nodes-sb-render",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "n8n community node for video rendering with customizable subtitles, BGM, and narration",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"n8n-community-node-package",
|
|
7
|
-
"n8n",
|
|
8
|
-
"video",
|
|
9
|
-
"ffmpeg",
|
|
10
|
-
"subtitle",
|
|
11
|
-
"rendering",
|
|
12
|
-
"video-composition"
|
|
13
|
-
],
|
|
14
|
-
"license": "MIT",
|
|
15
|
-
"homepage": "https://github.com/choisb87/sb-render",
|
|
16
|
-
"author": {
|
|
17
|
-
"name": "choisb87",
|
|
18
|
-
"email": "choisb87@gmail.com"
|
|
19
|
-
},
|
|
20
|
-
"repository": {
|
|
21
|
-
"type": "git",
|
|
22
|
-
"url": "git+https://github.com/choisb87/sb-render.git"
|
|
23
|
-
},
|
|
24
|
-
"main": "index.js",
|
|
25
|
-
"scripts": {
|
|
26
|
-
"build": "tsc && gulp build:icons",
|
|
27
|
-
"dev": "tsc --watch",
|
|
28
|
-
"format": "prettier nodes --write",
|
|
29
|
-
"lint": "eslint nodes --ext .ts",
|
|
30
|
-
"lintfix": "eslint nodes --ext .ts --fix",
|
|
31
|
-
"prepublishOnly": "npm run build && npm run lint",
|
|
32
|
-
"postinstall": "node scripts/fix-ffprobe-permissions.js"
|
|
33
|
-
},
|
|
34
|
-
"files": [
|
|
35
|
-
"dist",
|
|
36
|
-
"fonts",
|
|
37
|
-
"scripts"
|
|
38
|
-
],
|
|
39
|
-
"n8n": {
|
|
40
|
-
"n8nNodesApiVersion": 1,
|
|
41
|
-
"credentials": [],
|
|
42
|
-
"nodes": [
|
|
43
|
-
"dist/nodes/SbRender/SbRender.node.js"
|
|
44
|
-
]
|
|
45
|
-
},
|
|
46
|
-
"devDependencies": {
|
|
47
|
-
"@types/fluent-ffmpeg": "^2.1.21",
|
|
48
|
-
"@types/node": "^18.16.0",
|
|
49
|
-
"@types/node-fetch": "^2.6.13",
|
|
50
|
-
"@typescript-eslint/eslint-plugin": "^5.59.0",
|
|
51
|
-
"@typescript-eslint/parser": "^5.59.0",
|
|
52
|
-
"eslint": "^8.40.0",
|
|
53
|
-
"eslint-plugin-n8n-nodes-base": "^1.11.0",
|
|
54
|
-
"gulp": "^4.0.2",
|
|
55
|
-
"n8n-workflow": "^1.0.0",
|
|
56
|
-
"prettier": "^2.8.8",
|
|
57
|
-
"ts-node": "^10.9.2",
|
|
58
|
-
"typescript": "^5.0.4"
|
|
59
|
-
},
|
|
60
|
-
"dependencies": {
|
|
61
|
-
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
|
62
|
-
"@ffprobe-installer/ffprobe": "^2.1.2",
|
|
63
|
-
"fluent-ffmpeg": "^2.1.2",
|
|
64
|
-
"node-fetch": "^2.6.12",
|
|
65
|
-
"tmp-promise": "^3.0.3"
|
|
66
|
-
},
|
|
67
|
-
"peerDependencies": {
|
|
68
|
-
"n8n-workflow": "*"
|
|
69
|
-
},
|
|
70
|
-
"engines": {
|
|
71
|
-
"node": ">=16.0.0"
|
|
72
|
-
}
|
|
73
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-sb-render",
|
|
3
|
+
"version": "1.1.5",
|
|
4
|
+
"description": "n8n community node for video rendering with customizable subtitles, BGM, and narration",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"n8n",
|
|
8
|
+
"video",
|
|
9
|
+
"ffmpeg",
|
|
10
|
+
"subtitle",
|
|
11
|
+
"rendering",
|
|
12
|
+
"video-composition"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"homepage": "https://github.com/choisb87/sb-render",
|
|
16
|
+
"author": {
|
|
17
|
+
"name": "choisb87",
|
|
18
|
+
"email": "choisb87@gmail.com"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/choisb87/sb-render.git"
|
|
23
|
+
},
|
|
24
|
+
"main": "index.js",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc && gulp build:icons",
|
|
27
|
+
"dev": "tsc --watch",
|
|
28
|
+
"format": "prettier nodes --write",
|
|
29
|
+
"lint": "eslint nodes --ext .ts",
|
|
30
|
+
"lintfix": "eslint nodes --ext .ts --fix",
|
|
31
|
+
"prepublishOnly": "npm run build && npm run lint",
|
|
32
|
+
"postinstall": "node scripts/fix-ffprobe-permissions.js"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"fonts",
|
|
37
|
+
"scripts"
|
|
38
|
+
],
|
|
39
|
+
"n8n": {
|
|
40
|
+
"n8nNodesApiVersion": 1,
|
|
41
|
+
"credentials": [],
|
|
42
|
+
"nodes": [
|
|
43
|
+
"dist/nodes/SbRender/SbRender.node.js"
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/fluent-ffmpeg": "^2.1.21",
|
|
48
|
+
"@types/node": "^18.16.0",
|
|
49
|
+
"@types/node-fetch": "^2.6.13",
|
|
50
|
+
"@typescript-eslint/eslint-plugin": "^5.59.0",
|
|
51
|
+
"@typescript-eslint/parser": "^5.59.0",
|
|
52
|
+
"eslint": "^8.40.0",
|
|
53
|
+
"eslint-plugin-n8n-nodes-base": "^1.11.0",
|
|
54
|
+
"gulp": "^4.0.2",
|
|
55
|
+
"n8n-workflow": "^1.0.0",
|
|
56
|
+
"prettier": "^2.8.8",
|
|
57
|
+
"ts-node": "^10.9.2",
|
|
58
|
+
"typescript": "^5.0.4"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
|
62
|
+
"@ffprobe-installer/ffprobe": "^2.1.2",
|
|
63
|
+
"fluent-ffmpeg": "^2.1.2",
|
|
64
|
+
"node-fetch": "^2.6.12",
|
|
65
|
+
"tmp-promise": "^3.0.3"
|
|
66
|
+
},
|
|
67
|
+
"peerDependencies": {
|
|
68
|
+
"n8n-workflow": "*"
|
|
69
|
+
},
|
|
70
|
+
"engines": {
|
|
71
|
+
"node": ">=16.0.0"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Automatically set execute permissions on ffprobe binary after npm install
|
|
5
|
-
* This is required for @ffprobe-installer package to work correctly
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { chmodSync, existsSync } = require('fs');
|
|
9
|
-
const { resolve, join } = require('path');
|
|
10
|
-
|
|
11
|
-
// Try multiple possible paths where ffprobe might be installed
|
|
12
|
-
const possiblePaths = [
|
|
13
|
-
// Direct dependency path
|
|
14
|
-
resolve(__dirname, '../node_modules/@ffprobe-installer/linux-x64/ffprobe'),
|
|
15
|
-
// Nested dependency path (when installed as part of another package)
|
|
16
|
-
resolve(__dirname, '../node_modules/@ffprobe-installer/ffprobe/node_modules/@ffprobe-installer/linux-x64/ffprobe'),
|
|
17
|
-
// Platform-agnostic approach - check all platform binaries
|
|
18
|
-
resolve(__dirname, '../node_modules/@ffprobe-installer/darwin-x64/ffprobe'),
|
|
19
|
-
resolve(__dirname, '../node_modules/@ffprobe-installer/darwin-arm64/ffprobe'),
|
|
20
|
-
resolve(__dirname, '../node_modules/@ffprobe-installer/win32-x64/ffprobe.exe'),
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
let successCount = 0;
|
|
24
|
-
let errorCount = 0;
|
|
25
|
-
|
|
26
|
-
console.log('🔧 Fixing ffprobe permissions...');
|
|
27
|
-
|
|
28
|
-
for (const ffprobePath of possiblePaths) {
|
|
29
|
-
if (existsSync(ffprobePath)) {
|
|
30
|
-
try {
|
|
31
|
-
// Set execute permissions (755 = rwxr-xr-x)
|
|
32
|
-
chmodSync(ffprobePath, 0o755);
|
|
33
|
-
console.log(` ✅ Set permissions: ${ffprobePath}`);
|
|
34
|
-
successCount++;
|
|
35
|
-
} catch (error) {
|
|
36
|
-
console.warn(` ⚠️ Could not set permissions for ${ffprobePath}:`, error.message);
|
|
37
|
-
errorCount++;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (successCount === 0 && errorCount === 0) {
|
|
43
|
-
console.warn('⚠️ No ffprobe binaries found. This is normal if @ffprobe-installer is not yet installed.');
|
|
44
|
-
console.warn(' If you encounter "EACCES" errors when running SB Render, try:');
|
|
45
|
-
console.warn(' chmod +x node_modules/@ffprobe-installer/linux-x64/ffprobe');
|
|
46
|
-
} else if (successCount > 0) {
|
|
47
|
-
console.log(`✅ Successfully set permissions on ${successCount} ffprobe binary(ies)`);
|
|
48
|
-
} else {
|
|
49
|
-
console.error('❌ Failed to set permissions on any ffprobe binaries');
|
|
50
|
-
console.error(' You may need to manually run:');
|
|
51
|
-
console.error(' chmod +x node_modules/@ffprobe-installer/linux-x64/ffprobe');
|
|
52
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Automatically set execute permissions on ffprobe binary after npm install
|
|
5
|
+
* This is required for @ffprobe-installer package to work correctly
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { chmodSync, existsSync } = require('fs');
|
|
9
|
+
const { resolve, join } = require('path');
|
|
10
|
+
|
|
11
|
+
// Try multiple possible paths where ffprobe might be installed
|
|
12
|
+
const possiblePaths = [
|
|
13
|
+
// Direct dependency path
|
|
14
|
+
resolve(__dirname, '../node_modules/@ffprobe-installer/linux-x64/ffprobe'),
|
|
15
|
+
// Nested dependency path (when installed as part of another package)
|
|
16
|
+
resolve(__dirname, '../node_modules/@ffprobe-installer/ffprobe/node_modules/@ffprobe-installer/linux-x64/ffprobe'),
|
|
17
|
+
// Platform-agnostic approach - check all platform binaries
|
|
18
|
+
resolve(__dirname, '../node_modules/@ffprobe-installer/darwin-x64/ffprobe'),
|
|
19
|
+
resolve(__dirname, '../node_modules/@ffprobe-installer/darwin-arm64/ffprobe'),
|
|
20
|
+
resolve(__dirname, '../node_modules/@ffprobe-installer/win32-x64/ffprobe.exe'),
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
let successCount = 0;
|
|
24
|
+
let errorCount = 0;
|
|
25
|
+
|
|
26
|
+
console.log('🔧 Fixing ffprobe permissions...');
|
|
27
|
+
|
|
28
|
+
for (const ffprobePath of possiblePaths) {
|
|
29
|
+
if (existsSync(ffprobePath)) {
|
|
30
|
+
try {
|
|
31
|
+
// Set execute permissions (755 = rwxr-xr-x)
|
|
32
|
+
chmodSync(ffprobePath, 0o755);
|
|
33
|
+
console.log(` ✅ Set permissions: ${ffprobePath}`);
|
|
34
|
+
successCount++;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.warn(` ⚠️ Could not set permissions for ${ffprobePath}:`, error.message);
|
|
37
|
+
errorCount++;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (successCount === 0 && errorCount === 0) {
|
|
43
|
+
console.warn('⚠️ No ffprobe binaries found. This is normal if @ffprobe-installer is not yet installed.');
|
|
44
|
+
console.warn(' If you encounter "EACCES" errors when running SB Render, try:');
|
|
45
|
+
console.warn(' chmod +x node_modules/@ffprobe-installer/linux-x64/ffprobe');
|
|
46
|
+
} else if (successCount > 0) {
|
|
47
|
+
console.log(`✅ Successfully set permissions on ${successCount} ffprobe binary(ies)`);
|
|
48
|
+
} else {
|
|
49
|
+
console.error('❌ Failed to set permissions on any ffprobe binaries');
|
|
50
|
+
console.error(' You may need to manually run:');
|
|
51
|
+
console.error(' chmod +x node_modules/@ffprobe-installer/linux-x64/ffprobe');
|
|
52
|
+
}
|