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);
}