simple-ffmpegjs 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +453 -295
- package/assets/example-thumbnail.jpg +0 -0
- package/index.cjs +2 -0
- package/index.mjs +2 -0
- package/package.json +64 -22
- package/src/core/constants.js +39 -1
- package/src/core/errors.js +64 -0
- package/src/core/gaps.js +81 -0
- package/src/core/validation.js +30 -26
- package/src/ffmpeg/command_builder.js +168 -8
- package/src/ffmpeg/text_renderer.js +10 -4
- package/src/ffmpeg/video_builder.js +47 -2
- package/src/lib/utils.js +200 -1
- package/src/loaders.js +4 -4
- package/src/simpleffmpeg.js +493 -237
- package/types/index.d.mts +342 -0
- package/types/index.d.ts +399 -116
- package/index.js +0 -1
package/types/index.d.ts
CHANGED
|
@@ -1,128 +1,411 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
| "music"
|
|
6
|
-
| "backgroundAudio"
|
|
7
|
-
| "image";
|
|
8
|
-
|
|
9
|
-
export interface BaseClip {
|
|
10
|
-
type: ClipType;
|
|
11
|
-
url?: string;
|
|
12
|
-
position: number;
|
|
13
|
-
end: number;
|
|
14
|
-
}
|
|
1
|
+
declare namespace SIMPLEFFMPEG {
|
|
2
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
3
|
+
// Error Classes
|
|
4
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
15
5
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
transition?: { type: string; duration: number };
|
|
21
|
-
}
|
|
6
|
+
/** Base error class for all simple-ffmpeg errors */
|
|
7
|
+
class SimpleffmpegError extends Error {
|
|
8
|
+
name: "SimpleffmpegError";
|
|
9
|
+
}
|
|
22
10
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
11
|
+
/** Thrown when clip validation fails */
|
|
12
|
+
class ValidationError extends SimpleffmpegError {
|
|
13
|
+
name: "ValidationError";
|
|
14
|
+
errors: string[];
|
|
15
|
+
warnings: string[];
|
|
16
|
+
}
|
|
29
17
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
18
|
+
/** Thrown when FFmpeg command execution fails */
|
|
19
|
+
class FFmpegError extends SimpleffmpegError {
|
|
20
|
+
name: "FFmpegError";
|
|
21
|
+
stderr: string;
|
|
22
|
+
command: string;
|
|
23
|
+
exitCode: number | null;
|
|
24
|
+
}
|
|
36
25
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
| "zoom-out"
|
|
43
|
-
| "pan-left"
|
|
44
|
-
| "pan-right"
|
|
45
|
-
| "pan-up"
|
|
46
|
-
| "pan-down";
|
|
47
|
-
}
|
|
26
|
+
/** Thrown when a media file cannot be found or accessed */
|
|
27
|
+
class MediaNotFoundError extends SimpleffmpegError {
|
|
28
|
+
name: "MediaNotFoundError";
|
|
29
|
+
path: string;
|
|
30
|
+
}
|
|
48
31
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
| "fade-in-out"
|
|
54
|
-
| "pop"
|
|
55
|
-
| "pop-bounce";
|
|
56
|
-
|
|
57
|
-
export interface TextWordWindow {
|
|
58
|
-
text: string;
|
|
59
|
-
start: number;
|
|
60
|
-
end: number;
|
|
61
|
-
}
|
|
32
|
+
/** Thrown when export is cancelled via AbortSignal */
|
|
33
|
+
class ExportCancelledError extends SimpleffmpegError {
|
|
34
|
+
name: "ExportCancelledError";
|
|
35
|
+
}
|
|
62
36
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
37
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
38
|
+
// Clip Types
|
|
39
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
type ClipType =
|
|
42
|
+
| "video"
|
|
43
|
+
| "audio"
|
|
44
|
+
| "text"
|
|
45
|
+
| "music"
|
|
46
|
+
| "backgroundAudio"
|
|
47
|
+
| "image";
|
|
48
|
+
|
|
49
|
+
interface BaseClip {
|
|
50
|
+
type: ClipType;
|
|
51
|
+
url?: string;
|
|
52
|
+
position: number;
|
|
53
|
+
end: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface VideoClip extends BaseClip {
|
|
57
|
+
type: "video";
|
|
58
|
+
url: string;
|
|
59
|
+
cutFrom?: number;
|
|
60
|
+
volume?: number;
|
|
61
|
+
transition?: { type: string; duration: number };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface AudioClip extends BaseClip {
|
|
65
|
+
type: "audio";
|
|
66
|
+
url: string;
|
|
67
|
+
cutFrom?: number;
|
|
68
|
+
volume?: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface BackgroundMusicClip extends BaseClip {
|
|
72
|
+
type: "music" | "backgroundAudio";
|
|
73
|
+
url: string;
|
|
74
|
+
cutFrom?: number;
|
|
75
|
+
volume?: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface ImageClip extends BaseClip {
|
|
79
|
+
type: "image";
|
|
80
|
+
url: string;
|
|
81
|
+
kenBurns?:
|
|
82
|
+
| "zoom-in"
|
|
83
|
+
| "zoom-out"
|
|
84
|
+
| "pan-left"
|
|
85
|
+
| "pan-right"
|
|
86
|
+
| "pan-up"
|
|
87
|
+
| "pan-down";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
type TextMode = "static" | "word-replace" | "word-sequential";
|
|
91
|
+
type TextAnimationType =
|
|
92
|
+
| "none"
|
|
93
|
+
| "fade-in"
|
|
94
|
+
| "fade-in-out"
|
|
95
|
+
| "pop"
|
|
96
|
+
| "pop-bounce";
|
|
97
|
+
|
|
98
|
+
interface TextWordWindow {
|
|
99
|
+
text: string;
|
|
100
|
+
start: number;
|
|
101
|
+
end: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
interface TextClip {
|
|
105
|
+
type: "text";
|
|
106
|
+
text?: string;
|
|
107
|
+
position: number;
|
|
108
|
+
end: number;
|
|
109
|
+
mode?: TextMode;
|
|
110
|
+
words?: TextWordWindow[];
|
|
111
|
+
wordTimestamps?: number[];
|
|
112
|
+
|
|
113
|
+
// Font
|
|
114
|
+
fontFile?: string;
|
|
115
|
+
fontFamily?: string; // defaults to 'Sans' via fontconfig
|
|
116
|
+
fontSize?: number; // default 48
|
|
117
|
+
fontColor?: string; // default '#FFFFFF'
|
|
118
|
+
|
|
119
|
+
// Position (xPercent/yPercent are percentages 0-1, x/y are pixels)
|
|
120
|
+
/** Horizontal position as percentage (0 = left, 0.5 = center, 1 = right) */
|
|
121
|
+
xPercent?: number;
|
|
122
|
+
/** Vertical position as percentage (0 = top, 0.5 = center, 1 = bottom) */
|
|
123
|
+
yPercent?: number;
|
|
124
|
+
/** Absolute X position in pixels */
|
|
125
|
+
x?: number;
|
|
126
|
+
/** Absolute Y position in pixels */
|
|
127
|
+
y?: number;
|
|
128
|
+
|
|
129
|
+
// Styling
|
|
130
|
+
borderColor?: string;
|
|
131
|
+
borderWidth?: number;
|
|
132
|
+
shadowColor?: string;
|
|
133
|
+
shadowX?: number;
|
|
134
|
+
shadowY?: number;
|
|
135
|
+
backgroundColor?: string;
|
|
136
|
+
backgroundOpacity?: number;
|
|
137
|
+
padding?: number;
|
|
138
|
+
|
|
139
|
+
// Animation
|
|
140
|
+
animation?: {
|
|
141
|
+
type: TextAnimationType;
|
|
142
|
+
in?: number; // seconds
|
|
143
|
+
out?: number; // seconds
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
type Clip =
|
|
148
|
+
| VideoClip
|
|
149
|
+
| AudioClip
|
|
150
|
+
| BackgroundMusicClip
|
|
151
|
+
| ImageClip
|
|
152
|
+
| TextClip;
|
|
153
|
+
|
|
154
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
155
|
+
// Options
|
|
156
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
interface SIMPLEFFMPEGOptions {
|
|
159
|
+
/** Frames per second (default: 30) */
|
|
160
|
+
fps?: number;
|
|
161
|
+
/** Output width in pixels (default: 1920) */
|
|
162
|
+
width?: number;
|
|
163
|
+
/** Output height in pixels (default: 1080) */
|
|
164
|
+
height?: number;
|
|
165
|
+
/** Validation mode: 'warn' logs warnings, 'strict' throws on warnings (default: 'warn') */
|
|
166
|
+
validationMode?: "warn" | "strict";
|
|
167
|
+
/** How to handle visual gaps: 'none' throws error, 'black' fills with black frames (default: 'none') */
|
|
168
|
+
fillGaps?: "none" | "black";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Progress information passed to onProgress callback */
|
|
172
|
+
interface ProgressInfo {
|
|
173
|
+
/** Current frame number being processed */
|
|
174
|
+
frame?: number;
|
|
175
|
+
/** Current processing speed in frames per second */
|
|
176
|
+
fps?: number;
|
|
177
|
+
/** Time processed in seconds */
|
|
178
|
+
timeProcessed?: number;
|
|
179
|
+
/** Progress percentage (0-100) */
|
|
180
|
+
percent?: number;
|
|
181
|
+
/** Processing speed multiplier (e.g., 2.0 = 2x realtime) */
|
|
182
|
+
speed?: number;
|
|
183
|
+
/** Current bitrate in kbits/s */
|
|
184
|
+
bitrate?: number;
|
|
185
|
+
/** Current output size in bytes */
|
|
186
|
+
size?: number;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Metadata to embed in output file */
|
|
190
|
+
interface MetadataOptions {
|
|
191
|
+
title?: string;
|
|
192
|
+
artist?: string;
|
|
193
|
+
album?: string;
|
|
194
|
+
comment?: string;
|
|
195
|
+
date?: string;
|
|
196
|
+
genre?: string;
|
|
197
|
+
/** Custom metadata key-value pairs */
|
|
198
|
+
custom?: Record<string, string>;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Thumbnail generation options */
|
|
202
|
+
interface ThumbnailOptions {
|
|
203
|
+
/** Output path for thumbnail image */
|
|
204
|
+
outputPath: string;
|
|
205
|
+
/** Time in seconds to capture (default: 0) */
|
|
206
|
+
time?: number;
|
|
207
|
+
/** Thumbnail width (maintains aspect if height omitted) */
|
|
208
|
+
width?: number;
|
|
209
|
+
/** Thumbnail height (maintains aspect if width omitted) */
|
|
210
|
+
height?: number;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Hardware acceleration options */
|
|
214
|
+
type HardwareAcceleration =
|
|
215
|
+
| "auto"
|
|
216
|
+
| "videotoolbox"
|
|
217
|
+
| "nvenc"
|
|
218
|
+
| "vaapi"
|
|
219
|
+
| "qsv"
|
|
220
|
+
| "none";
|
|
101
221
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
222
|
+
/** Video codec options */
|
|
223
|
+
type VideoCodec =
|
|
224
|
+
| "libx264"
|
|
225
|
+
| "libx265"
|
|
226
|
+
| "libvpx-vp9"
|
|
227
|
+
| "libaom-av1"
|
|
228
|
+
| "prores_ks"
|
|
229
|
+
| "h264_videotoolbox"
|
|
230
|
+
| "hevc_videotoolbox"
|
|
231
|
+
| "h264_nvenc"
|
|
232
|
+
| "hevc_nvenc"
|
|
233
|
+
| "h264_vaapi"
|
|
234
|
+
| "hevc_vaapi"
|
|
235
|
+
| "h264_qsv"
|
|
236
|
+
| "hevc_qsv"
|
|
237
|
+
| string;
|
|
238
|
+
|
|
239
|
+
/** Audio codec options */
|
|
240
|
+
type AudioCodec =
|
|
241
|
+
| "aac"
|
|
242
|
+
| "libmp3lame"
|
|
243
|
+
| "libopus"
|
|
244
|
+
| "pcm_s16le"
|
|
245
|
+
| "flac"
|
|
246
|
+
| "copy"
|
|
247
|
+
| string;
|
|
248
|
+
|
|
249
|
+
/** Encoding preset options */
|
|
250
|
+
type EncodingPreset =
|
|
251
|
+
| "ultrafast"
|
|
252
|
+
| "superfast"
|
|
253
|
+
| "veryfast"
|
|
254
|
+
| "faster"
|
|
255
|
+
| "fast"
|
|
256
|
+
| "medium"
|
|
257
|
+
| "slow"
|
|
258
|
+
| "slower"
|
|
259
|
+
| "veryslow";
|
|
260
|
+
|
|
261
|
+
/** Resolution presets */
|
|
262
|
+
type ResolutionPreset = "480p" | "720p" | "1080p" | "1440p" | "4k";
|
|
263
|
+
|
|
264
|
+
interface ExportOptions {
|
|
265
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
266
|
+
// Output
|
|
267
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
268
|
+
|
|
269
|
+
/** Output file path (default: './output.mp4') */
|
|
270
|
+
outputPath?: string;
|
|
271
|
+
|
|
272
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
273
|
+
// Video Encoding
|
|
274
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
275
|
+
|
|
276
|
+
/** Video codec (default: 'libx264') */
|
|
277
|
+
videoCodec?: VideoCodec;
|
|
278
|
+
/** Quality level 0-51, lower is better (default: 23) */
|
|
279
|
+
crf?: number;
|
|
280
|
+
/** Encoding speed/quality tradeoff (default: 'medium') */
|
|
281
|
+
preset?: EncodingPreset;
|
|
282
|
+
/** Target video bitrate (e.g., '5M', '2500k'). Overrides CRF when set. */
|
|
283
|
+
videoBitrate?: string;
|
|
284
|
+
|
|
285
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
286
|
+
// Audio Encoding
|
|
287
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
288
|
+
|
|
289
|
+
/** Audio codec (default: 'aac') */
|
|
290
|
+
audioCodec?: AudioCodec;
|
|
291
|
+
/** Audio bitrate (default: '192k') */
|
|
292
|
+
audioBitrate?: string;
|
|
293
|
+
/** Audio sample rate in Hz (default: 48000) */
|
|
294
|
+
audioSampleRate?: number;
|
|
295
|
+
|
|
296
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
297
|
+
// Hardware Acceleration
|
|
298
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
299
|
+
|
|
300
|
+
/** Hardware acceleration mode (default: 'none') */
|
|
301
|
+
hwaccel?: HardwareAcceleration;
|
|
302
|
+
|
|
303
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
304
|
+
// Output Resolution
|
|
305
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
306
|
+
|
|
307
|
+
/** Output width in pixels (scales the output) */
|
|
308
|
+
outputWidth?: number;
|
|
309
|
+
/** Output height in pixels (scales the output) */
|
|
310
|
+
outputHeight?: number;
|
|
311
|
+
/** Resolution preset ('720p', '1080p', '4k', etc.) */
|
|
312
|
+
outputResolution?: ResolutionPreset;
|
|
313
|
+
|
|
314
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
315
|
+
// Advanced Options
|
|
316
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
317
|
+
|
|
318
|
+
/** Export audio only (no video) */
|
|
319
|
+
audioOnly?: boolean;
|
|
320
|
+
/** Enable two-pass encoding for better quality at target bitrate */
|
|
321
|
+
twoPass?: boolean;
|
|
322
|
+
/** Metadata to embed in output file */
|
|
323
|
+
metadata?: MetadataOptions;
|
|
324
|
+
/** Generate a thumbnail from the output */
|
|
325
|
+
thumbnail?: ThumbnailOptions;
|
|
326
|
+
|
|
327
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
328
|
+
// Debug & Logging
|
|
329
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
330
|
+
|
|
331
|
+
/** Enable verbose logging */
|
|
332
|
+
verbose?: boolean;
|
|
333
|
+
/** FFmpeg log level (default: 'warning') */
|
|
334
|
+
logLevel?:
|
|
335
|
+
| "quiet"
|
|
336
|
+
| "panic"
|
|
337
|
+
| "fatal"
|
|
338
|
+
| "error"
|
|
339
|
+
| "warning"
|
|
340
|
+
| "info"
|
|
341
|
+
| "verbose"
|
|
342
|
+
| "debug";
|
|
343
|
+
/** Save FFmpeg command to file for debugging */
|
|
344
|
+
saveCommand?: string;
|
|
345
|
+
|
|
346
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
347
|
+
// Callbacks & Control
|
|
348
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
349
|
+
|
|
350
|
+
/** Progress callback for monitoring export progress */
|
|
351
|
+
onProgress?: (progress: ProgressInfo) => void;
|
|
352
|
+
/** AbortSignal for cancelling the export */
|
|
353
|
+
signal?: AbortSignal;
|
|
354
|
+
|
|
355
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
356
|
+
// Text Batching (Advanced)
|
|
357
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
358
|
+
|
|
359
|
+
/** Maximum text overlay nodes per FFmpeg pass (default: 75) */
|
|
360
|
+
textMaxNodesPerPass?: number;
|
|
361
|
+
/** Video codec for intermediate text passes (default: 'libx264') */
|
|
362
|
+
intermediateVideoCodec?: string;
|
|
363
|
+
/** CRF for intermediate text passes (default: 18) */
|
|
364
|
+
intermediateCrf?: number;
|
|
365
|
+
/** Preset for intermediate text passes (default: 'veryfast') */
|
|
366
|
+
intermediatePreset?: string;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** Result from preview() method */
|
|
370
|
+
interface PreviewResult {
|
|
371
|
+
/** The full FFmpeg command that would be executed */
|
|
372
|
+
command: string;
|
|
373
|
+
/** The filter_complex string */
|
|
374
|
+
filterComplex: string;
|
|
375
|
+
/** Total expected duration in seconds */
|
|
376
|
+
totalDuration: number;
|
|
377
|
+
}
|
|
114
378
|
}
|
|
115
379
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
380
|
+
declare class SIMPLEFFMPEG {
|
|
381
|
+
constructor(options?: SIMPLEFFMPEG.SIMPLEFFMPEGOptions);
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Load clips into the project
|
|
385
|
+
* @param clips Array of clip descriptors (video, audio, text, image, music)
|
|
386
|
+
*/
|
|
387
|
+
load(clips: SIMPLEFFMPEG.Clip[]): Promise<void[]>;
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Get a preview of the FFmpeg command without executing it (dry-run)
|
|
391
|
+
* @param options Export options
|
|
392
|
+
*/
|
|
393
|
+
preview(
|
|
394
|
+
options?: SIMPLEFFMPEG.ExportOptions
|
|
395
|
+
): Promise<SIMPLEFFMPEG.PreviewResult>;
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Export the project to a video file
|
|
399
|
+
* @param options Export options including outputPath, onProgress, and signal
|
|
400
|
+
*/
|
|
401
|
+
export(options?: SIMPLEFFMPEG.ExportOptions): Promise<string>;
|
|
122
402
|
}
|
|
123
403
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
404
|
+
/**
|
|
405
|
+
* Synthetic default export type for ESM default-import IntelliSense
|
|
406
|
+
*/
|
|
407
|
+
declare namespace _defaultExportType {
|
|
408
|
+
export { SIMPLEFFMPEG as default };
|
|
128
409
|
}
|
|
410
|
+
|
|
411
|
+
export = SIMPLEFFMPEG;
|
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = require("./src/simpleffmpeg");
|