Resizing Videos By fluent-ffmpeg & Upload to s3

resizing video node.js

We will learn how to resize video via fluent-ffmpeg. In addition, to upload and delete media from s3 and Digital Ocean spaces for storing.

As they use the same aws-sdk package for Amazon s3. The only difference is the endpoint that will be the Digital Ocean space URL.

Let’s install the dependencies

Here is the list of the packages I use

"dependencies": {
    "@ffmpeg-installer/ffmpeg": "^1.1.0",
    "@ffprobe-installer/ffprobe": "^1.4.1",
    "aws-sdk": "^2.1189.0",
    "express": "~4.16.1",
    "ffmpeg": "^0.0.4",
    "fluent-ffmpeg": "^2.1.2",
    "mongoose": "^6.3.3",
    "mongoose-unique-validator": "^3.0.0",
    "multer": "^1.4.4-lts.1",
    "multer-sharp-s3": "^0.2.5",
  }

As you can see I’m using express with mongoose and Multer for uploading files. In addition to multer-sharp-s3 for uploading files to s3 and resizing photos. And for resizing videos we use fluent-ffmpeg. This package transforms the complex command-line usage of ffmpeg into a user-friendly, easy-to-use node.js module.

npm install fluent-ffmpeg
npm install --save @ffmpeg-installer/ffmpeg
npm install --save @ffprobe-installer/ffprobe

The scenario of resizing the video

We upload the video and save it to s3 then we call it by fluent-ffmpeg to resize it and save it on the local machine after that we uploaded it to s3 and overwrite the old video. Finally, we delete the local video.

Creating the route and middleware for uploading.

Let’s create the route file in routes\products.js, we add the multerMultiSharp middleware for uploading an image, icon, and video for a product that’s already existed in the database.

var express = require("express");
const multerMultiSharp = require("../middleware/multerMultiSharp");
const { editProduct} = require("../controller/shop/product");

var router = express.Router();

router.post("/edit-product", auth, admin, 
multerMultiSharp.fields([
  {name: 'image', maxCount: 1},
  {name: 'icon', maxCount: 1},
  {name: 'video', maxCount: 1},
]), editProduct);

Add the below code to app.js

//
var productRouter = require('./routes/products');

app.use('/api/product', productRouter)
//

Here is the middleware code middleware\multerMultiSharp.js

const aws = require('aws-sdk');
var multerVideo = require("multer");
const multerS3 = require('multer-sharp-s3');
aws.config.loadFromPath('./credentials.json');
const spacesEndpoint = new aws.Endpoint('spacename.digitaloceanspaces.com');
const s3 = new aws.S3({
  endpoint: spacesEndpoint,
});
const MIME_TYPE_MAP = {
  "image/png": "png",
  "image/jpeg": "jpeg",
  "image/jpg": "jpg",
  "image/gif":"gif",
  "image/webp":"webp",
  "video/mp4": "mp4",
};
const storage = multerS3({
    s3: s3,
    Bucket: 'mybucket/uploads',
    ACL: 'public-read',
    Key: function (request, file, cb) {
      console.log(file);
      const isValid = MIME_TYPE_MAP[file.mimetype];
    let error = new Error("Invalid mime type");
    if (isValid) {
    
      error = null;
    }
      let rand = file?.fieldname+Math.floor(Math.random()*90) + 10; 
    
      const ext = MIME_TYPE_MAP[file.mimetype];
      filename =Date.now() + "-"+ rand+'.'+ext
      console.log(filename);
      cb(error,filename);
    },
    resize: {
      fit: 'contain',
      width: 120
    },

  })
  
 


module.exports = multerVideo({ storage: storage });
  

Creating Helper for fluent-ffmpeg and uploading the local video.

In the helper\ffmpegFun.js file add the code for resizing the file and uploading to Digital Ocean Spaces via the helper module we create later and delete the local file.

var ffmpeg = require('fluent-ffmpeg');
const fs = require('fs');
const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path;
const ffprobePath = require('@ffprobe-installer/ffprobe').path;
ffmpeg.setFfmpegPath(ffmpegPath);
ffmpeg.setFfprobePath(ffprobePath);
const uploadToDigital = require("./uploadToDigital");
ffmegFun = async function  (Path,filename){
    return new Promise((resolve,reject)=>{
        console.log('promise',Path);
        ffmpeg(Path)
        .size('240x?') 

        .toFormat("mp4")
        .output('./'+filename)
        .on("error", function (err) {
          console.log("An error occurred: " + err.message);
        })
        .on("progress", function (progress) {
          console.log("Processing: " + progress.percent + "% done");
        })
       .on('end', async  function() {
         console.log('Finished processing');
       // uploading to s3
         await    uploadToDigital('./'+filename,filename).then((data)=>{
            console.log(data)
            fs.unlinkSync('./'+filename) //deleting the local video
          })
          resolve();
       })   
       .run()
    })

} 

module.exports = ffmegFun

In the helper\uploadToDigital.js file

const AWS = require('aws-sdk');
const fs = require('fs');
AWS.config.loadFromPath('./credentials.json');
const spacesEndpoint = new AWS.Endpoint('spacename.digitaloceanspaces.com');
const s3 = new AWS.S3({
  endpoint: spacesEndpoint,
});

const uploadFile = async (fileLocation,key) =>{
    try{
        const fileContent = fs.readFileSync(fileLocation);
        var params = {
            Bucket: 'joaker',
            ACL: 'public-read',
            Key: key,
            Body: fileContent        //got buffer by reading file path
          };
          return  await s3.upload(params).promise();
           
    }catch(error){
        console.log(error)
    }

}

module.exports = uploadFile

Creating helper for deleting old media on s3 helper\deleteMediaDigitalOction.js

const AWS = require('aws-sdk');
AWS.config.loadFromPath('./credentials.json');
const spacesEndpoint = new AWS.Endpoint('spacename.digitaloceanspaces.com');
const s3 = new AWS.S3({
  endpoint: spacesEndpoint,
});

const deleteMedia = (fileLocation) =>{
    try{
        if(fileLocation){
    let pathArray = fileLocation.split('/');
    //3 bucket name 4 is the file name

//set delete param bucket name and file name with extension
const param = {
    Bucket: 'mybucket/'+pathArray[3],
    Key: pathArray[4]
};
console.log(param)
//call delete method 
s3.deleteObject(param, function (err, data) {
    if (err) {
        console.log('err', err)

    }
    console.log('data', data)
});
}
}catch(error){
    console.log(error)
}

}

module.exports = deleteMedia

Finally, We create the controller controller\shop\product.js

const Product = require("../../model/shop/product");
const deleteMedia = require("../../helper/deleteMediaDigitalOction");
const ffmpegFun = require("../../helper/ffmpegFun");

exports.editProduct = async (req, res) => {
  try {
      const url = 'https://joaker.fra1.digitaloceanspaces.com';
      const data = req.body;
      let img = "";
      // Get user
      const product = await Product.findOne({ _id: data.productId });
      if (!product) {
        res.status(404).json({
          message: "Product  Not Found",
        });
      }
      if (product) {
        let img= product.image;
        let icon= product.icon;
        let video = product.video;
      
        if (req.files.image && req.files.image[0].Location) {
          deleteMedia(img)
          img =  req.files.image[0].Location;
        } 
        if (req.files.video && req.files.video[0].Location) {
          console.log(req.files.video)
          
         // ffmpegFun(req.files.video[0].Location,req.files.video[0].Key)
          deleteMedia(video)
          video =  req.files.video[0].Location;
        //  console.log("video>>"+video)
        } 
        if (req.files.icon && req.files.icon[0].Location) {
          deleteMedia(icon)
          icon = req.files.icon[0].Location;
        } 
        let result = await Product.updateOne(
          { _id: data.productId },
          {  image: img,
             video:video,
             icon:icon,
            }
        );
        if (result.modifiedCount === 0) {
          return res.status(204).json({
            message: "No changes",
          });
        }
  
        let productfetch = await Product.findOne({ _id: data.productId});
        return res.status(200).json({
          message: "Product Updated",
          result: productfetch,
        });
      }
    } catch (error) {
           console.log("error", error);
      res.status(401).json({
        message: "Update Product Fiald",
        err: error,
      });
    }
};

That’s all, thanks