@stremio/stremio-video 0.0.22 → 0.0.23

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.22",
3
+ "version": "0.0.23",
4
4
  "description": "Abstraction layer on top of different media players",
5
5
  "author": "Smart Code OOD",
6
6
  "main": "src/index.js",
@@ -2,6 +2,7 @@ var ChromecastSenderVideo = require('../ChromecastSenderVideo');
2
2
  var ShellVideo = require('../ShellVideo');
3
3
  var HTMLVideo = require('../HTMLVideo');
4
4
  var TizenVideo = require('../TizenVideo');
5
+ var WebOsVideo = require('../WebOsVideo');
5
6
  var IFrameVideo = require('../IFrameVideo');
6
7
  var YouTubeVideo = require('../YouTubeVideo');
7
8
  var withStreamingServer = require('../withStreamingServer');
@@ -32,10 +33,16 @@ function selectVideoImplementation(commandArgs, options) {
32
33
  if (typeof global.tizen !== 'undefined') {
33
34
  return withStreamingServer(withHTMLSubtitles(TizenVideo));
34
35
  }
36
+ if (typeof global.webOS !== 'undefined') {
37
+ return withStreamingServer(withHTMLSubtitles(WebOsVideo));
38
+ }
35
39
  return withStreamingServer(withHTMLSubtitles(HTMLVideo));
36
40
  }
37
41
 
