<html lang="en">
<head>
<meta charset=utf-8>
<title>Video player with chapter menu</title>
</head>
<body>
<section id="all">
<h1>Using JSON to describe chapter markers</h1>
This example uses a WebVTT file with kind=chapters, that contains JSON cues for associating images and chapter marker descriptions.
<p>
<video id="myVideo" preload="metadata" controls crossOrigin="anonymous">
<source src="https://mainline.i3s.unice.fr/mooc/elephants-dream-medium.mp4" type="video/mp4">
<source src="https://mainline.i3s.unice.fr/mooc/elephants-dream-medium.webm" type="video/webm">
<track label="English subtitles" kind="subtitles" srclang="en" src="https://mainline.i3s.unice.fr/mooc/elephants-dream-subtitles-en.vtt" >
<track label="Deutsch subtitles" kind="subtitles" srclang="de" src="https://mainline.i3s.unice.fr/mooc/elephants-dream-subtitles-de.vtt" default>
<track label="English chapters" kind="chapters" srclang="en" src="https://mainline.i3s.unice.fr/mooc/elephants-dream-chapters-en-JSON.vtt">
</video>
<h2>Chapter menu</h2>
<div id="chapterMenu"></div>
</section>
</body>
</html>
#all {
background-color: lightgrey;
border-radius:10px;
padding: 20px;
border:1px solid;
display:inline-block;
/*height:500px;*/
margin:30px;
width:90%;
}
#myVideo {
border-radius:10px;
border:1px solid;
display: block;
margin-right: 2.85714%;
width: 100%;
background-color: black;
position: relative;
box-shadow: 5px 5px 5px grey;
}
#chapterMenuSection {
background-color: lightgrey;
border-radius:10px;
padding: 20px;
border:1px solid;
display:inline-block;
margin:0px 30px 30px 30px;
width:90%;
}
figure.img {
margin: 2px;
float: left;
}
figcaption.desc {
text-align: center;
font-weight: normal;
margin: 2px;
}
.thumb {
height: 75px;
border: 1px solid #000;
margin: 10px 5px 0 0;
box-shadow: 5px 5px 5px grey;
transition: all 0.5s;
}
.thumb:hover {
box-shadow: 5px 5px 5px black;
}
var video, chapterMenuDiv;
var tracks, trackElems, tracksURLs = [];
window.onload = function() {
console.log("init");
// when the page is loaded
video = document.querySelector("#myVideo");
chapterMenuDiv = document.querySelector("#chapterMenu");
// The tracks as HTML elements
trackElems = document.querySelectorAll("track");
for(var i = 0; i < trackElems.length; i++) {
var currentTrackElem = trackElems[i];
tracksURLs[i] = currentTrackElem.src;
}
// The tracks as JS objects
tracks = video.textTracks;
buildChapterMenu('en', 'chapters');
};
function buildChapterMenu(lang, kind) {
// Locate the track with language = lang and kind="chapters"
for(var i = 0; i < tracks.length; i++) {
// current track
var track = tracks[i];
var trackAsHtmlElem = trackElems[i];
if((track.language === lang) && (track.kind === kind)) {
// the track must be active if we want to highlight the
// current chapter while the video is playing
track.mode="showing";
if(trackAsHtmlElem.readyState === 2) {
// the track has already been loaded
displayChapterMarkers(track);
} else {
displayChapterMarkersAfterTrackLoaded(trackAsHtmlElem, track);
}
}
}
}
function displayChapterMarkers(track) {
var cues = track.cues;
// We should not see the cues on the video.
track.mode="hidden";
// Iterate on cues
for(var i=0, len = cues.length; i < len; i++) {
var cue = cues[i];
//addCueListeners(cue);
var cueObject = JSON.parse(cue.text);
var description = cueObject.description;
var imageFileName = cueObject.image;
var imageURL = "https://mainline.i3s.unice.fr/mooc/" + imageFileName;
// add an image to the menu
var figure = document.createElement('figure');
figure.classList.add("img");
figure.innerHTML = "<img onclick='jumpTo(" + cue.startTime + ");' class='thumb' src='" + imageURL + "'><figcaption class='desc'>" + description + "</figcaption></figure>";
chapterMenuDiv.insertBefore(figure, null);
}
}
function displayChapterMarkersAfterTrackLoaded(trackElem, track) {
// Create a listener that will be called only when the track has
// been loaded
trackElem.addEventListener('load', function(e) {
console.log("chapter track loaded");
displayChapterMarkers(track);
});
}
function jumpTo(time) {
video.currentTime = time;
video.play();
}
Output
300px
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |