simple-ffmpegjs 0.2.0 → 0.3.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.
@@ -1,3 +1,14 @@
1
+ const os = require("os");
2
+ const { escapeFilePath } = require("./strings");
3
+
4
+ /**
5
+ * Get the null device path for the current platform
6
+ * @returns {string} '/dev/null' on Unix, 'NUL' on Windows
7
+ */
8
+ function getNullDevice() {
9
+ return os.platform() === "win32" ? "NUL" : "/dev/null";
10
+ }
11
+
1
12
  /**
2
13
  * Build the main FFmpeg export command
3
14
  */
@@ -77,7 +88,7 @@ function buildMainCommand({
77
88
  if (twoPass && passNumber) {
78
89
  cmd += `-pass ${passNumber} `;
79
90
  if (passLogFile) {
80
- cmd += `-passlogfile "${passLogFile}" `;
91
+ cmd += `-passlogfile "${escapeFilePath(passLogFile)}" `;
81
92
  }
82
93
  }
83
94
 
@@ -112,7 +123,7 @@ function buildMainCommand({
112
123
 
113
124
  // For two-pass first pass, output to null
114
125
  if (twoPass && passNumber === 1) {
115
- cmd += "-f null /dev/null";
126
+ cmd += `-f null ${getNullDevice()}`;
116
127
  return cmd;
117
128
  }
118
129
 
@@ -148,7 +159,7 @@ function buildMainCommand({
148
159
  }
149
160
  }
150
161
 
151
- cmd += `"${outputPath}"`;
162
+ cmd += `"${escapeFilePath(outputPath)}"`;
152
163
  return cmd;
153
164
  }
154
165
 
@@ -163,14 +174,14 @@ function buildTextBatchCommand({
163
174
  intermediateCrf,
164
175
  outputPath,
165
176
  }) {
166
- return `ffmpeg -y -i "${inputPath}" -filter_complex "[0:v]null[invid];${filterString}" -map "[outVideoAndText]" -map 0:a? -c:v ${intermediateVideoCodec} -preset ${intermediatePreset} -crf ${intermediateCrf} -c:a copy -movflags +faststart "${outputPath}"`;
177
+ return `ffmpeg -y -i "${escapeFilePath(inputPath)}" -filter_complex "[0:v]null[invid];${filterString}" -map "[outVideoAndText]" -map 0:a? -c:v ${intermediateVideoCodec} -preset ${intermediatePreset} -crf ${intermediateCrf} -c:a copy -movflags +faststart "${escapeFilePath(outputPath)}"`;
167
178
  }
168
179
 
169
180
  /**
170
181
  * Build command to generate a thumbnail
171
182
  */
172
183
  function buildThumbnailCommand({ inputPath, outputPath, time, width, height }) {
173
- let cmd = `ffmpeg -y -ss ${time} -i "${inputPath}" -vframes 1 `;
184
+ let cmd = `ffmpeg -y -ss ${time} -i "${escapeFilePath(inputPath)}" -vframes 1 `;
174
185
 
175
186
  if (width || height) {
176
187
  const w = width || -1;
@@ -178,7 +189,7 @@ function buildThumbnailCommand({ inputPath, outputPath, time, width, height }) {
178
189
  cmd += `-vf "scale=${w}:${h}" `;
179
190
  }
180
191
 
181
- cmd += `"${outputPath}"`;
192
+ cmd += `"${escapeFilePath(outputPath)}"`;
182
193
  return cmd;
183
194
  }
184
195
 
@@ -2,9 +2,52 @@ function escapeSingleQuotes(text) {
2
2
  return String(text).replace(/'/g, "\\'");
3
3
  }
4
4
 
5
+ /**
6
+ * Escape a file path for use in FFmpeg command line arguments.
7
+ * Prevents command injection by escaping quotes and backslashes.
8
+ * @param {string} filePath - The file path to escape
9
+ * @returns {string} Escaped file path safe for use in double-quoted command strings
10
+ */
11
+ function escapeFilePath(filePath) {
12
+ if (typeof filePath !== "string") return "";
13
+ // Escape backslashes first, then double quotes
14
+ // This makes the path safe for use inside double-quoted shell strings
15
+ return filePath.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
16
+ }
17
+
18
+ /**
19
+ * Check if text contains characters that are problematic for FFmpeg's
20
+ * drawtext filter and should use the textfile approach instead.
21
+ */
22
+ function hasProblematicChars(text) {
23
+ if (typeof text !== "string") return false;
24
+ // These characters cannot be reliably escaped in filter_complex parsing
25
+ // when passed through shell double-quoting
26
+ return /[,;{}\[\]"]/.test(text);
27
+ }
28
+
5
29
  function escapeDrawtextText(text) {
6
30
  if (typeof text !== "string") return "";
7
- return text.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/:/g, "\\:");
31
+ // Escape characters that have special meaning in FFmpeg drawtext filter
32
+ // AND characters that break shell parsing (since filter_complex is double-quoted)
33
+ return text
34
+ .replace(/\\/g, "\\\\") // Escape backslashes first
35
+ .replace(/'/g, "\\'") // Escape single quotes (text delimiter)
36
+ .replace(/"/g, '\\"') // Escape double quotes (shell safety)
37
+ .replace(/:/g, "\\:") // Escape colons (option separator)
38
+ .replace(/\n/g, " ") // Replace newlines with space (multiline not supported)
39
+ .replace(/\r/g, ""); // Remove carriage returns
40
+ }
41
+
42
+ /**
43
+ * Escape a file path for use in FFmpeg filters (Windows paths need special handling)
44
+ */
45
+ function escapeTextFilePath(filePath) {
46
+ if (typeof filePath !== "string") return "";
47
+ // FFmpeg on Windows needs forward slashes and escaped colons
48
+ return filePath
49
+ .replace(/\\/g, "/") // Convert backslashes to forward slashes
50
+ .replace(/:/g, "\\:"); // Escape colons (for Windows drive letters)
8
51
  }
9
52
 
10
53
  function getClipAudioString(clip, inputIndex) {
@@ -18,4 +61,11 @@ function getClipAudioString(clip, inputIndex) {
18
61
  return { audioStringPart, audioConcatInput };
19
62
  }
20
63
 
21
- module.exports = { escapeSingleQuotes, escapeDrawtextText, getClipAudioString };
64
+ module.exports = {
65
+ escapeSingleQuotes,
66
+ escapeFilePath,
67
+ escapeDrawtextText,
68
+ getClipAudioString,
69
+ hasProblematicChars,
70
+ escapeTextFilePath,
71
+ };