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
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
declare namespace SIMPLEFFMPEG {
|
|
2
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
3
|
+
// Error Classes
|
|
4
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
/** Base error class for all simple-ffmpeg errors */
|
|
7
|
+
class SimpleffmpegError extends Error {
|
|
8
|
+
name: "SimpleffmpegError";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Thrown when clip validation fails */
|
|
12
|
+
class ValidationError extends SimpleffmpegError {
|
|
13
|
+
name: "ValidationError";
|
|
14
|
+
errors: string[];
|
|
15
|
+
warnings: string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
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
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Thrown when a media file cannot be found or accessed */
|
|
27
|
+
class MediaNotFoundError extends SimpleffmpegError {
|
|
28
|
+
name: "MediaNotFoundError";
|
|
29
|
+
path: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Thrown when export is cancelled via AbortSignal */
|
|
33
|
+
class ExportCancelledError extends SimpleffmpegError {
|
|
34
|
+
name: "ExportCancelledError";
|
|
35
|
+
}
|
|
36
|
+
|
|
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;
|
|
116
|
+
fontSize?: number;
|
|
117
|
+
fontColor?: string;
|
|
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;
|
|
143
|
+
out?: number;
|
|
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?: Record<string, string>;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Thumbnail generation options */
|
|
201
|
+
interface ThumbnailOptions {
|
|
202
|
+
outputPath: string;
|
|
203
|
+
time?: number;
|
|
204
|
+
width?: number;
|
|
205
|
+
height?: number;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
type HardwareAcceleration =
|
|
209
|
+
| "auto"
|
|
210
|
+
| "videotoolbox"
|
|
211
|
+
| "nvenc"
|
|
212
|
+
| "vaapi"
|
|
213
|
+
| "qsv"
|
|
214
|
+
| "none";
|
|
215
|
+
|
|
216
|
+
type VideoCodec =
|
|
217
|
+
| "libx264"
|
|
218
|
+
| "libx265"
|
|
219
|
+
| "libvpx-vp9"
|
|
220
|
+
| "libaom-av1"
|
|
221
|
+
| "prores_ks"
|
|
222
|
+
| "h264_videotoolbox"
|
|
223
|
+
| "hevc_videotoolbox"
|
|
224
|
+
| "h264_nvenc"
|
|
225
|
+
| "hevc_nvenc"
|
|
226
|
+
| "h264_vaapi"
|
|
227
|
+
| "hevc_vaapi"
|
|
228
|
+
| "h264_qsv"
|
|
229
|
+
| "hevc_qsv"
|
|
230
|
+
| string;
|
|
231
|
+
|
|
232
|
+
type AudioCodec =
|
|
233
|
+
| "aac"
|
|
234
|
+
| "libmp3lame"
|
|
235
|
+
| "libopus"
|
|
236
|
+
| "pcm_s16le"
|
|
237
|
+
| "flac"
|
|
238
|
+
| "copy"
|
|
239
|
+
| string;
|
|
240
|
+
|
|
241
|
+
type EncodingPreset =
|
|
242
|
+
| "ultrafast"
|
|
243
|
+
| "superfast"
|
|
244
|
+
| "veryfast"
|
|
245
|
+
| "faster"
|
|
246
|
+
| "fast"
|
|
247
|
+
| "medium"
|
|
248
|
+
| "slow"
|
|
249
|
+
| "slower"
|
|
250
|
+
| "veryslow";
|
|
251
|
+
|
|
252
|
+
type ResolutionPreset = "480p" | "720p" | "1080p" | "1440p" | "4k";
|
|
253
|
+
|
|
254
|
+
interface ExportOptions {
|
|
255
|
+
// Output
|
|
256
|
+
outputPath?: string;
|
|
257
|
+
|
|
258
|
+
// Video Encoding
|
|
259
|
+
videoCodec?: VideoCodec;
|
|
260
|
+
crf?: number;
|
|
261
|
+
preset?: EncodingPreset;
|
|
262
|
+
videoBitrate?: string;
|
|
263
|
+
|
|
264
|
+
// Audio Encoding
|
|
265
|
+
audioCodec?: AudioCodec;
|
|
266
|
+
audioBitrate?: string;
|
|
267
|
+
audioSampleRate?: number;
|
|
268
|
+
|
|
269
|
+
// Hardware Acceleration
|
|
270
|
+
hwaccel?: HardwareAcceleration;
|
|
271
|
+
|
|
272
|
+
// Output Resolution
|
|
273
|
+
outputWidth?: number;
|
|
274
|
+
outputHeight?: number;
|
|
275
|
+
outputResolution?: ResolutionPreset;
|
|
276
|
+
|
|
277
|
+
// Advanced Options
|
|
278
|
+
audioOnly?: boolean;
|
|
279
|
+
twoPass?: boolean;
|
|
280
|
+
metadata?: MetadataOptions;
|
|
281
|
+
thumbnail?: ThumbnailOptions;
|
|
282
|
+
|
|
283
|
+
// Debug & Logging
|
|
284
|
+
verbose?: boolean;
|
|
285
|
+
logLevel?:
|
|
286
|
+
| "quiet"
|
|
287
|
+
| "panic"
|
|
288
|
+
| "fatal"
|
|
289
|
+
| "error"
|
|
290
|
+
| "warning"
|
|
291
|
+
| "info"
|
|
292
|
+
| "verbose"
|
|
293
|
+
| "debug";
|
|
294
|
+
saveCommand?: string;
|
|
295
|
+
|
|
296
|
+
// Callbacks & Control
|
|
297
|
+
onProgress?: (progress: ProgressInfo) => void;
|
|
298
|
+
signal?: AbortSignal;
|
|
299
|
+
|
|
300
|
+
// Text Batching
|
|
301
|
+
textMaxNodesPerPass?: number;
|
|
302
|
+
intermediateVideoCodec?: string;
|
|
303
|
+
intermediateCrf?: number;
|
|
304
|
+
intermediatePreset?: string;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** Result from preview() method */
|
|
308
|
+
interface PreviewResult {
|
|
309
|
+
/** The full FFmpeg command that would be executed */
|
|
310
|
+
command: string;
|
|
311
|
+
/** The filter_complex string */
|
|
312
|
+
filterComplex: string;
|
|
313
|
+
/** Total expected duration in seconds */
|
|
314
|
+
totalDuration: number;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
declare class SIMPLEFFMPEG {
|
|
319
|
+
constructor(options?: SIMPLEFFMPEG.SIMPLEFFMPEGOptions);
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Load clips into the project
|
|
323
|
+
* @param clips Array of clip descriptors (video, audio, text, image, music)
|
|
324
|
+
*/
|
|
325
|
+
load(clips: SIMPLEFFMPEG.Clip[]): Promise<void[]>;
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Get a preview of the FFmpeg command without executing it (dry-run)
|
|
329
|
+
* @param options Export options
|
|
330
|
+
*/
|
|
331
|
+
preview(
|
|
332
|
+
options?: SIMPLEFFMPEG.ExportOptions
|
|
333
|
+
): Promise<SIMPLEFFMPEG.PreviewResult>;
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Export the project to a video file
|
|
337
|
+
* @param options Export options including outputPath, onProgress, and signal
|
|
338
|
+
*/
|
|
339
|
+
export(options?: SIMPLEFFMPEG.ExportOptions): Promise<string>;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export default SIMPLEFFMPEG;
|