38
42
  if (typeof commandArgs.stream.url === 'string') {
43
+ if (typeof global.webOS !== 'undefined') {
44
+ return withHTMLSubtitles(WebOsVideo);
45
+ }
39
46
  if (typeof global.tizen !== 'undefined') {
40
47
  return withHTMLSubtitles(TizenVideo);
41
48
  }
@@ -0,0 +1,1089 @@
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 luna(params, call, fail, method) {
7
+ if (call) params.onSuccess = call || function() {};
8
+
9
+ params.onFailure = function () { // function(result)
10
+ // console.log('WebOS',(params.method || method) + ' [fail][' + result.errorCode + '] ' + result.errorText );
11
+
12
+ if (fail) fail();
13
+ };
14
+
15
+ window.webOS.service.request(method || 'luna://com.webos.media', params);
16
+ }
17
+
18
+ function runWebOS(params, failed) {
19
+ // console.log('run web os', params);
20
+ window.webOS.service.request('luna://com.webos.applicationManager', {
21
+ method: 'launch',
22
+ parameters: {
23
+ 'id': params.need,
24
+ 'params': {
25
+ 'payload':[
26
+ {
27
+ 'fullPath': params.url,
28
+ 'artist':'',
29
+ 'subtitle':'',
30
+ 'dlnaInfo':{
31
+ 'flagVal':4096,
32
+ 'cleartextSize':'-1',
33
+ 'contentLength':'-1',
34
+ 'opVal':1,
35
+ 'protocolInfo':'http-get:*:video/x-matroska:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000',
36
+ 'duration':0
37
+ },
38
+ 'mediaType':'VIDEO',
39
+ 'thumbnail':'',
40
+ 'deviceType':'DMR',
41
+ 'album':'',
42
+ 'fileName': params.name,
43
+ 'lastPlayPosition': params.position
44
+ }
45
+ ]
46
+ }
47
+ },
48
+ onSuccess: function () {
49
+ // console.log('The app is launched');
50
+ },
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();
62
+ }
63
+ }
64
+ });
65
+ }
66
+
67
+ var webOsColors = ['black', 'white', 'yellow', 'red', 'green', 'blue'];
68
+ var stremioColors = {
69
+ // rgba
70
+ 'rgba(0, 0, 0, 255)': 'black',
71
+ 'rgba(255, 255, 255, 255)': 'white',
72
+ 'rgba(255, 255, 0, 255)': 'yellow',
73
+ 'rgba(255, 0, 0, 255)': 'red',
74
+ 'rgba(0, 255, 0, 255)': 'green',
75
+ 'rgba(0, 0, 255, 255)': 'blue',
76
+ // rgba case 2
77
+ 'rgba(0, 0, 0, 1)': 'black',
78
+ 'rgba(255, 255, 255, 1)': 'white',
79
+ 'rgba(255, 255, 0, 1)': 'yellow',
80
+ 'rgba(255, 0, 0, 1)': 'red',
81
+ 'rgba(0, 255, 0, 1)': 'green',
82
+ 'rgba(0, 0, 255, 1)': 'blue',
83
+ // rgb
84
+ 'rgba(0, 0, 0)': 'black',
85
+ 'rgba(255, 255, 255)': 'white',
86
+ 'rgba(255, 255, 0)': 'yellow',
87
+ 'rgba(255, 0, 0)': 'red',
88
+ 'rgba(0, 255, 0)': 'green',
89
+ 'rgba(0, 0, 255)': 'blue',
90
+ // 8-digit hex
91
+ '#000000FF': 'black',
92
+ '#FFFFFFFF': 'white',
93
+ '#FFFF00FF': 'yellow',
94
+ '#FF0000FF': 'red',
95
+ '#00FF00FF': 'green',
96
+ '#0000FFFF': 'blue',
97
+ // 6-digit hex
98
+ '#000000': 'black',
99
+ '#FFFFFF': 'white',
100
+ '#FFFF00': 'yellow',
101
+ '#FF0000': 'red',
102
+ '#00FF00': 'green',
103
+ '#0000FF': 'blue'
104
+ };
105
+
106
+ function stremioSubOffsets(offset) {
107
+ if (offset === 0) {
108
+ return -3;
109
+ } else if (offset <= 2) {
110
+ return -2;
111
+ } else if (offset <= 3) {
112
+ return -1;
113
+ } else if (offset <= 5) {
114
+ return 0;
115
+ } else if (offset <= 10) {
116
+ return 1;
117
+ } else if (offset <= 25) {
118
+ return 2;
119
+ } else if (offset <= 50) {
120
+ return 3;
121
+ } else if (offset <= 100) {
122
+ return 4;
123
+ }
124
+ return false;
125
+ }
126
+
127
+ function stremioSubSizes(size) {
128
+ // there is also: 0 (tiny)
129
+ // adding zero will break the logic
130
+ if (size <= 75) {
131
+ return 1;
132
+ } else if (size <= 100) {
133
+ return 2;
134
+ } else if (size <= 150) {
135
+ return 3;
136
+ } else if (size <= 250) {
137
+ return 4;
138
+ }
139
+ return false;
140
+ }
141
+
142
+ function WebOsVideo(options) {
143
+
144
+ options = options || {};
145
+
146
+ var containerElement = options.containerElement;
147
+ if (!(containerElement instanceof HTMLElement)) {
148
+ throw new Error('Container element required to be instance of HTMLElement');
149
+ }
150
+
151
+ var knownMediaId = false;
152
+
153
+ var subSize = 75;
154
+
155
+ var disabledSubs = true;
156
+
157
+ var subscribed = false;
158
+
159
+ var currentSubTrack = false;
160
+
161
+ var currentAudioTrack = false;
162
+
163
+ var textTracks = [];
164
+
165
+ var audioTracks = [];
166
+
167
+ var count_message = 0;
168
+
169
+ var subtitleOffset = 5;
170
+
171
+ var setSubs = function (info) {
172
+ textTracks = [];
173
+ // console.log('sub tracks 1, nr of sub tracks: ', info.numSubtitleTracks);
174
+ if (info.numSubtitleTracks) {
175
+
176
+ // console.log('sub tracks 2');
177
+
178
+ // try {
179
+ // console.log('got sub info', JSON.stringify(info.subtitleTrackInfo));
180
+ // } catch(e) {};
181
+ for (var i = 0; i < info.subtitleTrackInfo.length; i++) {
182
+ var textTrack = info.subtitleTrackInfo[i];
183
+ textTrack.index = i;
184
+ var textTrackLang = textTrack.language === '(null)' ? '' : textTrack.language;
185
+
186
+ var textTrackId = 'EMBEDDED_' + textTrack.index;
187
+
188
+ if (!currentSubTrack && !textTracks.length) {
189
+ currentSubTrack = textTrackId;
190
+ }
191
+
192
+ textTracks.push({
193
+ id: textTrackId,
194
+ lang: textTrackLang,
195
+ label: textTrackLang,
196
+ origin: 'EMBEDDED',
197
+ embedded: true,
198
+ mode: textTrackId === currentSubTrack ? 'showing' : 'disabled',
199
+ });
200
+
201
+ }
202
+
203
+ // console.log('sub tracks all', textTracks);
204
+
205
+ onPropChanged('subtitlesTracks');
206
+ onPropChanged('selectedSubtitlesTrackId');
207
+
208
+ }
209
+ };
210
+
211
+ var setTracks = function (info) {
212
+ audioTracks = [];
213
+ // console.log('audio tracks 1, nr of audio tracks: ', info.numAudioTracks);
214
+ if (info.numAudioTracks) {
215
+
216
+ //console.log('audio tracks 2');
217
+
218
+ // try {
219
+ // console.log('got audio info', JSON.stringify(info.audioTrackInfo));
220
+ // } catch(e) {};
221
+ for (var i = 0; i < info.audioTrackInfo.length; i++) {
222
+ var audioTrack = info.audioTrackInfo[i];
223
+ audioTrack.index = i;
224
+ var audioTrackId = 'EMBEDDED_' + audioTrack.index;
225
+ if (!currentAudioTrack && !audioTracks.length) {
226
+ currentAudioTrack = audioTrackId;
227
+ }
228
+ var audioTrackLang = audioTrack.language === '(null)' ? '' : audioTrack.language;
229
+ audioTracks.push({
230
+ id: audioTrackId,
231
+ lang: audioTrackLang,
232
+ label: audioTrackLang,
233
+ origin: 'EMBEDDED',
234
+ embedded: true,
235
+ mode: audioTrackId === currentAudioTrack ? 'showing' : 'disabled',
236
+ });
237
+ }
238
+ // console.log('audio tracks all', audioTracks);
239
+ onPropChanged('audioTracks');
240
+ onPropChanged('selectedAudioTrackId');
241
+
242
+ }
243
+ };
244
+
245
+ var subscribe = function (cb) {
246
+ if (subscribed) return;
247
+ subscribed = true;
248
+ var answered = false;
249
+ // console.log('subscribing');
250
+ luna({
251
+ method: 'subscribe',
252
+ parameters: {
253
+ 'mediaId': knownMediaId,
254
+ 'subscribe': true
255
+ }
256
+ }, function (result) {
257
+ if (result.sourceInfo && !answered) {
258
+ answered = true;
259
+ // try {
260
+ // console.log('got source info', JSON.stringify(result.sourceInfo.programInfo[0]));
261
+ // } catch(e) {};
262
+ var info = result.sourceInfo.programInfo[0];
263
+
264
+ setSubs(info);
265
+
266
+ setTracks(info);
267
+
268
+ unsubscribe(cb);
269
+ }
270
+
271
+ if ((result.error || {}).errorCode) {
272
+ answered = true;
273
+ // console.error('luna playback error', result.error);
274
+ unsubscribe(cb);
275
+ // unsubscribe();
276
+ // onVideoError();
277
+ return;
278
+ }
279
+
280
+ if ((result.unloadCompleted || {}).mediaId === knownMediaId && (result.unloadCompleted || {}).state) {
281
+ // strange case where it just.. ends? without ever getting result.sourceInfo
282
+ // onEnded();
283
+ // console.log('strange case of end');
284
+ // unsubscribe(cb);
285
+ return;
286
+ }
287
+
288
+ // console.log('WebOS', 'subscribe', JSON.stringify(result));
289
+ count_message++;
290
+
291
+ if (count_message === 30 && !answered) {
292
+ // cb();
293
+ unsubscribe(cb);
294
+ }
295
+ }, function() { // function(err)
296
+ // console.log('luna error log 2');
297
+ // console.error(err);
298
+ });
299
+ };
300
+
301
+ var unsubscribe = function (cb) {
302
+ if (!subscribed) return;
303
+ subscribed = false;
304
+ luna({
305
+ method: 'unsubscribe',
306
+ parameters: {
307
+ 'mediaId': knownMediaId
308
+ }
309
+ }, function () { // function(result)
310
+ // console.log('unsubscribe result', JSON.stringify(result));
311
+ cb();
312
+ }, function () { // function(err)
313
+ // console.log('unsubscribe error', JSON.stringify(err));
314
+ cb();
315
+ });
316
+ cb();
317
+ };
318
+
319
+ // var unload = function (cb) {
320
+ // luna({
321
+ // method: 'unload',
322
+ // parameters: {
323
+ // 'mediaId': knownMediaId
324
+ // }
325
+ // }, cb, cb);
326
+ // };
327
+
328
+ var toggleSubtitles = function (status) {
329
+ if (!knownMediaId) return;
330
+
331
+ disabledSubs = !status;
332
+
333
+ // console.log('enable subs: ' + status);
334
+
335
+ luna({
336
+ method: 'setSubtitleEnable',
337
+ parameters: {
338
+ 'mediaId': knownMediaId,
339
+ 'enable': status
340
+ }
341
+ });
342
+ };
343
+
344
+ var styleElement = document.createElement('style');
345
+ containerElement.appendChild(styleElement);
346
+ styleElement.sheet.insertRule('video::cue { font-size: 4vmin; color: rgb(255, 255, 255); background-color: rgba(0, 0, 0, 0); text-shadow: rgb(34, 34, 34) 1px 1px 0.1em; }');
347
+ var videoElement = document.createElement('video');
348
+ videoElement.style.width = '100%';
349
+ videoElement.style.height = '100%';
350
+ videoElement.style.backgroundColor = 'black';
351
+ // videoElement.crossOrigin = 'anonymous';
352
+ videoElement.controls = false;
353
+ videoElement.onerror = function() {
354
+ onVideoError();
355
+ };
356
+ videoElement.onended = function() {
357
+ onEnded();
358
+ };
359
+ videoElement.onpause = function() {
360
+ onPropChanged('paused');
361
+ };
362
+ videoElement.onplay = function() {
363
+ onPropChanged('paused');
364
+ };
365
+ videoElement.ontimeupdate = function() {
366
+ onPropChanged('time');
367
+ onPropChanged('buffered');
368
+ };
369
+ videoElement.ondurationchange = function() {
370
+ onPropChanged('duration');
371
+ };
372
+ videoElement.onwaiting = function() {
373
+ onPropChanged('buffering');
374
+ onPropChanged('buffered');
375
+ };
376
+ videoElement.onseeking = function() {
377
+ onPropChanged('buffering');
378
+ onPropChanged('buffered');
379
+ };
380
+ videoElement.onseeked = function() {
381
+ onPropChanged('buffering');
382
+ onPropChanged('buffered');
383
+ };
384
+ videoElement.onstalled = function() {
385
+ onPropChanged('buffering');
386
+ onPropChanged('buffered');
387
+ };
388
+ videoElement.onplaying = function() {
389
+ onPropChanged('buffering');
390
+ onPropChanged('buffered');
391
+ };
392
+ videoElement.oncanplay = function() {
393
+ onPropChanged('buffering');
394
+ onPropChanged('buffered');
395
+ };
396
+ videoElement.canplaythrough = function() {
397
+ onPropChanged('buffering');
398
+ onPropChanged('buffered');
399
+ };
400
+ videoElement.onloadeddata = function() {
401
+ onPropChanged('buffering');
402
+ onPropChanged('buffered');
403
+ };
404
+ videoElement.onloadedmetadata = function() {
405
+ onPropChanged('buffering');
406
+ onPropChanged('buffered');
407
+ setProp('time', startTime);
408
+ };
409
+ videoElement.onvolumechange = function() {
410
+ onPropChanged('volume');
411
+ onPropChanged('muted');
412
+ };
413
+ videoElement.onratechange = function() {
414
+ onPropChanged('playbackSpeed');
415
+ };
416
+ videoElement.textTracks.onchange = function() {
417
+ onPropChanged('subtitlesTracks');
418
+ onPropChanged('selectedSubtitlesTrackId');
419
+ onCueChange();
420
+ Array.from(videoElement.textTracks).forEach(function(track) {
421
+ track.oncuechange = onCueChange;
422
+ });
423
+ };
424
+ containerElement.appendChild(videoElement);
425
+
426
+ var lastSubColor = null;
427
+ var lastSubBgColor = null;
428
+ var lastSubBgOpacity = 0;
429
+ var lastPlaybackSpeed = 1;
430
+
431
+ var events = new EventEmitter();
432
+ var destroyed = false;
433
+ var stream = null;
434
+ var startTime = null;
435
+ var subtitlesOffset = 0;
436
+ var observedProps = {
437
+ stream: false,
438
+ paused: false,
439
+ time: false,
440
+ duration: false,
441
+ buffering: false,
442
+ buffered: false,
443
+ subtitlesTracks: false,
444
+ selectedSubtitlesTrackId: false,
445
+ subtitlesOffset: false,
446
+ subtitlesSize: false,
447
+ subtitlesTextColor: false,
448
+ subtitlesBackgroundColor: false,
449
+ audioTracks: false,
450
+ selectedAudioTrackId: false,
451
+ volume: false,
452
+ muted: false,
453
+ playbackSpeed: false
454
+ };
455
+
456
+ function getProp(propName) {
457
+ switch (propName) {
458
+ case 'stream': {
459
+ return stream;
460
+ }
461
+ case 'paused': {
462
+ if (stream === null) {
463
+ return null;
464
+ }
465
+
466
+ return !!videoElement.paused;
467
+ }
468
+ case 'time': {
469
+ if (stream === null || videoElement.currentTime === null || !isFinite(videoElement.currentTime)) {
470
+ return null;
471
+ }
472
+
473
+ return Math.floor(videoElement.currentTime * 1000);
474
+ }
475
+ case 'duration': {
476
+ if (stream === null || videoElement.duration === null || !isFinite(videoElement.duration)) {
477
+ return null;
478
+ }
479
+
480
+ return Math.floor(videoElement.duration * 1000);
481
+ }
482
+ case 'buffering': {
483
+ if (stream === null) {
484
+ return null;
485
+ }
486
+
487
+ return videoElement.readyState < videoElement.HAVE_FUTURE_DATA;
488
+ }
489
+ case 'buffered': {
490
+ if (stream === null) {
491
+ return null;
492
+ }
493
+
494
+ var time = videoElement.currentTime !== null && isFinite(videoElement.currentTime) ? videoElement.currentTime : 0;
495
+ for (var i = 0; i < videoElement.buffered.length; i++) {
496
+ if (videoElement.buffered.start(i) <= time && time <= videoElement.buffered.end(i)) {
497
+ return Math.floor(videoElement.buffered.end(i) * 1000);
498
+ }
499
+ }
500
+
501
+ return Math.floor(time * 1000);
502
+ }
503
+ case 'subtitlesTracks': {
504
+ if (stream === null) {
505
+ return [];
506
+ }
507
+
508
+ return textTracks;
509
+ }
510
+ case 'selectedSubtitlesTrackId': {
511
+ if (stream === null || disabledSubs) {
512
+ return null;
513
+ }
514
+
515
+ return currentSubTrack;
516
+ }
517
+ case 'subtitlesOffset': {
518
+ if (destroyed) {
519
+ return null;
520
+ }
521
+
522
+ return subtitlesOffset;
523
+ }
524
+ case 'subtitlesSize': {
525
+ if (destroyed) {
526
+ return null;
527
+ }
528
+
529
+ return subSize;
530
+ }
531
+ case 'subtitlesTextColor': {
532
+ if (destroyed) {
533
+ return null;
534
+ }
535
+
536
+ return lastSubColor || 'rgba(255, 255, 255, 255)';
537
+ }
538
+ case 'subtitlesBackgroundColor': {
539
+ if (destroyed) {
540
+ return null;
541
+ }
542
+
543
+ return lastSubBgColor || 'rgba(255, 255, 255, 0)';
544
+ }
545
+ case 'audioTracks': {
546
+ return audioTracks;
547
+ }
548
+ case 'selectedAudioTrackId': {
549
+ return currentAudioTrack;
550
+ }
551
+ case 'volume': {
552
+ if (destroyed || videoElement.volume === null || !isFinite(videoElement.volume)) {
553
+ return null;
554
+ }
555
+
556
+ return Math.floor(videoElement.volume * 100);
557
+ }
558
+ case 'muted': {
559
+ if (destroyed) {
560
+ return null;
561
+ }
562
+
563
+ return !!videoElement.muted;
564
+ }
565
+ case 'playbackSpeed': {
566
+ if (destroyed || lastPlaybackSpeed === null || !isFinite(lastPlaybackSpeed)) {
567
+ return null;
568
+ }
569
+
570
+ return lastPlaybackSpeed;
571
+ }
572
+ default: {
573
+ return null;
574
+ }
575
+ }
576
+ }
577
+ function onCueChange() {
578
+ Array.from(videoElement.textTracks).forEach(function(track) {
579
+ Array.from(track.cues || []).forEach(function(cue) {
580
+ cue.snapToLines = false;
581
+ cue.line = 100 - subtitlesOffset;
582
+ });
583
+ });
584
+ }
585
+ function onVideoError() {
586
+ if (destroyed) {
587
+ return;
588
+ }
589
+
590
+ var error;
591
+ switch ((videoElement.error || {}).code) {
592
+ case 1: {
593
+ error = ERROR.HTML_VIDEO.MEDIA_ERR_ABORTED;
594
+ break;
595
+ }
596
+ case 2: {
597
+ error = ERROR.HTML_VIDEO.MEDIA_ERR_NETWORK;
598
+ break;
599
+ }
600
+ case 3: {
601
+ error = ERROR.HTML_VIDEO.MEDIA_ERR_DECODE;
602
+ runWebOS({
603
+ need: 'com.webos.app.photovideo',
604
+ url: stream.url,
605
+ name: 'Stremio',
606
+ position: -1,
607
+ });
608
+ break;
609
+ }
610
+ case 4: {
611
+ error = ERROR.HTML_VIDEO.MEDIA_ERR_SRC_NOT_SUPPORTED;
612
+ runWebOS({
613
+ need: 'com.webos.app.photovideo',
614
+ url: stream.url,
615
+ name: 'Stremio',
616
+ position: -1,
617
+ });
618
+ break;
619
+ }
620
+ default: {
621
+ error = ERROR.UNKNOWN_ERROR;
622
+ }
623
+ }
624
+ onError(Object.assign({}, error, {
625
+ critical: true,
626
+ error: videoElement.error
627
+ }));
628
+ }
629
+ function onError(error) {
630
+ events.emit('error', error);
631
+ if (error.critical) {
632
+ command('unload');
633
+ }
634
+ }
635
+ function onEnded() {
636
+ events.emit('ended');
637
+ }
638
+ function onPropChanged(propName) {
639
+ if (observedProps[propName]) {
640
+ events.emit('propChanged', propName, getProp(propName));
641
+ }
642
+ }
643
+ function observeProp(propName) {
644
+ if (observedProps.hasOwnProperty(propName)) {
645
+ events.emit('propValue', propName, getProp(propName));
646
+ observedProps[propName] = true;
647
+ }
648
+ }
649
+ function setProp(propName, propValue) {
650
+ switch (propName) {
651
+ case 'paused': {
652
+ if (stream !== null) {
653
+ propValue ? videoElement.pause() : videoElement.play();
654
+ }
655
+
656
+ break;
657
+ }
658
+ case 'time': {
659
+ if (stream !== null && videoElement.readyState >= videoElement.HAVE_METADATA && propValue !== null && isFinite(propValue)) {
660
+ try {
661
+ videoElement.currentTime = parseInt(propValue, 10) / 1000;
662
+ } catch(e) {
663
+ // console.log('webos video change time error');
664
+ // console.error(e);
665
+ }
666
+ }
667
+
668
+ break;
669
+ }
670
+ case 'selectedSubtitlesTrackId': {
671
+ if (stream !== null) {
672
+ if ((propValue || '').indexOf('EMBEDDED_') === 0) {
673
+ if (disabledSubs) {
674
+ toggleSubtitles(true);
675
+ }
676
+
677
+ // console.log('WebOS', 'change subtitles for id: ', knownMediaId, ' index:', propValue);
678
+
679
+ currentSubTrack = propValue;
680
+ var trackIndex = parseInt(propValue.replace('EMBEDDED_', ''));
681
+ // console.log('set subs to track idx: ' + trackIndex);
682
+ luna({
683
+ method: 'selectTrack',
684
+ parameters: {
685
+ 'type': 'text',
686
+ 'mediaId': knownMediaId,
687
+ 'index': trackIndex
688
+ }
689
+ }, function() {
690
+ // console.log('changed subs track successfully');
691
+ var selectedSubtitlesTrack = getProp('subtitlesTracks')
692
+ .find(function(track) {
693
+ return track.id === propValue;
694
+ });
695
+ textTracks = textTracks.map(function(track) {
696
+ track.mode = track.id === currentSubTrack ? 'showing' : 'disabled';
697
+ return track;
698
+ });
699
+ if (selectedSubtitlesTrack) {
700
+ events.emit('subtitlesTrackLoaded', selectedSubtitlesTrack);
701
+ onPropChanged('selectedSubtitlesTrackId');
702
+ }
703
+ });
704
+ } else if (!propValue) {
705
+ toggleSubtitles(false);
706
+ }
707
+ }
708
+
709
+ break;
710
+ }
711
+ case 'subtitlesOffset': {
712
+ if (propValue !== null && isFinite(propValue)) {
713
+ subtitlesOffset = Math.max(0, Math.min(100, parseInt(propValue, 10)));
714
+ var nextOffset = stremioSubOffsets(subtitleOffset);
715
+ if (nextOffset === false) { // use default
716
+ nextOffset = 0;
717
+ }
718
+ luna({
719
+ method: 'setSubtitlePosition',
720
+ parameters: {
721
+ 'mediaId': knownMediaId,
722
+ 'position': nextOffset,
723
+ }
724
+ }, function() {
725
+ // console.log('successfully changed sub offset to: ' + nextOffset);
726
+ });
727
+
728
+ onPropChanged('subtitlesOffset');
729
+ }
730
+
731
+ break;
732
+ }
733
+ case 'subtitlesSize': {
734
+ if (propValue !== null && isFinite(propValue)) {
735
+ subSize = Math.max(0, parseInt(propValue, 10));
736
+ var nextSubSize = stremioSubSizes(subSize);
737
+ if (nextSubSize === false) { // use default
738
+ nextSubSize = 2;
739
+ }
740
+ luna({
741
+ method: 'setSubtitleFontSize',
742
+ parameters: {
743
+ 'mediaId': knownMediaId,
744
+ 'fontSize': nextSubSize,
745
+ }
746
+ }, function() {
747
+ // console.log('successfully changed sub size to: ' + nextSubSize);
748
+ });
749
+
750
+ onPropChanged('subtitlesSize');
751
+ }
752
+
753
+ break;
754
+ }
755
+ case 'subtitlesTextColor': {
756
+ if (typeof propValue === 'string') {
757
+ // we use setSubtitleCharacterColor instead of setSubtitleColor
758
+ // because it has the same color options as the sub background
759
+ var nextColor = 'white';
760
+ if (stremioColors[propValue] && webOsColors.indexOf(stremioColors[propValue]) > -1) {
761
+ nextColor = stremioColors[propValue];
762
+ }
763
+ luna({
764
+ method: 'setSubtitleCharacterColor',
765
+ parameters: {
766
+ 'mediaId': knownMediaId,
767
+ 'charColor': nextColor,
768
+ }
769
+ }, function() {
770
+ // console.log('changed subtitle color successfully to: ' + nextColor);
771
+ });
772
+ lastSubColor = propValue;
773
+ onPropChanged('subtitlesTextColor');
774
+ }
775
+
776
+ break;
777
+ }
778
+ case 'subtitlesBackgroundColor': {
779
+ if (typeof propValue === 'string') {
780
+ if (stremioColors[propValue] && webOsColors.indexOf(stremioColors[propValue]) > -1) {
781
+ luna({
782
+ method: 'setSubtitleBackgroundColor',
783
+ parameters: {
784
+ 'mediaId': knownMediaId,
785
+ 'color': stremioColors[propValue],
786
+ }
787
+ }, function() {
788
+ // console.log('changed subtitle background color successfully to: ' + stremioColors[propValue]);
789
+ if (!lastSubBgOpacity) {
790
+ luna({
791
+ method: 'setSubtitleBackgroundOpacity',
792
+ parameters: {
793
+ 'mediaId': knownMediaId,
794
+ 'bgOpacity': 255,
795
+ }
796
+ }, function() {
797
+ // console.log('changed subtitle background opacity successfully to: ' + 255);
798
+ lastSubBgOpacity = 255;
799
+ });
800
+ }
801
+ });
802
+ } else {
803
+ // we don't know this color, set sub background opacity to 0
804
+ luna({
805
+ method: 'setSubtitleBackgroundOpacity',
806
+ parameters: {
807
+ 'mediaId': knownMediaId,
808
+ 'bgOpacity': 0,
809
+ }
810
+ }, function() {
811
+ // console.log('changed subtitle background opacity successfully to: ' + 0);
812
+ lastSubBgOpacity = 0;
813
+ });
814
+ }
815
+ lastSubBgColor = propValue;
816
+ onPropChanged('subtitlesBackgroundColor');
817
+ }
818
+
819
+ break;
820
+ }
821
+ case 'selectedAudioTrackId': {
822
+ // console.log('WebOS', 'change audio track for id: ', knownMediaId, ' index:', propValue);
823
+
824
+ if ((propValue || '').indexOf('EMBEDDED_') === 0) {
825
+ currentAudioTrack = propValue;
826
+ var trackIndex = parseInt(propValue.replace('EMBEDDED_', ''));
827
+ luna({
828
+ method: 'selectTrack',
829
+ parameters: {
830
+ 'type': 'audio',
831
+ 'mediaId': knownMediaId,
832
+ 'index': trackIndex
833
+ }
834
+ }, function() {
835
+ // console.log('changed audio track successfully');
836
+ var selectedAudioTrack = getProp('audioTracks')
837
+ .find(function(track) {
838
+ return track.id === propValue;
839
+ });
840
+
841
+ audioTracks = audioTracks.map(function(track) {
842
+ track.mode = track.id === currentAudioTrack ? 'showing' : 'disabled';
843
+ return track;
844
+ });
845
+
846
+ if (selectedAudioTrack) {
847
+ events.emit('audioTrackLoaded', selectedAudioTrack);
848
+ onPropChanged('selectedAudioTrackId');
849
+ }
850
+ });
851
+ if (videoElement.audioTracks) {
852
+ for (var i = 0; i < videoElement.audioTracks.length; i++) {
853
+ videoElement.audioTracks[i].enabled = false;
854
+ }
855
+
856
+ if(videoElement.audioTracks[trackIndex]) {
857
+ videoElement.audioTracks[trackIndex].enabled = true;
858
+
859
+ // console.log('WebOS', 'change audio two method:', trackIndex);
860
+ }
861
+ }
862
+
863
+ }
864
+
865
+ break;
866
+ }
867
+ case 'volume': {
868
+ if (propValue !== null && isFinite(propValue)) {
869
+ videoElement.muted = false;
870
+ videoElement.volume = Math.max(0, Math.min(100, parseInt(propValue, 10))) / 100;
871
+ }
872
+
873
+ break;
874
+ }
875
+ case 'muted': {
876
+ videoElement.muted = !!propValue;
877
+ break;
878
+ }
879
+ case 'playbackSpeed': {
880
+ // console.log('start change play rate to: ' + propValue);
881
+ // console.log(typeof propValue);
882
+ if (propValue !== null && isFinite(propValue)) {
883
+ lastPlaybackSpeed = parseFloat(propValue);
884
+ luna({
885
+ method: 'setPlayRate',
886
+ parameters: {
887
+ 'mediaId': knownMediaId,
888
+ 'playRate': lastPlaybackSpeed,
889
+ 'audioOutput': true,
890
+ }
891
+ }, function() {
892
+ // console.log('set playback rate success: ', lastPlaybackSpeed);
893
+ }, function() {
894
+ // console.log('failed setting playback rate success: ', lastPlaybackSpeed);
895
+ });
896
+ onPropChanged('playbackSpeed');
897
+ }
898
+
899
+ break;
900
+ }
901
+ }
902
+ }
903
+ function command(commandName, commandArgs) {
904
+ switch (commandName) {
905
+ case 'load': {
906
+ // not sure about this
907
+ // command('unload');
908
+ if (commandArgs && commandArgs.stream && typeof commandArgs.stream.url === 'string') {
909
+ stream = commandArgs.stream;
910
+ startTime = commandArgs.time;
911
+
912
+ onPropChanged('stream');
913
+ videoElement.autoplay = typeof commandArgs.autoplay === 'boolean' ? commandArgs.autoplay : true;
914
+
915
+ onPropChanged('paused');
916
+ onPropChanged('time');
917
+ onPropChanged('duration');
918
+ onPropChanged('buffering');
919
+ onPropChanged('buffered');
920
+ onPropChanged('subtitlesTracks');
921
+ onPropChanged('selectedSubtitlesTrackId');
922
+ onPropChanged('audioTracks');
923
+ onPropChanged('selectedAudioTrackId');
924
+
925
+ var count = 0;
926
+
927
+ var initMediaId = function (cb) {
928
+ function retrieveMediaId() {
929
+ if (videoElement.mediaId) {
930
+ knownMediaId = videoElement.mediaId;
931
+ // console.log('got media id: ', videoElement.mediaId);
932
+ clearInterval(timer);
933
+ subscribe(cb);
934
+ return;
935
+ }
936
+ count++;
937
+ if (count > 4) {
938
+ // console.log('failed to get media id');
939
+ clearInterval(timer);
940
+ cb();
941
+ }
942
+ }
943
+ var timer = setInterval(retrieveMediaId, 300);
944
+ };
945
+
946
+ var startVideo = function () {
947
+ // console.log('startVideo');
948
+ // not needed?
949
+ // videoElement.src = stream.url;
950
+
951
+ try {
952
+ videoElement.load();
953
+ } catch(e) {
954
+ // console.log('can\'t load video');
955
+ // console.error(e);
956
+ }
957
+
958
+ try {
959
+ // console.log('try play');
960
+ videoElement.play();
961
+ } catch(e) {
962
+ // console.log('can\'t start video');
963
+ // console.error(e);
964
+ }
965
+ };
966
+
967
+ videoElement.src = stream.url;
968
+
969
+ initMediaId(startVideo);
970
+ } else {
971
+ onError(Object.assign({}, ERROR.UNSUPPORTED_STREAM, {
972
+ critical: true,
973
+ stream: commandArgs ? commandArgs.stream : null
974
+ }));
975
+ }
976
+ break;
977
+ }
978
+ case 'unload': {
979
+ stream = null;
980
+ startTime = null;
981
+ Array.from(videoElement.textTracks).forEach(function(track) {
982
+ track.oncuechange = null;
983
+ });
984
+ videoElement.removeAttribute('src');
985
+ videoElement.load();
986
+ // not sure about this:
987
+ // try {
988
+ // videoElement.currentTime = 0;
989
+ // } catch(e) {
990
+ // console.log('webos video unload error');
991
+ // console.error(e);
992
+ // }
993
+ onPropChanged('stream');
994
+ onPropChanged('paused');
995
+ onPropChanged('time');
996
+ onPropChanged('duration');
997
+ onPropChanged('buffering');
998
+ onPropChanged('buffered');
999
+ onPropChanged('subtitlesTracks');
1000
+ onPropChanged('selectedSubtitlesTrackId');
1001
+ onPropChanged('audioTracks');
1002
+ onPropChanged('selectedAudioTrackId');
1003
+ // not sure about this:
1004
+ // unload(function() {});
1005
+ break;
1006
+ }
1007
+ case 'destroy': {
1008
+ command('unload');
1009
+ destroyed = true;
1010
+ onPropChanged('subtitlesOffset');
1011
+ onPropChanged('subtitlesSize');
1012
+ onPropChanged('subtitlesTextColor');
1013
+ onPropChanged('subtitlesBackgroundColor');
1014
+ onPropChanged('volume');
1015
+ onPropChanged('muted');
1016
+ onPropChanged('playbackSpeed');
1017
+ events.removeAllListeners();
1018
+ videoElement.onerror = null;
1019
+ videoElement.onended = null;
1020
+ videoElement.onpause = null;
1021
+ videoElement.onplay = null;
1022
+ videoElement.ontimeupdate = null;
1023
+ videoElement.ondurationchange = null;
1024
+ videoElement.onwaiting = null;
1025
+ videoElement.onseeking = null;
1026
+ videoElement.onseeked = null;
1027
+ videoElement.onstalled = null;
1028
+ videoElement.onplaying = null;
1029
+ videoElement.oncanplay = null;
1030
+ videoElement.canplaythrough = null;
1031
+ videoElement.onloadeddata = null;
1032
+ videoElement.onloadedmetadata = null;
1033
+ videoElement.onvolumechange = null;
1034
+ videoElement.onratechange = null;
1035
+ videoElement.textTracks.onchange = null;
1036
+ containerElement.removeChild(videoElement);
1037
+ containerElement.removeChild(styleElement);
1038
+ break;
1039
+ }
1040
+ }
1041
+ }
1042
+
1043
+ this.on = function(eventName, listener) {
1044
+ if (destroyed) {
1045
+ throw new Error('Video is destroyed');
1046
+ }
1047
+
1048
+ events.on(eventName, listener);
1049
+ };
1050
+ this.dispatch = function(action) {
1051
+ if (destroyed) {
1052
+ throw new Error('Video is destroyed');
1053
+ }
1054
+
1055
+ if (action) {
1056
+ action = deepFreeze(cloneDeep(action));
1057
+ switch (action.type) {
1058
+ case 'observeProp': {
1059
+ observeProp(action.propName);
1060
+ return;
1061
+ }
1062
+ case 'setProp': {
1063
+ setProp(action.propName, action.propValue);
1064
+ return;
1065
+ }
1066
+ case 'command': {
1067
+ command(action.commandName, action.commandArgs);
1068
+ return;
1069
+ }
1070
+ }
1071
+ }
1072
+
1073
+ throw new Error('Invalid action dispatched: ' + JSON.stringify(action));
1074
+ };
1075
+ }
1076
+
1077
+ WebOsVideo.canPlayStream = function() { // function(stream)
1078
+ return Promise.resolve(true);
1079
+ };
1080
+
1081
+ WebOsVideo.manifest = {
1082
+ name: 'WebOsVideo',
1083
+ external: false,
1084
+ props: ['stream', 'paused', 'time', 'duration', 'buffering', 'buffered', 'audioTracks', 'selectedAudioTrackId', 'subtitlesTracks', 'selectedSubtitlesTrackId', 'subtitlesOffset', 'subtitlesSize', 'subtitlesTextColor', 'subtitlesBackgroundColor', 'volume', 'muted', 'playbackSpeed'],
1085
+ commands: ['load', 'unload', 'destroy'],
1086
+ events: ['propValue', 'propChanged', 'ended', 'error', 'subtitlesTrackLoaded', 'audioTrackLoaded']
1087
+ };
1088
+
1089
+ module.exports = WebOsVideo;
@@ -0,0 +1,3 @@
1
+ var WebOsVideo = require('./WebOsVideo');
2
+
3
+ module.exports = WebOsVideo;