Skip to content

Instantly share code, notes, and snippets.

@amtins
Last active September 30, 2023 17:12
Show Gist options
  • Save amtins/49ce4e97ef46e24eb1ef23b24a4e83e4 to your computer and use it in GitHub Desktop.
Save amtins/49ce4e97ef46e24eb1ef23b24a4e83e4 to your computer and use it in GitHub Desktop.
PlayedControl

Quick start

Installation

npm i gist:49ce4e97ef46e24eb1ef23b24a4e83e4

Usage

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>playedControl</title>
    <link
      rel="stylesheet"
      href="https://unpkg.com/video.js@next/dist/video-js.css"
    />
    <link
      rel="stylesheet"
      href="/node_modules/@amtins/playedControl/playedControl.css"
    />
    <style>
      body {
        display: flex;
        justify-content: center;
        align-items: center;
        height: 100vh;
        background: #111;
      }

      .player {
        width: 960px;
        height: 540px;
        font-size: 20px;

        /* redefined the default color */
        --played-range-color: red;
      }
    </style>
    <script src="https://unpkg.com/video.js@next/dist/video.js"></script>
    <script type="module">
      import './node_modules/@amtins/playedControl/playedControl.js';

      const player = videojs('player', {
        muted: true,
        controls: true,
        controlBar: {
          progressControl: {
            seekBar: {
              playedControl: true, //add the component to the seek bar
            },
          },
        },
      });

      window.player = player;

      player.src({
        src: 'https://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8',
        type: 'application/x-mpegURL',
      });
    </script>
  </head>

  <body>
    <video
      playsinline="playsinline"
      id="player"
      class="video-js player"
    ></video>
  </body>
</html>
{
"name": "@amtins/playedControl",
"version": "1.0.0",
"description": "A component that displays the parts of media that have been played.",
"main": "playedControl.js",
"keywords": [],
"author": "amtins",
"license": "MIT"
}
.vjs-played-range {
position: absolute;
background: var(--played-range-color, rgba(43, 51, 63, 0.7));
height: 100%;
}
/**
* A component that displays the parts of media that have been played.
*
* **WARNING**: Doesn't work with live streaming content
*/
class PlayedControl extends videojs.getComponent('Component') {
constructor(player, options) {
super(player, options);
this.timeUpdateHandler = this.timeUpdateHandler.bind(this);
this.player().on('timeupdate', this.timeUpdateHandler);
}
/**
* Create PlayedControl DOM Element.
*
* @returns {Element}
*/
createEl() {
return super.createEl('div', {
className: 'vjs-played-control',
});
}
/**
* Dispose PlayedControl component.
*
* @param {Object} options
*/
dispose(options) {
this.player().off('timeupdate', this.timeUpdateHandler);
super.dispose(options);
}
/**
* Create or update the range element.
*
* @param {Number} index
* @param {Number} start
* @param {Number} end
* @param {Number} duration
*/
rangeEl(index, start, end, duration) {
/** @type {Element} */
let rangeEl = this.$(`.vjs-range-${index}`);
if (!rangeEl) {
rangeEl = videojs.dom.createEl('div', {
className: `vjs-played-range vjs-range-${index}`,
});
this.el().append(rangeEl);
}
rangeEl.style.left = `${this.rangeElLeft(start, duration)}%`;
rangeEl.style.width = `${this.rangeElWidth(start, end, duration)}%`;
}
/**
* Defines width of rangeEl element.
*
* @param {Number} start
* @param {Number} end
* @param {Number} duration
*
* @returns {Number}
*/
rangeElWidth(start, end, duration) {
const percentage = ((end - start) / duration) * 100;
return Math.round(percentage);
}
/**
* Defines left position of rangeEl element.
*
* @param {Number} start
* @param {Number} duration
*
* @returns
*/
rangeElLeft(start, duration) {
const percentage = (start / duration) * 100;
return Math.round(percentage);
}
/**
* Information about the current playback session.
*
* @typedef {Object} PlayedInfo
* @property {Array.<Array.<Number>>} ranges
* @property {Number} timePlayed
* @property {Number} percentagePlayed
*
* @returns {PlayedInfo}
*/
playedInfo() {
const ranges = [];
for (let i = 0; i != player.played().length; i++) {
ranges.push([player.played().start(i), player.played().end(i)]);
}
const timePlayed = ranges.reduce(
(accumulator, currentValue) =>
currentValue[1] - currentValue[0] + accumulator,
0
);
const percentagePlayed = (timePlayed / this.player().duration()) * 100;
return {
ranges,
timePlayed,
percentagePlayed,
};
}
timeUpdateHandler() {
/** @type {TimeRanges} */
const playedRanges = this.player().played();
/** @type {Number} */
const duration = this.player().duration();
if (playedRanges.length < this.el().children.length) {
videojs.dom.emptyEl(this.el());
}
for (let i = 0; i < playedRanges.length; i++) {
const start = playedRanges.start(i);
const end = playedRanges.end(i);
this.rangeEl(i, start, end, duration);
}
}
}
videojs.registerComponent('PlayedControl', PlayedControl);
export default PlayedControl;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment