@lee-jisoo/n8n-nodes-mediafx 1.6.0 → 1.6.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/fonts/DejaVuSans.ttf +0 -0
- package/dist/fonts/Inter-Regular.ttf +0 -0
- package/dist/fonts/NanumGothic-Regular.ttf +0 -0
- package/dist/fonts/NotoSansKR-Regular.ttf +0 -0
- package/dist/fonts/Pretendard-Regular.otf +0 -0
- package/dist/fonts/Roboto-Regular.ttf +0 -0
- package/dist/nodes/MediaFX/MediaFX.node.d.ts +12 -0
- package/dist/nodes/MediaFX/MediaFX.node.js +559 -0
- package/dist/nodes/MediaFX/mediafx.png +0 -0
- package/dist/nodes/MediaFX/operations/addSubtitle.d.ts +2 -0
- package/dist/nodes/MediaFX/operations/addSubtitle.js +146 -0
- package/dist/nodes/MediaFX/operations/addText.d.ts +2 -0
- package/dist/nodes/MediaFX/operations/addText.js +108 -0
- package/dist/nodes/MediaFX/operations/extractAudio.d.ts +2 -0
- package/dist/nodes/MediaFX/operations/extractAudio.js +57 -0
- package/dist/nodes/MediaFX/operations/imageToVideo.d.ts +5 -0
- package/dist/nodes/MediaFX/operations/imageToVideo.js +65 -0
- package/dist/nodes/MediaFX/operations/index.d.ts +13 -0
- package/dist/nodes/MediaFX/operations/index.js +29 -0
- package/dist/nodes/MediaFX/operations/merge.d.ts +2 -0
- package/dist/nodes/MediaFX/operations/merge.js +121 -0
- package/dist/nodes/MediaFX/operations/mixAudio.d.ts +2 -0
- package/dist/nodes/MediaFX/operations/mixAudio.js +141 -0
- package/dist/nodes/MediaFX/operations/multiVideoTransition.d.ts +2 -0
- package/dist/nodes/MediaFX/operations/multiVideoTransition.js +245 -0
- package/dist/nodes/MediaFX/operations/overlayVideo.d.ts +16 -0
- package/dist/nodes/MediaFX/operations/overlayVideo.js +240 -0
- package/dist/nodes/MediaFX/operations/separateAudio.d.ts +17 -0
- package/dist/nodes/MediaFX/operations/separateAudio.js +78 -0
- package/dist/nodes/MediaFX/operations/singleVideoFade.d.ts +2 -0
- package/dist/nodes/MediaFX/operations/singleVideoFade.js +60 -0
- package/dist/nodes/MediaFX/operations/speed.d.ts +12 -0
- package/dist/nodes/MediaFX/operations/speed.js +110 -0
- package/dist/nodes/MediaFX/operations/stampImage.d.ts +2 -0
- package/dist/nodes/MediaFX/operations/stampImage.js +146 -0
- package/dist/nodes/MediaFX/operations/trim.d.ts +2 -0
- package/dist/nodes/MediaFX/operations/trim.js +49 -0
- package/dist/nodes/MediaFX/properties/audio.properties.d.ts +2 -0
- package/dist/nodes/MediaFX/properties/audio.properties.js +394 -0
- package/dist/nodes/MediaFX/properties/font.properties.d.ts +2 -0
- package/dist/nodes/MediaFX/properties/font.properties.js +186 -0
- package/dist/nodes/MediaFX/properties/image.properties.d.ts +2 -0
- package/dist/nodes/MediaFX/properties/image.properties.js +333 -0
- package/dist/nodes/MediaFX/properties/resources.properties.d.ts +2 -0
- package/dist/nodes/MediaFX/properties/resources.properties.js +34 -0
- package/dist/nodes/MediaFX/properties/subtitle.properties.d.ts +2 -0
- package/dist/nodes/MediaFX/properties/subtitle.properties.js +306 -0
- package/dist/nodes/MediaFX/properties/video.properties.d.ts +2 -0
- package/dist/nodes/MediaFX/properties/video.properties.js +1135 -0
- package/dist/nodes/MediaFX/utils/ffmpegVersion.d.ts +14 -0
- package/dist/nodes/MediaFX/utils/ffmpegVersion.js +97 -0
- package/dist/nodes/MediaFX/utils.d.ts +43 -0
- package/dist/nodes/MediaFX/utils.js +410 -0
- package/package.json +1 -1
- package/CHANGELOG.md +0 -65
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.executeSeparateAudio = void 0;
|
|
27
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
28
|
+
const ffmpeg = require("fluent-ffmpeg");
|
|
29
|
+
const utils_1 = require("../utils");
|
|
30
|
+
const fs = __importStar(require("fs-extra"));
|
|
31
|
+
/**
|
|
32
|
+
* Separates audio from video, returning both a muted video and the extracted audio track.
|
|
33
|
+
*
|
|
34
|
+
* @param input - Path to the input video file
|
|
35
|
+
* @param videoFormat - Output format for the muted video (e.g., 'mp4', 'mov')
|
|
36
|
+
* @param audioFormat - Output format for the extracted audio (e.g., 'mp3', 'aac', 'wav')
|
|
37
|
+
* @param audioCodec - Audio codec to use (e.g., 'copy', 'libmp3lame', 'aac')
|
|
38
|
+
* @param audioBitrate - Audio bitrate (e.g., '192k', '320k')
|
|
39
|
+
* @param itemIndex - Current item index for error handling
|
|
40
|
+
* @returns Object containing paths to both the muted video and extracted audio
|
|
41
|
+
*/
|
|
42
|
+
async function executeSeparateAudio(input, videoFormat, audioFormat, audioCodec, audioBitrate, itemIndex) {
|
|
43
|
+
const mutedVideoPath = (0, utils_1.getTempFile)(`.${videoFormat}`);
|
|
44
|
+
const audioOutputPath = (0, utils_1.getTempFile)(`.${audioFormat}`);
|
|
45
|
+
let finalAudioCodec = audioCodec;
|
|
46
|
+
// If stream copy ('copy') is selected for a format that requires a specific
|
|
47
|
+
// codec (like mp3), we must override it to prevent an FFmpeg error.
|
|
48
|
+
if (finalAudioCodec === 'copy' && audioFormat === 'mp3') {
|
|
49
|
+
finalAudioCodec = 'libmp3lame';
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
// Step 1: Create muted video (remove audio track)
|
|
53
|
+
const mutedVideoCommand = ffmpeg(input)
|
|
54
|
+
.output(mutedVideoPath)
|
|
55
|
+
.noAudio()
|
|
56
|
+
.videoCodec('copy'); // Copy video stream without re-encoding
|
|
57
|
+
await (0, utils_1.runFfmpeg)(mutedVideoCommand);
|
|
58
|
+
// Step 2: Extract audio track
|
|
59
|
+
const audioCommand = ffmpeg(input)
|
|
60
|
+
.output(audioOutputPath)
|
|
61
|
+
.noVideo()
|
|
62
|
+
.audioCodec(finalAudioCodec)
|
|
63
|
+
.audioBitrate(audioBitrate)
|
|
64
|
+
.outputOptions('-map', '0:a:0'); // Select first audio stream
|
|
65
|
+
await (0, utils_1.runFfmpeg)(audioCommand);
|
|
66
|
+
return {
|
|
67
|
+
videoPath: mutedVideoPath,
|
|
68
|
+
audioPath: audioOutputPath,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
// Clean up any created files on error
|
|
73
|
+
await fs.remove(mutedVideoPath).catch(() => { });
|
|
74
|
+
await fs.remove(audioOutputPath).catch(() => { });
|
|
75
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Error separating audio from video. Please ensure the source video contains both video and audio tracks. FFmpeg error: ${error.message}`, { itemIndex });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.executeSeparateAudio = executeSeparateAudio;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.executeSingleVideoFade = void 0;
|
|
27
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
28
|
+
const ffmpeg = require("fluent-ffmpeg");
|
|
29
|
+
const utils_1 = require("../utils");
|
|
30
|
+
const fs = __importStar(require("fs-extra"));
|
|
31
|
+
async function executeSingleVideoFade(input,
|
|
32
|
+
// effect is 'in' or 'out' from the UI options
|
|
33
|
+
effect, startTime, duration, outputFormat, itemIndex) {
|
|
34
|
+
const outputPath = (0, utils_1.getTempFile)(`.${outputFormat}`);
|
|
35
|
+
const command = ffmpeg(input);
|
|
36
|
+
// Apply both video and audio fades for a consistent effect
|
|
37
|
+
const videoFade = `fade=type=${effect}:st=${startTime}:d=${duration}`;
|
|
38
|
+
const audioFade = `afade=type=${effect}:st=${startTime}:d=${duration}`;
|
|
39
|
+
command.videoFilters(videoFade).audioFilters(audioFade);
|
|
40
|
+
command.save(outputPath);
|
|
41
|
+
try {
|
|
42
|
+
await (0, utils_1.runFfmpeg)(command);
|
|
43
|
+
return outputPath;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
// Clean up output file if creation failed
|
|
47
|
+
await fs.remove(outputPath).catch(() => { });
|
|
48
|
+
// If the video has no audio, ffmpeg might throw an error for afade.
|
|
49
|
+
// We provide a more descriptive error message in that case.
|
|
50
|
+
const errorMessage = error.message;
|
|
51
|
+
let displayMessage = `Error applying fade effect. Please check the effect parameters (start time, duration).`;
|
|
52
|
+
if (errorMessage.includes('Cannot find a matching stream for unlabeled')) {
|
|
53
|
+
displayMessage = `Error applying audio fade: The source video may not have an audio track.`;
|
|
54
|
+
}
|
|
55
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `${displayMessage} FFmpeg error: ${errorMessage}`, {
|
|
56
|
+
itemIndex,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.executeSingleVideoFade = executeSingleVideoFade;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IExecuteFunctions } from 'n8n-workflow';
|
|
2
|
+
/**
|
|
3
|
+
* Execute speed adjustment on a video.
|
|
4
|
+
*
|
|
5
|
+
* @param input - Path to the input video file
|
|
6
|
+
* @param speed - Speed multiplier (0.25 = 4x slower, 2.0 = 2x faster, etc.)
|
|
7
|
+
* @param adjustAudio - Whether to adjust audio speed as well (pitch will change)
|
|
8
|
+
* @param maintainPitch - Whether to maintain audio pitch when adjusting speed (requires rubberband filter)
|
|
9
|
+
* @param outputFormat - Output file format
|
|
10
|
+
* @param itemIndex - Index of the current item being processed
|
|
11
|
+
*/
|
|
12
|
+
export declare function executeSpeed(this: IExecuteFunctions, input: string, speed: number, adjustAudio: boolean, maintainPitch: boolean, outputFormat: string, itemIndex: number): Promise<string>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.executeSpeed = void 0;
|
|
27
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
28
|
+
const ffmpeg = require("fluent-ffmpeg");
|
|
29
|
+
const utils_1 = require("../utils");
|
|
30
|
+
const fs = __importStar(require("fs-extra"));
|
|
31
|
+
/**
|
|
32
|
+
* Execute speed adjustment on a video.
|
|
33
|
+
*
|
|
34
|
+
* @param input - Path to the input video file
|
|
35
|
+
* @param speed - Speed multiplier (0.25 = 4x slower, 2.0 = 2x faster, etc.)
|
|
36
|
+
* @param adjustAudio - Whether to adjust audio speed as well (pitch will change)
|
|
37
|
+
* @param maintainPitch - Whether to maintain audio pitch when adjusting speed (requires rubberband filter)
|
|
38
|
+
* @param outputFormat - Output file format
|
|
39
|
+
* @param itemIndex - Index of the current item being processed
|
|
40
|
+
*/
|
|
41
|
+
async function executeSpeed(input, speed, adjustAudio, maintainPitch, outputFormat, itemIndex) {
|
|
42
|
+
const outputPath = (0, utils_1.getTempFile)(`.${outputFormat}`);
|
|
43
|
+
// Validate speed value
|
|
44
|
+
if (speed <= 0) {
|
|
45
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Speed must be greater than 0', { itemIndex });
|
|
46
|
+
}
|
|
47
|
+
// FFmpeg uses PTS (Presentation Time Stamp) for video speed
|
|
48
|
+
// setpts=0.5*PTS means 2x faster (less time per frame)
|
|
49
|
+
// setpts=2*PTS means 2x slower (more time per frame)
|
|
50
|
+
const videoPtsFactor = 1 / speed;
|
|
51
|
+
const videoFilter = `setpts=${videoPtsFactor}*PTS`;
|
|
52
|
+
// For audio, atempo filter accepts values between 0.5 and 2.0
|
|
53
|
+
// For values outside this range, we need to chain multiple atempo filters
|
|
54
|
+
const buildAtempoFilter = (targetSpeed) => {
|
|
55
|
+
const filters = [];
|
|
56
|
+
let remaining = targetSpeed;
|
|
57
|
+
// atempo only accepts 0.5 to 2.0, so we chain multiple filters
|
|
58
|
+
while (remaining > 2.0) {
|
|
59
|
+
filters.push('atempo=2.0');
|
|
60
|
+
remaining /= 2.0;
|
|
61
|
+
}
|
|
62
|
+
while (remaining < 0.5) {
|
|
63
|
+
filters.push('atempo=0.5');
|
|
64
|
+
remaining /= 0.5;
|
|
65
|
+
}
|
|
66
|
+
filters.push(`atempo=${remaining}`);
|
|
67
|
+
return filters;
|
|
68
|
+
};
|
|
69
|
+
try {
|
|
70
|
+
let command = ffmpeg(input);
|
|
71
|
+
// Apply video speed filter
|
|
72
|
+
command = command.videoFilters([videoFilter]);
|
|
73
|
+
if (adjustAudio) {
|
|
74
|
+
if (maintainPitch) {
|
|
75
|
+
// Use rubberband filter to maintain pitch (requires libavfilter compiled with rubberband)
|
|
76
|
+
// Fallback to atempo if rubberband is not available
|
|
77
|
+
try {
|
|
78
|
+
const audioFilter = `rubberband=tempo=${speed}`;
|
|
79
|
+
command = command.audioFilters([audioFilter]);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Fallback to atempo (pitch will change slightly)
|
|
83
|
+
const atempoFilters = buildAtempoFilter(speed);
|
|
84
|
+
command = command.audioFilters(atempoFilters);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// Use atempo filter (pitch will change proportionally)
|
|
89
|
+
const atempoFilters = buildAtempoFilter(speed);
|
|
90
|
+
command = command.audioFilters(atempoFilters);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// Remove audio entirely when not adjusting
|
|
95
|
+
command = command.noAudio();
|
|
96
|
+
}
|
|
97
|
+
command = command
|
|
98
|
+
.videoCodec('libx264')
|
|
99
|
+
.audioCodec('aac')
|
|
100
|
+
.output(outputPath);
|
|
101
|
+
await (0, utils_1.runFfmpeg)(command);
|
|
102
|
+
return outputPath;
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
// Clean up output file if creation failed
|
|
106
|
+
await fs.remove(outputPath).catch(() => { });
|
|
107
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Error adjusting video speed. FFmpeg error: ${error.message}`, { itemIndex });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
exports.executeSpeed = executeSpeed;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.executeStampImage = void 0;
|
|
27
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
28
|
+
const path = __importStar(require("path"));
|
|
29
|
+
const ffmpeg = require("fluent-ffmpeg");
|
|
30
|
+
const utils_1 = require("../utils");
|
|
31
|
+
const fs = __importStar(require("fs-extra"));
|
|
32
|
+
async function executeStampImage(videoPath, imagePath, options, itemIndex) {
|
|
33
|
+
var _a;
|
|
34
|
+
const outputPath = (0, utils_1.getTempFile)(path.extname(videoPath));
|
|
35
|
+
// Basic stamp options
|
|
36
|
+
const width = options.width || 150;
|
|
37
|
+
const height = options.height || -1;
|
|
38
|
+
const x = options.x || '10';
|
|
39
|
+
const y = options.y || '10';
|
|
40
|
+
const rotationDegrees = options.rotation || 0;
|
|
41
|
+
const opacity = (_a = options.opacity) !== null && _a !== void 0 ? _a : 1.0;
|
|
42
|
+
// Time control options
|
|
43
|
+
const enableTimeControl = options.enableTimeControl;
|
|
44
|
+
const startTime = options.startTime || 0;
|
|
45
|
+
const endTime = options.endTime || 5;
|
|
46
|
+
try {
|
|
47
|
+
// Get video duration for calculations
|
|
48
|
+
// const videoDuration = await getDuration(videoPath);
|
|
49
|
+
// console.log('=== STAMP IMAGE DEBUG ===');
|
|
50
|
+
// console.log('Video path:', videoPath);
|
|
51
|
+
// console.log('Image path:', imagePath);
|
|
52
|
+
// console.log('Video duration:', videoDuration);
|
|
53
|
+
// console.log('Stamp options:', {
|
|
54
|
+
// width, height, x, y, rotationDegrees, opacity,
|
|
55
|
+
// enableTimeControl, startTime, endTime
|
|
56
|
+
// });
|
|
57
|
+
// Start with the simplest possible filter
|
|
58
|
+
let complexFilter = '';
|
|
59
|
+
// Check if we need any processing
|
|
60
|
+
const needsScaling = width !== -1 || height !== -1;
|
|
61
|
+
const needsRotation = rotationDegrees !== 0;
|
|
62
|
+
const needsOpacity = opacity < 1.0;
|
|
63
|
+
const needsProcessing = needsScaling || needsRotation || needsOpacity;
|
|
64
|
+
if (!needsProcessing && !enableTimeControl) {
|
|
65
|
+
// Simplest case: direct overlay
|
|
66
|
+
complexFilter = '[0:v][1:v]overlay=x=' + x + ':y=' + y;
|
|
67
|
+
}
|
|
68
|
+
else if (!enableTimeControl) {
|
|
69
|
+
// Process image but no time control
|
|
70
|
+
let imageFilter = '[1:v]';
|
|
71
|
+
if (needsScaling) {
|
|
72
|
+
imageFilter += 'scale=' + width + ':' + height;
|
|
73
|
+
}
|
|
74
|
+
if (needsRotation) {
|
|
75
|
+
if (!needsScaling) {
|
|
76
|
+
imageFilter += 'rotate=' + (rotationDegrees * Math.PI / 180) + ':fillcolor=none';
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
imageFilter += ',rotate=' + (rotationDegrees * Math.PI / 180) + ':fillcolor=none';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (needsOpacity) {
|
|
83
|
+
if (!needsScaling && !needsRotation) {
|
|
84
|
+
imageFilter += 'colorchannelmixer=aa=' + opacity;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
imageFilter += ',colorchannelmixer=aa=' + opacity;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
imageFilter += '[processed]';
|
|
91
|
+
complexFilter = imageFilter + ';[0:v][processed]overlay=x=' + x + ':y=' + y;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// With time control
|
|
95
|
+
let imageFilter = '[1:v]';
|
|
96
|
+
if (needsScaling) {
|
|
97
|
+
imageFilter += 'scale=' + width + ':' + height;
|
|
98
|
+
}
|
|
99
|
+
if (needsRotation) {
|
|
100
|
+
if (!needsScaling) {
|
|
101
|
+
imageFilter += 'rotate=' + (rotationDegrees * Math.PI / 180) + ':fillcolor=none';
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
imageFilter += ',rotate=' + (rotationDegrees * Math.PI / 180) + ':fillcolor=none';
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (needsOpacity) {
|
|
108
|
+
if (!needsScaling && !needsRotation) {
|
|
109
|
+
imageFilter += 'colorchannelmixer=aa=' + opacity;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
imageFilter += ',colorchannelmixer=aa=' + opacity;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (needsProcessing) {
|
|
116
|
+
imageFilter += '[processed]';
|
|
117
|
+
complexFilter = imageFilter + ';[0:v][processed]overlay=x=' + x + ':y=' + y;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
complexFilter = '[0:v][1:v]overlay=x=' + x + ':y=' + y;
|
|
121
|
+
}
|
|
122
|
+
// Add time control
|
|
123
|
+
if (enableTimeControl) {
|
|
124
|
+
complexFilter += ':enable=\'between(t,' + startTime + ',' + endTime + ')\'';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
console.log('Generated FFmpeg filter:', complexFilter);
|
|
128
|
+
console.log('========================');
|
|
129
|
+
const command = ffmpeg()
|
|
130
|
+
.input(videoPath)
|
|
131
|
+
.input(imagePath)
|
|
132
|
+
.complexFilter(complexFilter)
|
|
133
|
+
.output(outputPath);
|
|
134
|
+
await (0, utils_1.runFfmpeg)(command);
|
|
135
|
+
return outputPath;
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
// Clean up output file if creation failed
|
|
139
|
+
await fs.remove(outputPath).catch(() => { });
|
|
140
|
+
console.error('=== STAMP IMAGE ERROR ===');
|
|
141
|
+
console.error('Error details:', error);
|
|
142
|
+
console.error('========================');
|
|
143
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to stamp image: ${error instanceof Error ? error.message : 'Unknown error'}`, { itemIndex });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
exports.executeStampImage = executeStampImage;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.executeTrim = void 0;
|
|
27
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
28
|
+
const ffmpeg = require("fluent-ffmpeg");
|
|
29
|
+
const utils_1 = require("../utils");
|
|
30
|
+
const fs = __importStar(require("fs-extra"));
|
|
31
|
+
async function executeTrim(input, from, to, outputFormat, itemIndex) {
|
|
32
|
+
const outputPath = (0, utils_1.getTempFile)(`.${outputFormat}`);
|
|
33
|
+
const command = ffmpeg(input)
|
|
34
|
+
.setStartTime(from)
|
|
35
|
+
.setDuration(to - from)
|
|
36
|
+
.output(outputPath)
|
|
37
|
+
.videoCodec('libx264')
|
|
38
|
+
.audioCodec('aac');
|
|
39
|
+
try {
|
|
40
|
+
await (0, utils_1.runFfmpeg)(command);
|
|
41
|
+
return outputPath;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
// Clean up output file if creation failed
|
|
45
|
+
await fs.remove(outputPath).catch(() => { });
|
|
46
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Error trimming video. Please check that start and end times are valid and within the video's duration. FFmpeg error: ${error.message}`, { itemIndex });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.executeTrim = executeTrim;
|