You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
101 lines
2.7 KiB
101 lines
2.7 KiB
// article.js
|
|
// Copyright (C) 2022 DTP Technologies, LLC
|
|
// License: Apache-2.0
|
|
|
|
'use strict';
|
|
|
|
const util = require('util');
|
|
const execFile = util.promisify(require('child_process').execFile);
|
|
|
|
const { SiteService } = require('../../lib/site-lib');
|
|
|
|
class MediaService extends SiteService {
|
|
|
|
constructor (dtp) {
|
|
super(dtp, module.exports);
|
|
}
|
|
|
|
async ffmpeg (ffmpegArgs) {
|
|
try {
|
|
await execFile(process.env.DTP_FFMPEG_PATH, ffmpegArgs, {
|
|
cwd: this.dtp.config.root,
|
|
encoding: 'utf8',
|
|
});
|
|
} catch (error) {
|
|
this.log.error('failed to execute ffprobe', { ffmpegArgs, error });
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async ffprobe (input, options) {
|
|
options = Object.assign({
|
|
streams: true,
|
|
format: true,
|
|
error: true,
|
|
}, options);
|
|
const ffprobeOpts = ['-print_format', 'json'];
|
|
if (options.streams) {
|
|
ffprobeOpts.push('-show_streams');
|
|
}
|
|
if (options.format) {
|
|
ffprobeOpts.push('-show_format');
|
|
}
|
|
if (options.error) {
|
|
ffprobeOpts.push('-show_error');
|
|
}
|
|
ffprobeOpts.push(input);
|
|
|
|
try {
|
|
const { stdout } = await execFile(process.env.DTP_FFPROBE_PATH, ffprobeOpts, {
|
|
cwd: this.dtp.config.root,
|
|
encoding: 'utf8',
|
|
});
|
|
const probe = JSON.parse(stdout);
|
|
|
|
if (probe.format && probe.format.tags) {
|
|
let keys = Object.keys(probe.format.tags);
|
|
keys.forEach((key) => {
|
|
probe.format.tags[key.replace(/\./g, '_')] = probe.format.tags[key];
|
|
delete probe.format.tags[key];
|
|
});
|
|
}
|
|
|
|
probe.duration = probe.format.duration;
|
|
probe.width = probe.format.width;
|
|
|
|
if (Array.isArray(probe.streams) && probe.streams.length) {
|
|
const stream = probe.streams
|
|
.find((stream) => stream.codec_type === 'video') || probe.streams[0];
|
|
|
|
if (stream.duration) {
|
|
probe.duration = parseFloat(stream.duration);
|
|
} else {
|
|
probe.duration = parseFloat(probe.format.duration);
|
|
}
|
|
probe.width = stream.width;
|
|
probe.height = stream.height;
|
|
|
|
const fpsFraction = stream.avg_frame_rate.split('/').map((value) => parseInt(value, 10));
|
|
if (Array.isArray(fpsFraction) && fpsFraction[1] !== 0) {
|
|
probe.fps = fpsFraction[0] / fpsFraction[1];
|
|
}
|
|
} else {
|
|
probe.duration = undefined;
|
|
probe.width = undefined;
|
|
probe.height = undefined;
|
|
}
|
|
|
|
return probe;
|
|
} catch (error) {
|
|
this.log.error('failed to execute ffprobe', { input, error });
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
logId: 'svc:media',
|
|
index: 'media',
|
|
className: 'MediaService',
|
|
create: (dtp) => { return new MediaService(dtp); },
|
|
};
|