From 6d34768b11c466e74511c73ad04feabee94bb604 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sun, 28 Jul 2019 19:36:33 +0000 Subject: [PATCH 01/11] assets/scripts.js: Fix case of unmapped segment Fixes behavior and JS errors during the post-credit studio logos. --- assets/scripts.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/assets/scripts.js b/assets/scripts.js index bf0b652..24ad557 100644 --- a/assets/scripts.js +++ b/assets/scripts.js @@ -169,10 +169,10 @@ function addZones(segmentId) { let caption = 'currentSegment(' + segmentId + ')'; addItem(ul, caption, 'javascript:playSegment("' + segmentId + '")'); - var v = segmentMap.segments[segmentId]; - if (v && v.ui && v.ui.interactionZones) { + var segment = segmentMap.segments[segmentId]; + if (segment && segment.ui && segment.ui.interactionZones) { var index = 0; - for (var z of v.ui.interactionZones) { + for (var z of segment.ui.interactionZones) { var startMs = z[0]; var stopMs = z[1]; let caption = segmentId + ' interactionZone ' + index; @@ -183,13 +183,15 @@ function addZones(segmentId) { ul = newList("nextSegments"); let defaultSegmentId = null; - for (const [k, v] of Object.entries(segmentMap.segments[segmentId].next)) { - let caption = k; - if (segmentMap.segments[segmentId].defaultNext == k) { - caption = '[' + caption + ']'; - defaultSegmentId = k; + if (segment) { + for (const [k, v] of Object.entries(segment.next)) { + let caption = k; + if (segment.defaultNext == k) { + caption = '[' + caption + ']'; + defaultSegmentId = k; + } + addItem(ul, caption, 'javascript:playSegment("' + k + '")'); } - addItem(ul, caption, 'javascript:playSegment("' + k + '")'); } setNextSegment(defaultSegmentId); } @@ -251,6 +253,7 @@ var lastMoments = []; function ontimeupdate(evt) { var ms = getCurrentMs(); currentSegment = getSegmentId(ms); + let segment = segmentMap.segments[currentSegment]; if (timerId) { clearTimeout(timerId); @@ -318,7 +321,7 @@ function ontimeupdate(evt) { let hash = currentSegment; // Pick the moment which starts closer to the current timestamp. - let bestMomentStart = segmentMap.segments[currentSegment].startTimeMs; + let bestMomentStart = segment ? segment.startTimeMs : 0; for (let k in currentMoments) { let m = currentMoments[k]; if (m.startMs > bestMomentStart) { @@ -333,7 +336,7 @@ function ontimeupdate(evt) { } // ontimeupdate resolution is about a second. Augment it using timer. - let nextEvent = segmentMap.segments[currentSegment].endTimeMs; + let nextEvent = segment ? segment.endTimeMs : 0; for (let k in currentMoments) { let m = currentMoments[k]; if (m.endMs < nextEvent) From 9df4416b2bffac43b75d44a2e2dfbcc8bcfe110a Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sun, 28 Jul 2019 19:38:29 +0000 Subject: [PATCH 02/11] assets/scripts.js: Use consistent variable name --- assets/scripts.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/scripts.js b/assets/scripts.js index 24ad557..6d6a0a6 100644 --- a/assets/scripts.js +++ b/assets/scripts.js @@ -368,10 +368,10 @@ function playNextSegment() { ls[breadcrumb] = lastSegment; segmentTransition = true; - let segment = nextSegment; + let segmentId = nextSegment; nextSegment = null; - if (segment) - return playSegment(segment, true); + if (segmentId) + return playSegment(segmentId, true); return false; } From 9ea55f3c020f8812a6dd10c8f61b8940c15b9dd0 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sun, 28 Jul 2019 21:25:10 +0000 Subject: [PATCH 03/11] assets/scripts.js: Fix recording of breadcrumbs after jumps --- assets/scripts.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/scripts.js b/assets/scripts.js index 6d6a0a6..d8542fe 100644 --- a/assets/scripts.js +++ b/assets/scripts.js @@ -247,6 +247,7 @@ var timerId = 0; var lastMs = 0; var currentSegment; var lastSegment = null; +var prevSegment = null; // for breadcrumbs var segmentTransition = false; var lastMoments = []; @@ -272,6 +273,7 @@ function ontimeupdate(evt) { // Handle segment change if (lastSegment != currentSegment) { console.log('ontimeupdate', lastSegment, '->', currentSegment, ms, msToString(ms), seeked); + prevSegment = lastSegment; lastSegment = currentSegment; if (!seeked) { if (playNextSegment()) { @@ -365,7 +367,7 @@ function playNextSegment() { let breadcrumb = 'breadcrumb_' + nextSegment; if (!(breadcrumb in ls)) - ls[breadcrumb] = lastSegment; + ls[breadcrumb] = prevSegment; segmentTransition = true; let segmentId = nextSegment; From 9bf91b095b7aefd38f8dd5ce1cb0c75418a89ca7 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sun, 28 Jul 2019 21:25:31 +0000 Subject: [PATCH 04/11] assets/scripts.js: Initialize persistent state from data Fixes JS errors when seeking past first choice with empty localStorage. --- assets/scripts.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/assets/scripts.js b/assets/scripts.js index d8542fe..06d0aca 100644 --- a/assets/scripts.js +++ b/assets/scripts.js @@ -34,6 +34,11 @@ var segmentGroups = bv.segmentGroups; // Persistent state var ls = window.localStorage || {}; +if (!('initialized' in ls)) { + for (let k in bv.stateHistory) + ls["persistentState_" + k] = JSON.stringify(bv.stateHistory[k]); + ls['initialized'] = 't'; +} function msToString(ms) { return new Date(ms).toUTCString().split(' ')[4]; From 941c975e360567eff36b0f413a119bfcbe7ce44e Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sun, 28 Jul 2019 21:25:53 +0000 Subject: [PATCH 05/11] assets/scripts.js: Log previous persistentState values --- assets/scripts.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/assets/scripts.js b/assets/scripts.js index 06d0aca..b340dec 100644 --- a/assets/scripts.js +++ b/assets/scripts.js @@ -560,8 +560,9 @@ function choice(choiceIndex) { function applyImpression(impressionData) { if (impressionData && impressionData.type == 'userState') { for (const [variable, value] of Object.entries(impressionData.data.persistent)) { - console.log('persistentState set', variable, '=', value); - ls["persistentState_" + variable] = JSON.stringify(value); + let key = "persistentState_" + variable; + console.log('persistentState set', variable, '=', value, '(was', key in ls ? ls[key] : 'unset', ')'); + ls[key] = JSON.stringify(value); } } } From 3e17def06777ff2c4b35aa4e75cfa2d04ee8d559 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sun, 28 Jul 2019 22:13:59 +0000 Subject: [PATCH 06/11] assets/scripts.js: Fix handling of IDNT segment This segment does not have an end-time, so treat all timestamps past its start time as belonging to it. Fixes #null appearing in hashes and stored locations. --- assets/scripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/scripts.js b/assets/scripts.js index b340dec..0ddbc52 100644 --- a/assets/scripts.js +++ b/assets/scripts.js @@ -115,7 +115,7 @@ function resolveSegmentGroup(sg) { /// There will be exactly one segment for any timestamp within the video file. function getSegmentId(ms) { for (const [k, v] of Object.entries(segmentMap.segments)) { - if (ms >= v.startTimeMs && ms < v.endTimeMs) { + if (ms >= v.startTimeMs && (!v.endTimeMs || ms < v.endTimeMs)) { return k; } } From 927aeac7b44ca600d239594399b836337efc7f5c Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Sun, 28 Jul 2019 22:19:56 +0000 Subject: [PATCH 07/11] assets/scripts.js: Return strings from preconditionToJS Minor correctness fix. --- assets/scripts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/scripts.js b/assets/scripts.js index 0ddbc52..e85408b 100644 --- a/assets/scripts.js +++ b/assets/scripts.js @@ -60,9 +60,9 @@ function preconditionToJS(cond) { } else if (cond[0] == 'eql' && cond.length == 3) { return '(' + cond.slice(1).map(preconditionToJS).join(' == ') + ')'; } else if (cond === false) { - return false; + return 'false'; } else if (cond === true) { - return true; + return 'true'; } else if (typeof cond === 'string') { return JSON.stringify(cond); } else { From 0bccf68de1ed57d0fe20842819de294b31e7276a Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Mon, 29 Jul 2019 00:06:19 +0000 Subject: [PATCH 08/11] assets/scripts.js: Fix non-choice transitions The previous logic was completely wrong. --- assets/scripts.js | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/assets/scripts.js b/assets/scripts.js index e85408b..c78bec4 100644 --- a/assets/scripts.js +++ b/assets/scripts.js @@ -159,16 +159,6 @@ function addItem(ul, text, url) { var nextChoice = -1; var nextSegment = null; -function setNextSegment(segmentId, comment) { - console.log('setNextSegment', segmentId, comment); - nextSegment = segmentId; - nextChoice = -1; - var ul = newList("nextSegment"); - var caption = 'nextSegment: ' + segmentId; - addItem(ul, comment ? caption + ' (' + comment + ')' : caption, - 'javascript:playSegment("' + segmentId + '")'); -} - function addZones(segmentId) { var ul = newList("interactionZones"); let caption = 'currentSegment(' + segmentId + ')'; @@ -187,18 +177,14 @@ function addZones(segmentId) { } ul = newList("nextSegments"); - let defaultSegmentId = null; if (segment) { for (const [k, v] of Object.entries(segment.next)) { let caption = k; - if (segment.defaultNext == k) { + if (segment.defaultNext == k) caption = '[' + caption + ']'; - defaultSegmentId = k; - } addItem(ul, caption, 'javascript:playSegment("' + k + '")'); } } - setNextSegment(defaultSegmentId); } var currentChoiceMoment = null; @@ -280,8 +266,8 @@ function ontimeupdate(evt) { console.log('ontimeupdate', lastSegment, '->', currentSegment, ms, msToString(ms), seeked); prevSegment = lastSegment; lastSegment = currentSegment; - if (!seeked) { - if (playNextSegment()) { + if (!seeked && prevSegment) { + if (playNextSegment(prevSegment)) { // playSegment decided to seek, which means that this // currentSegment is invalid, and a recursive // ontimeupdate invocation should have taken care of @@ -357,7 +343,8 @@ function ontimeupdate(evt) { timerId = setTimeout(ontimeupdate, timeLeft); } -function playNextSegment() { +function playNextSegment(prevSegment) { + let nextSegment = null; if (nextChoice >= 0) { let x = currentChoiceMoment.choices[nextChoice]; if (x.segmentId) @@ -367,19 +354,25 @@ function playNextSegment() { else nextSegment = null; console.log('choice', nextChoice, 'nextSegment', nextSegment); + nextChoice = -1; applyImpression(x.impressionData); } + if (!nextSegment && prevSegment && prevSegment in segmentGroups) + nextSegment = resolveSegmentGroup(prevSegment); + + if (!nextSegment && prevSegment && segmentMap.segments[prevSegment].defaultNext) + nextSegment = segmentMap.segments[prevSegment].defaultNext; + + if (!nextSegment) + return false; + let breadcrumb = 'breadcrumb_' + nextSegment; if (!(breadcrumb in ls)) ls[breadcrumb] = prevSegment; segmentTransition = true; - let segmentId = nextSegment; - nextSegment = null; - if (segmentId) - return playSegment(segmentId, true); - return false; + return playSegment(nextSegment, true); } function jumpForward() { @@ -397,7 +390,7 @@ function jumpForward() { if (interactionMs) { seek(interactionMs); } else { - playNextSegment(); + playNextSegment(segmentId); } } @@ -554,7 +547,7 @@ function choice(choiceIndex) { nextChoice = choiceIndex; newList("choices"); if (!currentChoiceMoment.config.disableImmediateSceneTransition) - playNextSegment(); + playNextSegment(prevSegment); } function applyImpression(impressionData) { From 86939363087f39ecb73e32c1aeb9e17c01a9bb93 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Mon, 29 Jul 2019 01:45:17 +0000 Subject: [PATCH 09/11] Improve HTML - Improve English - Credit all authors - Mention more controls - Slightly improve layout - Remove Google Analytics --- assets/styles.css | 6 ++--- index.html | 63 ++++++++++++++++++----------------------------- 2 files changed, 27 insertions(+), 42 deletions(-) diff --git a/assets/styles.css b/assets/styles.css index 3a8acb7..201cdda 100644 --- a/assets/styles.css +++ b/assets/styles.css @@ -262,7 +262,7 @@ ul.controls-tips { padding: 16px; width: inherit; margin: 0; - margin: 29px auto; + margin: 15px auto; text-align: left; } @@ -271,8 +271,8 @@ h2 { font-size: 1em; } -ul.file-tips li, -ul.controls-tips li { +ul.file-tips > li, +ul.controls-tips > li { font-size: .45em; } diff --git a/index.html b/index.html index 58b9c70..0c79bbb 100644 --- a/index.html +++ b/index.html @@ -34,22 +34,12 @@ Bandersnatch Interactive Player - + content="Stand-alone open-source interactive HTML player for Black Mirror - Bandersnatch" /> + - - - - @@ -60,12 +50,10 @@

- Developed by Mehotkhan ,for - info https://github.com/mehotkhan/BandersnatchInteractive - - ,if you want some donation : my bitcoin wallet : - 1CxaCJbh4VMNicnpktsBVfnVU5xq4QYyHW + Authors: + Original version - joric | + Web page, subtitles - Mehotkhan (donate) | + Many fixes, improvements, and everything else - CyberShadow

@@ -74,31 +62,30 @@ Select Bandersnatch video file ( 5:12:14 ) Great, your file is selected
    -
  • this player only work on google chrome ( on firefox dont work , codec problem,test - other browser ?)
  • -
  • download Bandersnatch video file ( 5:12:14 )
  • -
  • drag it on white box on page :)
  • -
  • Persian , English , Arabic , Spanish , Hebrew ,Portuguese ,Greek ,Turkish ,Polish ,Indonesian - subtitle available , if you want add another language - subtitle , tell me
  • -
  • to change subtitle : right click on video , enable show controls , after that . on - bottom,right you see - ,then you can subtitle section
  • -
  • Note : after change subtitle , disable show controls ,if dont disable it on full - screen video,you cant see - option selector
  • -
  • tested with Black.Mirror.Bandersnatch.2018.720p.WEB-DL.x264.DUAL.mkv file
  • - +
  • This player is known to work only on Google Chrome or Chromium.
  • +
  • Obtain the Bandersnatch video file (duration should be 5:12:14).
    + Tested with Black.Mirror.Bandersnatch.2018.720p.WEB-DL.x264.DUAL.mkv.
  • +
  • Drag it on top of this box (or click here to select it).
  • +
  • Subtitles in many languages are available.
  • +
  • To change the subtitle: +
      +
    1. Right click on the video
    2. +
    3. Enable "Show controls"
    4. +
    5. Click on the in the bottom-right
    6. +
    7. Select the desired subtitle
    8. +
    9. You can now hide the player controls in the same way as step 1-2.
    10. +
  • +
  • Note: do not use the "full screen" button in the video player if you enable "Show controls". + Choice selection interface will not be visible if you do this.
    -
    controls tips
    +
    Keyboard controls
  • F - Toggle fullscreen
  • R - Restart video
  • -
  • - Jump to the next segment (or to the next interaction zone)
  • -
  • - Jump to the previous segment (or interaction zone)
  • +
  • / - Jump to the next / previous segment (or interaction zone)
  • +
  • / - Speed up / slow down playback
  • Space - Toggle play and pause
  • -
@@ -127,8 +114,6 @@ src="subtitle/Czarne.lustro_.Bandersnatch.WEBRip.Netflix.pl.vtt"> - -
From 88c7b5dced6a6c636814ded1c96b1921af8ad591 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Mon, 29 Jul 2019 02:00:18 +0000 Subject: [PATCH 10/11] Add "reset" link in HTML --- assets/scripts.js | 6 ++++++ index.html | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/assets/scripts.js b/assets/scripts.js index c78bec4..621d7c9 100644 --- a/assets/scripts.js +++ b/assets/scripts.js @@ -573,6 +573,12 @@ function playSegment(segmentId, noSeek) { return false; } +function reset() { + ls.clear(); + location.hash = ''; + location.reload(); +} + var lastHash = ''; function playHash(hash) { // console.log('playHash', lastHash, '->', hash); diff --git a/index.html b/index.html index 0c79bbb..6b3de90 100644 --- a/index.html +++ b/index.html @@ -39,6 +39,13 @@ + @@ -54,6 +61,8 @@ Original version - joric | Web page, subtitles - Mehotkhan (donate) | Many fixes, improvements, and everything else - CyberShadow +
+ This player remembers your choices and watch history. Click here to reset and start from scratch.

From 80f4bd0b6e79abe30e3be5f8494e3adaceebb520 Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Fri, 2 Aug 2019 03:06:17 +0000 Subject: [PATCH 11/11] assets/scripts.js: Segment group preconditions override segment preconditions --- assets/scripts.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/assets/scripts.js b/assets/scripts.js index 621d7c9..043ecbd 100644 --- a/assets/scripts.js +++ b/assets/scripts.js @@ -96,10 +96,6 @@ function resolveSegmentGroup(sg) { if (v.segmentGroup) { results.push(resolveSegmentGroup(v.segmentGroup)); } else if (v.segment) { - // TODO: does the included precondition override or - // complement the segment precondition? - if (!checkPrecondition(v.segment)) - continue; results.push(v.segment); } else { if (!checkPrecondition(v))