@stremio/stremio-video 0.0.8 → 0.0.12

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.8",
3
+ "version": "0.0.12",
4
4
  "description": "Abstraction layer on top of different media players",
5
5
  "author": "Smart Code OOD",
6
6
  "main": "src/index.js",
@@ -15,14 +15,15 @@
15
15
  "dependencies": {
16
16
  "deep-freeze": "0.0.1",
17
17
  "eventemitter3": "4.0.7",
18
- "hls.js": "0.14.16",
18
+ "hat": "0.0.3",
19
+ "hls.js": "https://github.com/Stremio/hls.js/releases/download/v1.0.10-patch1/hls.js-1.0.10-patch1.tgz",
19
20
  "lodash.clonedeep": "4.5.0",
20
- "magnet-uri": "5.2.4",
21
+ "magnet-uri": "6.2.0",
21
22
  "url": "0.11.0",
22
23
  "video-name-parser": "1.4.6",
23
- "vtt.js": "0.13.0"
24
+ "vtt.js": "git://github.com/jaruba/vtt.js.git#e4f5f5603730866bacb174a93f51b734c9f29e6a"
24
25
  },
25
26
  "devDependencies": {
26
- "eslint": "7.6.0"
27
+ "eslint": "7.32.0"
27
28
  }
28
29
  }
@@ -63,9 +63,10 @@ function ChromecastSenderVideo(options) {
63
63
  extraSubtitlesShadowColor: false
64
64
  };
65
65
 
