mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-11 17:15:48 +00:00
HTMLSubtitles uses simpler API
This commit is contained in:
parent
8ff4990c2f
commit
d22e42875b
3 changed files with 180 additions and 158 deletions
|
|
@ -35,10 +35,10 @@ class Video extends Component {
|
|||
if (this.video === null || this.video.constructor !== Video) {
|
||||
this.dispatch('command', 'destroy');
|
||||
this.video = new Video(this.containerRef.current);
|
||||
this.video.on('ended', this.props.onEnded);
|
||||
this.video.on('error', this.props.onError);
|
||||
this.video.on('propValue', this.props.onPropValue);
|
||||
this.video.on('propChanged', this.props.onPropChanged);
|
||||
this.video.addListener('ended', this.props.onEnded);
|
||||
this.video.addListener('error', this.props.onError);
|
||||
this.video.addListener('propValue', this.props.onPropValue);
|
||||
this.video.addListener('propChanged', this.props.onPropChanged);
|
||||
this.video.constructor.manifest.props.forEach((propName) => {
|
||||
this.dispatch('observeProp', propName);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@ var subtitleUtils = require('./utils/subtitles');
|
|||
|
||||
var HTMLSubtitles = function(containerElement) {
|
||||
if (!(containerElement instanceof HTMLElement)) {
|
||||
throw new Error('Instance of HTMLElement required as a first argument to HTMLSubtitles');
|
||||
throw new Error('Instance of HTMLElement required as a first argument');
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var events = new EventEmitter();
|
||||
var destroyed = false;
|
||||
var tracks = Object.freeze([]);
|
||||
var cues = Object.freeze({});
|
||||
var selectedTrackId = null;
|
||||
|
|
@ -21,156 +23,169 @@ var HTMLSubtitles = function(containerElement) {
|
|||
containerElement.appendChild(subtitlesElement);
|
||||
subtitlesElement.classList.add('subtitles');
|
||||
|
||||
Object.defineProperty(this, 'tracks', {
|
||||
configurable: false,
|
||||
enumerable: true,
|
||||
get: function() { return Object.freeze(tracks.slice()); }
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'selectedTrackId', {
|
||||
configurable: false,
|
||||
enumerable: true,
|
||||
get: function() { return selectedTrackId; },
|
||||
set: function(nextSelectedTrackId) {
|
||||
cues = Object.freeze({});
|
||||
selectedTrackId = null;
|
||||
delay = 0;
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
var track = tracks[i];
|
||||
if (track.id === nextSelectedTrackId) {
|
||||
selectedTrackId = track.id;
|
||||
fetch(track.url)
|
||||
.then(function(resp) {
|
||||
return resp.text();
|
||||
})
|
||||
.catch(function() {
|
||||
events.emit('error', Object.freeze({
|
||||
code: 70,
|
||||
track: track
|
||||
}));
|
||||
})
|
||||
.then(function(text) {
|
||||
if (typeof text === 'string' && selectedTrackId === track.id) {
|
||||
cues = subtitleUtils.parse(text);
|
||||
events.emit('load', Object.freeze({
|
||||
track: track
|
||||
}));
|
||||
}
|
||||
})
|
||||
.catch(function() {
|
||||
events.emit('error', Object.freeze({
|
||||
code: 71,
|
||||
track: track
|
||||
}));
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'delay', {
|
||||
configurable: false,
|
||||
enumerable: true,
|
||||
get: function() { return delay; },
|
||||
set: function(nextDelay) {
|
||||
if (!isNaN(nextDelay)) {
|
||||
delay = parseFloat(nextDelay);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'size', {
|
||||
configurable: false,
|
||||
enumerable: true,
|
||||
get: function() { return parseFloat(stylesElement.sheet.cssRules[subtitleStylesIndex].style.fontSize); },
|
||||
set: function(nextSize) {
|
||||
if (!isNaN(nextSize)) {
|
||||
stylesElement.sheet.cssRules[subtitleStylesIndex].style.fontSize = parseFloat(nextSize) + 'pt';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'darkBackground', {
|
||||
configurable: false,
|
||||
enumerable: true,
|
||||
get: function() { return subtitlesElement.classList.contains('dark-background'); },
|
||||
set: function(nextDarkBackground) {
|
||||
if (!!nextDarkBackground) {
|
||||
subtitlesElement.classList.add('dark-background');
|
||||
} else {
|
||||
subtitlesElement.classList.remove('dark-background');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.addListener = function(eventName, listener) {
|
||||
if (destroyed) {
|
||||
throw new Error('Unable to add ' + eventName + ' listener');
|
||||
}
|
||||
|
||||
events.addListener(eventName, listener);
|
||||
};
|
||||
|
||||
this.removeListener = function(eventName, listener) {
|
||||
if (destroyed) {
|
||||
throw new Error('Unable to remove ' + eventName + ' listener');
|
||||
}
|
||||
|
||||
events.removeListener(eventName, listener);
|
||||
};
|
||||
|
||||
this.addTracks = function(extraTracks) {
|
||||
tracks = (Array.isArray(extraTracks) ? extraTracks : [])
|
||||
.filter(function(track) {
|
||||
return track &&
|
||||
typeof track.url === 'string' &&
|
||||
track.url.length > 0 &&
|
||||
typeof track.origin === 'string' &&
|
||||
track.origin.length > 0 &&
|
||||
track.origin !== 'EMBEDDED';
|
||||
})
|
||||
.map(function(track) {
|
||||
return Object.freeze(Object.assign({}, track, {
|
||||
id: track.url
|
||||
}));
|
||||
})
|
||||
.concat(tracks)
|
||||
.filter(function(track, index, tracks) {
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
if (tracks[i].id === track.id) {
|
||||
return i === index;
|
||||
}
|
||||
this.dispatch = function() {
|
||||
if (destroyed) {
|
||||
throw new Error('Unable to dispatch ' + arguments[0]);
|
||||
}
|
||||
|
||||
switch (arguments[0]) {
|
||||
case 'getProp':
|
||||
switch (arguments[1]) {
|
||||
case 'tracks':
|
||||
return Object.freeze(tracks.slice());
|
||||
case 'selectedTrackId':
|
||||
return selectedTrackId;
|
||||
case 'delay':
|
||||
return delay;
|
||||
case 'size':
|
||||
return parseFloat(stylesElement.sheet.cssRules[subtitleStylesIndex].style.fontSize);
|
||||
case 'darkBackground':
|
||||
return subtitlesElement.classList.contains('dark-background');
|
||||
default:
|
||||
throw new Error('getProp not supported: ' + arguments[1]);
|
||||
}
|
||||
case 'setProp':
|
||||
switch (arguments[1]) {
|
||||
case 'selectedTrackId':
|
||||
cues = Object.freeze({});
|
||||
selectedTrackId = null;
|
||||
delay = 0;
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
var track = tracks[i];
|
||||
if (track.id === arguments[2]) {
|
||||
selectedTrackId = track.id;
|
||||
fetch(track.url)
|
||||
.then(function(resp) {
|
||||
return resp.text();
|
||||
})
|
||||
.catch(function() {
|
||||
events.emit('error', Object.freeze({
|
||||
code: 70,
|
||||
track: track
|
||||
}));
|
||||
})
|
||||
.then(function(text) {
|
||||
if (typeof text === 'string' && selectedTrackId === track.id) {
|
||||
cues = subtitleUtils.parse(text);
|
||||
events.emit('load', Object.freeze({
|
||||
track: track
|
||||
}));
|
||||
}
|
||||
})
|
||||
.catch(function() {
|
||||
events.emit('error', Object.freeze({
|
||||
code: 71,
|
||||
track: track
|
||||
}));
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
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 'darkBackground':
|
||||
if (arguments[2]) {
|
||||
subtitlesElement.classList.add('dark-background');
|
||||
} else {
|
||||
subtitlesElement.classList.remove('dark-background');
|
||||
}
|
||||
return;
|
||||
default:
|
||||
throw new Error('setProp not supported: ' + arguments[1]);
|
||||
}
|
||||
case 'command':
|
||||
switch (arguments[1]) {
|
||||
case 'addTracks':
|
||||
tracks = (Array.isArray(arguments[2]) ? arguments[2] : [])
|
||||
.filter(function(track) {
|
||||
return track &&
|
||||
typeof track.url === 'string' &&
|
||||
track.url.length > 0 &&
|
||||
typeof track.origin === 'string' &&
|
||||
track.origin.length > 0 &&
|
||||
track.origin !== 'EMBEDDED';
|
||||
})
|
||||
.map(function(track) {
|
||||
return Object.freeze(Object.assign({}, track, {
|
||||
id: track.url
|
||||
}));
|
||||
})
|
||||
.concat(tracks)
|
||||
.filter(function(track, index, tracks) {
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
if (tracks[i].id === track.id) {
|
||||
return i === index;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
Object.freeze(tracks);
|
||||
};
|
||||
return false;
|
||||
});
|
||||
Object.freeze(tracks);
|
||||
return;
|
||||
case 'clearTracks':
|
||||
tracks = Object.freeze([]);
|
||||
cues = Object.freeze({});
|
||||
selectedTrackId = null;
|
||||
delay = 0;
|
||||
return;
|
||||
case 'updateText':
|
||||
while (subtitlesElement.hasChildNodes()) {
|
||||
subtitlesElement.removeChild(subtitlesElement.lastChild);
|
||||
}
|
||||
|
||||
this.updateTextForTime = function(mediaTime) {
|
||||
while (subtitlesElement.hasChildNodes()) {
|
||||
subtitlesElement.removeChild(subtitlesElement.lastChild);
|
||||
if (isNaN(arguments[2]) || !Array.isArray(cues.times)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var time = arguments[2] + delay;
|
||||
var cuesForTime = subtitleUtils.cuesForTime(cues, time);
|
||||
for (var i = 0; i < cuesForTime.length; i++) {
|
||||
var cueNode = subtitleUtils.render(cuesForTime[i]);
|
||||
cueNode.classList.add('cue');
|
||||
subtitlesElement.append(cueNode, document.createElement('br'));
|
||||
}
|
||||
return;
|
||||
case 'destroy':
|
||||
destroyed = true;
|
||||
events.removeAllListeners();
|
||||
self.dispatch('clearTracks');
|
||||
containerElement.removeChild(stylesElement);
|
||||
containerElement.removeChild(subtitlesElement);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
throw new Error('Invalid dispatch call: ' + Array.from(arguments).map(String));
|
||||
}
|
||||
|
||||
if (isNaN(mediaTime) || !Array.isArray(cues.times)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var time = mediaTime + delay;
|
||||
var cuesForTime = subtitleUtils.cuesForTime(cues, time);
|
||||
for (var i = 0; i < cuesForTime.length; i++) {
|
||||
var cueNode = subtitleUtils.render(cuesForTime[i]);
|
||||
cueNode.classList.add('cue');
|
||||
subtitlesElement.append(cueNode, document.createElement('br'));
|
||||
}
|
||||
};
|
||||
|
||||
this.clearTracks = function() {
|
||||
tracks = Object.freeze([]);
|
||||
cues = Object.freeze({});
|
||||
selectedTrackId = null;
|
||||
delay = 0;
|
||||
};
|
||||
|
||||
this.detachElements = function() {
|
||||
containerElement.removeChild(stylesElement);
|
||||
containerElement.removeChild(subtitlesElement);
|
||||
};
|
||||
|
||||
Object.freeze(this);
|
||||
};
|
||||
|
||||
Object.freeze(HTMLSubtitles);
|
||||
|
||||
module.exports = HTMLSubtitles;
|
||||
|
|
|
|||
|
|
@ -61,38 +61,38 @@ var HTMLVideo = function(containerElement) {
|
|||
return [];
|
||||
}
|
||||
|
||||
return Object.freeze(subtitles.tracks.slice());
|
||||
return subtitles.dispatch('getProp', 'tracks');
|
||||
}
|
||||
function getSelectedSubtitleTrackId() {
|
||||
if (!loaded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return subtitles.selectedTrackId;
|
||||
return subtitles.dispatch('getProp', 'selectedTrackId');
|
||||
}
|
||||
function getSubtitleDelay() {
|
||||
if (!loaded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return subtitles.delay;
|
||||
return subtitles.dispatch('getProp', 'delay');
|
||||
}
|
||||
function getSubtitleSize() {
|
||||
if (destroyed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return subtitles.size;
|
||||
return subtitles.dispatch('getProp', 'size');
|
||||
}
|
||||
function getSubtitleDarkBackground() {
|
||||
if (destroyed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return subtitles.darkBackground;
|
||||
return subtitles.dispatch('getProp', 'darkBackground');
|
||||
}
|
||||
function onError(error) {
|
||||
Object.freeze(error)
|
||||
Object.freeze(error);
|
||||
events.emit('error', error);
|
||||
if (error.critical) {
|
||||
self.dispatch('command', 'stop');
|
||||
|
|
@ -182,8 +182,7 @@ var HTMLVideo = function(containerElement) {
|
|||
events.emit('propChanged', 'subtitleDarkBackground', getSubtitleDarkBackground());
|
||||
}
|
||||
function updateSubtitleText() {
|
||||
var time = getTime();
|
||||
subtitles.updateTextForTime(time);
|
||||
subtitles.dispatch('command', 'updateText', getTime());
|
||||
}
|
||||
function flushArgsQueue() {
|
||||
for (var i = 0; i < dispatchArgsQueue.length; i++) {
|
||||
|
|
@ -193,12 +192,20 @@ var HTMLVideo = function(containerElement) {
|
|||
dispatchArgsQueue = [];
|
||||
}
|
||||
|
||||
this.on = function(eventName, listener) {
|
||||
this.addListener = function(eventName, listener) {
|
||||
if (destroyed) {
|
||||
throw new Error('Unable to add ' + eventName + ' listener to destroyed video');
|
||||
throw new Error('Unable to add ' + eventName + ' listener');
|
||||
}
|
||||
|
||||
events.on(eventName, listener);
|
||||
events.addListener(eventName, listener);
|
||||
};
|
||||
|
||||
this.removeListener = function(eventName, listener) {
|
||||
if (destroyed) {
|
||||
throw new Error('Unable to add ' + eventName + ' listener');
|
||||
}
|
||||
|
||||
events.removeListener(eventName, listener);
|
||||
};
|
||||
|
||||
this.dispatch = function() {
|
||||
|
|
@ -274,7 +281,7 @@ var HTMLVideo = function(containerElement) {
|
|||
break;
|
||||
case 'selectedSubtitleTrackId':
|
||||
if (loaded) {
|
||||
subtitles.selectedTrackId = arguments[2];
|
||||
subtitles.dispatch('setProp', 'selectedTrackId', arguments[2]);
|
||||
onSubtitleDelayChanged();
|
||||
onSelectedSubtitleTrackIdChanged();
|
||||
updateSubtitleText();
|
||||
|
|
@ -283,7 +290,7 @@ var HTMLVideo = function(containerElement) {
|
|||
case 'subtitleDelay':
|
||||
if (loaded) {
|
||||
if (!isNaN(arguments[2])) {
|
||||
subtitles.delay = arguments[2];
|
||||
subtitles.dispatch('setProp', 'delay', arguments[2]);
|
||||
onSubtitleDelayChanged();
|
||||
updateSubtitleText();
|
||||
}
|
||||
|
|
@ -291,12 +298,12 @@ var HTMLVideo = function(containerElement) {
|
|||
break;
|
||||
case 'subtitleSize':
|
||||
if (!isNaN(arguments[2])) {
|
||||
subtitles.size = arguments[2];
|
||||
subtitles.dispatch('setProp', 'size', arguments[2]);
|
||||
onSubtitleSizeChanged();
|
||||
}
|
||||
return;
|
||||
case 'subtitleDarkBackground':
|
||||
subtitles.darkBackground = arguments[2];
|
||||
subtitles.dispatch('setProp', 'darkBackground', arguments[2]);
|
||||
onSubtitleDarkBackgroundChanged();
|
||||
return;
|
||||
case 'volume':
|
||||
|
|
@ -313,7 +320,7 @@ var HTMLVideo = function(containerElement) {
|
|||
switch (arguments[1]) {
|
||||
case 'addSubtitleTracks':
|
||||
if (loaded) {
|
||||
subtitles.addTracks(arguments[2]);
|
||||
subtitles.dispatch('command', 'addTracks', arguments[2]);
|
||||
onSubtitleTracksChanged();
|
||||
}
|
||||
break;
|
||||
|
|
@ -332,7 +339,7 @@ var HTMLVideo = function(containerElement) {
|
|||
subtitles.removeListener('load', updateSubtitleText);
|
||||
loaded = false;
|
||||
dispatchArgsQueue = [];
|
||||
subtitles.clearTracks();
|
||||
subtitles.dispatch('command', 'clearTracks');
|
||||
videoElement.removeAttribute('src');
|
||||
videoElement.load();
|
||||
videoElement.currentTime = 0;
|
||||
|
|
@ -383,7 +390,7 @@ var HTMLVideo = function(containerElement) {
|
|||
videoElement.removeEventListener('loadeddata', onBufferingChanged);
|
||||
containerElement.removeChild(videoElement);
|
||||
containerElement.removeChild(stylesElement);
|
||||
subtitles.detachElements();
|
||||
subtitles.dispatch('command', 'destroy');
|
||||
return;
|
||||
default:
|
||||
throw new Error('command not supported: ' + arguments[1]);
|
||||
|
|
|
|||
Loading…
Reference in a new issue