diff --git a/src/routes/Player/Video/stremio-video/HTMLSubtitles.js b/src/routes/Player/Video/stremio-video/HTMLSubtitles.js index 75d6f8146..4697d41ae 100644 --- a/src/routes/Player/Video/stremio-video/HTMLSubtitles.js +++ b/src/routes/Player/Video/stremio-video/HTMLSubtitles.js @@ -1,16 +1,16 @@ var EventEmitter = require('events'); var subtitleUtils = require('./utils/subtitles'); -var HTMLSubtitles = function(containerElement) { +function HTMLSubtitles(containerElement) { if (!(containerElement instanceof HTMLElement)) { throw new Error('Instance of HTMLElement required as a first argument'); } var self = this; - var events = new EventEmitter(); var destroyed = false; + var events = new EventEmitter(); var tracks = Object.freeze([]); - var cues = Object.freeze({}); + var cuesByTime = null; var selectedTrackId = null; var delay = 0; var stylesElement = document.createElement('style'); @@ -51,10 +51,10 @@ var HTMLSubtitles = function(containerElement) { return Object.freeze(tracks.slice()); case 'selectedTrackId': return selectedTrackId; - case 'delay': - return delay; case 'size': return parseFloat(stylesElement.sheet.cssRules[subtitleStylesIndex].style.fontSize); + case 'delay': + return delay; case 'darkBackground': return subtitlesElement.classList.contains('dark-background'); default: @@ -63,7 +63,7 @@ var HTMLSubtitles = function(containerElement) { case 'setProp': switch (arguments[1]) { case 'selectedTrackId': - cues = Object.freeze({}); + cuesByTime = null; selectedTrackId = null; delay = 0; for (var i = 0; i < tracks.length; i++) { @@ -82,7 +82,7 @@ var HTMLSubtitles = function(containerElement) { }) .then(function(text) { if (typeof text === 'string' && selectedTrackId === track.id) { - cues = subtitleUtils.parse(text); + cuesByTime = subtitleUtils.parse(text); events.emit('load', Object.freeze({ track: track })); @@ -98,16 +98,16 @@ var HTMLSubtitles = function(containerElement) { } } return; - case 'delay': - if (!isNaN(arguments[2])) { - delay = parseFloat(arguments[2]); - } - return; case 'size': if (!isNaN(arguments[2])) { stylesElement.sheet.cssRules[subtitleStylesIndex].style.fontSize = parseFloat(arguments[2]) + 'pt'; } return; + case 'delay': + if (!isNaN(arguments[2])) { + delay = parseFloat(arguments[2]); + } + return; case 'darkBackground': if (arguments[2]) { subtitlesElement.classList.add('dark-background'); @@ -149,7 +149,7 @@ var HTMLSubtitles = function(containerElement) { return; case 'clearTracks': tracks = Object.freeze([]); - cues = Object.freeze({}); + cuesByTime = null; selectedTrackId = null; delay = 0; return; @@ -158,14 +158,14 @@ var HTMLSubtitles = function(containerElement) { subtitlesElement.removeChild(subtitlesElement.lastChild); } - if (isNaN(arguments[2]) || !Array.isArray(cues.times)) { + if (isNaN(arguments[2]) || cuesByTime === null) { return; } var time = arguments[2] + delay; - var cuesForTime = subtitleUtils.cuesForTime(cues, time); + var cuesForTime = subtitleUtils.cuesForTime(cuesByTime, time); for (var i = 0; i < cuesForTime.length; i++) { - var cueNode = subtitleUtils.render(cuesForTime[i]); + var cueNode = subtitleUtils.render(cuesForTime[i].text); cueNode.classList.add('cue'); subtitlesElement.append(cueNode, document.createElement('br')); } diff --git a/src/routes/Player/Video/stremio-video/utils/subtitles.js b/src/routes/Player/Video/stremio-video/utils/subtitles.js index aaa4e1f38..36286165d 100644 --- a/src/routes/Player/Video/stremio-video/utils/subtitles.js +++ b/src/routes/Player/Video/stremio-video/utils/subtitles.js @@ -26,60 +26,67 @@ function binarySearchUpperBound(array, value) { function parse(text) { var nativeVTTCue = VTTCue; global.VTTCue = VTTJS.VTTCue; - var cues = []; - var cuesForTime = {}; var parser = new VTTJS.WebVTT.Parser(window, VTTJS.WebVTT.StringDecoder()); + global.VTTCue = nativeVTTCue; + var cues = []; + var cuesByTime = {}; parser.oncue = function(c) { - var cue = { + var cue = Object.freeze({ startTime: (c.startTime * 1000) | 0, endTime: (c.endTime * 1000) | 0, text: c.text - }; + }); cues.push(cue); - cuesForTime[cue.startTime] = cuesForTime[cue.startTime] || []; - cuesForTime[cue.endTime] = cuesForTime[cue.endTime] || []; + cuesByTime[cue.startTime] = cuesByTime[cue.startTime] || []; + cuesByTime[cue.endTime] = cuesByTime[cue.endTime] || []; }; parser.parse(text); parser.flush(); - cuesForTime.times = Object.keys(cuesForTime) + cuesByTime.times = Object.keys(cuesByTime) .map(function(time) { return parseInt(time); }) .sort(function(t1, t2) { return t1 - t2; }); + Object.freeze(cues); + Object.freeze(cuesByTime); + Object.freeze(cuesByTime.times); for (var i = 0; i < cues.length; i++) { - cuesForTime[cues[i].startTime].push(cues[i]); - var startTimeIndex = binarySearchUpperBound(cuesForTime.times, cues[i].startTime); - for (var j = startTimeIndex + 1; j < cuesForTime.times.length; j++) { - if (cues[i].endTime <= cuesForTime.times[j]) { + cuesByTime[cues[i].startTime].push(cues[i]); + var startTimeIndex = binarySearchUpperBound(cuesByTime.times, cues[i].startTime); + for (var j = startTimeIndex + 1; j < cuesByTime.times.length; j++) { + if (cues[i].endTime <= cuesByTime.times[j]) { break; } - cuesForTime[cuesForTime.times[j]].push(cues[i]); + cuesByTime[cuesByTime.times[j]].push(cues[i]); } } - for (var i = 0; i < cuesForTime.times.length; i++) { - cuesForTime[cuesForTime.times[i]].sort(function(c1, c2) { + + for (var i = 0; i < cuesByTime.times.length; i++) { + cuesByTime[cuesByTime.times[i]].sort(function(c1, c2) { return c1.startTime - c2.startTime || c1.endTime - c2.endTime; }); + + Object.freeze(cuesByTime[cuesByTime.times[i]]); } - global.VTTCue = nativeVTTCue; - return Object.freeze(cuesForTime); + + return cuesByTime; } -function cuesForTime(cues, time) { - var index = binarySearchUpperBound(cues.times, time); - return index !== -1 ? cues[cues.times[index]] : []; +function cuesForTime(cuesByTime, time) { + var index = binarySearchUpperBound(cuesByTime.times, time); + return index !== -1 ? cuesByTime[cuesByTime.times[index]] : Object.freeze([]); } -function render(cue) { - return VTTJS.WebVTT.convertCueToDOMTree(window, cue.text); +function render(text) { + return VTTJS.WebVTT.convertCueToDOMTree(window, text); } -module.exports = { - parse, - cuesForTime, - render -}; +module.exports = Object.freeze({ + parse: parse, + cuesForTime: cuesForTime, + render: render +});