aboutsummaryrefslogtreecommitdiff
path: root/src/api/utils/videoPreview/FragmentPreview.js
blob: 1d1ee021f1ec195b334a4aba39e824bf4914885f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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
/* eslint-disable no-bitwise */
const ffmpeg = require('fluent-ffmpeg');
const probe = require('ffmpeg-probe');

const noop = () => {};

const getRandomInt = (min, max) => {
	const minInt = Math.ceil(min);
	const maxInt = Math.floor(max);

	// eslint-disable-next-line no-mixed-operators
	return Math.floor(Math.random() * (maxInt - minInt + 1) + minInt);
};

const getStartTime = (vDuration, fDuration, ignoreBeforePercent, ignoreAfterPercent) => {
	// by subtracting the fragment duration we can be sure that the resulting
	// start time + fragment duration will be less than the video duration
	const safeVDuration = vDuration - fDuration;

	// if the fragment duration is longer than the video duration
	if (safeVDuration <= 0) {
		return 0;
	}

	return getRandomInt(ignoreBeforePercent * safeVDuration, ignoreAfterPercent * safeVDuration);
};

module.exports = async opts => {
	const {
		log = noop,

		// general output options
		quality = 2,
		width,
		height,
		input,
		output,

		fragmentDurationSecond = 3,
		ignoreBeforePercent = 0.25,
		ignoreAfterPercent = 0.75
	} = opts;

	const info = await probe(input);

	let { duration } = info.format;
	duration = parseInt(duration, 10);

	const startTime = getStartTime(duration, fragmentDurationSecond, ignoreBeforePercent, ignoreAfterPercent);

	const result = { startTime, duration };

	await new Promise((resolve, reject) => {
		let scale = null;

		if (width && height) {
			result.width = width | 0;
			result.height = height | 0;
			scale = `scale=${width}:${height}`;
		} else if (width) {
			result.width = width | 0;
			result.height = ((info.height * width) / info.width) | 0;
			scale = `scale=${width}:-1`;
		} else if (height) {
			result.height = height | 0;
			result.width = ((info.width * height) / info.height) | 0;
			scale = `scale=-1:${height}`;
		} else {
			result.width = info.width;
			result.height = info.height;
		}

		return ffmpeg()
			.input(input)
			.inputOptions([`-ss ${startTime}`])
			.outputOptions(['-vsync', 'vfr'])
			.outputOptions(['-q:v', quality, '-vf', scale])
			.outputOptions([`-t ${fragmentDurationSecond}`])
			.noAudio()
			.output(output)
			.on('start', cmd => log && log({ cmd }))
			.on('end', resolve)
			.on('error', reject)
			.run();
	});

	return result;
};