-
Notifications
You must be signed in to change notification settings - Fork 0
/
createThumbnail.js
98 lines (84 loc) · 3.25 KB
/
createThumbnail.js
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
89
90
91
92
93
94
95
96
97
const ffmpeg = require('fluent-ffmpeg');
const path = require('path');
const fs = require('fs');
// Function to maintain aspect ratio
function maintainAspectRatio(height, aspectRatio = 16 / 9) {
const width = Math.round(height * aspectRatio);
return `${width}:${height}`;
}
const aspectRatioSize = maintainAspectRatio(360);
// Function to detect scene changes and create a thumbnail
function detectScenesAndCreateThumbnail(inputVideoPath, outputThumbnailPath) {
return new Promise((resolve, reject) => {
const tempSceneFilePath = path.resolve(`scenes_${path.basename(inputVideoPath, '.mp4')}.txt`);
ffmpeg(inputVideoPath)
.outputOptions([
'-vf', 'select=gt(scene\\,0.3),metadata=print:key=lavfi.scene_score', // Detect scene changes
'-vsync', 'vfr',
'-f', 'null'
])
.on('start', function(commandLine) {
console.log(`Spawned FFmpeg with command: ${commandLine}`);
})
.on('stderr', function(stderrLine) {
fs.appendFileSync(tempSceneFilePath, stderrLine);
})
.on('end', function() {
fs.readFile(tempSceneFilePath, 'utf8', (err, data) => {
if (err) {
createThumbnail(inputVideoPath, outputThumbnailPath, true); // Fallback to simple thumbnail
} else {
const hasSceneChanges = data.includes('scene_score');
createThumbnail(inputVideoPath, outputThumbnailPath, !hasSceneChanges);
resolve();
}
fs.unlink(tempSceneFilePath, () => {}); // Clean up temporary file
});
})
.on('error', function(err) {
createThumbnail(inputVideoPath, outputThumbnailPath, true); // Fallback to simple thumbnail
})
.saveToFile('/dev/null'); // Specify a dummy output to satisfy ffmpeg requirements
})
}
// Function to create a thumbnail
function createThumbnail(inputVideoPath, outputThumbnailPath, fallback = false) {
return new Promise((resolve,reject)=>{
const vfFilter = fallback
? `thumbnail,scale=${aspectRatioSize}`
: `select=gt(scene\\,0.3),scale=${aspectRatioSize}`;
ffmpeg(inputVideoPath)
.outputOptions([
'-vf', vfFilter, // Scene detection and scaling or fallback
'-frames:v', '1', // Extract one frame
'-vsync', 'vfr' // Variable frame rate to handle selected frames
])
.on('end', () => {
console.log(`Thumbnail for ${path.basename(inputVideoPath)} extracted successfully`);
resolve();
})
.on('error', (err) => {
if (!fallback) {
console.log(`Scene detection failed for ${path.basename(inputVideoPath)}, retrying with thumbnail filter...`);
createThumbnail(inputVideoPath, outputThumbnailPath, true);
} else {
reject();
console.error(`Error extracting thumbnail for ${path.basename(inputVideoPath)}:`, err.message);
}
})
.save(outputThumbnailPath);
})
}
// Loop through the videos
const thumbnailCreator = async (videoFilePath,thumbnailFilePath)=>{
const inputVideoPath = path.resolve(videoFilePath);
const outputThumbnailPath = path.resolve(thumbnailFilePath);
try{
await detectScenesAndCreateThumbnail(inputVideoPath, outputThumbnailPath);
}
catch(err){
logger.error(`Error extracting thumbnail for ${videoFilePath}`);
logger.error(JSON.stringify(err));
}
}
module.exports = thumbnailCreator;