n8n-nodes-ffmpeg-wasm 1.2.4 → 1.2.6
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.
|
@@ -4,32 +4,16 @@ exports.FFmpegWasmApi = void 0;
|
|
|
4
4
|
class FFmpegWasmApi {
|
|
5
5
|
constructor() {
|
|
6
6
|
this.name = "ffmpegWasmApi";
|
|
7
|
-
this.displayName = "FFmpeg.wasm
|
|
8
|
-
this.documentationUrl = "https://
|
|
7
|
+
this.displayName = "FFmpeg.wasm Configuration";
|
|
8
|
+
this.documentationUrl = "https://github.com/ffmpegwasm/ffmpeg.wasm";
|
|
9
9
|
this.properties = [
|
|
10
10
|
{
|
|
11
|
-
displayName: "Core
|
|
12
|
-
name: "
|
|
11
|
+
displayName: "Core Path",
|
|
12
|
+
name: "corePath",
|
|
13
13
|
type: "string",
|
|
14
14
|
default: "",
|
|
15
|
-
placeholder: "https://unpkg.com/@ffmpeg/core@0.
|
|
16
|
-
description: "Custom URL
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
displayName: "Wasm URL",
|
|
20
|
-
name: "wasmURL",
|
|
21
|
-
type: "string",
|
|
22
|
-
default: "",
|
|
23
|
-
placeholder: "https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd/ffmpeg-core.wasm",
|
|
24
|
-
description: "Custom URL for FFmpeg WASM binary (optional - leave empty to use default)",
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
displayName: "Worker URL",
|
|
28
|
-
name: "workerURL",
|
|
29
|
-
type: "string",
|
|
30
|
-
default: "",
|
|
31
|
-
placeholder: "https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd/ffmpeg-core.worker.js",
|
|
32
|
-
description: "Custom URL for FFmpeg worker (optional - leave empty to use default)",
|
|
15
|
+
placeholder: "https://unpkg.com/@ffmpeg/core@0.11.0/dist/ffmpeg-core.js",
|
|
16
|
+
description: "Custom URL or local path to ffmpeg-core.js (optional - leave empty to use CDN default)",
|
|
33
17
|
},
|
|
34
18
|
];
|
|
35
19
|
}
|
|
@@ -960,29 +960,26 @@ class FFmpegWasm {
|
|
|
960
960
|
async execute() {
|
|
961
961
|
const items = this.getInputData();
|
|
962
962
|
const returnData = [];
|
|
963
|
-
|
|
963
|
+
let corePath;
|
|
964
964
|
try {
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
if (credentials.coreURL)
|
|
969
|
-
loadOptions.coreURL = credentials.coreURL;
|
|
970
|
-
if (credentials.wasmURL)
|
|
971
|
-
loadOptions.wasmURL = credentials.wasmURL;
|
|
972
|
-
if (credentials.workerURL)
|
|
973
|
-
loadOptions.workerURL = credentials.workerURL;
|
|
965
|
+
const credentials = await this.getCredentials("ffmpegWasmApi");
|
|
966
|
+
if (credentials.corePath) {
|
|
967
|
+
corePath = credentials.corePath;
|
|
974
968
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
969
|
+
}
|
|
970
|
+
catch {
|
|
971
|
+
}
|
|
972
|
+
let lastLogOutput = "";
|
|
973
|
+
const firstOpts = this.getNodeParameter("additionalOptions", 0, {});
|
|
974
|
+
const ffmpeg = (0, ffmpeg_1.createFFmpeg)({
|
|
975
|
+
log: firstOpts.enableLogging || false,
|
|
976
|
+
logger: ({ message }) => {
|
|
981
977
|
lastLogOutput += message + "\n";
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
978
|
+
},
|
|
979
|
+
...(corePath ? { corePath } : {}),
|
|
980
|
+
});
|
|
981
|
+
try {
|
|
982
|
+
await ffmpeg.load();
|
|
986
983
|
for (let i = 0; i < items.length; i++) {
|
|
987
984
|
try {
|
|
988
985
|
const binaryPropertyName = this.getNodeParameter("binaryPropertyName", i);
|
|
@@ -996,12 +993,16 @@ class FFmpegWasm {
|
|
|
996
993
|
const inputFilename = `input_${i}_${Date.now()}${inputExt}`;
|
|
997
994
|
const outputFilename = `output_${i}_${Date.now()}`;
|
|
998
995
|
const inputData = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
|
|
999
|
-
|
|
996
|
+
ffmpeg.FS("writeFile", inputFilename, new Uint8Array(inputData));
|
|
1000
997
|
let ffmpegCommand = [];
|
|
1001
998
|
let outputExt = "";
|
|
1002
999
|
if (operation === "metadata") {
|
|
1003
1000
|
lastLogOutput = "";
|
|
1004
|
-
|
|
1001
|
+
try {
|
|
1002
|
+
await ffmpeg.run("-i", inputFilename, "-hide_banner");
|
|
1003
|
+
}
|
|
1004
|
+
catch {
|
|
1005
|
+
}
|
|
1005
1006
|
const metadata = (0, helpers_1.parseMetadataFromLogs)(lastLogOutput);
|
|
1006
1007
|
returnData.push({
|
|
1007
1008
|
json: {
|
|
@@ -1010,7 +1011,7 @@ class FFmpegWasm {
|
|
|
1010
1011
|
},
|
|
1011
1012
|
});
|
|
1012
1013
|
try {
|
|
1013
|
-
|
|
1014
|
+
ffmpeg.FS("unlink", inputFilename);
|
|
1014
1015
|
}
|
|
1015
1016
|
catch { }
|
|
1016
1017
|
continue;
|
|
@@ -1133,14 +1134,14 @@ class FFmpegWasm {
|
|
|
1133
1134
|
: ".mp4";
|
|
1134
1135
|
const videoData = await this.helpers.getBinaryDataBuffer(i, propName);
|
|
1135
1136
|
const inputName = `input_${i}_${j}_${Date.now()}${ext}`;
|
|
1136
|
-
|
|
1137
|
+
ffmpeg.FS("writeFile", inputName, new Uint8Array(videoData));
|
|
1137
1138
|
inputFiles.push(inputName);
|
|
1138
1139
|
}
|
|
1139
1140
|
const concatList = inputFiles
|
|
1140
1141
|
.map((f) => `file '${f}'`)
|
|
1141
1142
|
.join("\n");
|
|
1142
1143
|
const listFilename = `list_${i}_${Date.now()}.txt`;
|
|
1143
|
-
|
|
1144
|
+
ffmpeg.FS("writeFile", listFilename, new TextEncoder().encode(concatList));
|
|
1144
1145
|
outputExt = (0, helpers_1.normalizeExtension)(mergeOutputFormat);
|
|
1145
1146
|
const outputName = `${outputFilename}${outputExt}`;
|
|
1146
1147
|
if (addTransition) {
|
|
@@ -1353,7 +1354,7 @@ class FFmpegWasm {
|
|
|
1353
1354
|
: ".wav";
|
|
1354
1355
|
const audioData = await this.helpers.getBinaryDataBuffer(i, propName);
|
|
1355
1356
|
const inputName = `audio_input_${i}_${j}_${Date.now()}${ext}`;
|
|
1356
|
-
|
|
1357
|
+
ffmpeg.FS("writeFile", inputName, new Uint8Array(audioData));
|
|
1357
1358
|
inputFiles.push(inputName);
|
|
1358
1359
|
}
|
|
1359
1360
|
outputExt = (0, helpers_1.normalizeExtension)(audioMixOutputFormat);
|
|
@@ -1448,7 +1449,7 @@ class FFmpegWasm {
|
|
|
1448
1449
|
: ".png";
|
|
1449
1450
|
const overlayData = await this.helpers.getBinaryDataBuffer(i, overlayBinaryProperty);
|
|
1450
1451
|
const overlayFilename = `overlay_${i}_${Date.now()}${overlayExt}`;
|
|
1451
|
-
|
|
1452
|
+
ffmpeg.FS("writeFile", overlayFilename, new Uint8Array(overlayData));
|
|
1452
1453
|
outputExt = (0, helpers_1.normalizeExtension)(overlayOutputFormat);
|
|
1453
1454
|
const outputName = `${outputFilename}${outputExt}`;
|
|
1454
1455
|
let videoFilter = "";
|
|
@@ -1520,7 +1521,7 @@ class FFmpegWasm {
|
|
|
1520
1521
|
const subtitleOutputFormat = this.getNodeParameter("subtitleOutputFormat", i);
|
|
1521
1522
|
const subtitleData = await this.helpers.getBinaryDataBuffer(i, subtitleBinaryProperty);
|
|
1522
1523
|
const subtitleFilename = `subtitle_${i}_${Date.now()}.${subtitleFormat}`;
|
|
1523
|
-
|
|
1524
|
+
ffmpeg.FS("writeFile", subtitleFilename, new Uint8Array(subtitleData));
|
|
1524
1525
|
outputExt = (0, helpers_1.normalizeExtension)(subtitleOutputFormat);
|
|
1525
1526
|
const outputName = `${outputFilename}${outputExt}`;
|
|
1526
1527
|
const assColor = (0, helpers_1.colorToAssFormat)(subtitleFontColor);
|
|
@@ -1639,7 +1640,7 @@ class FFmpegWasm {
|
|
|
1639
1640
|
const seqTimeoutMs = (additionalOptions.timeout || 300) * 1000;
|
|
1640
1641
|
lastLogOutput = "";
|
|
1641
1642
|
await Promise.race([
|
|
1642
|
-
ffmpeg.
|
|
1643
|
+
ffmpeg.run(...ffmpegCommand),
|
|
1643
1644
|
new Promise((_, reject) => setTimeout(() => reject(new Error(`FFmpeg timed out after ${additionalOptions.timeout || 300}s`)), seqTimeoutMs)),
|
|
1644
1645
|
]);
|
|
1645
1646
|
const mimeType = (0, helpers_1.getMimeTypeFromExtension)(sequenceOutputFormat);
|
|
@@ -1647,7 +1648,7 @@ class FFmpegWasm {
|
|
|
1647
1648
|
while (true) {
|
|
1648
1649
|
const frameName = `frame_${String(frameIndex).padStart(4, "0")}.${sequenceOutputFormat}`;
|
|
1649
1650
|
try {
|
|
1650
|
-
const frameData =
|
|
1651
|
+
const frameData = ffmpeg.FS("readFile", frameName);
|
|
1651
1652
|
returnData.push({
|
|
1652
1653
|
json: {
|
|
1653
1654
|
...items[i].json,
|
|
@@ -1667,7 +1668,10 @@ class FFmpegWasm {
|
|
|
1667
1668
|
},
|
|
1668
1669
|
},
|
|
1669
1670
|
});
|
|
1670
|
-
|
|
1671
|
+
try {
|
|
1672
|
+
ffmpeg.FS("unlink", frameName);
|
|
1673
|
+
}
|
|
1674
|
+
catch { }
|
|
1671
1675
|
frameIndex++;
|
|
1672
1676
|
}
|
|
1673
1677
|
catch {
|
|
@@ -1675,7 +1679,7 @@ class FFmpegWasm {
|
|
|
1675
1679
|
}
|
|
1676
1680
|
}
|
|
1677
1681
|
try {
|
|
1678
|
-
|
|
1682
|
+
ffmpeg.FS("unlink", inputFilename);
|
|
1679
1683
|
}
|
|
1680
1684
|
catch { }
|
|
1681
1685
|
continue;
|
|
@@ -1733,11 +1737,11 @@ class FFmpegWasm {
|
|
|
1733
1737
|
outputExt = (0, helpers_1.normalizeExtension)(compressOutputFormat);
|
|
1734
1738
|
const outputName = `${outputFilename}${outputExt}`;
|
|
1735
1739
|
lastLogOutput = "";
|
|
1736
|
-
|
|
1737
|
-
"-i",
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1740
|
+
try {
|
|
1741
|
+
await ffmpeg.run("-i", inputFilename, "-hide_banner");
|
|
1742
|
+
}
|
|
1743
|
+
catch {
|
|
1744
|
+
}
|
|
1741
1745
|
const meta = (0, helpers_1.parseMetadataFromLogs)(lastLogOutput);
|
|
1742
1746
|
const durationSec = meta.durationSeconds || 60;
|
|
1743
1747
|
const audioBitrateKbps = parseInt(compressAudioBitrate) || 128;
|
|
@@ -1771,11 +1775,11 @@ class FFmpegWasm {
|
|
|
1771
1775
|
const timeoutMs = (additionalOptions.timeout || 300) * 1000;
|
|
1772
1776
|
lastLogOutput = "";
|
|
1773
1777
|
await Promise.race([
|
|
1774
|
-
ffmpeg.
|
|
1778
|
+
ffmpeg.run(...ffmpegCommand),
|
|
1775
1779
|
new Promise((_, reject) => setTimeout(() => reject(new Error(`FFmpeg timed out after ${additionalOptions.timeout || 300}s`)), timeoutMs)),
|
|
1776
1780
|
]);
|
|
1777
1781
|
const outputName = `${outputFilename}${outputExt}`;
|
|
1778
|
-
const outputData =
|
|
1782
|
+
const outputData = ffmpeg.FS("readFile", outputName);
|
|
1779
1783
|
const outputMimeType = (0, helpers_1.getMimeTypeFromExtension)(outputExt.replace(".", ""));
|
|
1780
1784
|
const outputItem = {
|
|
1781
1785
|
json: {
|
|
@@ -1797,8 +1801,8 @@ class FFmpegWasm {
|
|
|
1797
1801
|
};
|
|
1798
1802
|
returnData.push(outputItem);
|
|
1799
1803
|
try {
|
|
1800
|
-
|
|
1801
|
-
|
|
1804
|
+
ffmpeg.FS("unlink", inputFilename);
|
|
1805
|
+
ffmpeg.FS("unlink", outputName);
|
|
1802
1806
|
}
|
|
1803
1807
|
catch { }
|
|
1804
1808
|
}
|
|
@@ -1809,18 +1813,20 @@ class FFmpegWasm {
|
|
|
1809
1813
|
...items[i].json,
|
|
1810
1814
|
error: error instanceof Error
|
|
1811
1815
|
? error.message
|
|
1812
|
-
:
|
|
1816
|
+
: String(error),
|
|
1813
1817
|
},
|
|
1814
1818
|
});
|
|
1815
1819
|
}
|
|
1816
1820
|
else {
|
|
1817
|
-
|
|
1821
|
+
if (error instanceof Error)
|
|
1822
|
+
throw error;
|
|
1823
|
+
throw new Error(String(error));
|
|
1818
1824
|
}
|
|
1819
1825
|
}
|
|
1820
1826
|
}
|
|
1821
1827
|
}
|
|
1822
1828
|
finally {
|
|
1823
|
-
ffmpeg.
|
|
1829
|
+
ffmpeg.exit();
|
|
1824
1830
|
}
|
|
1825
1831
|
return [returnData];
|
|
1826
1832
|
}
|
package/logo.png
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-ffmpeg-wasm",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"description": "n8n community node for FFmpeg using ffmpeg.wasm - process audio and video files directly in your workflows",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"typescript": "^5.3.0"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@ffmpeg/ffmpeg": "^0.
|
|
71
|
+
"@ffmpeg/ffmpeg": "^0.11.6"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
74
|
"n8n-workflow": ">=1.0.0"
|