2019-06-09 00:51:00 +00:00
|
|
|
'use strict';
|
|
|
|
|
2019-06-09 01:43:14 +00:00
|
|
|
import { exists } from 'fs-extra';
|
2019-06-25 01:11:14 +00:00
|
|
|
import { extname } from 'path';
|
2019-06-09 01:43:14 +00:00
|
|
|
import { exec } from 'exec';
|
2024-05-19 22:14:33 +00:00
|
|
|
import { Log } from 'log';
|
|
|
|
import type { Logger } from 'winston';
|
2024-05-23 23:54:42 +00:00
|
|
|
import type { System } from 'system';
|
2019-06-09 00:51:00 +00:00
|
|
|
|
2024-05-24 19:23:58 +00:00
|
|
|
/** @module lib/ffprobe */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class representing all ffprobe features.
|
|
|
|
*/
|
|
|
|
|
2024-05-23 22:49:18 +00:00
|
|
|
export class FFPROBE {
|
2020-01-20 16:51:15 +00:00
|
|
|
private bin : string;
|
2024-05-19 22:14:33 +00:00
|
|
|
private log : Logger;
|
2024-05-23 23:54:42 +00:00
|
|
|
|
|
|
|
constructor (sys : System) {
|
2020-01-20 16:51:15 +00:00
|
|
|
this.bin = sys.deps.ffprobe;
|
2024-05-19 22:14:33 +00:00
|
|
|
this.init();
|
|
|
|
}
|
|
|
|
|
|
|
|
private async init () {
|
|
|
|
this.log = await Log({ label : 'ffprobe' });
|
2019-08-04 22:04:06 +00:00
|
|
|
}
|
2020-03-09 19:46:06 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse the fps entry into a float representing the fps of a video
|
|
|
|
**/
|
|
|
|
private parseFps (fpsStr : string) {
|
|
|
|
let fps : number = 30.0;
|
|
|
|
let parts : string[];
|
|
|
|
if (fpsStr.indexOf('/') !== -1) {
|
|
|
|
parts = fpsStr.split('/');
|
|
|
|
fps = parseFloat(parts[0]) / parseFloat(parts[1]);
|
|
|
|
} else {
|
|
|
|
fps = parseFloat(fpsStr);
|
|
|
|
}
|
|
|
|
return fps
|
|
|
|
}
|
2019-08-04 22:04:06 +00:00
|
|
|
/**
|
|
|
|
* Get info on a video in json format. Use for filmout.
|
|
|
|
*
|
|
|
|
* @param {string} video Path to video
|
|
|
|
*
|
|
|
|
* @returns {object} Video info in an object
|
|
|
|
**/
|
|
|
|
public async info (video : string) {
|
2020-01-20 16:51:15 +00:00
|
|
|
const cmd : string = `${this.bin} -v quiet -print_format json -show_format -show_streams "${video}"`
|
2019-08-04 22:04:06 +00:00
|
|
|
let fileExists : boolean;
|
|
|
|
let raw : any;
|
|
|
|
let json : any;
|
|
|
|
let vid : any; //whether video has stream with video data
|
2019-06-09 00:51:00 +00:00
|
|
|
|
2019-08-04 22:04:06 +00:00
|
|
|
try {
|
|
|
|
fileExists = await exists(video);
|
|
|
|
} catch (err) {
|
|
|
|
return exit(err, 5);
|
|
|
|
}
|
|
|
|
if (!fileExists) {
|
|
|
|
//return exit(`File ${video} does not exist`, 6);
|
2024-05-19 22:14:33 +00:00
|
|
|
this.log.error(new Error(`File ${video} does not exist`));
|
2019-08-04 22:04:06 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2024-05-19 22:14:33 +00:00
|
|
|
this.log.info(cmd);
|
2019-08-04 22:04:06 +00:00
|
|
|
raw = await exec(cmd);
|
|
|
|
} catch (err) {
|
|
|
|
//return exit(err, 7);
|
2024-05-19 22:14:33 +00:00
|
|
|
this.log.error(err);
|
2019-08-04 22:04:06 +00:00
|
|
|
return false
|
|
|
|
}
|
2019-06-09 00:51:00 +00:00
|
|
|
|
2019-08-04 22:04:06 +00:00
|
|
|
try {
|
|
|
|
json = JSON.parse(raw.stdout);
|
|
|
|
} catch (err) {
|
2024-05-19 22:14:33 +00:00
|
|
|
this.log.error('Error parsing stdout', err);
|
|
|
|
this.log.error(raw.stdout);
|
2019-08-04 22:04:06 +00:00
|
|
|
return raw.stdout;
|
|
|
|
}
|
2019-06-09 00:51:00 +00:00
|
|
|
|
2020-03-09 19:46:06 +00:00
|
|
|
if (json.format && json.format.duration) {
|
|
|
|
json.seconds = parseFloat(json.format.duration);
|
|
|
|
}
|
|
|
|
|
2019-08-04 22:04:06 +00:00
|
|
|
if (json && json.streams) {
|
|
|
|
vid = json.streams.find((stream : any) => {
|
|
|
|
if (stream.width && stream.height) return stream;
|
|
|
|
});
|
|
|
|
}
|
2019-06-09 00:51:00 +00:00
|
|
|
|
2019-08-04 22:04:06 +00:00
|
|
|
if (vid) {
|
|
|
|
json.width = vid.width;
|
|
|
|
json.height = vid.height;
|
2020-03-09 19:46:06 +00:00
|
|
|
json.fps = this.parseFps(vid.r_frame_rate)
|
2019-08-04 22:04:06 +00:00
|
|
|
}
|
2019-06-09 00:51:00 +00:00
|
|
|
|
2019-08-04 22:04:06 +00:00
|
|
|
return json;
|
2019-06-09 00:51:00 +00:00
|
|
|
}
|
2019-08-04 22:04:06 +00:00
|
|
|
/**
|
|
|
|
* Count the number of frames in the video using one of two methods.
|
|
|
|
* The first uses -select_streams and is very fast. The second uses
|
|
|
|
* -count_frames and is VERY slow.
|
|
|
|
*
|
|
|
|
* @param {string} video Path to video
|
|
|
|
*
|
|
|
|
* @returns {integer} Number of frames in video
|
|
|
|
**/
|
|
|
|
public async frames (video : string) {
|
|
|
|
const ext : string = extname(video.toLowerCase());
|
2020-01-21 16:38:50 +00:00
|
|
|
let cmd : string = `${this.bin} -v error -select_streams v:0 -show_entries stream=nb_frames -of default=nokey=1:noprint_wrappers=1 "${video}"`;
|
|
|
|
let backup_cmd : string = `${this.bin} -v error -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 "${video}"`;
|
2019-08-15 19:25:33 +00:00
|
|
|
let gif_cmd : string = `identify -format "%n\n" "${video}" | head -1`
|
2019-08-04 22:04:06 +00:00
|
|
|
let fileExists : boolean;
|
|
|
|
let raw : any;
|
|
|
|
let frames : number;
|
2019-06-09 00:51:00 +00:00
|
|
|
|
2019-08-04 22:04:06 +00:00
|
|
|
try {
|
|
|
|
fileExists = await exists(video);
|
|
|
|
} catch (err) {
|
|
|
|
//return exit(err, 5);
|
2024-05-19 22:14:33 +00:00
|
|
|
this.log.error(err);
|
2019-08-04 22:04:06 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
if (!fileExists) {
|
|
|
|
//return exit(`File ${video} does not exist`, 6);
|
|
|
|
console.error(new Error(`File ${video} does not exist`));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ext === '.mkv') {
|
|
|
|
cmd = backup_cmd;
|
2019-08-15 19:25:33 +00:00
|
|
|
} else if (ext === '.gif') {
|
|
|
|
cmd = gif_cmd;
|
2019-08-04 22:04:06 +00:00
|
|
|
}
|
|
|
|
try {
|
2024-05-19 22:14:33 +00:00
|
|
|
this.log.info(cmd);
|
2019-08-04 22:04:06 +00:00
|
|
|
raw = await exec(cmd);
|
|
|
|
} catch (err) {
|
2024-05-19 22:14:33 +00:00
|
|
|
this.log.error(err);
|
2019-08-04 22:04:06 +00:00
|
|
|
return false;
|
|
|
|
}
|
2019-06-09 00:51:00 +00:00
|
|
|
|
2019-08-04 22:04:06 +00:00
|
|
|
try {
|
|
|
|
frames = parseInt(raw.stdout);
|
|
|
|
} catch (err) {
|
|
|
|
return raw.stdout;
|
|
|
|
}
|
2019-06-09 00:51:00 +00:00
|
|
|
|
2019-08-04 22:04:06 +00:00
|
|
|
return frames;
|
2019-06-09 00:51:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-04 22:04:06 +00:00
|
|
|
/*
|
2019-06-09 00:51:00 +00:00
|
|
|
function map (obj : any) {
|
|
|
|
console.dir(obj);
|
|
|
|
}
|
2019-08-04 22:04:06 +00:00
|
|
|
*/
|
2019-06-09 00:51:00 +00:00
|
|
|
|
2024-05-24 00:51:35 +00:00
|
|
|
module.exports = { FFPROBE };
|