@stremio/stremio-video 0.0.73 → 0.0.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stremio/stremio-video",
3
- "version": "0.0.73",
3
+ "version": "0.0.75",
4
4
  "description": "Abstraction layer on top of different media players",
5
5
  "author": "Smart Code OOD",
6
6
  "main": "src/index.js",
@@ -401,8 +401,8 @@ function HTMLVideo(options) {
401
401
  .find(function(track) {
402
402
  return track.id === propValue;
403
403
  });
404
+ onPropChanged('selectedSubtitlesTrackId');
404
405
  if (selecterdSubtitlesTrack) {
405
- onPropChanged('selectedSubtitlesTrackId');
406
406
  events.emit('subtitlesTrackLoaded', selecterdSubtitlesTrack);
407
407
  }
408
408
  }
@@ -1,9 +1,7 @@
1
1
  var VTTJS = require('vtt.js');
2
- var binarySearchUpperBound = require('./binarySearchUpperBound');
3
2
 
4
- function render(cuesByTime, time) {
3
+ function render(cuesByTime, timeIndex) {
5
4
  var nodes = [];
6
- var timeIndex = binarySearchUpperBound(cuesByTime.times, time);
7
5
  if (timeIndex !== -1) {
8
6
  var cuesForTime = cuesByTime[cuesByTime.times[timeIndex]];
9
7
  for (var i = 0; i < cuesForTime.length; i++) {
@@ -3,6 +3,7 @@ var cloneDeep = require('lodash.clonedeep');
3
3
  var deepFreeze = require('deep-freeze');
4
4
  var Color = require('color');
5
5
  var ERROR = require('../error');
6
+ var binarySearchUpperBound = require('./binarySearchUpperBound');
6
7
  var subtitlesParser = require('./subtitlesParser');
7
8
  var subtitlesRenderer = require('./subtitlesRenderer');
8
9
  var subtitlesConverter = require('./subtitlesConverter');
@@ -40,8 +41,15 @@ function withHTMLSubtitles(Video) {
40
41
  containerElement.appendChild(subtitlesElement);
41
42
 
42
43
  var videoState = {
43
- time: null
44
+ time: null,
45
+ paused: false,
46
+ buffering: false,
47
+ lastSyncAt: null,
48
+ playbackSpeed: 1
44
49
  };
50
+ var rafId = null;
51
+ var lastTimeIndex = null;
52
+ var forceRender = false;
45
53
  var cuesByTime = null;
46
54
  var events = new EventEmitter();
47
55
  var destroyed = false;
@@ -67,18 +75,62 @@ function withHTMLSubtitles(Video) {
67
75
  extraSubtitlesOpacity: false
68
76
  };
69
77
 
78
+ function getCurrentTime() {
79
+ if (videoState.time === null || !isFinite(videoState.time)) {
80
+ return null;
81
+ }
82
+ if (videoState.paused || videoState.buffering || videoState.lastSyncAt === null) {
83
+ return videoState.time;
84
+ }
85
+ return videoState.time + (Date.now() - videoState.lastSyncAt) * videoState.playbackSpeed;
86
+ }
87
+ function startRenderLoop() {
88
+ if (rafId !== null) {
89
+ return;
90
+ }
91
+ (function loop() {
92
+ renderSubtitles();
93
+ rafId = requestAnimationFrame(loop);
94
+ })();
95
+ }
96
+ function stopRenderLoop() {
97
+ if (rafId !== null) {
98
+ cancelAnimationFrame(rafId);
99
+ rafId = null;
100
+ }
101
+ }
70
102
  function renderSubtitles() {
103
+ var time = getCurrentTime();
104
+
105
+ if (cuesByTime === null || time === null) {
106
+ if (lastTimeIndex !== null) {
107
+ while (subtitlesElement.hasChildNodes()) {
108
+ subtitlesElement.removeChild(subtitlesElement.lastChild);
109
+ }
110
+ lastTimeIndex = null;
111
+ }
112
+ forceRender = false;
113
+ return;
114
+ }
115
+
116
+ var timeIndex = binarySearchUpperBound(cuesByTime.times, time - delay);
117
+ if (timeIndex === lastTimeIndex && !forceRender) {
118
+ return;
119
+ }
120
+ lastTimeIndex = timeIndex;
121
+ forceRender = false;
122
+
71
123
  while (subtitlesElement.hasChildNodes()) {
72
124
  subtitlesElement.removeChild(subtitlesElement.lastChild);
73
125
  }
74
126
 
75
- if (cuesByTime === null || videoState.time === null || !isFinite(videoState.time)) {
127
+ if (timeIndex === -1) {
76
128
  return;
77
129
  }
78
130
 
79
131
  subtitlesElement.style.bottom = offset + '%';
80
132
  subtitlesElement.style.opacity = opacity;
81
- subtitlesRenderer.render(cuesByTime, videoState.time - delay).forEach(function(cueNode) {
133
+ subtitlesRenderer.render(cuesByTime, timeIndex).forEach(function(cueNode) {
82
134
  cueNode.style.display = 'inline-block';
83
135
  cueNode.style.padding = '0.2em';
84
136
  cueNode.style.whiteSpace = 'pre-wrap';
@@ -101,7 +153,37 @@ function withHTMLSubtitles(Video) {
101
153
  switch (propName) {
102
154
  case 'time': {
103
155
  videoState.time = propValue;
104
- renderSubtitles();
156
+ videoState.lastSyncAt = Date.now();
157
+ break;
158
+ }
159
+ case 'paused': {
160
+ if (propValue && !videoState.paused && !videoState.buffering && videoState.lastSyncAt !== null && videoState.time !== null) {
161
+ videoState.time = videoState.time + (Date.now() - videoState.lastSyncAt) * videoState.playbackSpeed;
162
+ videoState.lastSyncAt = Date.now();
163
+ } else if (!propValue && videoState.paused) {
164
+ videoState.lastSyncAt = Date.now();
165
+ }
166
+ videoState.paused = propValue;
167
+ break;
168
+ }
169
+ case 'buffering': {
170
+ if (propValue && !videoState.buffering && !videoState.paused && videoState.lastSyncAt !== null && videoState.time !== null) {
171
+ videoState.time = videoState.time + (Date.now() - videoState.lastSyncAt) * videoState.playbackSpeed;
172
+ videoState.lastSyncAt = Date.now();
173
+ } else if (!propValue && videoState.buffering) {
174
+ videoState.lastSyncAt = Date.now();
175
+ }
176
+ videoState.buffering = propValue;
177
+ break;
178
+ }
179
+ case 'playbackSpeed': {
180
+ if (propValue !== null && isFinite(propValue)) {
181
+ if (!videoState.paused && !videoState.buffering && videoState.lastSyncAt !== null && videoState.time !== null) {
182
+ videoState.time = videoState.time + (Date.now() - videoState.lastSyncAt) * videoState.playbackSpeed;
183
+ videoState.lastSyncAt = Date.now();
184
+ }
185
+ videoState.playbackSpeed = propValue;
186
+ }
105
187
  break;
106
188
  }
107
189
  }
@@ -218,12 +300,18 @@ function withHTMLSubtitles(Video) {
218
300
  function setProp(propName, propValue) {
219
301
  switch (propName) {
220
302
  case 'selectedExtraSubtitlesTrackId': {
303
+ if (propValue !== null && selectedTrackId === propValue) {
304
+ return true;
305
+ }
221
306
  cuesByTime = null;
222
307
  selectedTrackId = null;
223
308
  delay = null;
224
309
  var selectedTrack = tracks.find(function(track) {
225
310
  return track.id === propValue;
226
311
  });
312
+ if (!selectedTrack) {
313
+ stopRenderLoop();
314
+ }
227
315
  if (selectedTrack) {
228
316
  selectedTrackId = selectedTrack.id;
229
317
  delay = 0;
@@ -269,7 +357,7 @@ function withHTMLSubtitles(Video) {
269
357
  }
270
358
 
271
359
  cuesByTime = result;
272
- renderSubtitles();
360
+ startRenderLoop();
273
361
  events.emit('extraSubtitlesTrackLoaded', selectedTrack);
274
362
  })
275
363
  .catch(function(error) {
@@ -299,6 +387,7 @@ function withHTMLSubtitles(Video) {
299
387
  case 'extraSubtitlesDelay': {
300
388
  if (selectedTrackId !== null && propValue !== null && isFinite(propValue)) {
301
389
  delay = parseInt(propValue, 10);
390
+ forceRender = true;
302
391
  renderSubtitles();
303
392
  onPropChanged('extraSubtitlesDelay');
304
393
  }
@@ -308,6 +397,7 @@ function withHTMLSubtitles(Video) {
308
397
  case 'extraSubtitlesSize': {
309
398
  if (propValue !== null && isFinite(propValue)) {
310
399
  size = Math.max(0, parseInt(propValue, 10));
400
+ forceRender = true;
311
401
  renderSubtitles();
312
402
  onPropChanged('extraSubtitlesSize');
313
403
  }
@@ -317,6 +407,7 @@ function withHTMLSubtitles(Video) {
317
407
  case 'extraSubtitlesOffset': {
318
408
  if (propValue !== null && isFinite(propValue)) {
319
409
  offset = Math.max(0, Math.min(100, parseInt(propValue, 10)));
410
+ forceRender = true;
320
411
  renderSubtitles();
321
412
  onPropChanged('extraSubtitlesOffset');
322
413
  }
@@ -332,6 +423,7 @@ function withHTMLSubtitles(Video) {
332
423
  console.error('withHTMLSubtitles', error);
333
424
  }
334
425
 
426
+ forceRender = true;
335
427
  renderSubtitles();
336
428
  onPropChanged('extraSubtitlesTextColor');
337
429
  }
@@ -347,6 +439,7 @@ function withHTMLSubtitles(Video) {
347
439
  console.error('withHTMLSubtitles', error);
348
440
  }
349
441
 
442
+ forceRender = true;
350
443
  renderSubtitles();
351
444
  onPropChanged('extraSubtitlesBackgroundColor');
352
445
  }
@@ -362,6 +455,7 @@ function withHTMLSubtitles(Video) {
362
455
  console.error('withHTMLSubtitles', error);
363
456
  }
364
457
 
458
+ forceRender = true;
365
459
  renderSubtitles();
366
460
  onPropChanged('extraSubtitlesOutlineColor');
367
461
  }
@@ -377,6 +471,7 @@ function withHTMLSubtitles(Video) {
377
471
  console.error('withHTMLSubtitles', error);
378
472
  }
379
473
 
474
+ forceRender = true;
380
475
  renderSubtitles();
381
476
  onPropChanged('extraSubtitlesOpacity');
382
477
  }
@@ -450,6 +545,8 @@ function withHTMLSubtitles(Video) {
450
545
  return false;
451
546
  }
452
547
  case 'unload': {
548
+ stopRenderLoop();
549
+ lastTimeIndex = null;
453
550
  cuesByTime = null;
454
551
  tracks = [];
455
552
  selectedTrackId = null;