first commit

This commit is contained in:
Imanoel Ali 2019-01-17 02:28:27 +03:30
commit 08e385231b
8 changed files with 33543 additions and 0 deletions

2
.gitignore vendored Normal file
View 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
View file

@ -0,0 +1 @@
# BandersnatchInteractive

7131
assets/SegmentMap.js Normal file

File diff suppressed because it is too large Load diff

742
assets/SubPlayerJS.js Normal file
View 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

File diff suppressed because it is too large Load diff

430
assets/scripts.js Normal file
View 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
View 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
View 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>