66
- function onTransportError(error) {
66
+ function onTransportError(error, message) {
67
67
  events.emit('error', Object.assign({}, ERROR.CHROMECAST_SENDER_VIDEO.MESSAGE_SEND_FAILED, {
68
- error: error
68
+ error: error,
69
+ message: message
69
70
  }));
70
71
  }
71
72
  function onMessage(message) {
@@ -75,14 +76,14 @@ function ChromecastSenderVideo(options) {
75
76
  } catch (error) {
76
77
  events.emit('error', Object.assign({}, ERROR.CHROMECAST_SENDER_VIDEO.INVALID_MESSAGE_RECEIVED, {
77
78
  error: error,
78
- data: message
79
+ message: message
79
80
  }));
80
81
  return;
81
82
  }
82
83
 
83
84
  if (!parsedMessage || typeof parsedMessage.event !== 'string') {
84
85
  events.emit('error', Object.assign({}, ERROR.CHROMECAST_SENDER_VIDEO.INVALID_MESSAGE_RECEIVED, {
85
- data: message
86
+ message: message
86
87
  }));
87
88
  return;
88
89
  }
@@ -146,16 +147,22 @@ function ChromecastSenderVideo(options) {
146
147
  switch (action.type) {
147
148
  case 'observeProp': {
148
149
  observeProp(action.propName);
149
- chromecastTransport.sendMessage(action).catch(onTransportError);
150
+ chromecastTransport.sendMessage(action).catch(function(error) {
151
+ onTransportError(error, action);
152
+ });
150
153
  return;
151
154
  }
152
155
  case 'setProp': {
153
- chromecastTransport.sendMessage(action).catch(onTransportError);
156
+ chromecastTransport.sendMessage(action).catch(function(error) {
157
+ onTransportError(error, action);
158
+ });
154
159
  return;
155
160
  }
156
161
  case 'command': {
157
162
  command(action.commandName, action.commandArgs);
158
- chromecastTransport.sendMessage(action).catch(onTransportError);
163
+ chromecastTransport.sendMessage(action).catch(function(error) {
164
+ onTransportError(error, action);
165
+ });
159
166
  return;
160
167
  }
161
168
  }
@@ -174,7 +181,7 @@ ChromecastSenderVideo.manifest = {
174
181
  external: true,
175
182
  props: ['stream', 'paused', 'time', 'duration', 'buffering', 'buffered', 'volume', 'muted', 'subtitlesTracks', 'selectedSubtitlesTrackId', 'extraSubtitlesTracks', 'selectedExtraSubtitlesTrackId', 'extraSubtitlesDelay', 'extraSubtitlesSize', 'extraSubtitlesOffset', 'extraSubtitlesTextColor', 'extraSubtitlesBackgroundColor', 'extraSubtitlesShadowColor'],
176
183
  commands: ['load', 'unload', 'destroy', 'addExtraSubtitlesTracks'],
177
- events: ['propChanged', 'propValue', 'ended', 'error', 'subtitlesTrackLoaded', 'extraSubtitlesTrackLoaded', 'implementationChanged']
184
+ events: ['propValue', 'propChanged', 'ended', 'error', 'subtitlesTrackLoaded', 'extraSubtitlesTrackLoaded', 'implementationChanged']
178
185
  };
179
186
 
180
187
  module.exports = ChromecastSenderVideo;
@@ -3,6 +3,8 @@ var Hls = require('hls.js');
3
3
  var cloneDeep = require('lodash.clonedeep');
4
4
  var deepFreeze = require('deep-freeze');
5
5
  var ERROR = require('../error');
6
+ var getContentType = require('./getContentType');
7
+ var HLS_CONFIG = require('./hlsConfig');
6
8
 
7
9
  function HTMLVideo(options) {
8
10
  options = options || {};
@@ -151,6 +153,9 @@ function HTMLVideo(options) {
151
153
 
152
154
  return !!videoElement.muted;
153
155
  }
156
+ default: {
157
+ return null;
158
+ }
154
159
  }
155
160
  }
156
161
  function onVideoError() {
@@ -256,7 +261,7 @@ function HTMLVideo(options) {
256
261
  }
257
262
 
258
263
  if (contentType === 'application/vnd.apple.mpegurl' && Hls.isSupported()) {
259
- hls = new Hls();
264
+ hls = new Hls(HLS_CONFIG);
260
265
  hls.loadSource(stream.url);
261
266
  hls.attachMedia(videoElement);
262
267
  } else {
@@ -356,21 +361,6 @@ function HTMLVideo(options) {
356
361
  };
357
362
  }
358
363
 
359
- function getContentType(stream) {
360
- if (!stream || typeof stream.url !== 'string') {
361
- return Promise.reject(new Error('Invalid stream parameter!'));
362
- }
363
-
364
- if (stream.behaviorHints && stream.behaviorHints.headers && typeof stream.behaviorHints.headers['content-type'] === 'string') {
365
- return Promise.resolve(stream.behaviorHints.headers['content-type']);
366
- }
367
-
368
- return fetch(stream.url, { method: 'HEAD' })
369
- .then(function(resp) {
370
- return resp.headers.get('content-type');
371
- });
372
- }
373
-
374
364
  HTMLVideo.canPlayStream = function(stream) {
375
365
  if (!stream || (stream.behaviorHints && stream.behaviorHints.notWebReady)) {
376
366
  return Promise.resolve(false);
@@ -391,7 +381,7 @@ HTMLVideo.manifest = {
391
381
  external: false,
392
382
  props: ['stream', 'paused', 'time', 'duration', 'buffering', 'buffered', 'volume', 'muted'],
393
383
  commands: ['load', 'unload', 'destroy'],
394
- events: ['propChanged', 'propValue', 'ended', 'error']
384
+ events: ['propValue', 'propChanged', 'ended', 'error']
395
385
  };
396
386
 
397
387
  module.exports = HTMLVideo;
@@ -0,0 +1,16 @@
1
+ function getContentType(stream) {
2
+ if (!stream || typeof stream.url !== 'string') {
3
+ return Promise.reject(new Error('Invalid stream parameter!'));
4
+ }
5
+
6
+ if (stream.behaviorHints && stream.behaviorHints.headers && typeof stream.behaviorHints.headers['content-type'] === 'string') {
7
+ return Promise.resolve(stream.behaviorHints.headers['content-type']);
8
+ }
9
+
10
+ return fetch(stream.url, { method: 'HEAD' })
11
+ .then(function(resp) {
12
+ return resp.headers.get('content-type');
13
+ });
14
+ }
15
+
16
+ module.exports = getContentType;
@@ -0,0 +1,15 @@
1
+ module.exports = {
2
+ debug: false,
3
+ enableWorker: true,
4
+ lowLatencyMode: false,
5
+ backBufferLength: 0,
6
+ maxBufferLength: 80,
7
+ maxMaxBufferLength: 80,
8
+ maxFragLookUpTolerance: 0,
9
+ maxBufferHole: 0,
10
+ appendErrorMaxRetry: 20,
11
+ nudgeMaxRetry: 20,
12
+ manifestLoadingTimeOut: 30000,
13
+ manifestLoadingMaxRetry: 10,
14
+ // liveDurationInfinity: false
15
+ };
@@ -0,0 +1,130 @@
1
+ var EventEmitter = require('eventemitter3');
2
+ var cloneDeep = require('lodash.clonedeep');
3
+ var deepFreeze = require('deep-freeze');
4
+ var ERROR = require('../error');
5
+
6
+ function IFrameVideo(options) {
7
+ options = options || {};
8
+
9
+ var containerElement = options.containerElement;
10
+ if (!(containerElement instanceof HTMLElement)) {
11
+ throw new Error('Container element required to be instance of HTMLElement');
12
+ }
13
+
14
+ var iframeElement = document.createElement('iframe');
15
+ iframeElement.style.width = '100%';
16
+ iframeElement.style.height = '100%';
17
+ iframeElement.style.border = 0;
18
+ iframeElement.style.backgroundColor = 'black';
19
+ iframeElement.allowFullscreen = false;
20
+ containerElement.appendChild(iframeElement);
21
+
22
+ var events = new EventEmitter();
23
+ var destroyed = false;
24
+ var stream = null;
25
+ var observedProps = {
26
+ stream: false
27
+ };
28
+
29
+ function getProp(propName) {
30
+ switch (propName) {
31
+ case 'stream': {
32
+ return stream;
33
+ }
34
+ default: {
35
+ return null;
36
+ }
37
+ }
38
+ }
39
+ function onError(error) {
40
+ events.emit('error', error);
41
+ if (error.critical) {
42
+ command('unload');
43
+ }
44
+ }
45
+ function onPropChanged(propName) {
46
+ if (observedProps[propName]) {
47
+ events.emit('propChanged', propName, getProp(propName));
48
+ }
49
+ }
50
+ function observeProp(propName) {
51
+ if (observedProps.hasOwnProperty(propName)) {
52
+ events.emit('propValue', propName, getProp(propName));
53
+ observedProps[propName] = true;
54
+ }
55
+ }
56
+ function command(commandName, commandArgs) {
57
+ switch (commandName) {
58
+ case 'load': {
59
+ command('unload');
60
+ if (commandArgs && commandArgs.stream && typeof commandArgs.stream.playerFrameUrl === 'string') {
61
+ stream = commandArgs.stream;
62
+ onPropChanged('stream');
63
+ iframeElement.src = commandArgs.stream.playerFrameUrl;
64
+ } else {
65
+ onError(Object.assign({}, ERROR.UNSUPPORTED_STREAM, {
66
+ critical: true,
67
+ stream: commandArgs ? commandArgs.stream : null
68
+ }));
69
+ }
70
+ break;
71
+ }
72
+ case 'unload': {
73
+ stream = null;
74
+ iframeElement.removeAttribute('src');
75
+ onPropChanged('stream');
76
+ break;
77
+ }
78
+ case 'destroy': {
79
+ command('unload');
80
+ destroyed = true;
81
+ events.removeAllListeners();
82
+ containerElement.removeChild(iframeElement);
83
+ break;
84
+ }
85
+ }
86
+ }
87
+
88
+ this.on = function(eventName, listener) {
89
+ if (destroyed) {
90
+ throw new Error('Video is destroyed');
91
+ }
92
+
93
+ events.on(eventName, listener);
94
+ };
95
+ this.dispatch = function(action) {
96
+ if (destroyed) {
97
+ throw new Error('Video is destroyed');
98
+ }
99
+
100
+ if (action) {
101
+ action = deepFreeze(cloneDeep(action));
102
+ switch (action.type) {
103
+ case 'observeProp': {
104
+ observeProp(action.propName);
105
+ return;
106
+ }
107
+ case 'command': {
108
+ command(action.commandName, action.commandArgs);
109
+ return;
110
+ }
111
+ }
112
+ }
113
+
114
+ throw new Error('Invalid action dispatched: ' + JSON.stringify(action));
115
+ };
116
+ }
117
+
118
+ IFrameVideo.canPlayStream = function(stream) {
119
+ return Promise.resolve(stream && typeof stream.playerFrameUrl === 'string');
120
+ };
121
+
122
+ IFrameVideo.manifest = {
123
+ name: 'IFrameVideo',
124
+ external: false,
125
+ props: ['stream'],
126
+ commands: ['load', 'unload', 'destroy'],
127
+ events: ['propValue', 'propChanged', 'error']
128
+ };
129
+
130
+ module.exports = IFrameVideo;
@@ -0,0 +1,3 @@
1
+ var IFrameVideo = require('./IFrameVideo');
2
+
3
+ module.exports = IFrameVideo;
@@ -1,15 +1,12 @@
1
1
  var EventEmitter = require('eventemitter3');
2
2
  var cloneDeep = require('lodash.clonedeep');
3
3
  var deepFreeze = require('deep-freeze');
4
+ var selectVideoImplementation = require('./selectVideoImplementation');
5
+ var ERROR = require('../error');
4
6
 
5
7
  function StremioVideo(options) {
6
8
  options = options || {};
7
9
 
8
- var selectVideoImplementation = options.selectVideoImplementation;
9
- if (typeof selectVideoImplementation !== 'function') {
10
- throw new Error('selectVideoImplementation argument required');
11
- }
12
-
13
10
  var video = null;
14
11
  var events = new EventEmitter();
15
12
  var destroyed = false;
@@ -35,6 +32,15 @@ function StremioVideo(options) {
35
32
  video = null;
36
33
  }
37
34
  if (video === null) {
35
+ if (Video === null) {
36
+ events.emit('error', Object.assign({}, ERROR.UNSUPPORTED_STREAM, {
37
+ error: new Error('No video implementation was selected'),
38
+ critical: true,
39
+ stream: action.commandArgs.stream
40
+ }));
41
+ return;
42
+ }
43
+
38
44
  video = new Video(Object.assign({}, options, action.commandArgs));
39
45
  video.on('ended', function() {
40
46
  events.emit('ended');
@@ -0,0 +1,36 @@
1
+ var ChromecastSenderVideo = require('../ChromecastSenderVideo');
2
+ var HTMLVideo = require('../HTMLVideo');
3
+ var IFrameVideo = require('../IFrameVideo');
4
+ var YouTubeVideo = require('../YouTubeVideo');
5
+ var withStreamingServer = require('../withStreamingServer');
6
+ var withHTMLSubtitles = require('../withHTMLSubtitles');
7
+
8
+ function selectVideoImplementation(args) {
9
+ if (!args.stream || typeof args.stream.externalUrl === 'string') {
10
+ return null;
11
+ }
12
+
13
+ if (args.chromecastTransport && args.chromecastTransport.getCastState() === cast.framework.CastState.CONNECTED) {
14
+ return ChromecastSenderVideo;
15
+ }
16
+
17
+ if (typeof args.stream.ytId === 'string') {
18
+ return withHTMLSubtitles(YouTubeVideo);
19
+ }
20
+
21
+ if (typeof args.stream.playerFrameUrl === 'string') {
22
+ return IFrameVideo;
23
+ }
24
+
25
+ if (typeof args.streamingServerURL === 'string') {
26
+ return withStreamingServer(withHTMLSubtitles(HTMLVideo));
27
+ }
28
+
29
+ if (typeof args.stream.url === 'string') {
30
+ return withHTMLSubtitles(HTMLVideo);
31
+ }
32
+
33
+ return null;
34
+ }
35
+
36
+ module.exports = selectVideoImplementation;
@@ -258,6 +258,9 @@ function YouTubeVideo(options) {
258
258
 
259
259
  return selectedSubtitlesTrackId;
260
260
  }
261
+ default: {
262
+ return null;
263
+ }
261
264
  }
262
265
  }
263
266
  function onError(error) {
@@ -352,9 +355,9 @@ function YouTubeVideo(options) {
352
355
  function command(commandName, commandArgs) {
353
356
  switch (commandName) {
354
357
  case 'load': {
355
- if (ready) {
356
- command('unload');
357
- if (commandArgs && commandArgs.stream && typeof commandArgs.stream.ytId === 'string') {
358
+ command('unload');
359
+ if (commandArgs && commandArgs.stream && typeof commandArgs.stream.ytId === 'string') {
360
+ if (ready) {
358
361
  stream = commandArgs.stream;
359
362
  onPropChanged('stream');
360
363
  var autoplay = typeof commandArgs.autoplay === 'boolean' ? commandArgs.autoplay : true;
@@ -379,13 +382,13 @@ function YouTubeVideo(options) {
379
382
  onPropChanged('subtitlesTracks');
380
383
  onPropChanged('selectedSubtitlesTrackId');
381
384
  } else {
382
- onError(Object.assign({}, ERROR.UNSUPPORTED_STREAM, {
383
- critical: true,
384
- stream: commandArgs ? commandArgs.stream : null
385
- }));
385
+ pendingLoadArgs = commandArgs;
386
386
  }
387
387
  } else {
388
- pendingLoadArgs = commandArgs;
388
+ onError(Object.assign({}, ERROR.UNSUPPORTED_STREAM, {
389
+ critical: true,
390
+ stream: commandArgs ? commandArgs.stream : null
391
+ }));
389
392
  }
390
393
 
391
394
  break;
@@ -466,7 +469,7 @@ YouTubeVideo.manifest = {
466
469
  external: false,
467
470
  props: ['stream', 'paused', 'time', 'duration', 'buffering', 'volume', 'muted', 'subtitlesTracks', 'selectedSubtitlesTrackId'],
468
471
  commands: ['load', 'unload', 'destroy'],
469
- events: ['propChanged', 'propValue', 'ended', 'error', 'subtitlesTrackLoaded']
472
+ events: ['propValue', 'propChanged', 'ended', 'error', 'subtitlesTrackLoaded']
470
473
  };
471
474
 
472
475
  module.exports = YouTubeVideo;
package/src/error.js CHANGED
@@ -50,36 +50,16 @@ var ERROR = {
50
50
  }
51
51
  },
52
52
  WITH_HTML_SUBTITLES: {
53
- FETCH_FAILED: {
53
+ LOAD_FAILED: {
54
54
  code: 70,
55
- message: 'Failed to fetch subtitles'
56
- },
57
- PARSE_FAILED: {
58
- code: 71,
59
- message: 'Failed to parse subtitles'
55
+ message: 'Failed to load external subtitles'
60
56
  }
61
57
  },
62
58
  WITH_STREAMING_SERVER: {
63
- STREAM_CONVERT_FAILED: {
59
+ CONVERT_FAILED: {
64
60
  code: 60,
65
- message: 'Unable to convert stream to url',
66
- },
67
- TRANSCODING_FAILED: {
68
- code: 61,
69
- message: 'Unable to transcode the next segment of the stream',
70
- },
71
- TRANSCODER_CREATE_FAILED: {
72
- code: 62,
73
- message: 'Failed to create transcoder'
74
- },
75
- TORRENT_CREATE_FAILED: {
76
- code: 63,
77
- message: 'Failed to create torrent'
78
- },
79
- NO_MEDIA_FILES_FOUND: {
80
- code: 64,
81
- message: 'No media files found into the torrent'
82
- },
61
+ message: 'Unable to convert stream'
62
+ }
83
63
  },
84
64
  UNKNOWN_ERROR: {
85
65
  code: 1,
package/src/index.js CHANGED
@@ -1,15 +1,3 @@
1
1
  var StremioVideo = require('./StremioVideo');
2
- var HTMLVideo = require('./HTMLVideo');
3
- var YouTubeVideo = require('./YouTubeVideo');
4
- var ChromecastSenderVideo = require('./ChromecastSenderVideo');
5
- var withHTMLSubtitles = require('./withHTMLSubtitles');
6
- var withStreamingServer = require('./withStreamingServer');
7
2
 
8
- module.exports = {
9
- StremioVideo: StremioVideo,
10
- HTMLVideo: HTMLVideo,
11
- YouTubeVideo: YouTubeVideo,
12
- ChromecastSenderVideo: ChromecastSenderVideo,
13
- withHTMLSubtitles: withHTMLSubtitles,
14
- withStreamingServer: withStreamingServer
15
- };
3
+ module.exports = StremioVideo;
@@ -1,52 +1,81 @@
1
1
  var VTTJS = require('vtt.js');
2
2
  var binarySearchUpperBound = require('./binarySearchUpperBound');
3
3
 
4
+ var CRITICAL_ERROR_CODE = 0;
5
+
4
6
  function parse(text) {
5
- var nativeVTTCue = window.VTTCue;
6
- window.VTTCue = VTTJS.VTTCue;
7
- var parser = new VTTJS.WebVTT.Parser(window, VTTJS.WebVTT.StringDecoder());
8
- var cues = [];
9
- var cuesByTime = {};
10
- parser.oncue = function(c) {
11
- var cue = {
12
- startTime: (c.startTime * 1000) | 0,
13
- endTime: (c.endTime * 1000) | 0,
14
- text: c.text
7
+ return new Promise(function(resolve, reject) {
8
+ var parser = new VTTJS.WebVTT.Parser(window, VTTJS.WebVTT.StringDecoder());
9
+ var errors = [];
10
+ var cues = [];
11
+ var cuesByTime = {};
12
+
13
+ parser.oncue = function(c) {
14
+ var cue = {
15
+ startTime: (c.startTime * 1000) | 0,
16
+ endTime: (c.endTime * 1000) | 0,
17
+ text: c.text
18
+ };
19
+ cues.push(cue);
20
+ cuesByTime[cue.startTime] = cuesByTime[cue.startTime] || [];
21
+ cuesByTime[cue.endTime] = cuesByTime[cue.endTime] || [];
22
+ };
23
+
24
+ parser.onparsingerror = function(error) {
25
+ if (error.code === CRITICAL_ERROR_CODE) {
26
+ parser.oncue = null;
27
+ parser.onparsingerror = null;
28
+ parser.onflush = null;
29
+ reject(error);
30
+ } else {
31
+ console.warn('Subtitles parsing error', error);
32
+ errors.push(error);
33
+ }
15
34
  };
16
- cues.push(cue);
17
- cuesByTime[cue.startTime] = cuesByTime[cue.startTime] || [];
18
- cuesByTime[cue.endTime] = cuesByTime[cue.endTime] || [];
19
- };
20
- parser.parse(text);
21
- parser.flush();
22
- window.VTTCue = nativeVTTCue;
23
- cuesByTime.times = Object.keys(cuesByTime)
24
- .map(function(time) {
25
- return parseInt(time, 10);
26
- })
27
- .sort(function(t1, t2) {
28
- return t1 - t2;
29
- });
30
- for (var i = 0; i < cues.length; i++) {
31
- cuesByTime[cues[i].startTime].push(cues[i]);
32
- var startTimeIndex = binarySearchUpperBound(cuesByTime.times, cues[i].startTime);
33
- for (var j = startTimeIndex + 1; j < cuesByTime.times.length; j++) {
34
- if (cues[i].endTime <= cuesByTime.times[j]) {
35
- break;
35
+
36
+ parser.onflush = function() {
37
+ cuesByTime.times = Object.keys(cuesByTime)
38
+ .map(function(time) {
39
+ return parseInt(time, 10);
40
+ })
41
+ .sort(function(t1, t2) {
42
+ return t1 - t2;
43
+ });
44
+ for (var i = 0; i < cues.length; i++) {
45
+ cuesByTime[cues[i].startTime].push(cues[i]);
46
+ var startTimeIndex = binarySearchUpperBound(cuesByTime.times, cues[i].startTime);
47
+ for (var j = startTimeIndex + 1; j < cuesByTime.times.length; j++) {
48
+ if (cues[i].endTime <= cuesByTime.times[j]) {
49
+ break;
50
+ }
51
+
52
+ cuesByTime[cuesByTime.times[j]].push(cues[i]);
53
+ }
36
54
  }
37
55
 
38
- cuesByTime[cuesByTime.times[j]].push(cues[i]);
39
- }
40
- }
56
+ for (var k = 0; k < cuesByTime.times.length; k++) {
57
+ cuesByTime[cuesByTime.times[k]].sort(function(c1, c2) {
58
+ return c1.startTime - c2.startTime ||
59
+ c1.endTime - c2.endTime;
60
+ });
61
+ }
41
62
 
42
- for (var k = 0; k < cuesByTime.times.length; k++) {
43
- cuesByTime[cuesByTime.times[k]].sort(function(c1, c2) {
44
- return c1.startTime - c2.startTime ||
45
- c1.endTime - c2.endTime;
46
- });
47
- }
63
+ parser.oncue = null;
64
+ parser.onparsingerror = null;
65
+ parser.onflush = null;
66
+ // we may have multiple parsing errors here, but will only respond with the first
67
+ // if subtitle cues are available, we will not reject the promise
68
+ if (cues.length === 0 && errors.length) {
69
+ reject(errors[0]);
70
+ } else if (cuesByTime.times.length === 0) {
71
+ reject(new Error('Missing subtitle track cues'));
72
+ } else {
73
+ resolve(cuesByTime);
74
+ }
75
+ };
48
76
 
49
- return cuesByTime;
77
+ parser.parse(text);
78
+ });
50
79
  }
51
80
 
52
81
  module.exports = {