aboutsummaryrefslogtreecommitdiff
path: root/src/api
diff options
context:
space:
mode:
authorZephyrrus <[email protected]>2020-07-02 20:13:05 +0300
committerZephyrrus <[email protected]>2020-07-02 20:13:05 +0300
commit24157dd1b944a2cfdc64f3ad0ae7cf9c2015145b (patch)
tree3dcb3c35d5a9f52941df8c3cf9296432bccb2844 /src/api
parentfeat: add experimental meaningful preview extraction from videos (diff)
downloadhost.fuwn.me-24157dd1b944a2cfdc64f3ad0ae7cf9c2015145b.tar.xz
host.fuwn.me-24157dd1b944a2cfdc64f3ad0ae7cf9c2015145b.zip
feat: experimental video preview generator in webm form
Diffstat (limited to 'src/api')
-rw-r--r--src/api/utils/PreviewUtil.js74
-rw-r--r--src/api/utils/ThumbUtil.js11
2 files changed, 80 insertions, 5 deletions
diff --git a/src/api/utils/PreviewUtil.js b/src/api/utils/PreviewUtil.js
new file mode 100644
index 0000000..bf3a480
--- /dev/null
+++ b/src/api/utils/PreviewUtil.js
@@ -0,0 +1,74 @@
+const ffmpeg = require('fluent-ffmpeg');
+const probe = require('ffmpeg-probe');
+
+const path = require('path');
+
+const noop = () => {};
+
+module.exports = async opts => {
+ const {
+ log = noop,
+
+ // general output options
+ quality = 2,
+ width,
+ height,
+ input,
+ output,
+
+ numFrames,
+ numFramesPercent = 0.05
+ } = opts;
+
+ const info = await probe(input);
+ // const numFramesTotal = parseInt(info.streams[0].nb_frames, 10);
+ const { avg_frame_rate: avgFrameRate, duration } = info.streams[0];
+ const [frames, time] = avgFrameRate.split('/').map(e => parseInt(e, 10));
+
+ const numFramesTotal = (frames / time) * duration;
+
+ let numFramesToCapture = numFrames || numFramesPercent * numFramesTotal;
+ numFramesToCapture = Math.max(1, Math.min(numFramesTotal, numFramesToCapture)) | 0;
+ const nthFrame = (numFramesTotal / numFramesToCapture) | 0;
+
+ const result = {
+ output,
+ numFrames: numFramesToCapture
+ };
+
+ 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;
+ }
+
+ const filter = [`select=not(mod(n\\,${nthFrame}))`, scale].filter(Boolean).join(',');
+
+ ffmpeg(input)
+ .outputOptions(['-vsync', 'vfr'])
+ .outputOptions(['-q:v', quality, '-vf', filter])
+ .outputOption('-an')
+ .outputFormat('webm')
+ .output(output)
+ .on('start', cmd => log && log({ cmd }))
+ .on('end', () => resolve())
+ .on('error', err => reject(err))
+ .run();
+ });
+
+ return result;
+};
diff --git a/src/api/utils/ThumbUtil.js b/src/api/utils/ThumbUtil.js
index e508969..5cfd9c0 100644
--- a/src/api/utils/ThumbUtil.js
+++ b/src/api/utils/ThumbUtil.js
@@ -2,7 +2,7 @@ const jetpack = require('fs-jetpack');
const path = require('path');
const sharp = require('sharp');
const ffmpeg = require('fluent-ffmpeg');
-const generatePreview = require('ffmpeg-generate-video-preview');
+const previewUtil = require('./PreviewUtil');
const log = require('./Log');
@@ -17,7 +17,7 @@ class ThumbUtil {
static generateThumbnails(filename) {
const ext = path.extname(filename).toLowerCase();
const output = `${filename.slice(0, -ext.length)}.png`;
- const previewOutput = `${filename.slice(0, -ext.length)}.gif`;
+ const previewOutput = `${filename.slice(0, -ext.length)}.webm`;
if (ThumbUtil.imageExtensions.includes(ext)) return this.generateThumbnailForImage(filename, output);
if (ThumbUtil.videoExtensions.includes(ext)) return this.generateThumbnailForVideo(filename, previewOutput);
@@ -38,7 +38,7 @@ class ThumbUtil {
.toFile(path.join(ThumbUtil.thumbPath, output));
}
- static generateThumbnailForVideo(filename, output) {
+ static async generateThumbnailForVideo(filename, output) {
const filePath = path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, filename);
ffmpeg(filePath)
@@ -60,10 +60,11 @@ class ThumbUtil {
.on('error', error => log.error(error.message));
try {
- generatePreview({
+ await previewUtil({
input: filePath,
width: 150,
- output: path.join(ThumbUtil.videoPreviewPath, output)
+ output: path.join(ThumbUtil.videoPreviewPath, output),
+ log: console.log
});
} catch (e) {
console.error(e);