Generate video thumbnails in ReactJS, an open source thumbnail library.
Tags: javascript, react, tutorial, productivity
Published: 2022-03-27 04:10:22

 **Have you ever needed where the user uploads the video and has the option to select a thumbnail?** if yes keep reading.

Recently I needed to create a service in one of my projects where users will upload a video and can select a thumbnail of that video, and I needed to generate multiple thumbnails from the different timeframes of video. So the user can select one of the thumbnail from the given

something like this [can't upload the original screen]


![Video thumbnails generate with vanilla JS. Like YouTube ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nq3j67cdt0yqkzfll00y.jpg)
 
I search for JS libraries which can do this for me but ended up creating my own `NPM` package.
[video-thumbnails-generator](https://www.npmjs.com/package/@rajesh896/video-thumbnails-generator)
Contributions are more than welcome, I have already added a roadmap in the `Readme.md`.
 
#### But first lets see how its working behind the scene.
🔵 **`Index.js`** *The Driver*

```javascript
// convert image to object part instead of base64 for better performance
// https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
export const importFileandPreview = (file, revoke) => {
    return new Promise((resolve, reject) => {
        window.URL = window.URL || window.webkitURL;
        let preview = window.URL.createObjectURL(file);
        // remove reference
        if (revoke) {
            window.URL.revokeObjectURL(preview);
        }
        setTimeout(() => {
            resolve(preview);
        }, 100);
    });
}


/**
 *
 * @param videoFile {FIle} // the video file
 * @param numberOfThumbnails {number} //number of thumbnails you want to generate
 * @returns {string[]} // an array of base64 thumbnails images
 *
 * @abstract
 * Idea taken from - https://codepen.io/aertmann/pen/mrVaPx
 * The original functionality of getVideoThumbnail() function is customized as per working code
 * If it didn't work in future then replace it with about links working example
 */
export const generateVideoThumbnails = async (videoFile, numberOfThumbnails) => {
   
    let thumbnail = [];
    let fractions = [];
    return new Promise(async (resolve, reject) => {
        if (!videoFile.type?.includes("video")) reject("not a valid video file");
        await getVideoDuration(videoFile).then(async (duration) => {
            // divide the video timing into particular timestamps in respective to number of thumbnails
            // ex if time is 10 and numOfthumbnails is 4 then result will be -> 0, 2.5, 5, 7.5 ,10
            // we will use this timestamp to take snapshots
            for (let i = 0; i <= duration; i += duration / numberOfThumbnails) {
                fractions.push(Math.floor(i));
            }
            // the array of promises
            let promiseArray = fractions.map((time) => {
                return getVideoThumbnail(videoFile, time)
            })
            // console.log('promiseArray', promiseArray)
            // console.log('duration', duration)
            // console.log('fractions', fractions)
            await Promise.all(promiseArray).then((res) => {
                res.forEach((res) => {
                    // console.log('res', res.slice(0,8))
                    thumbnail.push(res);
                });
                // console.log('thumbnail', thumbnail)
                resolve(thumbnail);
            }).catch((err) => {
                console.error(err)
            }).finally((res) => {
                console.log(res);
                resolve(thumbnail);
            })
        });
        reject("something went wront");
    });
};

const getVideoThumbnail = (file, videoTimeInSeconds) => {
    return new Promise((resolve, reject) => {
        if (file.type.match("video")) {
            importFileandPreview(file).then((urlOfFIle) => {
                var video = document.createElement("video");
                var timeupdate = function () {
                    if (snapImage()) {
                        video.removeEventListener("timeupdate", timeupdate);
                        video.pause();
                    }
                };
                video.addEventListener("loadeddata", function () {
                    if (snapImage()) {
                        video.removeEventListener("timeupdate", timeupdate);
                    }
                });
                var snapImage = function () {
                    var canvas = document.createElement("canvas");
                    canvas.width = video.videoWidth;
                    canvas.height = video.videoHeight;
                    canvas.getContext("2d").drawImage(video, 0, 0, canvas.width, canvas.height);
                    var image = canvas.toDataURL();
                    var success = image.length > 100000;
                    if (success) {
                        URL.revokeObjectURL(urlOfFIle);
                        resolve(image);
                    }
                    return success;
                };
                video.addEventListener("timeupdate", timeupdate);
                video.preload = "metadata";
                video.src = urlOfFIle;
                // Load video in Safari / IE11
                video.muted = true;
                video.playsInline = true;
                video.currentTime = videoTimeInSeconds;
                video.play();
            });
        } else {
            reject("file not valid");
        }
    });
};

/**
 *
 * @param videoFile {File}
 * @returns {number} the duration of video in seconds
 */
export const getVideoDuration = (videoFile)=> {
    return new Promise((resolve, reject) => {
        if (videoFile) {
            if (videoFile.type.match("video")) {
                importFileandPreview(videoFile).then((url) => {
                    let video = document.createElement("video");
                    video.addEventListener("loadeddata", function () {
                        resolve(video.duration);
                    });
                    video.preload = "metadata";
                    video.src = url;
                    // Load video in Safari / IE11
                    video.muted = true;
                    video.playsInline = true;
                    video.play();
                    //  window.URL.revokeObjectURL(url);
                });
            }
        } else {
            reject(0);
        }
    });
};
```
🔵 Explanations

`importFileandPreview()`

```javascript
/**
* This function will take an File object and will convert it
* into windowObjectURI which look something like this - 
* blob:http://localhost/2d7b2c97-02f3-4e7d-a6c1-d04746c27730
*/
export const importFileandPreview = (file, revoke) => {
    return new Promise((resolve, reject) => {
       //@todo - your logic here
    });
}
```

`getVideoDuration()`

```javascript
/**
 * @abbrivation This function takes a video File object as an 
 * input and returns the duration of that video.
 *
 * @param videoFile {File}
 * @returns {number} the duration of video in seconds
 */
export const getVideoDuration = (videoFile)=> {
    return new Promise((resolve, reject) => {
        if (videoFile) {
           resolve(duration);
        } else {
            reject(0);
        }
    });
};

```

`getVideoThumbnail()`

```javascript
/**
* @abbrivation 
* This function takes a video File Object and the time where we
* need a snapshot of video screen.
* It will return a snapshot of the video at the given time
* in `base64` format.
*
* @param {File} file 
* @param {number} videoTimeInSeconds 
* @returns string // base64Image 
*/
const getVideoThumbnail = (file, videoTimeInSeconds) => {
    return new Promise((resolve, reject) => {
        if (file.type.match("video")) {
            resolve(thumbnail); //base64 image
        } else {
            reject("file not valid");
        }
    });
};

```

`generateVideoThumbnails()`
```javascript
/**
 * This functin will take two input video File and Number
 * And It will generate that many thumbnails.
 *
 * @param videoFile {FIle} // the video file
 * @param numberOfThumbnails {number} //number of thumbnails you want to generate
 * @returns {string[]} // an array of base64 thumbnails images
 *
 * @abstract
 * Idea taken from - https://codepen.io/aertmann/pen/mrVaPx
 * The original functionality of getVideoThumbnail() function is customized as per working code
 * If it didn't work in future then replace it with about links working example
 */
export const generateVideoThumbnails = async (videoFile, numberOfThumbnails) => {
   
    let thumbnail = [];
    let fractions = [];
    return new Promise(async (resolve, reject) => {
        if (!videoFile.type?.includes("video")) reject("not a valid video file");
            // first we get video duration
            // then we calculate how many thumbnails to generate
            // we cann generateThumbnail() function that many times
            // then we resolve all those promises and return result.
            await Promise.all(promiseArray).then((res) => {
                res.forEach((res) => {
                    thumbnail.push(res);
                });
                resolve(thumbnail);
            }).catch((err) => {
                console.error(err)
            }).finally((res) => {
                resolve(thumbnail);
            })
        });
        reject("something went wrong");
    });
};
```

See it in action - [Live Demo](https://rajesh-royal.github.io/video-thumbnails-generator/).


![Video thumbnails generate with vanilla JS. Like YouTube ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p2lj4zvlheo1qoh78f54.gif)
 

Hope you enjoyed it reading. Do not forget share of hit that heart icon 😀 see you soon with new material 👋.