@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.
@@ -1,8 +1,9 @@
1
1
  var EventEmitter = require('eventemitter3');
2
2
  var cloneDeep = require('lodash.clonedeep');
3
3
  var deepFreeze = require('deep-freeze');
4
+ var ERROR = require('../error');
5
+ var subtitlesParser = require('./subtitlesParser');
4
6
  var subtitlesRenderer = require('./subtitlesRenderer');
5
- var fetchSubtitles = require('./fetchSubtitles');
6
7
 
7
8
  function withHTMLSubtitles(Video) {
8
9
  function VideoWithHTMLSubtitles(options) {
@@ -14,10 +15,10 @@ function withHTMLSubtitles(Video) {
14
15
  video.on('propChanged', onVideoPropEvent.bind(null, 'propChanged'));
15
16
  Video.manifest.events
16
17
  .filter(function(eventName) {
17
- return !['error', 'propChanged', 'propValue'].includes(eventName);
18
+ return !['error', 'propValue', 'propChanged'].includes(eventName);
18
19
  })
19
20
  .forEach(function(eventName) {
20
- video.on(eventName, onVideoOtherEvent(eventName));
21
+ video.on(eventName, onOtherVideoEvent(eventName));
21
22
  });
22
23
 
23
24
  var containerElement = options.containerElement;
@@ -81,6 +82,40 @@ function withHTMLSubtitles(Video) {
81
82
  subtitlesElement.append(cueNode, document.createElement('br'));
82
83
  });
83
84
  }
85
+ function onVideoError(error) {
86
+ events.emit('error', error);
87
+ if (error.critical) {
88
+ command('unload');
89
+ }
90
+ }
91
+ function onVideoPropEvent(eventName, propName, propValue) {
92
+ switch (propName) {
93
+ case 'time': {
94
+ videoState.time = propValue;
95
+ renderSubtitles();
96
+ break;
97
+ }
98
+ }
99
+
100
+ events.emit(eventName, propName, getProp(propName, propValue));
101
+ }
102
+ function onOtherVideoEvent(eventName) {
103
+ return function() {
104
+ events.emit.apply(events, [eventName].concat(Array.from(arguments)));
105
+ };
106
+ }
107
+ function onPropChanged(propName) {
108
+ if (observedProps[propName]) {
109
+ events.emit('propChanged', propName, getProp(propName, null));
110
+ }
111
+ }
112
+ function onError(error) {
113
+ events.emit('error', error);
114
+ if (error.critical) {
115
+ command('unload');
116
+ video.dispatch({ type: 'command', commandName: 'unload' });
117
+ }
118
+ }
84
119
  function getProp(propName, videoPropValue) {
85
120
  switch (propName) {
86
121
  case 'extraSubtitlesTracks': {
@@ -144,40 +179,6 @@ function withHTMLSubtitles(Video) {
144
179
  }
145
180
  }
146
181
  }
147
- function onError(error) {
148
- events.emit('error', error);
149
- if (error.critical) {
150
- command('unload');
151
- video.dispatch({ type: 'command', commandName: 'unload' });
152
- }
153
- }
154
- function onVideoError(error) {
155
- events.emit('error', error);
156
- if (error.critical) {
157
- command('unload');
158
- }
159
- }
160
- function onVideoPropEvent(eventName, propName, propValue) {
161
- switch (propName) {
162
- case 'time': {
163
- videoState.time = propValue;
164
- renderSubtitles();
165
- break;
166
- }
167
- }
168
-
169
- events.emit(eventName, propName, getProp(propName, propValue));
170
- }
171
- function onVideoOtherEvent(eventName) {
172
- return function() {
173
- events.emit.apply(events, [eventName].concat(Array.from(arguments)));
174
- };
175
- }
176
- function onPropChanged(propName) {
177
- if (observedProps[propName]) {
178
- events.emit('propChanged', propName, getProp(propName));
179
- }
180
- }
181
182
  function observeProp(propName) {
182
183
  switch (propName) {
183
184
  case 'extraSubtitlesTracks':
@@ -188,7 +189,7 @@ function withHTMLSubtitles(Video) {
188
189
  case 'extraSubtitlesTextColor':
189
190
  case 'extraSubtitlesBackgroundColor':
190
191
  case 'extraSubtitlesShadowColor': {
191
- events.emit('propValue', propName, getProp(propName));
192
+ events.emit('propValue', propName, getProp(propName, null));
192
193
  observedProps[propName] = true;
193
194
  return true;
194
195
  }
@@ -203,28 +204,36 @@ function withHTMLSubtitles(Video) {
203
204
  cuesByTime = null;
204
205
  selectedTrackId = null;
205
206
  delay = null;
206
- var selecterdTrack = tracks.find(function(track) {
207
+ var selectedTrack = tracks.find(function(track) {
207
208
  return track.id === propValue;
208
209
  });
209
- if (selecterdTrack) {
210
- selectedTrackId = selecterdTrack.id;
210
+ if (selectedTrack) {
211
+ selectedTrackId = selectedTrack.id;
211
212
  delay = 0;
212
- fetchSubtitles(selecterdTrack)
213
+ fetch(selectedTrack.url)
213
214
  .then(function(resp) {
214
- if (selectedTrackId !== selecterdTrack.id) {
215
+ return resp.text();
216
+ })
217
+ .then(function(text) {
218
+ return subtitlesParser.parse(text);
219
+ })
220
+ .then(function(result) {
221
+ if (selectedTrackId !== selectedTrack.id) {
215
222
  return;
216
223
  }
217
224
 
218
- cuesByTime = resp;
225
+ cuesByTime = result;
219
226
  renderSubtitles();
220
- events.emit('extraSubtitlesTrackLoaded', selecterdTrack);
227
+ events.emit('extraSubtitlesTrackLoaded', selectedTrack);
221
228
  })
222
229
  .catch(function(error) {
223
- if (selectedTrackId !== selecterdTrack.id) {
230
+ if (selectedTrackId !== selectedTrack.id) {
224
231
  return;
225
232
  }
226
233
 
227
- onError(Object.assign({}, error, {
234
+ onError(Object.assign({}, ERROR.WITH_HTML_SUBTITLES.LOAD_FAILED, {
235
+ error: error,
236
+ track: selectedTrack,
228
237
  critical: false
229
238
  }));
230
239
  });
@@ -318,8 +327,8 @@ function withHTMLSubtitles(Video) {
318
327
  command('unload');
319
328
  if (commandArgs.stream && Array.isArray(commandArgs.stream.subtitles)) {
320
329
  command('addExtraSubtitlesTracks', {
321
- tracks: commandArgs.stream.subtitles.map(function(subtitles) {
322
- return Object.assign({}, subtitles, {
330
+ tracks: commandArgs.stream.subtitles.map(function(track) {
331
+ return Object.assign({}, track, {
323
332
  origin: 'EXCLUSIVE',
324
333
  exclusive: true,
325
334
  embedded: false
@@ -414,7 +423,7 @@ function withHTMLSubtitles(Video) {
414
423
  .filter(function(value, index, array) { return array.indexOf(value) === index; }),
415
424
  commands: Video.manifest.commands.concat(['load', 'unload', 'destroy', 'addExtraSubtitlesTracks'])
416
425
  .filter(function(value, index, array) { return array.indexOf(value) === index; }),
417
- events: Video.manifest.events.concat(['propChanged', 'propValue', 'error', 'extraSubtitlesTrackLoaded'])
426
+ events: Video.manifest.events.concat(['propValue', 'propChanged', 'error', 'extraSubtitlesTrackLoaded'])
418
427
  .filter(function(value, index, array) { return array.indexOf(value) === index; })
419
428
  };
420
429
 
@@ -0,0 +1,61 @@
1
+ var url = require('url');
2
+ var magnet = require('magnet-uri');
3
+ var inferTorrentFileIdx = require('./inferTorrentFileIdx');
4
+
5
+ function convertStream(streamingServerURL, stream, seriesInfo) {
6
+ return new Promise(function(resolve, reject) {
7
+ if (typeof stream.url === 'string') {
8
+ if (stream.url.indexOf('magnet:') === 0) {
9
+ var parsedMagnetURI;
10
+ try {
11
+ parsedMagnetURI = magnet.decode(stream.url);
12
+ if (!parsedMagnetURI || typeof parsedMagnetURI.infoHash !== 'string') {
13
+ reject(new Error('Failed to decode magnet url'));
14
+ return;
15
+ }
16
+ } catch (error) {
17
+ reject(error);
18
+ return;
19
+ }
20
+
21
+ var sources = Array.isArray(parsedMagnetURI.announce) ?
22
+ parsedMagnetURI.announce.map(function(source) {
23
+ return 'tracker:' + source;
24
+ })
25
+ :
26
+ [];
27
+ inferTorrentFileIdx(streamingServerURL, parsedMagnetURI.infoHash, sources, seriesInfo)
28
+ .then(function(fileIdx) {
29
+ resolve(url.resolve(streamingServerURL, '/' + encodeURIComponent(parsedMagnetURI.infoHash) + '/' + encodeURIComponent(fileIdx)));
30
+ })
31
+ .catch(function(error) {
32
+ reject(error);
33
+ });
34
+ } else {
35
+ resolve(stream.url);
36
+ }
37
+
38
+ return;
39
+ }
40
+
41
+ if (typeof stream.infoHash === 'string') {
42
+ if (stream.fileIdx !== null && isFinite(stream.fileIdx)) {
43
+ resolve(url.resolve(streamingServerURL, '/' + encodeURIComponent(stream.infoHash) + '/' + encodeURIComponent(stream.fileIdx)));
44
+ } else {
45
+ inferTorrentFileIdx(streamingServerURL, stream.infoHash, stream.announce, seriesInfo)
46
+ .then(function(fileIdx) {
47
+ resolve(url.resolve(streamingServerURL, '/' + encodeURIComponent(stream.infoHash) + '/' + encodeURIComponent(fileIdx)));
48
+ })
49
+ .catch(function(error) {
50
+ reject(error);
51
+ });
52
+ }
53
+
54
+ return;
55
+ }
56
+
57
+ reject(new Error('Stream cannot be converted'));
58
+ });
59
+ }
60
+
61
+ module.exports = convertStream;
@@ -0,0 +1,63 @@
1
+ var url = require('url');
2
+ var parseVideoName = require('video-name-parser');
3
+
4
+ var MEDIA_FILE_EXTENTIONS = /.mkv$|.avi$|.mp4$|.wmv$|.vp8$|.mov$|.mpg$|.ts$|.m3u8$|.webm$|.flac$|.mp3$|.wav$|.wma$|.aac$|.ogg$/i;
5
+
6
+ function inferTorrentFileIdx(streamingServerURL, infoHash, sources, seriesInfo) {
7
+ return fetch(url.resolve(streamingServerURL, '/' + encodeURIComponent(infoHash) + '/create'), {
8
+ method: 'POST',
9
+ headers: {
10
+ 'content-type': 'application/json'
11
+ },
12
+ body: JSON.stringify({
13
+ torrent: {
14
+ infoHash: infoHash,
15
+ peerSearch: {
16
+ sources: ['dht:' + infoHash].concat(Array.isArray(sources) ? sources : []),
17
+ min: 40,
18
+ max: 150
19
+ }
20
+ }
21
+ })
22
+ }).then(function(resp) {
23
+ return resp.json();
24
+ }).then(function(resp) {
25
+ if (!resp || !Array.isArray(resp.files) || resp.files.some(function(file) { return !file || typeof file.path !== 'string' || file.length === null || !isFinite(file.length); })) {
26
+ throw new Error('No files found in the torrent');
27
+ }
28
+
29
+ var mediaFiles = resp.files.filter(function(file) {
30
+ return file.path.match(MEDIA_FILE_EXTENTIONS);
31
+ });
32
+ if (mediaFiles.length === 0) {
33
+ throw new Error('No media files found in the torrent');
34
+ }
35
+
36
+ var mediaFilesForEpisode = seriesInfo ?
37
+ mediaFiles.filter(function(file) {
38
+ try {
39
+ var info = parseVideoName(file.path);
40
+ return info.season !== null &&
41
+ isFinite(info.season) &&
42
+ info.season === seriesInfo.season &&
43
+ Array.isArray(info.episode) &&
44
+ info.episode.indexOf(seriesInfo.episode) !== -1;
45
+ } catch (e) {
46
+ return false;
47
+ }
48
+ })
49
+ :
50
+ [];
51
+ var selectedFile = (mediaFilesForEpisode.length > 0 ? mediaFilesForEpisode : mediaFiles)
52
+ .reduce(function(result, file) {
53
+ if (!result || file.length > result.length) {
54
+ return file;
55
+ }
56
+
57
+ return result;
58
+ }, null);
59
+ return resp.files.indexOf(selectedFile);
60
+ });
61
+ }
62
+
63
+ module.exports = inferTorrentFileIdx;