How to display visual cues on the timeline to mark mid-rolls?
You may ask this question if you want to let the user know when and how many mid-rolls there are during the video.
Similar questions may be:
- Can I show where my ad is during the video?
- How can I let the user know when the ads will be during the video like in YouTube?
- Can ad markers be added to the timeline?
Showing visual cues indicating the moment in which the ads will start is certainly useful. This is not (yet) a THEOplayer standard feature, but what follows is a first step in that direction. Please keep in mind that this solution is still in development and has to date several limitations.
Prerequisites and limitations
- 
It only works for THEOplayer ad integration 
- 
A timeOffset for each roll must be included in the AdDescription 
- 
timeOffset values must be one of the following: 
- 
a keyword (“start” and “end” allowed); 
- 
a number (seconds from video beginning); 
- 
undefined (which indicates a pre-roll). 
- 
it has only been tested on Chrome, Firefox and Edge for Win 10 and with the standard THEOplayer UI. 
The code
Add this line to your CSS
.adCue {
  width: 0.3em;
  height: 100%;
  background-color: white;
  position: absolute;
  top: 0;
}
Your AdDescription should look something like this
player.source.ads: [
    {
        "sources": "path/to/your-pre/midroll1.xml",
            "timeOffset": value //valid values: ["start", "end", numbers]
    },
    {
        "sources": "path/to/your-pre/midroll2.xml",
            "timeOffset": value //valid values: ["start", "end", numbers]
    },
    {...}
]
Include (or call) this JavaScript snippet on your page:
THEOdisplayCues.js
var adsPresent = !!player.source.ads;
var contentDuration = 0;
var to = [];
var checker = true;
var playedAds = [];
player.addEventListener('pause', () => {
  checker = false;
});
player.addEventListener('playing', () => {
  var adIsPlaying = player.ads.playing;
  detectTimeOffset();
  detectDuration(adIsPlaying);
  if (!checker) {
    checker = adIsPlaying;
  }
  if (checker) {
    !adIsPlaying ? drawCues() : destroyCues();
  }
});
player.addEventListener('sourcechange', function () {
  playedAds.length = 0;
});
player.ads.addEventListener('adend', (adEvent) => {
  playedAds.push(adEvent.ad.adBreak.timeOffset);
});
function drawCues() {
  var progressBar = element.querySelector('.vjs-progress-control.vjs-control');
  for (var i = 0; i < to.length; i++) {
    var timeOffset = to[i];
    if (playedAds.indexOf(timeOffset) === -1) {
      var left = (to[i] * progressBar.offsetWidth) / contentDuration;
      var div = document.createElement('div');
      div.setAttribute('to', to[i]);
      div.classList.add('adCue');
      div.style.left = left + 'px';
      progressBar.children[0].appendChild(div);
    }
  }
}
function destroyCues() {
  var cues = element.querySelectorAll('.vjs-progress-control.vjs-control .adCue');
  for (cue of cues) {
    cue.parentNode.removeChild(cue);
  }
}
function detectDuration(adIsPlaying) {
  if (!adIsPlaying) {
    contentDuration = player.duration;
    //console.log("Duration ",contentDuration)
  }
  var t = 0;
  for (var ad of player.source.ads) {
    to[t] = to[t] == 'end' ? contentDuration : to[t];
    t++;
  }
}
function detectTimeOffset() {
  if (adsPresent) {
    var t = 0;
    for (var ad of player.source.ads) {
      to[t] = ad.timeOffset == undefined ? 0 : ad.timeOffset == 'start' ? 0 : ad.timeOffset;
      t++;
    }
  }
  //console.log("TimeOffsets ",to);
}