Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<html lang="en">
<head>
  <meta charset=utf-8>
  <title>Video player with clickable transcript</title>
</head>
<body>
  <section id="all">
    <h1>Using the track API to extract the content of webVTT files in <code>&lt;track&gt;</code> elements</h1>
    <p>Click on the buttons under the video to extract the english or german subtitles
    </p>
    <p>Look at the HTML and JS code.</p>
    <p>
      
      <button disabled 
              id="buttonEnglish" 
              onclick="loadTranscript('en');">
        Display English transcript
      </button>
      
      <button disabled 
              id="buttonDeutsch" 
              onclick="loadTranscript('de');">
        Display Deutsch transcript
      </button>
      
    </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.vtt">
    </video>
    <div id="transcript"></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%;
}
.cues {
  color:blue;
}
.cues:hover {
  text-decoration: underline;
}
.cues.current {
  color:black;
  font-weight: bold;
}
#myVideo {
    display: block;
    float : left;
    margin-right: 2.85714%;
    width: 65.71429%;
    background-color: black;
    position: relative;
}
#transcript {
  padding: 10px;
  border:1px solid;
  float: left;
  max-height: 225px;
  overflow: auto;
  width: 25%;
  margin: 0;
  font-size: 14px;
  list-style: none;
}
 
var video, transcriptDiv;
var tracks, trackElems, tracksURLs = [];
var buttonEnglish, buttonDeutsch;
window.onload = function() {
  console.log("init");
  // when the page is loaded
  video = document.querySelector("#myVideo");
  transcriptDiv = document.querySelector("#transcript");
  
  // 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;
  }
  
  buttonEnglish = document.querySelector("#buttonEnglish");
  buttonDeutsch = document.querySelector("#buttonDeutsch");
  
  // we enable the buttons and show transcript
  buttonEnglish.disabled = false;
  buttonDeutsch.disabled = false;
    
  // The tracks as JS objects
  tracks = video.textTracks;
};
function loadTranscript(lang) {
  // clear current transcript
  clearTranscriptDiv();
  
  // set all track mode to disabled. We will only activate the
  // one whose content will be displayed as transcript
  disableAllTracks();
  
  // Locate the track with language = lang
  for(var i = 0; i < tracks.length; i++) {
    // current track
    var track = tracks[i];
    var trackAsHtmlElem = trackElems[i];
  
    if((track.language === lang) && (track.kind !== "chapters")) {
      track.mode="showing";
      if(trackAsHtmlElem.readyState === 2) {
        // the track has already been loaded
        displayCues(track);
      } else {
        displayCuesAfterTrackLoaded(trackAsHtmlElem, track);
      }
      
/*      FOR FIREFOX....
        track.addEventListener("cuechange", function(e) {
           var cue = this.activeCues[0];
           console.log("cue change");
           var transcriptText = document.getElementById(cue.id);
           transcriptText.classList.add("current");
      });
      */
    }
  } 
}
  
function displayCuesAfterTrackLoaded(trackElem, track) {
  // Create a listener that will be called only when the track has
  // been loaded
  trackElem.addEventListener('load', function(e) {
      console.log("track loaded");
      displayCues(track);
   });
}
function disableAllTracks() {
    for(var i = 0; i < tracks.length; i++) 
      tracks[i].mode = "disabled";
}
function displayCues(track) {
    var cues = track.cues;
    
    //append all the subtitle texts to 
      for(var i=0, len = cues.length; i < len; i++) {
        var cue = cues[i];
        addCueListeners(cue);
        var voices = getVoices(cue.text);
        var transText="";
        if (voices.length > 0) {
            for (var j = 0; j < voices.length; j++) { // how many voices ?
                transText += voices[j].voice + ': ' + removeHTML(voices[j].text);
            }
          } else 
             transText = cue.text; // not a voice text
        var clickableTransText = "<li class='cues' id=" + cue.id +  " onclick='jumpTo(" + cue.startTime + ");'" + ">" + transText + "</li>";
        addToTranscriptDiv(clickableTransText);
      }
  }
function getVoices(speech) {  // takes a text content and check if there are voices 
  var voices = [];            // inside
  var pos = speech.indexOf('<v'); // voices are like <v michel> ....
  while (pos != -1) {
    endVoice = speech.indexOf('>');
    var voice = speech.substring(pos + 2, endVoice).trim();
    var endSpeech = speech.indexOf('</v>');
    var text = speech.substring(endVoice + 1, endSpeech);
    voices.push({
       'voice': voice,
       'text': text
    });
    speech = speech.substring(endSpeech + 4);
    pos = speech.indexOf('<v');
  }
  return voices;
}
function removeHTML(text) {
  var div = document.createElement('div');
  div.innerHTML = text;
  return div.textContent || div.innerText || '';
}
function jumpTo(time) {
  video.currentTime = time;
  video.play();
}
function clearTranscriptDiv() {
  transcriptDiv.innerHTML = "";
}
function addToTranscriptDiv(htmlText) {
  transcriptDiv.innerHTML += htmlText;
}
function addCueListeners(cue) {
  cue.onenter = function(){
    console.log('enter id=' + this.id);
    var transcriptText = document.getElementById(this.id);
    transcriptText.classList.add("current");
  };
  cue.onexit = function(){
    console.log('exit id=' + cue.id);
   var transcriptText = document.getElementById(this.id); transcriptText.classList.remove("current");
  };
}
Output 300px

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
AuroreDechampspro
0viewers