Skip to content

How can I modify the Vue Storefront API code so that it can receive a link to an mp4 video?

I have Vue Storefront system + Magento 2. I need to get links to videos that are on the Magento side. The file code that I attach below is responsible for getting the images. How can it be modified so that it can receive links to mp4 videos?

import {downloadImage, fit, identify, resize} from '../lib/image';
import mime from 'mime-types';
import URL from 'url';
import Redis from '../lib/redis';

const SUPPORTED_ACTIONS = ['fit', 'resize', 'identify'];
const SUPPORTED_MIMETYPES = ['image/gif', 'image/png', 'image/jpeg', 'image/webp', 'image/svg+xml'];
const ONE_YEAR = 31557600000;
const client = new Redis({ expire: process.env.REDIS_CACHE_EXPIRE_IMAGE || 1800 });

const asyncMiddleware = fn => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

export default ({ config, db }) =>
  asyncMiddleware(async (req, res, body) => {
    if (!(req.method == 'GET')) {
      res.set('Allow', 'GET');
      return res.status(405).send('Method Not Allowed');
    }

    req.socket.setMaxListeners(config.imageable.maxListeners || 50);

    let width
    let height
    let action
    let imgUrl

    if (req.query.url) { // url provided as the query param
      imgUrl = decodeURIComponent(req.query.url)
      width = parseInt(req.query.width)
      height = parseInt(req.query.height)
      action = req.query.action
    } else {
      let urlParts = req.url.split('/');
      width = parseInt(urlParts[1]);
      height = parseInt(urlParts[2]);
      action = urlParts[3];
      imgUrl = `${config[config.platform].imgUrl}/${urlParts.slice(4).join('/')}`; // full original image url

      if (urlParts.length < 4) {
        return res.status(400).send({
          code: 400,
          result: 'Please provide following parameters: /img/<width>/<height>/<action:fit,resize,identify>/<relative_url>'
        });
      }
    }


    if (isNaN(width) || isNaN(height) || !SUPPORTED_ACTIONS.includes(action)) {
      return res.status(400).send({
        code: 400,
        result: 'Please provide following parameters: /img/<width>/<height>/<action:fit,resize,identify>/<relative_url> OR ?url=&width=&height=&action='
      });
    }

    if (width > config.imageable.imageSizeLimit || width < 0 || height > config.imageable.imageSizeLimit || height < 0) {
      return res.status(400).send({
        code: 400,
        result: `Width and height must have a value between 0 and ${config.imageable.imageSizeLimit}`
      });
    }

    if (!isImageSourceHostAllowed(imgUrl, config.imageable.whitelist)) {
      return res.status(400).send({
        code: 400,
        result: `Host is not allowed`
      });
    }

    const mimeType = mime.lookup(imgUrl);

    if (mimeType === false || !SUPPORTED_MIMETYPES.includes(mimeType)) {
      return res.status(400).send({
        code: 400,
        result: 'Unsupported file type'
      });
    }

    console.log(`[URL]: ${imgUrl} - [ACTION]: ${action} - [WIDTH]: ${width} - [HEIGHT]: ${height}`);

    const key = `${imgUrl}/${width}/${height}/${action}`
    let cachedImage = ''
    if (client.isConnected()) {
      client.get(key)
        .then(img => {
          if (img !== null) {
            cachedImage = Buffer.from(img, 'base64')
            return sendImage(res, mimeType, cachedImage)
          }
        })
    }
    getImageBuffer(res, req, imgUrl).then(buffer => {
      if (res.statusCode !== 400 && !cachedImage) {
        imagePrepare(buffer, res, action, width, height)
        .then(img => {
          if (client.isConnected()) {
            client.set(key, img.toString('base64'))
          }
          return sendImage(res, mimeType, img)
        })
      }
    })
  });

function sendImage (res, mimeType, img) {
  return res
    .type(mimeType)
    .set({'Cache-Control': `max-age=${ONE_YEAR}`})
    .send(img)
}
async function getImageBuffer (res, req, imgUrl) {
  try {
    return await downloadImage(imgUrl);
  } catch (err) {
    try {
      if(/.webp/?$/.test(imgUrl) && req.body.beforeImageFormat) {
        const newImgUrl = imgUrl.replace(/.webp/, req.body.beforeImageFormat)
        return await downloadImage(newImgUrl)
      } else {
        throw new Error(err)
      }
    } catch (error) {
      return res.status(400).send({
        code: 400,
        result: `Unable to download the requested image ${imgUrl}`
      });
    }
  }
}
async function imagePrepare(buffer, res, action, width, height) {
  try {
    switch (action) {
      case 'resize':
        return resize(buffer, width, height)
      case 'fit':
        return fit(buffer, width, height)
      case 'identify':
        return identify(buffer)
      default:
        return Promise.reject(new Error('Unknown action'))
    }
  } catch (e) {
    return Promise.reject(new Error(e.message))
  }
}

function _isUrlWhitelisted(url, whitelistType, defaultValue, whitelist) {
  if (arguments.length != 4) throw new Error('params are not optional!');

  if (whitelist && whitelist.hasOwnProperty(whitelistType)) {
    const requestedHost = URL.parse(url).host;
    const matches = whitelist[whitelistType].map(allowedHost => {
      allowedHost = allowedHost instanceof RegExp ? allowedHost : new RegExp(allowedHost);
      return !!requestedHost.match(allowedHost);
    });

    return matches.indexOf(true) > -1;
  } else {
    return defaultValue;
  }
}

function isImageSourceHostAllowed(url, whitelist) {
  return _isUrlWhitelisted(url, 'allowedHosts', true, whitelist);
}

If I add type in SUPPORTED_MIMETYPES:

const SUPPORTED_MIMETYPES = ['image/gif', 'image/png', 'image/jpeg', 'image/webp', 'image/svg+xml', 'video/mp4'];

The system issues an errors:

UnhandledPromiseRejectionWarning: Error: Input buffer contains unsupported image format
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.