Files
video-frames/index.js
Utkarsh Verma b924133d5b Update index.js
2022-04-29 10:04:41 +05:30

130 lines
3.5 KiB
JavaScript

module.exports = async options => {
let index = 0;
let error = false;
let frames = [];
let interval;
let seekResolve;
let currentTime = 0;
let extractOffsets = false;
const isNumber = n => {
return +n + '' === n + '';
};
const isTimestamp = timestamp => {
return isNumber(timestamp) && +timestamp >= 0 && +timestamp <= video.duration;
};
const fallbackToDefault = (property, defaultValue) => {
options[property] = options.hasOwnProperty(property) ? options[property] : defaultValue;
};
// Buffer Video Element
const video = document.createElement('video');
video.src = options.url;
video.crossOrigin = 'anonymous';
video.onseeked = async () => {
if(seekResolve)
seekResolve();
};
video.onerror = () => {
error = true;
};
while((video.duration === Infinity || isNaN(video.duration)) && video.readyState < 2 && !error) {
await new Promise(r => setTimeout(r, 100));
video.currentTime = 10000000 * Math.random();
}
// Set options to default values if not set
fallbackToDefault('format', 'image/png');
fallbackToDefault('offsets', []);
fallbackToDefault('startTime', 0);
fallbackToDefault('endTime', video.duration);
fallbackToDefault('count', 1);
// Filter out invalid offsets
if(options.offsets.constructor !== Array)
options.offsets = [];
else
options.offsets = options.offsets.filter(offset => {
return isTimestamp(offset);
});
if(options.offsets.length !== 0)
extractOffsets = true;
// Check if start and end times are valid
if(!isTimestamp(options.startTime))
options.startTime = 0;
if(!isTimestamp(options.endTime))
options.endTime = video.duration;
if(options.startTime >= options.endTime) {
options.startTime = options.endTime;
options.count = 1;
}
// Convert count value to a positive integer (floor() or 0 if string)
options.count = Math.abs(~~options.count);
// Starting at startTime + interval and ending at endTime - interval
interval = (options.endTime - options.startTime) / (options.count + 1);
// Set Width and Height
let isWidthSet = options.hasOwnProperty('width');
let isHeightSet = options.hasOwnProperty('height');
let videoDimensionRatio = video.videoWidth / video.videoHeight;
// Reset Width and Height if not valid
if(isWidthSet && !isNumber(options.width))
isWidthSet = false;
if(isHeightSet && !isNumber(options.height))
isHeightSet = false;
if(!isWidthSet && !isHeightSet) {
// Both Width and Height not set
options.width = 128;
options.height = options.width / videoDimensionRatio;
} else if(isWidthSet && !isHeightSet) {
// Width set but Height not set
options.height = options.width / videoDimensionRatio;
} else if(!isWidthSet && isHeightSet) {
// Height set but Width not set
options.width = options.height * videoDimensionRatio;
}
// Float values
options.width = +options.width;
options.height = +options.height;
// Buffer Canvas Element
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = options.width;
canvas.height = options.height;
return new Promise(async resolve => {
if(error)
resolve([]);
while(
(extractOffsets && index < options.offsets.length) ||
(!extractOffsets && index < options.count)
) {
video.currentTime = extractOffsets ? options.offsets[index] : options.startTime + (index + 1) * interval;
await new Promise(r => seekResolve = r);
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(video, 0, 0, canvas.width, canvas.height);
frames.push(canvas.toDataURL(options.format));
index ++;
}
resolve(frames);
});
};