in javascript coding videojs ~ read.

Creating a videojs plugin

At Dotsub we work with a lot of video. For the last couple years we have been using videojs as our sites main video player. This week I've been updating some of our plugins to the latest videojs version (5.9.2) and also open sourcing them!

This week lets walk through how to create a videojs plugin. We'll make a simple watermark plugin that displays the company logo on a video.

Getting Started

The folks at videojs do provide a yeoman generator as an initial starting point. You can check out that project here: https://github.com/videojs/generator-videojs-plugin

To create the initial framework just install and run the generator.

$ npm install -g yo generator-videojs-plugin
$ yo videojs-plugin

I enabled the following for the watermark plugin.

{
  "generator-videojs-plugin": {
    "bcov": false,
    "scope": "",
    "name": "watermark",
    "description": "Adds a watermark image the video player",
    "author": "Brooks Lyrette <hidden>",
    "license": "apache2",
    "changelog": true,
    "sass": true,
    "docs": false,
    "lang": false,
    "bower": false
  }
}

You now have a ready to go project that uses grunt to build, karma and qunit to test. The plugin code is under src/plugin.js and src/plugin.scss.

You can make sure everything is working fine by typing npm test in your console.

Note: If you need to be able to use ES6 imports check this out: https://github.com/videojs/generator-videojs-plugin/pull/69#discussion_r62665092

Making the plugin do something

Now comes the fun part, making the plugin do something useful. The requirements are pretty simple for this plugin:

  • Show a configured image in the video player.
  • When the video is played for the first time wait the pre configured number of seconds to fade the logo out.
  • After the initial fade out, the logo should be shown when the player's controls are shown.
  • The logo can be a clickable link.
  • The logo position should be configurable (the four corners).

Defaults, Options and configuration.

Videojs provides a nice way deal with configuration. As the plugin developer you can provide a set of defaults, the user will input their settings using options and you can merge the two using a function the provide called: videojs.mergeOptions(defaults, options);. Based on the requirements our plugin will use the following settings with these defaults:

const defaults = {  
  position: 'top-right',
  fadeTime: 3000,
  url: undefined,
  image: undefined
};

image: The URL to the image to be used as the watermark.

position: The location to place the watermark (top-left, top-right, bottom-left, bottom-right). Defaults to 'top-right'.

fadeTime: The amount of time in milliseconds for the initial watermark fade. Defaults to 3000.

url: A url to be linked to from the watermark. If the user clicks the watermark the video will be paused and the link will open in a new window.

Coding the plugin

We'll need some DOM elements added to the video player. If you look at the generated code in src/plugin.js you'll see there is already a function listing for onPlayerReady. This is where we will add the needed DOM elements. onPlayerReady is an event videojs throws when the player is setup and ready to go.

const watermark = function(options) {  
  this.ready(() => {
    onPlayerReady(this, videojs.mergeOptions(defaults, options));
  });
};

Let's add a call to a new function setupWatermark(player, options);. In that function lets setup the required DOM.

const onPlayerReady = (player, options) => {  
  player.addClass('vjs-watermark');

  // if there is no image set just exit
  if (!options.image) {
    return;
  }
  setupWatermark(player, options);
  player.on('play', () => fadeWatermark(options));
};

As you see above we exit if there is not image configured. If there is one we continue on and set up the DOM. The setup ends up looking like:

const setupWatermark = (player, options) => {  
  // Add a div and img tag
  const videoEl = player.el();
  const div = document.createElement('div');
  const img = document.createElement('img');

  div.id = 'vjs-watermark';
  div.classList.add('vjs-watermark-content');
  div.classList.add(`vjs-watermark-${options.position}`);
  img.src = options.image;

  // if a url is provided make the image link to that URL.
  if (options.url) {
    const a = document.createElement('a');

    a.href = options.url;
    // if the user clicks the link pause and open a new window
    a.onclick = (e) => {
      e.preventDefault();
      player.pause();
      window.open(options.url);
    };
    a.appendChild(img);
    div.appendChild(a);
  } else {
    div.appendChild(img);
  }
  videoEl.appendChild(div);
};

We only add a a tag if there was a url provided in the configuration. To handle the logo positions we have a CSS class for each location. Let's write the SCSS for the plugin. We will us absolute positioning for the image container.

// Sass for videojs-watermark
.video-js {
  &.vjs-watermark {
    display: block;
  }
  .vjs-watermark-content {
    opacity: 0.99;
    position: absolute;
    padding: 5px;
  }

  // pre-defined positions
  .vjs-watermark-top-right {
    right: 0;
  }
  .vjs-watermark-top-left {
    left: 0;
  }
  .vjs-watermark-bottom-right {
    right: 0;
    bottom: 30px;
  }
  .vjs-watermark-bottom-left {
    left: 0;
    bottom: 30px;
  }
}

For the bottom logo positions the 30px of padding is to account for the height of the controlbar.

We've covered almost all of the requirements, you can configure the image, its location and an optional URL to link to.

Implementing fade out

We will want the logo to fade in and out. This will look smoother than having it just disappear. We will do this using CSS transitions. To do this we will animate the opacity from 1 to 0 over 1 second.

Since one of the requirements is that the logo remains visible on the initial play we can use a timeout to wait the correct amount of time.

const onPlayerReady = (player, options) => {  
  ...
  player.on('play', () => fadeWatermark(options));
};

const fadeWatermark = (options) => {  
  setTimeout(
    () => document.getElementById('vjs-watermark').classList.add('vjs-watermark-fade'),
  options.fadeTime);
};

Once the vjs-watermark-fade fade class is attached to the element it will start fading in and out. For the SCSS I recommend using a mixin that covers all the browser prefixes.

// cross browser mixin
@mixin transition($args...) {
  -webkit-transition: $args;
  -moz-transition: $args;
  -ms-transition: $args;
  -o-transition: $args;
  transition: $args;
}

// Sass for videojs-watermark
.video-js {
  &.vjs-watermark {
    display: block;
  }
  .vjs-watermark-content {
    opacity: 0.99;
    position: absolute;
    padding: 5px;
    @include transition(visibility 1.0s, opacity 1.0s);
  }
  ...
  //fade out when the user is not active and the video is playing.
  &.vjs-user-inactive.vjs-playing .vjs-watermark-fade {
   opacity: 0;
  }
}

There you have it. A working simple videojs watermark plugin. You can check out the completed project here: https://github.com/dotsub/videojs-watermark (Apache 2.0 licenced)

The final version includes Qunit tests and ES6 support.