ffmpeg-progress 1.3.0 → 1.5.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/README.md +31 -0
- package/core.d.ts +16 -1
- package/core.js +90 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,6 +42,24 @@ await convertFile({
|
|
|
42
42
|
timer.end()
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
**Get video resolution**:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { getVideoResolution } from 'ffmpeg-progress'
|
|
49
|
+
|
|
50
|
+
console.log(await getVideoResolution('test/rotate.mp4'))
|
|
51
|
+
// { width: 3024, height: 4032 }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Get video duration in seconds**:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { getVideoDuration } from 'ffmpeg-progress'
|
|
58
|
+
|
|
59
|
+
console.log(await getVideoDuration('test/in.mp4'))
|
|
60
|
+
// 15.04 (duration in seconds)
|
|
61
|
+
```
|
|
62
|
+
|
|
45
63
|
## Typescript Types
|
|
46
64
|
|
|
47
65
|
```typescript
|
|
@@ -88,6 +106,19 @@ export function attachChildProcess(
|
|
|
88
106
|
childProcess: ChildProcessWithoutNullStreams
|
|
89
107
|
} & ProgressArgs,
|
|
90
108
|
): Promise<void>
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* take a image frame from the video,
|
|
112
|
+
* this is more accurate than parsing the resolution string in ffmpeg,
|
|
113
|
+
* because the video has rotation metadata.
|
|
114
|
+
*/
|
|
115
|
+
export function getVideoResolution(video_file: string): Promise<{
|
|
116
|
+
width: number
|
|
117
|
+
height: number
|
|
118
|
+
}>
|
|
119
|
+
|
|
120
|
+
/** @description get video duration in seconds, e.g. `15.04` */
|
|
121
|
+
export async function getVideoDuration(video_file: string): Promise<number>
|
|
91
122
|
```
|
|
92
123
|
|
|
93
124
|
## License
|
package/core.d.ts
CHANGED
|
@@ -15,7 +15,11 @@ export type ScanVideoResult = {
|
|
|
15
15
|
duration: string;
|
|
16
16
|
/** @description e.g. 180.03 or 0 */
|
|
17
17
|
seconds: number;
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* @description e.g. "4032x3024"
|
|
20
|
+
* This is the stored resolution of the video, not the resolution of the video when it is played.
|
|
21
|
+
* The display image may be rotated if there are rotation metadata in the video.
|
|
22
|
+
*/
|
|
19
23
|
resolution: string | null;
|
|
20
24
|
/** @description e.g. 44100 for 44.1kHz */
|
|
21
25
|
audioSampleRate: number | null;
|
|
@@ -49,3 +53,14 @@ export declare function estimateOutSize(args: {
|
|
|
49
53
|
currentSeconds: number;
|
|
50
54
|
totalSeconds: number;
|
|
51
55
|
}): number;
|
|
56
|
+
/**
|
|
57
|
+
* take a image frame from the video,
|
|
58
|
+
* this is more accurate than parsing the resolution string in ffmpeg,
|
|
59
|
+
* because the video has rotation metadata.
|
|
60
|
+
*/
|
|
61
|
+
export declare function getVideoResolution(video_file: string): Promise<{
|
|
62
|
+
width: number;
|
|
63
|
+
height: number;
|
|
64
|
+
}>;
|
|
65
|
+
/** @description get video duration in seconds, e.g. `15.04` */
|
|
66
|
+
export declare function getVideoDuration(video_file: string): Promise<number>;
|
package/core.js
CHANGED
|
@@ -7,7 +7,11 @@ exports.scanVideo = scanVideo;
|
|
|
7
7
|
exports.convertFile = convertFile;
|
|
8
8
|
exports.attachChildProcess = attachChildProcess;
|
|
9
9
|
exports.estimateOutSize = estimateOutSize;
|
|
10
|
+
exports.getVideoResolution = getVideoResolution;
|
|
11
|
+
exports.getVideoDuration = getVideoDuration;
|
|
10
12
|
const child_process_1 = require("child_process");
|
|
13
|
+
const promises_1 = require("fs/promises");
|
|
14
|
+
const path_1 = require("path");
|
|
11
15
|
/** @description
|
|
12
16
|
* from "00:01:00.03" to 60.03;
|
|
13
17
|
* from "N/A" to NaN;
|
|
@@ -194,3 +198,89 @@ function estimateOutSize(args) {
|
|
|
194
198
|
let estimatedOutSize = args.currentOutSize + estimatedRate * remindSeconds;
|
|
195
199
|
return estimatedOutSize;
|
|
196
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* take a image frame from the video,
|
|
203
|
+
* this is more accurate than parsing the resolution string in ffmpeg,
|
|
204
|
+
* because the video has rotation metadata.
|
|
205
|
+
*/
|
|
206
|
+
async function getVideoResolution(video_file) {
|
|
207
|
+
let image_file = (0, path_1.join)('/tmp/', (0, path_1.basename)(video_file) + '.jpg');
|
|
208
|
+
// ffmpeg -ss 0 -i video.mp4 -frames:v 1 -f image2 -update 1 -y image.jpg
|
|
209
|
+
let childProcess = (0, child_process_1.spawn)('ffmpeg', [
|
|
210
|
+
'-ss',
|
|
211
|
+
'0',
|
|
212
|
+
'-i',
|
|
213
|
+
video_file,
|
|
214
|
+
'-frames:v',
|
|
215
|
+
'1',
|
|
216
|
+
'-f',
|
|
217
|
+
'image2',
|
|
218
|
+
'-update',
|
|
219
|
+
'1',
|
|
220
|
+
'-y',
|
|
221
|
+
image_file,
|
|
222
|
+
]);
|
|
223
|
+
var { stdout, stderr, code, signal } = await waitChildProcess(childProcess);
|
|
224
|
+
if (code != 0) {
|
|
225
|
+
throw new Error(`ffmpeg exit abnormally, exit code: ${code}, signal: ${signal}, stdout: ${stdout}, stderr: ${stderr}`);
|
|
226
|
+
}
|
|
227
|
+
childProcess = (0, child_process_1.spawn)('file', [image_file]);
|
|
228
|
+
var { stdout, stderr, code, signal } = await waitChildProcess(childProcess);
|
|
229
|
+
if (code != 0) {
|
|
230
|
+
throw new Error(`file exit abnormally, exit code: ${code}, signal: ${signal}, stdout: ${stdout}, stderr: ${stderr}`);
|
|
231
|
+
}
|
|
232
|
+
let resolution = parseImageResolution(stdout);
|
|
233
|
+
await (0, promises_1.unlink)(image_file);
|
|
234
|
+
return resolution;
|
|
235
|
+
}
|
|
236
|
+
/** @description get video duration in seconds, e.g. `15.04` */
|
|
237
|
+
async function getVideoDuration(video_file) {
|
|
238
|
+
let childProcess = (0, child_process_1.spawn)('ffprobe', [
|
|
239
|
+
'-v',
|
|
240
|
+
'error',
|
|
241
|
+
'-show_entries',
|
|
242
|
+
'format=duration',
|
|
243
|
+
'-of',
|
|
244
|
+
'default=noprint_wrappers=1:nokey=1',
|
|
245
|
+
video_file,
|
|
246
|
+
]);
|
|
247
|
+
var { stdout, stderr, code, signal } = await waitChildProcess(childProcess);
|
|
248
|
+
if (code != 0) {
|
|
249
|
+
throw new Error(`ffprobe exit abnormally, exit code: ${code}, signal: ${signal}, stdout: ${stdout}, stderr: ${stderr}`);
|
|
250
|
+
}
|
|
251
|
+
let duration = +stdout;
|
|
252
|
+
if (!duration) {
|
|
253
|
+
throw new Error('failed to parse video duration: ' + JSON.stringify(stdout));
|
|
254
|
+
}
|
|
255
|
+
return duration;
|
|
256
|
+
}
|
|
257
|
+
function waitChildProcess(childProcess) {
|
|
258
|
+
return new Promise((resolve, reject) => {
|
|
259
|
+
let stdout = '';
|
|
260
|
+
let stderr = '';
|
|
261
|
+
childProcess.stdout.on('data', (data) => {
|
|
262
|
+
stdout += data.toString();
|
|
263
|
+
});
|
|
264
|
+
childProcess.stderr.on('data', (data) => {
|
|
265
|
+
stderr += data.toString();
|
|
266
|
+
});
|
|
267
|
+
childProcess.on('exit', (code, signal) => {
|
|
268
|
+
resolve({ stdout, stderr, code, signal });
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
// e.g. sample.jpg: JPEG image data, baseline, precision 8, 3024x4032, components 3
|
|
273
|
+
function parseImageResolution(text) {
|
|
274
|
+
let parts = text.split(', ').reverse();
|
|
275
|
+
for (let part of parts) {
|
|
276
|
+
let parts = part.split('x');
|
|
277
|
+
if (parts.length == 2) {
|
|
278
|
+
let width = +parts[0];
|
|
279
|
+
let height = +parts[1];
|
|
280
|
+
if (width && height) {
|
|
281
|
+
return { width, height };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
throw new Error('failed to parse image resolution: ' + JSON.stringify(text));
|
|
286
|
+
}
|