@stremio/stremio-video 0.0.24 → 0.0.25-rc.2

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.24",
3
+ "version": "0.0.25-rc.2",
4
4
  "description": "Abstraction layer on top of different media players",
5
5
  "author": "Smart Code OOD",
6
6
  "main": "src/index.js",
@@ -63,6 +63,7 @@ function ChromecastSenderVideo(options) {
63
63
  volume: false,
64
64
  muted: false,
65
65
  playbackSpeed: false,
66
+ videoParams: false,
66
67
  extraSubtitlesTracks: false,
67
68
  selectedExtraSubtitlesTrackId: false,
68
69
  extraSubtitlesDelay: false,
@@ -126,6 +127,7 @@ function ChromecastSenderVideo(options) {
126
127
  onPropChanged('volume', null);
127
128
  onPropChanged('muted', null);
128
129
  onPropChanged('playbackSpeed', null);
130
+ onPropChanged('videoParams', null);
129
131
  onPropChanged('extraSubtitlesTracks', []);
130
132
  onPropChanged('selectedExtraSubtitlesTrackId', null);
131
133
  onPropChanged('extraSubtitlesDelay', null);
@@ -190,7 +192,7 @@ ChromecastSenderVideo.canPlayStream = function() {
190
192
  ChromecastSenderVideo.manifest = {
191
193
  name: 'ChromecastSenderVideo',
192
194
  external: true,
193
- props: ['stream', 'loaded', 'paused', 'time', 'duration', 'buffering', 'buffered', 'audioTracks', 'selectedAudioTrackId', 'subtitlesTracks', 'selectedSubtitlesTrackId', 'subtitlesOffset', 'subtitlesSize', 'subtitlesTextColor', 'subtitlesBackgroundColor', 'subtitlesOutlineColor', 'volume', 'muted', 'playbackSpeed', 'extraSubtitlesTracks', 'selectedExtraSubtitlesTrackId', 'extraSubtitlesDelay', 'extraSubtitlesSize', 'extraSubtitlesOffset', 'extraSubtitlesTextColor', 'extraSubtitlesBackgroundColor', 'extraSubtitlesOutlineColor'],
195
+ props: ['stream', 'loaded', 'paused', 'time', 'duration', 'buffering', 'buffered', 'audioTracks', 'selectedAudioTrackId', 'subtitlesTracks', 'selectedSubtitlesTrackId', 'subtitlesOffset', 'subtitlesSize', 'subtitlesTextColor', 'subtitlesBackgroundColor', 'subtitlesOutlineColor', 'volume', 'muted', 'playbackSpeed', 'videoParams', 'extraSubtitlesTracks', 'selectedExtraSubtitlesTrackId', 'extraSubtitlesDelay', 'extraSubtitlesSize', 'extraSubtitlesOffset', 'extraSubtitlesTextColor', 'extraSubtitlesBackgroundColor', 'extraSubtitlesOutlineColor'],
194
196
  commands: ['load', 'unload', 'destroy', 'addExtraSubtitlesTracks'],
195
197
  events: ['propValue', 'propChanged', 'ended', 'error', 'subtitlesTrackLoaded', 'audioTrackLoaded', 'extraSubtitlesTrackLoaded', 'implementationChanged']
196
198
  };
@@ -7,6 +7,7 @@ var IFrameVideo = require('../IFrameVideo');
7
7
  var YouTubeVideo = require('../YouTubeVideo');
8
8
  var withStreamingServer = require('../withStreamingServer');
9
9
  var withHTMLSubtitles = require('../withHTMLSubtitles');
10
+ var withVideoParams = require('../withVideoParams');
10
11
 
11
12
  function selectVideoImplementation(commandArgs, options) {
12
13
  if (!commandArgs.stream || typeof commandArgs.stream.externalUrl === 'string') {
@@ -18,11 +19,11 @@ function selectVideoImplementation(commandArgs, options) {
18
19
  }
19
20
 
20
21
  if (typeof commandArgs.stream.ytId === 'string') {
21
- return withHTMLSubtitles(YouTubeVideo);
22
+ return withVideoParams(withHTMLSubtitles(YouTubeVideo));
22
23
  }
23
24
 
24
25
  if (typeof commandArgs.stream.playerFrameUrl === 'string') {
25
- return IFrameVideo;
26
+ return withVideoParams(IFrameVideo);
26
27
  }
27
28
 
28
29
  if (options.shellTransport) {
@@ -41,12 +42,12 @@ function selectVideoImplementation(commandArgs, options) {
41
42
 
42
43
  if (typeof commandArgs.stream.url === 'string') {
43
44
  if (typeof global.webOS !== 'undefined') {
44
- return withHTMLSubtitles(WebOsVideo);
45
+ return withVideoParams(withHTMLSubtitles(WebOsVideo));
45
46
  }
46
47
  if (typeof global.tizen !== 'undefined') {
47
- return withHTMLSubtitles(TizenVideo);
48
+ return withVideoParams(withHTMLSubtitles(TizenVideo));
48
49
  }
49
- return withHTMLSubtitles(HTMLVideo);
50
+ return withVideoParams(withHTMLSubtitles(HTMLVideo));
50
51
  }
51
52
 
52
53
  return null;
@@ -204,7 +204,7 @@ function TizenVideo(options) {
204
204
  try {
205
205
  extra = JSON.parse(textTrack.extra_info);
206
206
  } catch(e) {}
207
- var textTrackLang = (extra.track_lang || '').trim();
207
+ var textTrackLang = typeof extra.track_lang === 'string' && extra.track_lang.length > 0 ? extra.track_lang.trim() : null;
208
208
  textTracks.push({
209
209
  id: textTrackId,
210
210
  lang: textTrackLang,
@@ -291,7 +291,7 @@ function TizenVideo(options) {
291
291
  try {
292
292
  extra = JSON.parse(audioTrack.extra_info);
293
293
  } catch(e) {}
294
- var audioTrackLang = extra.language || '';
294
+ var audioTrackLang = typeof extra.language === 'string' && extra.language.length > 0 ? extra.language : null;
295
295
  audioTracks.push({
296
296
  id: audioTrackId,
297
297
  lang: audioTrackLang,
@@ -15,12 +15,11 @@ function luna(params, call, fail, method) {
15
15
  window.webOS.service.request(method || 'luna://com.webos.media', params);
16
16
  }
17
17
 
18
- function runWebOS(params, failed) {
19
- // console.log('run web os', params);
18
+ function launchVideoApp(params, success, failure) {
20
19
  window.webOS.service.request('luna://com.webos.applicationManager', {
21
20
  method: 'launch',
22
21
  parameters: {
23
- 'id': params.need,
22
+ 'id': params.id,
24
23
  'params': {
25
24
  'payload':[
26
25
  {
@@ -46,19 +45,17 @@ function runWebOS(params, failed) {
46
45
  }
47
46
  },
48
47
  onSuccess: function () {
49
- // console.log('The app is launched');
48
+ success && success();
50
49
  },
51
- onFailure: function () { // function(inError)
52
- // console.log('Player', 'Failed to launch the app ('+params.need+'): ', '[' + inError.errorCode + ']: ' + inError.errorText);
53
-
54
- if (params.need === 'com.webos.app.photovideo') {
55
- params.need = 'com.webos.app.smartshare';
56
- runWebOS(params);
57
- } else if(params.need === 'com.webos.app.smartshare') {
58
- params.need = 'com.webos.app.mediadiscovery';
59
- runWebOS(params);
60
- } else if (params.need === 'com.webos.app.mediadiscovery') {
61
- failed();
50
+ onFailure: function () {
51
+ failure && failure(new Error('Failed to launch' + params.id));
52
+
53
+ if (params.id === 'com.webos.app.photovideo') {
54
+ params.id = 'com.webos.app.smartshare';
55
+ launchVideoApp(params, success, failure);
56
+ } else if(params.id === 'com.webos.app.smartshare') {
57
+ params.id = 'com.webos.app.mediadiscovery';
58
+ launchVideoApp(params, success, failure);
62
59
  }
63
60
  }
64
61
  });
@@ -599,21 +596,27 @@ function WebOsVideo(options) {
599
596
  }
600
597
  case 3: {
601
598
  error = ERROR.HTML_VIDEO.MEDIA_ERR_DECODE;
602
- runWebOS({
603
- need: 'com.webos.app.photovideo',
599
+ launchVideoApp({
600
+ id: 'com.webos.app.photovideo',
604
601
  url: stream.url,
605
602
  name: 'Stremio',
606
603
  position: -1,
604
+ }, null, function(e) {
605
+ // eslint-disable-next-line no-console
606
+ console.error(e);
607
607
  });
608
608
  break;
609
609
  }
610
610
  case 4: {
611
611
  error = ERROR.HTML_VIDEO.MEDIA_ERR_SRC_NOT_SUPPORTED;
612
- runWebOS({
613
- need: 'com.webos.app.photovideo',
612
+ launchVideoApp({
613
+ id: 'com.webos.app.photovideo',
614
614
  url: stream.url,
615
615
  name: 'Stremio',
616
616
  position: -1,
617
+ }, null, function(e) {
618
+ // eslint-disable-next-line no-console
619
+ console.error(e);
617
620
  });
618
621
  break;
619
622
  }
@@ -23,14 +23,14 @@ function convertStream(streamingServerURL, stream, seriesInfo) {
23
23
  :
24
24
  [];
25
25
  createTorrent(streamingServerURL, parsedMagnetURI.infoHash, null, sources, seriesInfo)
26
- .then(function(url) {
27
- resolve(url);
26
+ .then(function(torrent) {
27
+ resolve({ url: torrent.url, infoHash: torrent.infoHash, fileIdx: torrent.fileIdx });
28
28
  })
29
29
  .catch(function(error) {
30
30
  reject(error);
31
31
  });
32
32
  } else {
33
- resolve(stream.url);
33
+ resolve({ url: stream.url });
34
34
  }
35
35
 
36
36
  return;
@@ -38,8 +38,8 @@ function convertStream(streamingServerURL, stream, seriesInfo) {
38
38
 
39
39
  if (typeof stream.infoHash === 'string') {
40
40
  createTorrent(streamingServerURL, stream.infoHash, stream.fileIdx, stream.announce, seriesInfo)
41
- .then(function(url) {
42
- resolve(url);
41
+ .then(function(torrent) {
42
+ resolve({ url: torrent.url, infoHash: torrent.infoHash, fileIdx: torrent.fileIdx });
43
43
  })
44
44
  .catch(function(error) {
45
45
  reject(error);
@@ -1,18 +1,23 @@
1
1
  var url = require('url');
2
2
 
3
- function buildTorrentUrl(streamingServerURL, infoHash, fileIdx, sources) {
3
+ function buildTorrent(streamingServerURL, infoHash, fileIdx, sources) {
4
4
  var query = Array.isArray(sources) && sources.length > 0 ?
5
5
  '?' + new URLSearchParams(sources.map(function(source) {
6
6
  return ['tr', source];
7
7
  }))
8
8
  :
9
9
  '';
10
- return url.resolve(streamingServerURL, '/' + encodeURIComponent(infoHash) + '/' + encodeURIComponent(fileIdx)) + query;
10
+ return {
11
+ url: url.resolve(streamingServerURL, '/' + encodeURIComponent(infoHash) + '/' + encodeURIComponent(fileIdx)) + query,
12
+ infoHash: infoHash,
13
+ fileIdx: fileIdx,
14
+ sources: sources
15
+ };
11
16
  }
12
17
 
13
18
  function createTorrent(streamingServerURL, infoHash, fileIdx, sources, seriesInfo) {
14
19
  if ((!Array.isArray(sources) || sources.length === 0) && (fileIdx !== null && isFinite(fileIdx))) {
15
- return Promise.resolve(buildTorrentUrl(streamingServerURL, infoHash, fileIdx, sources));
20
+ return Promise.resolve(buildTorrent(streamingServerURL, infoHash, fileIdx, sources));
16
21
  }
17
22
 
18
23
  var body = {
@@ -58,7 +63,7 @@ function createTorrent(streamingServerURL, infoHash, fileIdx, sources, seriesInf
58
63
 
59
64
  throw new Error(resp.status + ' (' + resp.statusText + ')');
60
65
  }).then(function(resp) {
61
- return buildTorrentUrl(streamingServerURL, infoHash, body.guessFileIdx ? resp.guessedFileIdx : fileIdx, body.peerSearch ? body.peerSearch.sources : []);
66
+ return buildTorrent(streamingServerURL, infoHash, body.guessFileIdx ? resp.guessedFileIdx : fileIdx, body.peerSearch ? body.peerSearch.sources : []);
62
67
  });
63
68
  }
64
69
 
@@ -0,0 +1,95 @@
1
+ var url = require('url');
2
+
3
+ function fetchOpensubtitlesParams(streamingServerURL, mediaURL, behaviorHints) {
4
+ var hash = behaviorHints && typeof behaviorHints.videoHash === 'string' ? behaviorHints.videoHash : null;
5
+ var size = behaviorHints && isFinite(behaviorHints.videoSize) ? behaviorHints.videoSize : null;
6
+ if (typeof hash === 'string' && size !== null && isFinite(size)) {
7
+ return Promise.resolve({ hash: hash, size: size });
8
+ }
9
+
10
+ var queryParams = new URLSearchParams([['videoUrl', mediaURL]]);
11
+ return fetch(url.resolve(streamingServerURL, '/opensubHash?' + queryParams.toString()))
12
+ .then(function(resp) {
13
+ if (resp.ok) {
14
+ return resp.json();
15
+ }
16
+
17
+ throw new Error(resp.status + ' (' + resp.statusText + ')');
18
+ })
19
+ .then(function(resp) {
20
+ if (resp.error) {
21
+ throw new Error(resp.error);
22
+ }
23
+
24
+ return {
25
+ hash: typeof hash === 'string' ?
26
+ hash
27
+ :
28
+ resp.result && typeof resp.result.hash === 'string' ?
29
+ resp.result.hash
30
+ :
31
+ null,
32
+ size: size !== null && isFinite(size) ?
33
+ size
34
+ :
35
+ resp.result && typeof resp.result.size ?
36
+ resp.result.size
37
+ :
38
+ null
39
+ };
40
+ });
41
+ }
42
+
43
+ function fetchFilename(streamingServerURL, mediaURL, infoHash, fileIdx, behaviorHints) {
44
+ if (behaviorHints && typeof behaviorHints.filename === 'string') {
45
+ return Promise.resolve(behaviorHints.filename);
46
+ }
47
+
48
+ if (infoHash) {
49
+ return fetch(url.resolve(streamingServerURL, '/' + encodeURIComponent(infoHash) + '/' + encodeURIComponent(fileIdx) + '/stats.json'))
50
+ .then(function(resp) {
51
+ if (resp.ok) {
52
+ return resp.json();
53
+ }
54
+
55
+ throw new Error(resp.status + ' (' + resp.statusText + ')');
56
+ })
57
+ .then(function(resp) {
58
+ if (!resp || typeof resp.streamName !== 'string') {
59
+ throw new Error('Could not retrieve filename from torrent');
60
+ }
61
+
62
+ return resp.streamName;
63
+ });
64
+ }
65
+
66
+ return Promise.resolve(decodeURIComponent(mediaURL.split('/').pop()));
67
+ }
68
+
69
+ function fetchVideoParams(streamingServerURL, mediaURL, infoHash, fileIdx, behaviorHints) {
70
+ return Promise.allSettled([
71
+ fetchOpensubtitlesParams(streamingServerURL, mediaURL, behaviorHints),
72
+ fetchFilename(streamingServerURL, mediaURL, infoHash, fileIdx, behaviorHints)
73
+ ]).then(function(results) {
74
+ var result = { hash: null, size: null, filename: null };
75
+
76
+ if (results[0].status === 'fulfilled') {
77
+ result.hash = results[0].value.hash;
78
+ result.size = results[0].value.size;
79
+ } else if (results[0].reason) {
80
+ // eslint-disable-next-line no-console
81
+ console.error(results[0].reason);
82
+ }
83
+
84
+ if (results[1].status === 'fulfilled') {
85
+ result.filename = results[1].value;
86
+ } else if (results[1].reason) {
87
+ // eslint-disable-next-line no-console
88
+ console.error(results[1].reason);
89
+ }
90
+
91
+ return result;
92
+ });
93
+ }
94
+
95
+ module.exports = fetchVideoParams;
@@ -5,6 +5,7 @@ var cloneDeep = require('lodash.clonedeep');
5
5
  var deepFreeze = require('deep-freeze');
6
6
  var mediaCapabilities = require('../mediaCapabilities');
7
7
  var convertStream = require('./convertStream');
8
+ var fetchVideoParams = require('./fetchVideoParams');
8
9
  var ERROR = require('../error');
9
10
 
10
11
  function withStreamingServer(Video) {
@@ -27,10 +28,12 @@ function withStreamingServer(Video) {
27
28
  var loadArgs = null;
28
29
  var loaded = false;
29
30
  var actionsQueue = [];
31
+ var videoParams = null;
30
32
  var events = new EventEmitter();
31
33
  var destroyed = false;
32
34
  var observedProps = {
33
- stream: false
35
+ stream: false,
36
+ videoParams: false
34
37
  };
35
38
 
36
39
  function flushActionsQueue() {
@@ -70,6 +73,9 @@ function withStreamingServer(Video) {
70
73
  case 'stream': {
71
74
  return loadArgs !== null ? loadArgs.stream : null;
72
75
  }
76
+ case 'videoParams': {
77
+ return videoParams;
78
+ }
73
79
  default: {
74
80
  return videoPropValue;
75
81
  }
@@ -77,7 +83,8 @@ function withStreamingServer(Video) {
77
83
  }
78
84
  function observeProp(propName) {
79
85
  switch (propName) {
80
- case 'stream': {
86
+ case 'stream':
87
+ case 'videoParams': {
81
88
  events.emit('propValue', propName, getProp(propName, null));
82
89
  observedProps[propName] = true;
83
90
  return true;
@@ -96,7 +103,10 @@ function withStreamingServer(Video) {
96
103
  loadArgs = commandArgs;
97
104
  onPropChanged('stream');
98
105
  convertStream(commandArgs.streamingServerURL, commandArgs.stream, commandArgs.seriesInfo)
99
- .then(function(mediaURL) {
106
+ .then(function(result) {
107
+ var mediaURL = result.url;
108
+ var infoHash = result.infoHash;
109
+ var fileIdx = result.fileIdx;
100
110
  var formats = Array.isArray(commandArgs.formats) ?
101
111
  commandArgs.formats
102
112
  :
@@ -127,7 +137,12 @@ function withStreamingServer(Video) {
127
137
  .then(function(canPlay) {
128
138
  if (canPlay) {
129
139
  return {
130
- url: mediaURL
140
+ mediaURL: mediaURL,
141
+ infoHash: infoHash,
142
+ fileIdx: fileIdx,
143
+ stream: {
144
+ url: mediaURL
145
+ }
131
146
  };
132
147
  }
133
148
 
@@ -148,27 +163,32 @@ function withStreamingServer(Video) {
148
163
  queryParams.set('maxAudioChannels', maxAudioChannels);
149
164
 
150
165
  return {
151
- url: url.resolve(commandArgs.streamingServerURL, '/hlsv2/' + id + '/master.m3u8?' + queryParams.toString()),
152
- subtitles: Array.isArray(commandArgs.stream.subtitles) ?
153
- commandArgs.stream.subtitles.map(function(track) {
154
- return Object.assign({}, track, {
155
- url: typeof track.url === 'string' ?
156
- url.resolve(commandArgs.streamingServerURL, '/subtitles.vtt?' + new URLSearchParams([['from', track.url]]).toString())
157
- :
158
- track.url
159
- });
160
- })
161
- :
162
- [],
163
- behaviorHints: {
164
- headers: {
165
- 'content-type': 'application/vnd.apple.mpegurl'
166
+ mediaURL: mediaURL,
167
+ infoHash: infoHash,
168
+ fileIdx: fileIdx,
169
+ stream: {
170
+ url: url.resolve(commandArgs.streamingServerURL, '/hlsv2/' + id + '/master.m3u8?' + queryParams.toString()),
171
+ subtitles: Array.isArray(commandArgs.stream.subtitles) ?
172
+ commandArgs.stream.subtitles.map(function(track) {
173
+ return Object.assign({}, track, {
174
+ url: typeof track.url === 'string' ?
175
+ url.resolve(commandArgs.streamingServerURL, '/subtitles.vtt?' + new URLSearchParams([['from', track.url]]).toString())
176
+ :
177
+ track.url
178
+ });
179
+ })
180
+ :
181
+ [],
182
+ behaviorHints: {
183
+ headers: {
184
+ 'content-type': 'application/vnd.apple.mpegurl'
185
+ }
166
186
  }
167
187
  }
168
188
  };
169
189
  });
170
190
  })
171
- .then(function(stream) {
191
+ .then(function(result) {
172
192
  if (commandArgs !== loadArgs) {
173
193
  return;
174
194
  }
@@ -177,11 +197,30 @@ function withStreamingServer(Video) {
177
197
  type: 'command',
178
198
  commandName: 'load',
179
199
  commandArgs: Object.assign({}, commandArgs, {
180
- stream: stream
200
+ stream: result.stream
181
201
  })
182
202
  });
183
203
  loaded = true;
184
204
  flushActionsQueue();
205
+ fetchVideoParams(commandArgs.streamingServerURL, result.mediaURL, result.infoHash, result.fileIdx, commandArgs.stream.behaviorHints)
206
+ .then(function(result) {
207
+ if (commandArgs !== loadArgs) {
208
+ return;
209
+ }
210
+
211
+ videoParams = result;
212
+ onPropChanged('videoParams');
213
+ })
214
+ .catch(function(error) {
215
+ if (commandArgs !== loadArgs) {
216
+ return;
217
+ }
218
+
219
+ // eslint-disable-next-line no-console
220
+ console.error(error);
221
+ videoParams = { hash: null, size: null, filename: null };
222
+ onPropChanged('videoParams');
223
+ });
185
224
  })
186
225
  .catch(function(error) {
187
226
  if (commandArgs !== loadArgs) {
@@ -237,7 +276,9 @@ function withStreamingServer(Video) {
237
276
  loadArgs = null;
238
277
  loaded = false;
239
278
  actionsQueue = [];
279
+ videoParams = null;
240
280
  onPropChanged('stream');
281
+ onPropChanged('videoParams');
241
282
  return false;
242
283
  }
243
284
  case 'destroy': {
@@ -336,7 +377,7 @@ function withStreamingServer(Video) {
336
377
  VideoWithStreamingServer.manifest = {
337
378
  name: Video.manifest.name + 'WithStreamingServer',
338
379
  external: Video.manifest.external,
339
- props: Video.manifest.props.concat(['stream'])
380
+ props: Video.manifest.props.concat(['stream', 'videoParams'])
340
381
  .filter(function(value, index, array) { return array.indexOf(value) === index; }),
341
382
  commands: Video.manifest.commands.concat(['load', 'unload', 'destroy', 'addExtraSubtitlesTracks'])
342
383
  .filter(function(value, index, array) { return array.indexOf(value) === index; }),
@@ -0,0 +1,3 @@
1
+ var withVideoParams = require('./withVideoParams');
2
+
3
+ module.exports = withVideoParams;
@@ -0,0 +1,143 @@
1
+ var EventEmitter = require('eventemitter3');
2
+ var cloneDeep = require('lodash.clonedeep');
3
+ var deepFreeze = require('deep-freeze');
4
+
5
+ function withVideoParams(Video) {
6
+ function VideoWithVideoParams(options) {
7
+ options = options || {};
8
+
9
+ var video = new Video(options);
10
+ video.on('propValue', onVideoPropEvent.bind(null, 'propValue'));
11
+ video.on('propChanged', onVideoPropEvent.bind(null, 'propChanged'));
12
+ Video.manifest.events
13
+ .filter(function(eventName) {
14
+ return !['propValue', 'propChanged'].includes(eventName);
15
+ })
16
+ .forEach(function(eventName) {
17
+ video.on(eventName, onOtherVideoEvent(eventName));
18
+ });
19
+
20
+ var stream = null;
21
+ var events = new EventEmitter();
22
+ var destroyed = false;
23
+ var observedProps = {
24
+ videoParams: false
25
+ };
26
+
27
+ function onVideoPropEvent(eventName, propName, propValue) {
28
+ if (propName !== 'videoParams') {
29
+ events.emit(eventName, propName, getProp(propName, propValue));
30
+ }
31
+ if (propName === 'stream') {
32
+ stream = propValue;
33
+ onPropChanged('videoParams');
34
+ }
35
+ }
36
+ function onOtherVideoEvent(eventName) {
37
+ return function() {
38
+ events.emit.apply(events, [eventName].concat(Array.from(arguments)));
39
+ };
40
+ }
41
+ function onPropChanged(propName) {
42
+ if (observedProps[propName]) {
43
+ events.emit('propChanged', propName, getProp(propName, null));
44
+ }
45
+ }
46
+ function getProp(propName, videoPropValue) {
47
+ switch (propName) {
48
+ case 'videoParams': {
49
+ if (stream === null) {
50
+ return null;
51
+ }
52
+
53
+ var hash = stream.behaviorHints && typeof stream.behaviorHints.videoHash === 'string' ? stream.behaviorHints.videoHash : null;
54
+ var size = stream.behaviorHints && stream.behaviorHints.videoSize !== null && isFinite(stream.behaviorHints.videoSize) ? stream.behaviorHints.videoSize : null;
55
+ var filename = stream.behaviorHints && typeof stream.behaviorHints.filename === 'string' ? stream.behaviorHints.filename : null;
56
+ return { hash: hash, size: size, filename: filename };
57
+ }
58
+ default: {
59
+ return videoPropValue;
60
+ }
61
+ }
62
+ }
63
+ function observeProp(propName) {
64
+ switch (propName) {
65
+ case 'videoParams': {
66
+ events.emit('propValue', propName, getProp(propName, null));
67
+ observedProps[propName] = true;
68
+ return true;
69
+ }
70
+ default: {
71
+ return false;
72
+ }
73
+ }
74
+ }
75
+ function command(commandName) {
76
+ switch (commandName) {
77
+ case 'destroy': {
78
+ destroyed = true;
79
+ video.dispatch({ type: 'command', commandName: 'destroy' });
80
+ events.removeAllListeners();
81
+ return true;
82
+ }
83
+ default: {
84
+ return false;
85
+ }
86
+ }
87
+ }
88
+
89
+ this.on = function(eventName, listener) {
90
+ if (destroyed) {
91
+ throw new Error('Video is destroyed');
92
+ }
93
+
94
+ events.on(eventName, listener);
95
+ };
96
+ this.dispatch = function(action) {
97
+ if (destroyed) {
98
+ throw new Error('Video is destroyed');
99
+ }
100
+
101
+ if (action) {
102
+ action = deepFreeze(cloneDeep(action));
103
+ switch (action.type) {
104
+ case 'observeProp': {
105
+ if (observeProp(action.propName)) {
106
+ return;
107
+ }
108
+
109
+ break;
110
+ }
111
+ case 'command': {
112
+ if (command(action.commandName, action.commandArgs)) {
113
+ return;
114
+ }
115
+
116
+ break;
117
+ }
118
+ }
119
+ }
120
+
121
+ video.dispatch(action);
122
+ };
123
+ }
124
+
125
+ VideoWithVideoParams.canPlayStream = function(stream, options) {
126
+ return Video.canPlayStream(stream, options);
127
+ };
128
+
129
+ VideoWithVideoParams.manifest = {
130
+ name: Video.manifest.name + 'WithVideoParams',
131
+ external: Video.manifest.external,
132
+ props: Video.manifest.props.concat(['videoParams'])
133
+ .filter(function(value, index, array) { return array.indexOf(value) === index; }),
134
+ commands: Video.manifest.commands.concat(['destroy'])
135
+ .filter(function(value, index, array) { return array.indexOf(value) === index; }),
136
+ events: Video.manifest.events.concat(['propValue', 'propChanged'])
137
+ .filter(function(value, index, array) { return array.indexOf(value) === index; })
138
+ };
139
+
140
+ return VideoWithVideoParams;
141
+ }
142
+
143
+ module.exports = withVideoParams;