mirror of
https://github.com/mehotkhan/BandersnatchInteractive.git
synced 2025-07-27 17:23:22 +00:00
first commit
This commit is contained in:
commit
08e385231b
8 changed files with 33543 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Black.Mirror.Bandersnatch.2018.720p.WEB-DL.x264.DUAL.mkv
|
||||||
|
Black.Mirror.Bandersnatch.2018.WEBRip.x264-NoGRP.srt
|
1
README.md
Normal file
1
README.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# BandersnatchInteractive
|
7131
assets/SegmentMap.js
Normal file
7131
assets/SegmentMap.js
Normal file
File diff suppressed because it is too large
Load diff
742
assets/SubPlayerJS.js
Normal file
742
assets/SubPlayerJS.js
Normal file
|
@ -0,0 +1,742 @@
|
||||||
|
!window.jQuery && document.write(unescape('%3Cscript src="https://code.jquery.com/jquery-2.1.1.min.js"%3E%3C%2Fscript%3E%3Cscript src="jquery.ajax-cross-origin.min.js"%3E%3C%2Fscript%3E'));
|
||||||
|
|
||||||
|
//loads video
|
||||||
|
var timeStamp = 0 + ":" + 0 + ":" + 0 + "." + 0;
|
||||||
|
var lineNumber = 0;
|
||||||
|
var currentTime = 0;
|
||||||
|
var videoPlayerLoaded = false;
|
||||||
|
var subtitleIsSet = false;
|
||||||
|
var guiIsvisible = false;
|
||||||
|
var isPlaying = false;
|
||||||
|
var isFullScreen = false;
|
||||||
|
var isSubtitleEnabled = true;
|
||||||
|
var isSeeking = false;
|
||||||
|
var isParsing = false;
|
||||||
|
var subtitleArray = [];
|
||||||
|
var ammountOfVideos = 0;
|
||||||
|
var fontsArray = [];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SubPlayerJS {
|
||||||
|
|
||||||
|
constructor(div, file) {
|
||||||
|
this.div = div;
|
||||||
|
this.file = file;
|
||||||
|
this.timeStamp = timeStamp;
|
||||||
|
this.previousVidWidth;
|
||||||
|
this.previousVidHeight;
|
||||||
|
this.lineNumber = lineNumber;
|
||||||
|
this.currentTime = currentTime;
|
||||||
|
this.interval;
|
||||||
|
this.videoPlayerLoaded = videoPlayerLoaded;
|
||||||
|
this.subtitleIsSet = subtitleIsSet;
|
||||||
|
this.guiIsvisible = false;
|
||||||
|
this.isPlaying = isPlaying;
|
||||||
|
this.isFullScreen = isFullScreen;
|
||||||
|
this.isSubtitleEnabled = isSubtitleEnabled;
|
||||||
|
this.subtitleArray = [];
|
||||||
|
this.isSeeking = isSeeking;
|
||||||
|
this.timer;
|
||||||
|
|
||||||
|
|
||||||
|
if (!$("link[href='http://fonts.googleapis.com/icon?family=Material+Icons']").length) {
|
||||||
|
loadjscssfile("http://fonts.googleapis.com/icon?family=Material+Icons", "css");
|
||||||
|
}
|
||||||
|
if (!$("link[href='https://rawgit.com/EldinZenderink/SubPlayerJS/master/SubPlayerJS.css']").length) {
|
||||||
|
loadjscssfile("https://rawgit.com/EldinZenderink/SubPlayerJS/master/SubPlayerJS.css", "css");
|
||||||
|
}
|
||||||
|
if (!$("link[href='https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/css/materialize.min.css']").length) {
|
||||||
|
loadjscssfile("https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/css/materialize.min.css", "css");
|
||||||
|
}
|
||||||
|
|
||||||
|
ammountOfVideos++;
|
||||||
|
subtitleArray.push([]);
|
||||||
|
fontsArray.push({});
|
||||||
|
this.loadVideo(ammountOfVideos);
|
||||||
|
|
||||||
|
this.videoid = ammountOfVideos;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
parseSubtitle(){
|
||||||
|
this.resetSubtitle();
|
||||||
|
var subPlayerVideo = SubPlayerJS.getVideo(this.videoid.toString());
|
||||||
|
var previousTime = 0;
|
||||||
|
var previousTotalAVBytesDecoded = 0;
|
||||||
|
var bitRate = 0;
|
||||||
|
var curTime = 0;
|
||||||
|
var totalAVBytesDecoded= 0;
|
||||||
|
var packsParsed = 0;
|
||||||
|
var packsSize = 0;
|
||||||
|
var packsContainingSubtitle = 0;
|
||||||
|
var timeItSeekedTo = 0;
|
||||||
|
var timeSeekTook = 0;
|
||||||
|
var audioBytesDecoded = 0;
|
||||||
|
var videoBytesDecoded = 0;
|
||||||
|
var bitRate = 0;
|
||||||
|
var curTime = 0;
|
||||||
|
var guessedBytePosition = 0;
|
||||||
|
var parsedSubtitle = [];
|
||||||
|
var needsToSeek = false;
|
||||||
|
var isPlaying = false;
|
||||||
|
var sourceUrl = this.file;
|
||||||
|
var videoid = this.videoid.toString();
|
||||||
|
console.log(videoid);
|
||||||
|
console.log(subPlayerVideo);
|
||||||
|
subPlayerVideo.onended = function(e) {
|
||||||
|
isPlaying = false;
|
||||||
|
};
|
||||||
|
subPlayerVideo.onplay = function(){
|
||||||
|
isPlaying = true;
|
||||||
|
}
|
||||||
|
subPlayerVideo.onpause = function(){
|
||||||
|
isPlaying = false;
|
||||||
|
console.log("is pausing");
|
||||||
|
console.log(subPlayerVideo.currentTime);
|
||||||
|
}
|
||||||
|
subPlayerVideo.onseeking = function(){
|
||||||
|
|
||||||
|
|
||||||
|
timeItSeekedTo = subPlayerVideo.currentTime;
|
||||||
|
|
||||||
|
|
||||||
|
if(!needsToSeek){
|
||||||
|
if(timeItSeekedTo < previousTime){
|
||||||
|
needsToSeek = true;
|
||||||
|
var parsedSubtitleLength = parsedSubtitle.length;
|
||||||
|
|
||||||
|
for(var x = 0; x < parsedSubtitleLength; x++){
|
||||||
|
var timeOfSubtitle = parsedSubtitle[x].time;
|
||||||
|
if(Math.round(timeOfSubtitle) > Math.round(timeItSeekedTo)){
|
||||||
|
guessedBytePosition = totalAVBytesDecoded - parsedSubtitle[x].decodedBytes;
|
||||||
|
subPlayerVideo.currentTime = timeOfSubtitle;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
needsToSeek = false;
|
||||||
|
} else {
|
||||||
|
subPlayerVideo.currentTime = previousTime;
|
||||||
|
needsToSeek = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
subPlayerVideo.ontimeupdate = function(){
|
||||||
|
|
||||||
|
audioBytesDecoded = subPlayerVideo.webkitAudioDecodedByteCount;
|
||||||
|
videoBytesDecoded = subPlayerVideo.webkitVideoDecodedByteCount;
|
||||||
|
totalAVBytesDecoded = videoBytesDecoded + audioBytesDecoded;
|
||||||
|
curTime = subPlayerVideo.currentTime;
|
||||||
|
bitRate = totalAVBytesDecoded / curTime;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var startRequest = Math.round(previousTotalAVBytesDecoded - guessedBytePosition) ;
|
||||||
|
var endRequest = Math.round(totalAVBytesDecoded - guessedBytePosition) ;
|
||||||
|
packsSize = endRequest - startRequest;
|
||||||
|
|
||||||
|
if(subPlayerVideo.currentTime < timeItSeekedTo){
|
||||||
|
subPlayerVideo.playbackRate = 100;
|
||||||
|
} else {
|
||||||
|
needsToSeek = false;
|
||||||
|
subPlayerVideo.playbackRate = 1;
|
||||||
|
var oReq2 = new XMLHttpRequest();
|
||||||
|
oReq2.open("GET", sourceUrl, true);
|
||||||
|
oReq2.setRequestHeader('Range', 'bytes=' + startRequest + '-' + endRequest);
|
||||||
|
oReq2.responseType = "blob";
|
||||||
|
|
||||||
|
oReq2.onload = function(oEvent) {
|
||||||
|
console.log("Received file!");
|
||||||
|
var blob = oReq2.response;
|
||||||
|
packsParsed++;
|
||||||
|
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = function(){
|
||||||
|
var binaryString = this.result;
|
||||||
|
if(binaryString.indexOf("0,,")){
|
||||||
|
var searchResult = binaryString.split("0,,");
|
||||||
|
var resultLength = searchResult.length;
|
||||||
|
if(resultLength > 1){
|
||||||
|
var parsed = "";
|
||||||
|
for(var x = 1; x < resultLength; x++){
|
||||||
|
var decoded = decodeURI(encodeURI(searchResult[x]).split('%E2%80%BA')[0]) + " \r\n";
|
||||||
|
if(decoded.indexOf('%E2%80%BA') < 0 && decoded !== undefined){
|
||||||
|
parsed = parsed + decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedSubtitle.push({time: curTime, decodedBytes: previousTotalAVBytesDecoded});
|
||||||
|
packsContainingSubtitle++;
|
||||||
|
//document.querySelector('#result').innerHTML = parsed;
|
||||||
|
$('#subtitle_' + videoid.toString()).html('<div style="font-family: Sans-Serif;">' + parsed.replace("\\N", "<br />") + '</div>');
|
||||||
|
console.log(videoid);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
reader.readAsText(blob, 'ISO-8859-1');
|
||||||
|
};
|
||||||
|
oReq2.send(null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var data = " \
|
||||||
|
Time: " + curTime + " \r\n \
|
||||||
|
BitRate: " + (bitRate / 100) + "kbps \r\n \
|
||||||
|
Audio Bytes Decoded: " + (audioBytesDecoded / 1000000) + "mb \r\n \
|
||||||
|
Video Bytes Decoded: " + (videoBytesDecoded / 1000000) + "mb \r\n \
|
||||||
|
Total Bytes Decoded: " + (totalAVBytesDecoded / 1000000) + " mb \r\n \
|
||||||
|
Buffer Length: " + subPlayerVideo.buffered.length + "\r\n \
|
||||||
|
Buffer Start: " + subPlayerVideo.buffered.start(subPlayerVideo.buffered.length - 1) + "\r\n \
|
||||||
|
Buffer End: " + subPlayerVideo.buffered.end(subPlayerVideo.buffered.length - 1) + "\r\n \
|
||||||
|
Packs Parsed: " + packsParsed + "\r\n \
|
||||||
|
Packs Contianing Subtitle: " + packsContainingSubtitle + " \r\n \
|
||||||
|
Timedifference seek: " + timeSeekTook + " \r\n \
|
||||||
|
guessedBytePosition: " + guessedBytePosition + " \r\n \
|
||||||
|
Packs Size: " + packsSize;
|
||||||
|
|
||||||
|
//document.querySelector('#videodata').innerHTML = data;
|
||||||
|
|
||||||
|
previousTotalAVBytesDecoded = totalAVBytesDecoded;
|
||||||
|
//previousTime = curTime;
|
||||||
|
|
||||||
|
}
|
||||||
|
setInterval(function(){
|
||||||
|
if(previousTime != curTime){
|
||||||
|
previousTime = curTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setSubtitle(subtitleurl) {
|
||||||
|
this.resetSubtitle();
|
||||||
|
this.subtitle = subtitleurl;
|
||||||
|
|
||||||
|
if (subtitleurl != "" && subtitleurl != null && subtitleurl != 0) {
|
||||||
|
this.getSubtitle(this.videoid);
|
||||||
|
} else {
|
||||||
|
this.subtitleIsSet = false;
|
||||||
|
$('#enableSub_' + ammountOfVideos.toString()).html('<i class="material-icons" style="color: rgb(96, 96, 96);">subtitles</i>');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getTimeStamp();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setWidth(width) {
|
||||||
|
this.videoWidth = width;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setHeight(height) {
|
||||||
|
this.videoHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
resetSubtitle() {
|
||||||
|
$('#subtitle_' + this.videoid.toString()).html('');
|
||||||
|
}
|
||||||
|
|
||||||
|
loadVideo(videoid) {
|
||||||
|
var vidwidth = 0;
|
||||||
|
var vidheight = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
clearInterval(this.interval);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("no interval running");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.videoWidth != null && this.videoWidth != "" && this.videoWidth != 0) {
|
||||||
|
vidwidth = this.videoWidth;
|
||||||
|
} else {
|
||||||
|
vidwidth = "100%";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.videoHeight != null && this.videoHeight != "" && this.videoHeight != 0) {
|
||||||
|
vidheight = this.videoHeight;
|
||||||
|
} else {
|
||||||
|
vidheight = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.videoPlayerLoaded || this.previousVidHeight != h || this.previousVidWidth != w) {
|
||||||
|
|
||||||
|
$(this.div).html('<div class="outer-container-SPJS " id="outerContainer_' + videoid.toString() + '">\
|
||||||
|
<div class="inner-container-SPJS " id="innerContainer_' + videoid.toString() + '">\
|
||||||
|
<div class="video-overlay-SPJS" id="subtitle_' + videoid.toString() + '"><br /></div>\
|
||||||
|
<div style="min-width: 100%;" class="control-SPJS" id="controlDiv_' + videoid.toString() + '"></div>\
|
||||||
|
<video id="SubPlayerVideo_' + videoid.toString() + '" width="' + vidwidth + '" height="' + vidheight + '">\
|
||||||
|
<source id="videoSource_' + videoid.toString() + '" src="">\
|
||||||
|
Your browser does not support HTML5 video.\
|
||||||
|
</video>\
|
||||||
|
</div>\
|
||||||
|
</div>');
|
||||||
|
this.videoPlayerLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.previousVidWidth = this.videoWidth;
|
||||||
|
this.previousVidHeight = this.videoHeight;
|
||||||
|
|
||||||
|
var subPlayerVideo = SubPlayerJS.getVideo(videoid.toString());
|
||||||
|
subPlayerVideo = subPlayerVideo;
|
||||||
|
subPlayerVideo.src = this.file;
|
||||||
|
subPlayerVideo.addEventListener('loadedmetadata', function() {
|
||||||
|
var max = subPlayerVideo.duration;
|
||||||
|
|
||||||
|
$('#controlDiv_' + videoid.toString()).html('<div id="allcontrols_' + videoid.toString() + '" style="width: 100%;"><a href="javascript:;" style="bottom: 7px;" id="playpause_' + videoid.toString() + '" onclick="SubPlayerJS.startPlayVideo(' + videoid.toString() + ')"><i class="material-icons" style="color: rgb(255, 255, 255);">play_arrow</i></a><span style="visibility:hidden"> | </span><input onclick="SubPlayerJS.onSeekBarClick(' + videoid.toString() + ')" style="min-width: 80%; bottom: 9px;" type="range" id="seekbar_' + videoid.toString() + '" min="0" max="' + max + '" /><span style="visibility:hidden"> | </span><a id="fullScreen_' + videoid.toString() + '" href="javascript:;" style=" style="" onclick="SubPlayerJS.makeFullScreen(' + videoid.toString() + ')"><i class="material-icons" style="font-size: 24px; color: rgb(255, 255, 255);" >fullscreen</i></a><span style="visibility:hidden"> | </span><a href="javascript:;" id="enableSub_' + videoid.toString() + '" onclick="SubPlayerJS.enaDisaSub(' + videoid.toString() + ')"> <i class="material-icons" style="color: rgb(255, 255, 255);">subtitles</i></a></div>');
|
||||||
|
$('#seekbar_' + videoid.toString()).val(0).css("width", "80%").css("right", "5px");
|
||||||
|
|
||||||
|
$('#allcontrols').hide();
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
setInterval(function() {
|
||||||
|
if (!this.isSeeking) {
|
||||||
|
$('#seekbar_' + videoid.toString()).val(subPlayerVideo.currentTime);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#outerContainer_' + videoid.toString()).on("change mousemove", function() {
|
||||||
|
$('#outerContainer_' + videoid.toString()).css({
|
||||||
|
cursor: "auto"
|
||||||
|
});
|
||||||
|
$('#allcontrols_' + videoid.toString()).show();
|
||||||
|
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
this.timer = setTimeout(function() {
|
||||||
|
$('#allcontrols_' + videoid.toString()).hide();
|
||||||
|
$('#outerContainer_' + videoid.toString()).css({
|
||||||
|
cursor: "none"
|
||||||
|
});
|
||||||
|
}, 4000);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getSubtitle(videoid) {
|
||||||
|
var extension = this.subtitle.replace(/^.*\./, '');
|
||||||
|
$.ajax({
|
||||||
|
url: this.subtitle,
|
||||||
|
type: 'get',
|
||||||
|
async: false,
|
||||||
|
success: function(data) {
|
||||||
|
switch (extension) {
|
||||||
|
case "ass":
|
||||||
|
console.log("SubPlayerJS: SSA (SubStationAlpha) Supported!");
|
||||||
|
SubPlayerJS.parseSubStationAlpha(data, videoid);
|
||||||
|
break;
|
||||||
|
case "srt":
|
||||||
|
|
||||||
|
console.log("SubPlayerJS: SRT (SubRip) Supported!");
|
||||||
|
SubPlayerJS.parseSubRip(data, videoid);
|
||||||
|
break;
|
||||||
|
case "vtt":
|
||||||
|
console.log("SubPlayerJS: Comming soon!");
|
||||||
|
break;
|
||||||
|
case "sub":
|
||||||
|
console.log("SubPlayerJS: Maybe supported in future!");
|
||||||
|
break;
|
||||||
|
case "smi":
|
||||||
|
console.log("SubPlayerJS: Maybe supported in future!");
|
||||||
|
break;
|
||||||
|
case "usf":
|
||||||
|
console.log("SubPlayerJS: Maybe supported in future!");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("SubPlayerJS: Subtitle with extension: " + extension + " is NOT supported!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
console.log("SubPlayerJS: Succesfully read subtitle!");
|
||||||
|
},
|
||||||
|
error: function(err) {
|
||||||
|
console.log("SubPlayerJS: FAILED TO LOAD SUBTITLE: " + err);
|
||||||
|
this.subtitleIsSet = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getTimeStamp() {
|
||||||
|
var subPlayerVideo = SubPlayerJS.getVideo(this.videoid.toString());
|
||||||
|
var that = this;
|
||||||
|
var videoid = this.videoid.toString();
|
||||||
|
this.interval = setInterval(function() {
|
||||||
|
var curTimeSecond = subPlayerVideo.currentTime;
|
||||||
|
this.currentTime = curTimeSecond;
|
||||||
|
setTimeout(that.showSubtitle(curTimeSecond, videoid), 0);
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
showSubtitle(time, videoid) {
|
||||||
|
var localArray = subtitleArray[videoid - 1];
|
||||||
|
var fonts = fontsArray[videoid - 1];
|
||||||
|
if(fonts.length < 1){
|
||||||
|
fonts["Default"] = "Verdana";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
var currentText = localArray[this.lineNumber];
|
||||||
|
var secondOfTimeStart = currentText[0];
|
||||||
|
var secondOfTimeEnd = currentText[1];
|
||||||
|
|
||||||
|
if (time < secondOfTimeStart) {
|
||||||
|
|
||||||
|
var index = 0;
|
||||||
|
this.lineNumber = 0;
|
||||||
|
$('#subtitle_' + videoid.toString()).html('');
|
||||||
|
var arrayLength = localArray.length;
|
||||||
|
for (var i = 0; i < arrayLength; i++) {
|
||||||
|
var timeStart = parseInt(localArray[i][0]);
|
||||||
|
var timeEnd = parseInt(localArray[i][1]);
|
||||||
|
|
||||||
|
if (time > timeStart) {
|
||||||
|
this.lineNumber = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$('#subtitle_' + videoid.toString()).html('');
|
||||||
|
} else {
|
||||||
|
if (time > secondOfTimeEnd) {
|
||||||
|
this.lineNumber++;
|
||||||
|
$('#subtitle_' + videoid.toString()).html('');
|
||||||
|
} else {
|
||||||
|
var fullText = currentText[2];
|
||||||
|
var fontstyletouse;
|
||||||
|
if(currentText[3] == null || currentText[3].length == 0){
|
||||||
|
fontstyletouse = "Default";
|
||||||
|
} else {
|
||||||
|
fontstyletouse = currentText[3];
|
||||||
|
}
|
||||||
|
$('#subtitle_' + videoid.toString()).html('<div style="font-family: ' + fonts[fontstyletouse] + '">' + fullText.substring(0, fullText.length - 1).replace("\\N", "<br />") + '</div>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
try{
|
||||||
|
var index = 0;
|
||||||
|
this.lineNumber = 0;
|
||||||
|
$('#subtitle_' + videoid.toString()).html('');
|
||||||
|
var arrayLength = localArray.length;
|
||||||
|
for (var i = 0; i < arrayLength; i++) {
|
||||||
|
var timeStart = parseInt(localArray[i][0]);
|
||||||
|
var timeEnd = parseInt(localArray[i][1]);
|
||||||
|
|
||||||
|
if (time > timeStart) {
|
||||||
|
this.lineNumber = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e){
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static getVideo(ammount){
|
||||||
|
return document.getElementById('SubPlayerVideo_' + ammount);
|
||||||
|
}
|
||||||
|
|
||||||
|
static timeStampToSeconds(timestamp, fileType) {
|
||||||
|
var totalSeconds = 0;
|
||||||
|
switch(fileType){
|
||||||
|
case "ass":
|
||||||
|
var parts = timestamp.split(':');
|
||||||
|
var hour = parts[0];
|
||||||
|
var minute = parts[1];
|
||||||
|
var second = parts[2];
|
||||||
|
totalSeconds = hour * 3600 + minute * 60 + parseInt(second);
|
||||||
|
break;
|
||||||
|
case "srt":
|
||||||
|
var parts = timestamp.split(':');
|
||||||
|
var hour = parts[0];
|
||||||
|
var minute = parts[1];
|
||||||
|
var second = parts[2].split('\r\n')[0];
|
||||||
|
totalSeconds = hour * 3600 + minute * 60 + parseInt(second);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
static parseSubStationAlpha(ssa, videoid) {
|
||||||
|
var fonts = {};
|
||||||
|
console.log(this.fonts);
|
||||||
|
var styling = ssa.split("Styles]")[1].split("[Events]")[0].split("\n");
|
||||||
|
$.each(styling, function(key, style) {
|
||||||
|
if(style.indexOf("Style:") > -1){
|
||||||
|
var information = style.split(':')[1].split(',');
|
||||||
|
var styletype = information[0].trim();
|
||||||
|
var font = information[1].trim();
|
||||||
|
console.log("STYLE TYPE = " + styletype + ", FONT: " + font);
|
||||||
|
fonts[styletype] = font;
|
||||||
|
fontsArray[videoid - 1] = fonts;
|
||||||
|
console.log(this.fonts);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
this.loadFont(videoid);
|
||||||
|
var lines = ssa.split("\n");
|
||||||
|
$.each(lines, function(key, line) {
|
||||||
|
if (line.indexOf("Dialogue") > -1) {
|
||||||
|
var parts = line.split(',');
|
||||||
|
parts[0] = SubPlayerJS.timeStampToSeconds(parts[1], "ass");
|
||||||
|
parts[1] = SubPlayerJS.timeStampToSeconds(parts[2], "ass");
|
||||||
|
var text = "";
|
||||||
|
for(var i = 9; i < line.split(',').length; i++){
|
||||||
|
text = text + line.split(',')[i] + ",";
|
||||||
|
}
|
||||||
|
parts[2] = text;
|
||||||
|
|
||||||
|
parts[3] = parts[3];
|
||||||
|
|
||||||
|
for(var i = 4; i < line.split(',').length; i++){
|
||||||
|
parts[i] = "";
|
||||||
|
}
|
||||||
|
subtitleArray[videoid - 1].push(parts);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.subtitleIsSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static parseSubRip(srt, videoid){
|
||||||
|
var lines = srt.split(/[\r\n]+[\r\n]+/);
|
||||||
|
$.each(lines, function(key, line) {
|
||||||
|
if (line.indexOf("-->") > -1) {
|
||||||
|
var parts = line.split(/[\r\n]+/)[1].split(/[\r\n]+/)[0].split('-->');
|
||||||
|
parts[0] = SubPlayerJS.timeStampToSeconds(parts[0], "srt");
|
||||||
|
parts[1] = SubPlayerJS.timeStampToSeconds(parts[1], "srt");
|
||||||
|
|
||||||
|
|
||||||
|
var text = "";
|
||||||
|
for(var i = 2; i < line.split(/[\r\n]+/).length; i++){
|
||||||
|
text = text + line.split(/[\r\n]+/)[i] + "<br />";
|
||||||
|
}
|
||||||
|
parts[2] = text;
|
||||||
|
subtitleArray[videoid - 1].push(parts);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static enaDisaSub(videoid) {
|
||||||
|
if (this.isSubtitleEnabled) {
|
||||||
|
this.isSubtitleEnabled = false;
|
||||||
|
|
||||||
|
$('#subtitle_' + videoid.toString()).css("z-index", "-1");
|
||||||
|
$('#enableSub_' + videoid.toString()).html('<i class="material-icons" style="color: rgb(96, 96, 96);">subtitles</i>');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.isSubtitleEnabled = true;
|
||||||
|
|
||||||
|
$('#subtitle_' + videoid.toString()).css("z-index", "1");
|
||||||
|
$('#enableSub_' + videoid.toString()).html('<i class="material-icons" style="color: rgb(255, 255, 255);">subtitles</i>');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static startPlayVideo(videoid) {
|
||||||
|
if (!this.isPlaying) {
|
||||||
|
SubPlayerJS.getVideo(videoid).play();
|
||||||
|
$('#playpause_' + videoid.toString()).html('<i class="material-icons" style="color: rgb(255, 255, 255);">pause</i>');
|
||||||
|
this.isPlaying = true;
|
||||||
|
} else {
|
||||||
|
SubPlayerJS.getVideo(videoid).pause();
|
||||||
|
$('#playpause_' + videoid.toString()).html('<i class="material-icons" style="color: rgb(255, 255, 255);">play_arrow</i>');
|
||||||
|
this.isPlaying = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static onSeekBarClick(videoid) {
|
||||||
|
var currentPosition = $('#seekbar_' + videoid.toString()).val();
|
||||||
|
SubPlayerJS.getVideo(videoid).currentTime = currentPosition;
|
||||||
|
// console.log(currentPosition);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static makeFullScreen(videoid) {
|
||||||
|
var i = document.getElementById('innerContainer_' + videoid.toString());
|
||||||
|
|
||||||
|
// go full-screen
|
||||||
|
|
||||||
|
if (!this.isFullScreen) {
|
||||||
|
|
||||||
|
if (i.requestFullscreen) {
|
||||||
|
i.requestFullscreen();
|
||||||
|
} else if (i.webkitRequestFullscreen) {
|
||||||
|
i.webkitRequestFullscreen();
|
||||||
|
} else if (i.mozRequestFullScreen) {
|
||||||
|
i.mozRequestFullScreen();
|
||||||
|
} else if (i.msRequestFullscreen) {
|
||||||
|
i.msRequestFullscreen();
|
||||||
|
}
|
||||||
|
console.log("going into fullscreen");
|
||||||
|
|
||||||
|
$('#SubPlayerVideo_' + videoid.toString()).css({
|
||||||
|
position: 'fixed', //or fixed depending on needs
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
height: '100%',
|
||||||
|
"background-color": "black"
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#subtitle_' + videoid.toString()).css({
|
||||||
|
position: 'fixed', //or fixed depending on needs
|
||||||
|
top: '80%',
|
||||||
|
left: 0,
|
||||||
|
height: '100%',
|
||||||
|
width: '100%'
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#controlDiv_' + videoid.toString()).css({
|
||||||
|
position: 'fixed', //or fixed depending on needs
|
||||||
|
top: '85%',
|
||||||
|
left: 0,
|
||||||
|
width: '100%'
|
||||||
|
});
|
||||||
|
$('#fullScreen_' + videoid.toString()).html('<i class="material-icons" style="color: rgb(255, 255, 255);">fullscreen_exit</i>');
|
||||||
|
this.isFullScreen = true;
|
||||||
|
} else {
|
||||||
|
console.log("exiting fullscreen");
|
||||||
|
if (document.exitFullscreen) {
|
||||||
|
document.exitFullscreen();
|
||||||
|
} else if (document.webkitExitFullscreen) {
|
||||||
|
document.webkitExitFullscreen();
|
||||||
|
} else if (document.mozCancelFullScreen) {
|
||||||
|
document.mozCancelFullScreen();
|
||||||
|
} else if (document.msExitFullscreen) {
|
||||||
|
document.msExitFullscreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#SubPlayerVideo_' + videoid.toString()).css({
|
||||||
|
position: 'relative',
|
||||||
|
"background-color": "",
|
||||||
|
height: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#subtitle_' + videoid.toString()).css({
|
||||||
|
position: 'absolute', //or fixed depending on needs
|
||||||
|
top: '80%',
|
||||||
|
left: 0,
|
||||||
|
height: '',
|
||||||
|
width: '100%'
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$('#controlDiv_' + videoid.toString()).css({
|
||||||
|
position: 'absolute', //or fixed depending on needs
|
||||||
|
top: '85%',
|
||||||
|
left: 0,
|
||||||
|
width: '100%'
|
||||||
|
});
|
||||||
|
$('#fullScreen_' + videoid.toString()).html('<i class="material-icons" style="color: rgb(255, 255, 255);">fullscreen</i>');
|
||||||
|
this.isFullScreen = false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static loadFont(videoid){
|
||||||
|
console.log("LOADING FONTS");
|
||||||
|
console.log(fontsArray[videoid - 1]);
|
||||||
|
var fonts = fontsArray[videoid - 1];
|
||||||
|
for (var style in fonts) {
|
||||||
|
var font = fonts[style].split(' ')[0];
|
||||||
|
|
||||||
|
var downloadFont = true;
|
||||||
|
if (typeof(Storage) !== "undefined") {
|
||||||
|
if(localStorage.getItem(style) !== null){
|
||||||
|
console.log("LOADED FONT: " + font + " FROM LOCAL STORAGE :D" );
|
||||||
|
loadjscssfile("data:text/css;base64," + localStorage.getItem(font), "css");
|
||||||
|
downloadFont = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(downloadFont){
|
||||||
|
console.log("downloading: " + font);
|
||||||
|
$.ajax({
|
||||||
|
async: false,
|
||||||
|
url: 'https://crossorigin.me/https://www.onlinewebfonts.com/search?q=' + font,
|
||||||
|
success: function(data) {
|
||||||
|
//console.log(data);
|
||||||
|
var foundUrl = data.substring(data.indexOf("class=\"url")).split('"')[3].split('"')[0].replace("/download/", "");
|
||||||
|
var fontstyle = data.substring(data.indexOf("class=\"url")).split('"')[5].substring(1 ).split('<')[0];
|
||||||
|
console.log("foundUrl: " + foundUrl);
|
||||||
|
console.log("fontstyle: " + fontstyle);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
async: true,
|
||||||
|
url: "https://crossorigin.me/https://db.onlinewebfonts.com/c/" + foundUrl + "?family=" + fontstyle,
|
||||||
|
success: function(data) {
|
||||||
|
if (typeof(Storage) !== "undefined") {
|
||||||
|
// Store
|
||||||
|
localStorage.setItem(font, btoa(unescape(encodeURIComponent(data))));
|
||||||
|
console.log("saved subtitle: " + fontstyle + " as base64 in localstorage");
|
||||||
|
} else {
|
||||||
|
console.log("did notsaved subtitle: " + fontstyle + " as base64 in localstorage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
loadjscssfile("https://db.onlinewebfonts.com/c/" + foundUrl + "?family=" + fontstyle, "css");
|
||||||
|
for (var style2 in fonts) {
|
||||||
|
//console.log(fonts[style2] + " =?= " + fonts[style] + "-?>" + fontstyle);
|
||||||
|
if( fonts[style2] == fonts[style]){
|
||||||
|
fonts[style2] = fontstyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fontsArray[videoid - 1] = fonts;
|
||||||
|
console.log("done");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
function loadjscssfile(filename, filetype) {
|
||||||
|
if (filetype == "js") { //if filename is a external JavaScript file
|
||||||
|
var fileref = document.createElement('script')
|
||||||
|
fileref.setAttribute("type", "text/javascript")
|
||||||
|
fileref.setAttribute("src", filename)
|
||||||
|
} else if (filetype == "css") { //if filename is an external CSS file
|
||||||
|
var fileref = document.createElement("link")
|
||||||
|
fileref.setAttribute("rel", "stylesheet")
|
||||||
|
fileref.setAttribute("type", "text/css")
|
||||||
|
fileref.setAttribute("href", filename)
|
||||||
|
}
|
||||||
|
if (typeof fileref != "undefined")
|
||||||
|
document.getElementsByTagName("head")[0].appendChild(fileref)
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadScript(url, callback) {
|
||||||
|
// Adding the script tag to the head as suggested before
|
||||||
|
var head = document.getElementsByTagName('head')[0];
|
||||||
|
var script = document.createElement('script');
|
||||||
|
script.type = 'text/javascript';
|
||||||
|
script.src = url;
|
||||||
|
|
||||||
|
// Then bind the event to the callback function.
|
||||||
|
// There are several events for cross browser compatibility.
|
||||||
|
script.onreadystatechange = callback;
|
||||||
|
script.onload = callback;
|
||||||
|
|
||||||
|
// Fire the loading
|
||||||
|
head.appendChild(script);
|
||||||
|
}
|
24959
assets/bandersnatch.js
Normal file
24959
assets/bandersnatch.js
Normal file
File diff suppressed because it is too large
Load diff
430
assets/scripts.js
Normal file
430
assets/scripts.js
Normal file
|
@ -0,0 +1,430 @@
|
||||||
|
var segmentMap = SegmentMap;
|
||||||
|
var bv = bandersnatch.videos['80988062'].interactiveVideoMoments.value;
|
||||||
|
var choicePoints = bv.choicePointNavigatorMetadata.choicePointsMetadata.choicePoints;
|
||||||
|
var momentsBySegment = bv.momentsBySegment;
|
||||||
|
var segmentGroups = bv.segmentGroups;
|
||||||
|
var captions = {};
|
||||||
|
var currentSegment;
|
||||||
|
var currentMoment;
|
||||||
|
var nextSegment = null;
|
||||||
|
var momentSelected = null;
|
||||||
|
var persistentState = bv.stateHistory;
|
||||||
|
var globalChoices = {};
|
||||||
|
|
||||||
|
function msToString(ms) {
|
||||||
|
return new Date(ms).toUTCString().split(' ')[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentMs() {
|
||||||
|
return Math.round(document.getElementById("video").currentTime * 1000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateJs(cond) {
|
||||||
|
if (cond[0] == 'persistentState') {
|
||||||
|
return '!!persistentState["' + cond[1] + '"]';
|
||||||
|
} else if (cond[0] == 'not') {
|
||||||
|
return '!(' + generateJs(cond[1]) + ')';
|
||||||
|
} else if (cond[0] == 'and') {
|
||||||
|
var conds = [];
|
||||||
|
for (var i = 1; i < cond.length; i++) {
|
||||||
|
conds.push('(' + generateJs(cond[i]) + ')');
|
||||||
|
}
|
||||||
|
return '(' + conds.join(' && ') + ')';
|
||||||
|
} else if (cond[0] == 'or') {
|
||||||
|
var conds = [];
|
||||||
|
for (var i = 1; i < cond.length; i++) {
|
||||||
|
conds.push('(' + generateJs(cond[i]) + ')');
|
||||||
|
}
|
||||||
|
return '(' + conds.join(' || ') + ')';
|
||||||
|
} else {
|
||||||
|
console.log('unsupported condition!', cond);
|
||||||
|
return 'true';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPrecondition(segmentId) {
|
||||||
|
let precondition = bv.preconditions[segmentId];
|
||||||
|
|
||||||
|
if (precondition) {
|
||||||
|
let cond = generateJs(precondition);
|
||||||
|
let match = eval(cond);
|
||||||
|
|
||||||
|
console.log(cond, '==', match);
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findSegment(id) {
|
||||||
|
if (id.startsWith('nsg-')) {
|
||||||
|
id = id.substr(4);
|
||||||
|
}
|
||||||
|
if (SegmentMap.segments[id]) {
|
||||||
|
// check precondition
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segmentGroups[id]) {
|
||||||
|
for (v of segmentGroups[id]) {
|
||||||
|
if (v.segmentGroup) {
|
||||||
|
return findSegment(v.segmentGroup);
|
||||||
|
} else if (v.segment) {
|
||||||
|
// check precondition
|
||||||
|
return v.segment;
|
||||||
|
} else {
|
||||||
|
if (checkPrecondition(v))
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChoiceMs(choiceId) {
|
||||||
|
var segmentId = findSegment(choiceId);
|
||||||
|
return getSegmentMs(segmentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSegmentId(ms) {
|
||||||
|
for (const [k, v] of Object.entries(SegmentMap.segments)) {
|
||||||
|
if (ms >= v.startTimeMs && ms < v.endTimeMs) {
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSegmentMs(segmentId) {
|
||||||
|
return segmentMap.segments[segmentId].startTimeMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMoment(ms) {
|
||||||
|
for (const [k, v] of Object.entries(momentsBySegment)) {
|
||||||
|
for (r of v)
|
||||||
|
if (r.type == 'scene:cs_bs') {
|
||||||
|
if (ms >= r.startMs && ms < r.endMs) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function newList(id) {
|
||||||
|
var ul = document.getElementById(id);
|
||||||
|
while (ul.firstChild) {
|
||||||
|
ul.removeChild(ul.firstChild);
|
||||||
|
}
|
||||||
|
return ul;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addItem(ul, text, url) {
|
||||||
|
var li = document.createElement("li");
|
||||||
|
var a = document.createElement("a");
|
||||||
|
a.textContent = text;
|
||||||
|
a.setAttribute('href', url);
|
||||||
|
li.appendChild(a);
|
||||||
|
ul.appendChild(li);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNextSegment(segmentId, comment) {
|
||||||
|
console.log('setNextSegment', segmentId, comment);
|
||||||
|
nextSegment = segmentId;
|
||||||
|
var ul = newList("nextSegment");
|
||||||
|
var caption = 'nextSegment: ' + segmentId;
|
||||||
|
addItem(ul, comment ? caption + ' (' + comment + ')' : caption,
|
||||||
|
'javascript:playSegment("' + segmentId + '")');
|
||||||
|
}
|
||||||
|
|
||||||
|
function addZones(segmentId) {
|
||||||
|
var ul = newList("interactionZones");
|
||||||
|
var caption = 'currentSegment(' + segmentId + ')';
|
||||||
|
addItem(ul, caption, 'javascript:playSegment("' + segmentId + '")');
|
||||||
|
|
||||||
|
var v = segmentMap.segments[segmentId];
|
||||||
|
if (v && v.ui && v.ui.interactionZones) {
|
||||||
|
var index = 0;
|
||||||
|
for (z of v.ui.interactionZones) {
|
||||||
|
var startMs = z[0];
|
||||||
|
var stopMs = z[1];
|
||||||
|
var caption = segmentId + ' interactionZone ' + index;
|
||||||
|
addItem(ul, caption, 'javascript:seek(' + startMs + ')');
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ul = newList("nextSegments");
|
||||||
|
for (const [k, v] of Object.entries(segmentMap.segments[segmentId].next)) {
|
||||||
|
var caption = captions[k] ? captions[k] : k;
|
||||||
|
if (segmentMap.segments[segmentId].defaultNext == k) {
|
||||||
|
caption = '[' + caption + ']';
|
||||||
|
setNextSegment(k);
|
||||||
|
}
|
||||||
|
addItem(ul, caption, 'javascript:playSegment("' + k + '")');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addChoices(r) {
|
||||||
|
var ul = newList("choices");
|
||||||
|
document.getElementById("choiceCaption").innerHTML = '';
|
||||||
|
if (!r) return;
|
||||||
|
index = 0;
|
||||||
|
|
||||||
|
for (x of r.choices) {
|
||||||
|
console.log(x.id, 'choice saved');
|
||||||
|
globalChoices[x.id] = x;
|
||||||
|
|
||||||
|
var caption = r.defaultChoiceIndex == index ? '[' + x.text + ']' : x.text;
|
||||||
|
addItem(ul, caption, 'javascript:choice("' +
|
||||||
|
(x.segmentId ? x.segmentId : (x.sg ? x.sg : x.id)) + '", "' + x.text + '", "' + x.id + '")');
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("choiceCaption").innerHTML = choicePoints[r.id].description;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function updateProgressBar(ms, r) {
|
||||||
|
var p = 0;
|
||||||
|
|
||||||
|
if (r && ms > r.startMs && ms < r.endMs) {
|
||||||
|
p = 100 - Math.floor((ms - r.startMs) * 100 / (r.endMs - r.startMs));
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("progress").style.width = p + '%';
|
||||||
|
}
|
||||||
|
|
||||||
|
var timerId = 0;
|
||||||
|
|
||||||
|
var switchFrom = null;
|
||||||
|
var switchTo = null;
|
||||||
|
|
||||||
|
function ontimeout(nextSegment) {
|
||||||
|
console.log('ontimeout', nextSegment);
|
||||||
|
|
||||||
|
if (switchFrom != currentSegment || switchTo != nextSegment) {
|
||||||
|
playSegment(nextSegment);
|
||||||
|
}
|
||||||
|
|
||||||
|
switchFrom = currentSegment;
|
||||||
|
switchTo = nextSegment;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ontimeupdate(evt) {
|
||||||
|
var ms = getCurrentMs();
|
||||||
|
|
||||||
|
var segmentId = getSegmentId(ms);
|
||||||
|
|
||||||
|
// ontimeupdate resolution is about a second, better use timer
|
||||||
|
clearTimeout(timerId);
|
||||||
|
if (segmentId && nextSegment && nextSegment != segmentId) {
|
||||||
|
var timeLeft = SegmentMap.segments[segmentId].endTimeMs - ms;
|
||||||
|
timerId = setTimeout(ontimeout, timeLeft, nextSegment);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentSegment != segmentId) {
|
||||||
|
console.log('ontimeupdate', currentSegment, segmentId, ms, msToString(ms));
|
||||||
|
currentSegment = segmentId;
|
||||||
|
addZones(segmentId);
|
||||||
|
currentMoment = null;
|
||||||
|
addChoices(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var r = getMoment(ms);
|
||||||
|
if (r && momentSelected != r.id) {
|
||||||
|
updateProgressBar(ms, r);
|
||||||
|
if (currentMoment != r.id) {
|
||||||
|
currentMoment = r.id;
|
||||||
|
console.log('interaction', currentMoment);
|
||||||
|
addChoices(r);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentMoment = null;
|
||||||
|
addChoices(0);
|
||||||
|
updateProgressBar(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function jumpForward(ms) {
|
||||||
|
var ms = getCurrentMs();
|
||||||
|
var segmentId = getSegmentId(ms);
|
||||||
|
var v = segmentMap.segments[segmentId];
|
||||||
|
|
||||||
|
var interactionMs = 0;
|
||||||
|
if (v && v.ui && v.ui.interactionZones) {
|
||||||
|
for (z of v.ui.interactionZones) {
|
||||||
|
var startMs = z[0];
|
||||||
|
var stopMs = z[1];
|
||||||
|
if (ms < startMs)
|
||||||
|
interactionMs = startMs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interactionMs) {
|
||||||
|
seek(interactionMs);
|
||||||
|
} else {
|
||||||
|
playSegment(nextSegment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function jumpBack() {
|
||||||
|
var ms = getCurrentMs();
|
||||||
|
var segmentId = getSegmentId(ms);
|
||||||
|
var startMs = getSegmentMs(segmentId);
|
||||||
|
var previousSegment = getSegmentId(startMs - 1000);
|
||||||
|
console.log('jumpBack from-to', segmentId, previousSegment);
|
||||||
|
playSegment(previousSegment);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleFullScreen() {
|
||||||
|
console.log('toggleFullScreen');
|
||||||
|
var c = document.getElementById("c");
|
||||||
|
if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) {
|
||||||
|
if (c.requestFullscreen) {
|
||||||
|
c.requestFullscreen();
|
||||||
|
} else if (c.msRequestFullscreen) {
|
||||||
|
c.msRequestFullscreen();
|
||||||
|
} else if (c.mozRequestFullScreen) {
|
||||||
|
c.mozRequestFullScreen();
|
||||||
|
} else if (c.webkitRequestFullscreen) {
|
||||||
|
c.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (document.exitFullscreen) {
|
||||||
|
document.exitFullscreen();
|
||||||
|
} else if (document.msExitFullscreen) {
|
||||||
|
document.msExitFullscreen();
|
||||||
|
} else if (document.mozCancelFullScreen) {
|
||||||
|
document.mozCancelFullScreen();
|
||||||
|
} else if (document.webkitExitFullscreen) {
|
||||||
|
document.webkitExitFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function togglePlayPause() {
|
||||||
|
var v = document.getElementById("video");
|
||||||
|
if (v.paused) v.play();
|
||||||
|
else v.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onload() {
|
||||||
|
var video_selector = document.getElementById("video");
|
||||||
|
var file_selector = document.getElementById("file-selector");
|
||||||
|
if (video_selector.getAttribute("src") == '') {
|
||||||
|
console.log('no video')
|
||||||
|
file_selector.style.display = 'table';
|
||||||
|
document.getElementById("wrapper-video").style.display = 'none';
|
||||||
|
}
|
||||||
|
document.getElementById('fileinput').addEventListener('change', function () {
|
||||||
|
var file = this.files[0];
|
||||||
|
// This code is only for demo ...
|
||||||
|
var fileUrl = URL.createObjectURL(file)
|
||||||
|
console.log(file);
|
||||||
|
console.log(fileUrl)
|
||||||
|
video_selector.src = fileUrl;
|
||||||
|
file_selector.style.display = 'none';
|
||||||
|
document.getElementById("wrapper-video").style.display = 'block';
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
video_selector.ontimeupdate = ontimeupdate;
|
||||||
|
|
||||||
|
var c = document.getElementById("c");
|
||||||
|
c.ondblclick = toggleFullScreen;
|
||||||
|
c.onclick = function () {
|
||||||
|
// can't togglePlayPause here, choice buttons stop the video
|
||||||
|
// should use preventdefault or something
|
||||||
|
// use spacebar for now
|
||||||
|
// mind that autoplay is disabled in latest chrome, so play after click
|
||||||
|
document.getElementById("video").play();
|
||||||
|
};
|
||||||
|
|
||||||
|
document.onkeypress = function (e) {
|
||||||
|
if (e.code == 'KeyF')
|
||||||
|
toggleFullScreen();
|
||||||
|
if (e.code == 'KeyR')
|
||||||
|
playSegment(0);
|
||||||
|
if (e.code == 'Space')
|
||||||
|
togglePlayPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.onkeydown = function (evt) {
|
||||||
|
var v = document.getElementById("video");
|
||||||
|
|
||||||
|
if (evt.key == 'ArrowLeft') {
|
||||||
|
jumpBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evt.key == 'ArrowRight') {
|
||||||
|
jumpForward();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (location.hash) {
|
||||||
|
var segmentId = location.hash.slice(1);
|
||||||
|
playSegment(segmentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function seek(ms) {
|
||||||
|
clearTimeout(timerId);
|
||||||
|
console.log('seek', ms);
|
||||||
|
momentSelected = null;
|
||||||
|
document.getElementById("video").currentTime = ms / 1000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function choice(choiceId, text, id) {
|
||||||
|
var segmentId = findSegment(choiceId);
|
||||||
|
console.log('choice', choiceId, 'nextSegment', segmentId);
|
||||||
|
applyImpression(globalChoices[id]);
|
||||||
|
setNextSegment(segmentId, text);
|
||||||
|
momentSelected = choiceId;
|
||||||
|
addChoices(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyImpression(obj) {
|
||||||
|
if (!obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
impressionData = obj.impressionData;
|
||||||
|
|
||||||
|
if (impressionData && impressionData.type == 'userState') {
|
||||||
|
for (const [variable, value] of Object.entries(impressionData.data.persistent)) {
|
||||||
|
console.log('persistentState set', variable, '=', value);
|
||||||
|
persistentState[variable] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyPlaybackImpression(segmentId) {
|
||||||
|
let moments = momentsBySegment[segmentId];
|
||||||
|
|
||||||
|
if (!moments) {
|
||||||
|
console.log('warning - no moments');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (moment of moments) {
|
||||||
|
if (moment.type != 'notification:playbackImpression') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyImpression(moment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function playSegment(segmentId) {
|
||||||
|
clearTimeout(timerId);
|
||||||
|
if (!segmentId || segmentId == "undefined")
|
||||||
|
segmentId = '1A';
|
||||||
|
console.log('playSegment', segmentId);
|
||||||
|
applyPlaybackImpression(segmentId);
|
||||||
|
location.hash = segmentId;
|
||||||
|
document.title = 'Bandersnatch - Chapter ' + segmentId;
|
||||||
|
var ms = getSegmentMs(segmentId);
|
||||||
|
seek(ms);
|
||||||
|
}
|
229
assets/styles.css
Normal file
229
assets/styles.css
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
body {
|
||||||
|
margin: auto;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
background: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
video {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
margin: auto;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
font: 20px sans-serif;
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: space-around;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
list-style-type: none;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
background: #0403031f;
|
||||||
|
display: table;
|
||||||
|
text-align: center;
|
||||||
|
position: absolute;
|
||||||
|
width: 50%;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li:first-child {
|
||||||
|
background: #ffffff45;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#progress {
|
||||||
|
background-color: #ff00007d;
|
||||||
|
width: 0%;
|
||||||
|
height: 29px;
|
||||||
|
margin: auto;
|
||||||
|
border-radius: 4px;
|
||||||
|
position: relative;
|
||||||
|
top: 0;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#choiceCaption,
|
||||||
|
#interactionZones,
|
||||||
|
#nextSegments,
|
||||||
|
#nextSegment {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* comment to show links */
|
||||||
|
a {
|
||||||
|
color: #ffffffa6;
|
||||||
|
text-decoration: none;
|
||||||
|
text-shadow: 0px 0px 10px #000;
|
||||||
|
animation: choices 3s forwards;
|
||||||
|
font-size: 4em;
|
||||||
|
/* display: block; */
|
||||||
|
/* margin: 50%; */
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:active,
|
||||||
|
a:focus {
|
||||||
|
color: white;
|
||||||
|
text-decoration: underline solid white
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes choices {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
video::cue {
|
||||||
|
font: 60% sans-serif;
|
||||||
|
color: white;
|
||||||
|
background: none;
|
||||||
|
text-shadow: 0px 0px 10px #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#c {
|
||||||
|
margin: auto;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#c:-webkit-full-screen {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#c:-moz-full-screen {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#c:-ms-fullscreen {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#c:fullscreen {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
section[role="banner"] {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrapper-video {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrapper-video video {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
min-width: 50%;
|
||||||
|
min-height: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
video::-webkit-media-controls-panel {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#file-selector {
|
||||||
|
display: none;
|
||||||
|
background: #00000073;
|
||||||
|
width: 100%;
|
||||||
|
position: fixed;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: table;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-area {
|
||||||
|
position: relative;
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-area input[type=file] {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
opacity: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-area .file-dummy {
|
||||||
|
width: 100%;
|
||||||
|
padding: 50px 30px;
|
||||||
|
border: 2px dashed #ccc;
|
||||||
|
background-color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
transition: background 0.3s ease-in-out;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 50%;
|
||||||
|
height: 25%;
|
||||||
|
font-size: 8em;
|
||||||
|
padding: 9%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-area .file-dummy .success {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-area:hover .file-dummy {
|
||||||
|
border: 2px dashed #1abc9c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-area input[type=file]:valid+.file-dummy {
|
||||||
|
border-color: #1abc9c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-area input[type=file]:valid+.file-dummy .success {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-area input[type=file]:valid+.file-dummy .default {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
49
index.html
Normal file
49
index.html
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<title>Bandersnatch Interactive</title>
|
||||||
|
<script src="assets/bandersnatch.js"></script>
|
||||||
|
<script src="assets/SegmentMap.js"></script>
|
||||||
|
<script src="assets/scripts.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="assets/styles.css">
|
||||||
|
<link </head> <body onload=onload()>
|
||||||
|
<div>
|
||||||
|
<div id="c">
|
||||||
|
<section role="banner">
|
||||||
|
<div id="file-selector">
|
||||||
|
<div class="file-area">
|
||||||
|
<input id="fileinput" type="file">
|
||||||
|
<div class="file-dummy">
|
||||||
|
<span class="default">Select video file </span>
|
||||||
|
<span class="success">Great, your file is selected</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="wrapper-video">
|
||||||
|
<video id="video" src="">
|
||||||
|
|
||||||
|
</video>
|
||||||
|
<div class="controls">
|
||||||
|
<div id="choiceCaption"></div>
|
||||||
|
<div id="progress"></div>
|
||||||
|
<div class="buttons">
|
||||||
|
<ul id="choices"></ul>
|
||||||
|
<ul id="interactionZones"></ul>
|
||||||
|
<ul id="nextSegments"></ul>
|
||||||
|
<ul id="nextSegment"></ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Loading…
Add table
Add a link
Reference in a new issue