Last active
April 19, 2019 15:04
-
-
Save namuit/f497c23f2994d0285a2b2271765fbbe5 to your computer and use it in GitHub Desktop.
Video player with clickable transcript// source https://jsbin.com/bevehi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <html lang="en"> | |
| <head> | |
| <meta charset=utf-8> | |
| <title>Video player with clickable transcript</title> | |
| <style id="jsbin-css"> | |
| #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; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <section id="all"> | |
| <h1>Using the track API to extract the content of webVTT files in <code><track></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="http://mainline.i3s.unice.fr/mooc/elephants-dream-medium.mp4" type="video/mp4"> | |
| <source src="http://mainline.i3s.unice.fr/mooc/elephants-dream-medium.webm" type="video/webm"> | |
| <track label="English subtitles" kind="subtitles" srclang="en" src="http://mainline.i3s.unice.fr/mooc/elephants-dream-subtitles-en.vtt" > | |
| <track label="Deutsch subtitles" kind="subtitles" srclang="de" src="http://mainline.i3s.unice.fr/mooc/elephants-dream-subtitles-de.vtt" default> | |
| <track label="English chapters" kind="chapters" srclang="en" src="http://mainline.i3s.unice.fr/mooc/elephants-dream-chapters-en.vtt"> | |
| </video> | |
| <div id="transcript"></div> | |
| </section> | |
| <script id="jsbin-javascript"> | |
| 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"); | |
| }; | |
| } | |
| </script> | |
| <script id="jsbin-source-html" type="text/html"><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><track></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="http://mainline.i3s.unice.fr/mooc/elephants-dream-medium.mp4" type="video/mp4"> | |
| <source src="http://mainline.i3s.unice.fr/mooc/elephants-dream-medium.webm" type="video/webm"> | |
| <track label="English subtitles" kind="subtitles" srclang="en" src="http://mainline.i3s.unice.fr/mooc/elephants-dream-subtitles-en.vtt" > | |
| <track label="Deutsch subtitles" kind="subtitles" srclang="de" src="http://mainline.i3s.unice.fr/mooc/elephants-dream-subtitles-de.vtt" default> | |
| <track label="English chapters" kind="chapters" srclang="en" src="http://mainline.i3s.unice.fr/mooc/elephants-dream-chapters-en.vtt"> | |
| </video> | |
| <div id="transcript"></div> | |
| </section> | |
| </body> | |
| </html> | |
| </script> | |
| <script id="jsbin-source-css" type="text/css">#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; | |
| } | |
| </script> | |
| <script id="jsbin-source-javascript" type="text/javascript">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"); | |
| }; | |
| } | |
| </script></body> | |
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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"); | |
| }; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment