@stremio/stremio-video 0.0.16 → 0.0.17-rc.1
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 +3 -2
- package/src/ChromecastSenderVideo/ChromecastSenderVideo.js +3 -3
- package/src/HTMLVideo/HTMLVideo.js +271 -3
- package/src/HTMLVideo/getContentType.js +5 -1
- package/src/YouTubeVideo/YouTubeVideo.js +1 -1
- package/src/withHTMLSubtitles/subtitlesConverter.js +12 -4
- package/src/withHTMLSubtitles/withHTMLSubtitles.js +41 -17
- package/src/withStreamingServer/inferTorrentFileIdx.js +5 -1
- package/src/withStreamingServer/withStreamingServer.js +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stremio/stremio-video",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17-rc.1",
|
|
4
4
|
"description": "Abstraction layer on top of different media players",
|
|
5
5
|
"author": "Smart Code OOD",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"magnet-uri": "6.2.0",
|
|
22
22
|
"url": "0.11.0",
|
|
23
23
|
"video-name-parser": "1.4.6",
|
|
24
|
-
"vtt.js": "github:jaruba/vtt.js#e4f5f5603730866bacb174a93f51b734c9f29e6a"
|
|
24
|
+
"vtt.js": "github:jaruba/vtt.js#e4f5f5603730866bacb174a93f51b734c9f29e6a",
|
|
25
|
+
"color": "4.2.3"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"eslint": "7.32.0"
|
|
@@ -60,7 +60,7 @@ function ChromecastSenderVideo(options) {
|
|
|
60
60
|
extraSubtitlesOffset: false,
|
|
61
61
|
extraSubtitlesTextColor: false,
|
|
62
62
|
extraSubtitlesBackgroundColor: false,
|
|
63
|
-
|
|
63
|
+
extraSubtitlesOutlineColor: false
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
function onTransportError(error, message) {
|
|
@@ -122,7 +122,7 @@ function ChromecastSenderVideo(options) {
|
|
|
122
122
|
onPropChanged('extraSubtitlesOffset', null);
|
|
123
123
|
onPropChanged('extraSubtitlesTextColor', null);
|
|
124
124
|
onPropChanged('extraSubtitlesBackgroundColor', null);
|
|
125
|
-
onPropChanged('
|
|
125
|
+
onPropChanged('extraSubtitlesOutlineColor', null);
|
|
126
126
|
events.removeAllListeners();
|
|
127
127
|
chromecastTransport.off('message', onMessage);
|
|
128
128
|
containerElement.removeChild(deviceNameContainerElement);
|
|
@@ -179,7 +179,7 @@ ChromecastSenderVideo.canPlayStream = function() {
|
|
|
179
179
|
ChromecastSenderVideo.manifest = {
|
|
180
180
|
name: 'ChromecastSenderVideo',
|
|
181
181
|
external: true,
|
|
182
|
-
props: ['stream', 'paused', 'time', 'duration', 'buffering', 'buffered', 'volume', 'muted', 'subtitlesTracks', 'selectedSubtitlesTrackId', 'extraSubtitlesTracks', 'selectedExtraSubtitlesTrackId', 'extraSubtitlesDelay', 'extraSubtitlesSize', 'extraSubtitlesOffset', 'extraSubtitlesTextColor', 'extraSubtitlesBackgroundColor', '
|
|
182
|
+
props: ['stream', 'paused', 'time', 'duration', 'buffering', 'buffered', 'volume', 'muted', 'subtitlesTracks', 'selectedSubtitlesTrackId', 'extraSubtitlesTracks', 'selectedExtraSubtitlesTrackId', 'extraSubtitlesDelay', 'extraSubtitlesSize', 'extraSubtitlesOffset', 'extraSubtitlesTextColor', 'extraSubtitlesBackgroundColor', 'extraSubtitlesOutlineColor'],
|
|
183
183
|
commands: ['load', 'unload', 'destroy', 'addExtraSubtitlesTracks'],
|
|
184
184
|
events: ['propValue', 'propChanged', 'ended', 'error', 'subtitlesTrackLoaded', 'extraSubtitlesTrackLoaded', 'implementationChanged']
|
|
185
185
|
};
|
|
@@ -2,6 +2,7 @@ var EventEmitter = require('eventemitter3');
|
|
|
2
2
|
var Hls = require('hls.js');
|
|
3
3
|
var cloneDeep = require('lodash.clonedeep');
|
|
4
4
|
var deepFreeze = require('deep-freeze');
|
|
5
|
+
var Color = require('color');
|
|
5
6
|
var ERROR = require('../error');
|
|
6
7
|
var getContentType = require('./getContentType');
|
|
7
8
|
var HLS_CONFIG = require('./hlsConfig');
|
|
@@ -14,6 +15,9 @@ function HTMLVideo(options) {
|
|
|
14
15
|
throw new Error('Container element required to be instance of HTMLElement');
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
var styleElement = document.createElement('style');
|
|
19
|
+
containerElement.appendChild(styleElement);
|
|
20
|
+
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; }');
|
|
17
21
|
var videoElement = document.createElement('video');
|
|
18
22
|
videoElement.style.width = '100%';
|
|
19
23
|
videoElement.style.height = '100%';
|
|
@@ -75,12 +79,24 @@ function HTMLVideo(options) {
|
|
|
75
79
|
onPropChanged('volume');
|
|
76
80
|
onPropChanged('muted');
|
|
77
81
|
};
|
|
82
|
+
videoElement.onratechange = function() {
|
|
83
|
+
onPropChanged('playbackSpeed');
|
|
84
|
+
};
|
|
85
|
+
videoElement.textTracks.onchange = function() {
|
|
86
|
+
onPropChanged('subtitlesTracks');
|
|
87
|
+
onPropChanged('selectedSubtitlesTrackId');
|
|
88
|
+
onCueChange();
|
|
89
|
+
Array.from(videoElement.textTracks).forEach(function(track) {
|
|
90
|
+
track.oncuechange = onCueChange;
|
|
91
|
+
});
|
|
92
|
+
};
|
|
78
93
|
containerElement.appendChild(videoElement);
|
|
79
94
|
|
|
80
95
|
var hls = null;
|
|
81
96
|
var events = new EventEmitter();
|
|
82
97
|
var destroyed = false;
|
|
83
98
|
var stream = null;
|
|
99
|
+
var subtitlesOffset = 0;
|
|
84
100
|
var observedProps = {
|
|
85
101
|
stream: false,
|
|
86
102
|
paused: false,
|
|
@@ -88,8 +104,18 @@ function HTMLVideo(options) {
|
|
|
88
104
|
duration: false,
|
|
89
105
|
buffering: false,
|
|
90
106
|
buffered: false,
|
|
107
|
+
subtitlesTracks: false,
|
|
108
|
+
selectedSubtitlesTrackId: false,
|
|
109
|
+
subtitlesOffset: false,
|
|
110
|
+
subtitlesSize: false,
|
|
111
|
+
subtitlesTextColor: false,
|
|
112
|
+
subtitlesBackgroundColor: false,
|
|
113
|
+
subtitlesOutlineColor: false,
|
|
114
|
+
audioTracks: false,
|
|
115
|
+
selectedAudioTrackId: false,
|
|
91
116
|
volume: false,
|
|
92
|
-
muted: false
|
|
117
|
+
muted: false,
|
|
118
|
+
playbackSpeed: false
|
|
93
119
|
};
|
|
94
120
|
|
|
95
121
|
function getProp(propName) {
|
|
@@ -139,6 +165,106 @@ function HTMLVideo(options) {
|
|
|
139
165
|
|
|
140
166
|
return Math.floor(time * 1000);
|
|
141
167
|
}
|
|
168
|
+
case 'subtitlesTracks': {
|
|
169
|
+
if (stream === null) {
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return Array.from(videoElement.textTracks)
|
|
174
|
+
.map(function(track, index) {
|
|
175
|
+
return Object.freeze({
|
|
176
|
+
id: 'EMBEDDED_' + String(index),
|
|
177
|
+
lang: track.language,
|
|
178
|
+
label: track.label,
|
|
179
|
+
origin: 'EMBEDDED',
|
|
180
|
+
embedded: true
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
case 'selectedSubtitlesTrackId': {
|
|
185
|
+
if (stream === null) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return Array.from(videoElement.textTracks)
|
|
190
|
+
.reduce(function(result, track, index) {
|
|
191
|
+
if (result === null && track.mode === 'showing') {
|
|
192
|
+
return 'EMBEDDED_' + String(index);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return result;
|
|
196
|
+
}, null);
|
|
197
|
+
}
|
|
198
|
+
case 'subtitlesOffset': {
|
|
199
|
+
if (destroyed) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return subtitlesOffset;
|
|
204
|
+
}
|
|
205
|
+
case 'subtitlesSize': {
|
|
206
|
+
if (destroyed) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return parseInt(styleElement.sheet.cssRules[0].style.fontSize, 10) * 25;
|
|
211
|
+
}
|
|
212
|
+
case 'subtitlesTextColor': {
|
|
213
|
+
if (destroyed) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return styleElement.sheet.cssRules[0].style.color;
|
|
218
|
+
}
|
|
219
|
+
case 'subtitlesBackgroundColor': {
|
|
220
|
+
if (destroyed) {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return styleElement.sheet.cssRules[0].style.backgroundColor;
|
|
225
|
+
}
|
|
226
|
+
case 'subtitlesOutlineColor': {
|
|
227
|
+
if (destroyed) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return styleElement.sheet.cssRules[0].style.textShadow.slice(0, styleElement.sheet.cssRules[0].style.textShadow.indexOf(')') + 1);
|
|
232
|
+
}
|
|
233
|
+
case 'audioTracks': {
|
|
234
|
+
if (hls === null || !Array.isArray(hls.audioTracks)) {
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return hls.audioTracks
|
|
239
|
+
.map(function(track) {
|
|
240
|
+
return Object.freeze({
|
|
241
|
+
id: 'EMBEDDED_' + String(track.id),
|
|
242
|
+
lang: typeof track.lang === 'string' && track.lang.length > 0 ?
|
|
243
|
+
track.lang
|
|
244
|
+
:
|
|
245
|
+
typeof track.name === 'string' && track.name.length > 0 ?
|
|
246
|
+
track.name
|
|
247
|
+
:
|
|
248
|
+
String(track.id),
|
|
249
|
+
label: typeof track.name === 'string' && track.name.length > 0 ?
|
|
250
|
+
track.name
|
|
251
|
+
:
|
|
252
|
+
typeof track.lang === 'string' && track.lang.length > 0 ?
|
|
253
|
+
track.lang
|
|
254
|
+
:
|
|
255
|
+
String(track.id),
|
|
256
|
+
origin: 'EMBEDDED',
|
|
257
|
+
embedded: true
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
case 'selectedAudioTrackId': {
|
|
262
|
+
if (hls === null || hls.audioTrack === null || !isFinite(hls.audioTrack) || hls.audioTrack === -1) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return 'EMBEDDED_' + String(hls.audioTrack);
|
|
267
|
+
}
|
|
142
268
|
case 'volume': {
|
|
143
269
|
if (destroyed || videoElement.volume === null || !isFinite(videoElement.volume)) {
|
|
144
270
|
return null;
|
|
@@ -153,11 +279,26 @@ function HTMLVideo(options) {
|
|
|
153
279
|
|
|
154
280
|
return !!videoElement.muted;
|
|
155
281
|
}
|
|
282
|
+
case 'playbackSpeed': {
|
|
283
|
+
if (destroyed || videoElement.playbackRate === null || !isFinite(videoElement.playbackRate)) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return videoElement.playbackRate;
|
|
288
|
+
}
|
|
156
289
|
default: {
|
|
157
290
|
return null;
|
|
158
291
|
}
|
|
159
292
|
}
|
|
160
293
|
}
|
|
294
|
+
function onCueChange() {
|
|
295
|
+
Array.from(videoElement.textTracks).forEach(function(track) {
|
|
296
|
+
Array.from(track.activeCues || []).forEach(function(cue) {
|
|
297
|
+
cue.snapToLines = false;
|
|
298
|
+
cue.line = 100 - subtitlesOffset;
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
}
|
|
161
302
|
function onVideoError() {
|
|
162
303
|
if (destroyed) {
|
|
163
304
|
return;
|
|
@@ -226,6 +367,96 @@ function HTMLVideo(options) {
|
|
|
226
367
|
|
|
227
368
|
break;
|
|
228
369
|
}
|
|
370
|
+
case 'selectedSubtitlesTrackId': {
|
|
371
|
+
if (stream !== null) {
|
|
372
|
+
Array.from(videoElement.textTracks)
|
|
373
|
+
.forEach(function(track, index) {
|
|
374
|
+
track.mode = 'EMBEDDED_' + String(index) === propValue ? 'showing' : 'disabled';
|
|
375
|
+
});
|
|
376
|
+
var selecterdSubtitlesTrack = getProp('subtitlesTracks')
|
|
377
|
+
.find(function(track) {
|
|
378
|
+
return track.id === propValue;
|
|
379
|
+
});
|
|
380
|
+
if (selecterdSubtitlesTrack) {
|
|
381
|
+
events.emit('subtitlesTrackLoaded', selecterdSubtitlesTrack);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
case 'subtitlesOffset': {
|
|
388
|
+
if (propValue !== null && isFinite(propValue)) {
|
|
389
|
+
subtitlesOffset = Math.max(0, Math.min(100, parseInt(propValue, 10)));
|
|
390
|
+
onCueChange();
|
|
391
|
+
onPropChanged('subtitlesOffset');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
case 'subtitlesSize': {
|
|
397
|
+
if (propValue !== null && isFinite(propValue)) {
|
|
398
|
+
styleElement.sheet.cssRules[0].style.fontSize = Math.floor(Math.max(0, parseInt(propValue, 10)) / 25) + 'vmin';
|
|
399
|
+
onPropChanged('subtitlesSize');
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
case 'subtitlesTextColor': {
|
|
405
|
+
if (typeof propValue === 'string') {
|
|
406
|
+
try {
|
|
407
|
+
styleElement.sheet.cssRules[0].style.color = Color(propValue).rgb().string();
|
|
408
|
+
} catch (error) {
|
|
409
|
+
// eslint-disable-next-line no-console
|
|
410
|
+
console.error('HTMLVideo', error);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
onPropChanged('subtitlesTextColor');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
case 'subtitlesBackgroundColor': {
|
|
419
|
+
if (typeof propValue === 'string') {
|
|
420
|
+
try {
|
|
421
|
+
styleElement.sheet.cssRules[0].style.backgroundColor = Color(propValue).rgb().string();
|
|
422
|
+
} catch (error) {
|
|
423
|
+
// eslint-disable-next-line no-console
|
|
424
|
+
console.error('HTMLVideo', error);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
onPropChanged('subtitlesBackgroundColor');
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
case 'subtitlesOutlineColor': {
|
|
433
|
+
if (typeof propValue === 'string') {
|
|
434
|
+
try {
|
|
435
|
+
styleElement.sheet.cssRules[0].style.textShadow = Color(propValue).rgb().string() + ' 1px 1px 0.1em';
|
|
436
|
+
} catch (error) {
|
|
437
|
+
// eslint-disable-next-line no-console
|
|
438
|
+
console.error('HTMLVideo', error);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
onPropChanged('subtitlesOutlineColor');
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
case 'selectedAudioTrackId': {
|
|
447
|
+
if (hls !== null) {
|
|
448
|
+
var selecterdAudioTrack = getProp('audioTracks')
|
|
449
|
+
.find(function(track) {
|
|
450
|
+
return track.id === propValue;
|
|
451
|
+
});
|
|
452
|
+
hls.audioTrack = selecterdAudioTrack ? parseInt(selecterdAudioTrack.id.split('_').pop(), 10) : -1;
|
|
453
|
+
if (selecterdAudioTrack) {
|
|
454
|
+
events.emit('audioTrackLoaded', selecterdAudioTrack);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
229
460
|
case 'volume': {
|
|
230
461
|
if (propValue !== null && isFinite(propValue)) {
|
|
231
462
|
videoElement.muted = false;
|
|
@@ -238,6 +469,13 @@ function HTMLVideo(options) {
|
|
|
238
469
|
videoElement.muted = !!propValue;
|
|
239
470
|
break;
|
|
240
471
|
}
|
|
472
|
+
case 'playbackSpeed': {
|
|
473
|
+
if (propValue !== null && isFinite(propValue)) {
|
|
474
|
+
videoElement.playbackRate = parseFloat(propValue);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
241
479
|
}
|
|
242
480
|
}
|
|
243
481
|
function command(commandName, commandArgs) {
|
|
@@ -254,6 +492,10 @@ function HTMLVideo(options) {
|
|
|
254
492
|
onPropChanged('duration');
|
|
255
493
|
onPropChanged('buffering');
|
|
256
494
|
onPropChanged('buffered');
|
|
495
|
+
onPropChanged('subtitlesTracks');
|
|
496
|
+
onPropChanged('selectedSubtitlesTrackId');
|
|
497
|
+
onPropChanged('audioTracks');
|
|
498
|
+
onPropChanged('selectedAudioTrackId');
|
|
257
499
|
getContentType(stream)
|
|
258
500
|
.then(function(contentType) {
|
|
259
501
|
if (stream !== commandArgs.stream) {
|
|
@@ -262,6 +504,14 @@ function HTMLVideo(options) {
|
|
|
262
504
|
|
|
263
505
|
if (contentType === 'application/vnd.apple.mpegurl' && Hls.isSupported()) {
|
|
264
506
|
hls = new Hls(HLS_CONFIG);
|
|
507
|
+
hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, function() {
|
|
508
|
+
onPropChanged('audioTracks');
|
|
509
|
+
onPropChanged('selectedAudioTrackId');
|
|
510
|
+
});
|
|
511
|
+
hls.on(Hls.Events.AUDIO_TRACK_SWITCHED, function() {
|
|
512
|
+
onPropChanged('audioTracks');
|
|
513
|
+
onPropChanged('selectedAudioTrackId');
|
|
514
|
+
});
|
|
265
515
|
hls.loadSource(stream.url);
|
|
266
516
|
hls.attachMedia(videoElement);
|
|
267
517
|
} else {
|
|
@@ -285,7 +535,12 @@ function HTMLVideo(options) {
|
|
|
285
535
|
}
|
|
286
536
|
case 'unload': {
|
|
287
537
|
stream = null;
|
|
538
|
+
Array.from(videoElement.textTracks).forEach(function(track) {
|
|
539
|
+
track.oncuechange = null;
|
|
540
|
+
});
|
|
288
541
|
if (hls !== null) {
|
|
542
|
+
hls.removeAllListeners();
|
|
543
|
+
hls.detachMedia(videoElement);
|
|
289
544
|
hls.destroy();
|
|
290
545
|
hls = null;
|
|
291
546
|
}
|
|
@@ -298,13 +553,23 @@ function HTMLVideo(options) {
|
|
|
298
553
|
onPropChanged('duration');
|
|
299
554
|
onPropChanged('buffering');
|
|
300
555
|
onPropChanged('buffered');
|
|
556
|
+
onPropChanged('subtitlesTracks');
|
|
557
|
+
onPropChanged('selectedSubtitlesTrackId');
|
|
558
|
+
onPropChanged('audioTracks');
|
|
559
|
+
onPropChanged('selectedAudioTrackId');
|
|
301
560
|
break;
|
|
302
561
|
}
|
|
303
562
|
case 'destroy': {
|
|
304
563
|
command('unload');
|
|
305
564
|
destroyed = true;
|
|
565
|
+
onPropChanged('subtitlesOffset');
|
|
566
|
+
onPropChanged('subtitlesSize');
|
|
567
|
+
onPropChanged('subtitlesTextColor');
|
|
568
|
+
onPropChanged('subtitlesBackgroundColor');
|
|
569
|
+
onPropChanged('subtitlesOutlineColor');
|
|
306
570
|
onPropChanged('volume');
|
|
307
571
|
onPropChanged('muted');
|
|
572
|
+
onPropChanged('playbackSpeed');
|
|
308
573
|
events.removeAllListeners();
|
|
309
574
|
videoElement.onerror = null;
|
|
310
575
|
videoElement.onended = null;
|
|
@@ -321,7 +586,10 @@ function HTMLVideo(options) {
|
|
|
321
586
|
videoElement.canplaythrough = null;
|
|
322
587
|
videoElement.onloadeddata = null;
|
|
323
588
|
videoElement.onvolumechange = null;
|
|
589
|
+
videoElement.onratechange = null;
|
|
590
|
+
videoElement.textTracks.onchange = null;
|
|
324
591
|
containerElement.removeChild(videoElement);
|
|
592
|
+
containerElement.removeChild(styleElement);
|
|
325
593
|
break;
|
|
326
594
|
}
|
|
327
595
|
}
|
|
@@ -379,9 +647,9 @@ HTMLVideo.canPlayStream = function(stream) {
|
|
|
379
647
|
HTMLVideo.manifest = {
|
|
380
648
|
name: 'HTMLVideo',
|
|
381
649
|
external: false,
|
|
382
|
-
props: ['stream', 'paused', 'time', 'duration', 'buffering', 'buffered', 'volume', 'muted'],
|
|
650
|
+
props: ['stream', 'paused', 'time', 'duration', 'buffering', 'buffered', 'audioTracks', 'selectedAudioTrackId', 'subtitlesTracks', 'selectedSubtitlesTrackId', 'subtitlesOffset', 'subtitlesSize', 'subtitlesTextColor', 'subtitlesBackgroundColor', 'subtitlesOutlineColor', 'volume', 'muted', 'playbackSpeed'],
|
|
383
651
|
commands: ['load', 'unload', 'destroy'],
|
|
384
|
-
events: ['propValue', 'propChanged', 'ended', 'error']
|
|
652
|
+
events: ['propValue', 'propChanged', 'ended', 'error', 'subtitlesTrackLoaded', 'audioTrackLoaded']
|
|
385
653
|
};
|
|
386
654
|
|
|
387
655
|
module.exports = HTMLVideo;
|
|
@@ -9,7 +9,11 @@ function getContentType(stream) {
|
|
|
9
9
|
|
|
10
10
|
return fetch(stream.url, { method: 'HEAD' })
|
|
11
11
|
.then(function(resp) {
|
|
12
|
-
|
|
12
|
+
if (resp.ok) {
|
|
13
|
+
return resp.headers.get('content-type');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
throw new Error(resp.status + ' (' + resp.statusText + ')');
|
|
13
17
|
});
|
|
14
18
|
}
|
|
15
19
|
|
|
@@ -243,7 +243,7 @@ function YouTubeVideo(options) {
|
|
|
243
243
|
})
|
|
244
244
|
.map(function(track, index) {
|
|
245
245
|
return Object.freeze({
|
|
246
|
-
id: String(index),
|
|
246
|
+
id: 'EMBEDDED_' + String(index),
|
|
247
247
|
lang: track.languageCode,
|
|
248
248
|
label: typeof track.displayName === 'string' ? track.displayName : track.languageCode,
|
|
249
249
|
origin: 'EMBEDDED',
|
|
@@ -10,7 +10,7 @@ function srt2webvtt(data) {
|
|
|
10
10
|
var result = '';
|
|
11
11
|
if (cuelist.length > 0) {
|
|
12
12
|
result += 'WEBVTT\n\n';
|
|
13
|
-
for (var i = 0; i < cuelist.length; i=i+1) {
|
|
13
|
+
for (var i = 0; i < cuelist.length; i = i + 1) {
|
|
14
14
|
result += convertSrtCue(cuelist[i]);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -41,8 +41,8 @@ function convertSrtCue(caption) {
|
|
|
41
41
|
// convert time string
|
|
42
42
|
var m = s[1].match(/(\d+):(\d+):(\d+)(?:,(\d+))?\s*--?>\s*(\d+):(\d+):(\d+)(?:,(\d+))?/);
|
|
43
43
|
if (m) {
|
|
44
|
-
cue += m[1]+':'+m[2]+':'+m[3]+'.'+m[4]+' --> '
|
|
45
|
-
|
|
44
|
+
cue += m[1] + ':' + m[2] + ':' + m[3] + '.' + m[4] + ' --> '
|
|
45
|
+
+ m[5] + ':' + m[6] + ':' + m[7] + '.' + m[8] + '\n';
|
|
46
46
|
line += 1;
|
|
47
47
|
} else {
|
|
48
48
|
// Unrecognized timestring
|
|
@@ -62,6 +62,14 @@ function convertSrtCue(caption) {
|
|
|
62
62
|
module.exports = {
|
|
63
63
|
convert: function(text) {
|
|
64
64
|
// presume all to be SRT if not WEBVTT
|
|
65
|
-
|
|
65
|
+
if (text.includes('WEBVTT')) {
|
|
66
|
+
return text;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
return srt2webvtt(text);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
throw new Error('Failed to convert srt to webvtt: ' + error.message);
|
|
73
|
+
}
|
|
66
74
|
}
|
|
67
75
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
var EventEmitter = require('eventemitter3');
|
|
2
2
|
var cloneDeep = require('lodash.clonedeep');
|
|
3
3
|
var deepFreeze = require('deep-freeze');
|
|
4
|
+
var Color = require('color');
|
|
4
5
|
var ERROR = require('../error');
|
|
5
6
|
var subtitlesParser = require('./subtitlesParser');
|
|
6
7
|
var subtitlesRenderer = require('./subtitlesRenderer');
|
|
@@ -49,9 +50,9 @@ function withHTMLSubtitles(Video) {
|
|
|
49
50
|
var delay = null;
|
|
50
51
|
var size = 100;
|
|
51
52
|
var offset = 0;
|
|
52
|
-
var textColor = '
|
|
53
|
-
var backgroundColor = '
|
|
54
|
-
var
|
|
53
|
+
var textColor = 'rgb(255, 255, 255)';
|
|
54
|
+
var backgroundColor = 'rgba(0, 0, 0, 0)';
|
|
55
|
+
var outlineColor = 'rgb(34, 34, 34)';
|
|
55
56
|
var observedProps = {
|
|
56
57
|
extraSubtitlesTracks: false,
|
|
57
58
|
selectedExtraSubtitlesTrackId: false,
|
|
@@ -60,7 +61,7 @@ function withHTMLSubtitles(Video) {
|
|
|
60
61
|
extraSubtitlesOffset: false,
|
|
61
62
|
extraSubtitlesTextColor: false,
|
|
62
63
|
extraSubtitlesBackgroundColor: false,
|
|
63
|
-
|
|
64
|
+
extraSubtitlesOutlineColor: false
|
|
64
65
|
};
|
|
65
66
|
|
|
66
67
|
function renderSubtitles() {
|
|
@@ -79,8 +80,9 @@ function withHTMLSubtitles(Video) {
|
|
|
79
80
|
cueNode.style.fontSize = Math.floor(size / 25) + 'vmin';
|
|
80
81
|
cueNode.style.color = textColor;
|
|
81
82
|
cueNode.style.backgroundColor = backgroundColor;
|
|
82
|
-
cueNode.style.textShadow = '1px 1px 0.1em ' +
|
|
83
|
-
subtitlesElement.
|
|
83
|
+
cueNode.style.textShadow = '1px 1px 0.1em ' + outlineColor;
|
|
84
|
+
subtitlesElement.appendChild(cueNode);
|
|
85
|
+
subtitlesElement.appendChild(document.createElement('br'));
|
|
84
86
|
});
|
|
85
87
|
}
|
|
86
88
|
function onVideoError(error) {
|
|
@@ -168,12 +170,12 @@ function withHTMLSubtitles(Video) {
|
|
|
168
170
|
|
|
169
171
|
return backgroundColor;
|
|
170
172
|
}
|
|
171
|
-
case '
|
|
173
|
+
case 'extraSubtitlesOutlineColor': {
|
|
172
174
|
if (destroyed) {
|
|
173
175
|
return null;
|
|
174
176
|
}
|
|
175
177
|
|
|
176
|
-
return
|
|
178
|
+
return outlineColor;
|
|
177
179
|
}
|
|
178
180
|
default: {
|
|
179
181
|
return videoPropValue;
|
|
@@ -189,7 +191,7 @@ function withHTMLSubtitles(Video) {
|
|
|
189
191
|
case 'extraSubtitlesOffset':
|
|
190
192
|
case 'extraSubtitlesTextColor':
|
|
191
193
|
case 'extraSubtitlesBackgroundColor':
|
|
192
|
-
case '
|
|
194
|
+
case 'extraSubtitlesOutlineColor': {
|
|
193
195
|
events.emit('propValue', propName, getProp(propName, null));
|
|
194
196
|
observedProps[propName] = true;
|
|
195
197
|
return true;
|
|
@@ -213,7 +215,11 @@ function withHTMLSubtitles(Video) {
|
|
|
213
215
|
delay = 0;
|
|
214
216
|
fetch(selectedTrack.url)
|
|
215
217
|
.then(function(resp) {
|
|
216
|
-
|
|
218
|
+
if (resp.ok) {
|
|
219
|
+
return resp.text();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
throw new Error(resp.status + ' (' + resp.statusText + ')');
|
|
217
223
|
})
|
|
218
224
|
.then(function(text) {
|
|
219
225
|
return subtitlesConverter.convert(text);
|
|
@@ -276,7 +282,13 @@ function withHTMLSubtitles(Video) {
|
|
|
276
282
|
}
|
|
277
283
|
case 'extraSubtitlesTextColor': {
|
|
278
284
|
if (typeof propValue === 'string') {
|
|
279
|
-
|
|
285
|
+
try {
|
|
286
|
+
textColor = Color(propValue).rgb().string();
|
|
287
|
+
} catch (error) {
|
|
288
|
+
// eslint-disable-next-line no-console
|
|
289
|
+
console.error('withHTMLSubtitles', error);
|
|
290
|
+
}
|
|
291
|
+
|
|
280
292
|
renderSubtitles();
|
|
281
293
|
onPropChanged('extraSubtitlesTextColor');
|
|
282
294
|
}
|
|
@@ -285,18 +297,30 @@ function withHTMLSubtitles(Video) {
|
|
|
285
297
|
}
|
|
286
298
|
case 'extraSubtitlesBackgroundColor': {
|
|
287
299
|
if (typeof propValue === 'string') {
|
|
288
|
-
|
|
300
|
+
try {
|
|
301
|
+
backgroundColor = Color(propValue).rgb().string();
|
|
302
|
+
} catch (error) {
|
|
303
|
+
// eslint-disable-next-line no-console
|
|
304
|
+
console.error('withHTMLSubtitles', error);
|
|
305
|
+
}
|
|
306
|
+
|
|
289
307
|
renderSubtitles();
|
|
290
308
|
onPropChanged('extraSubtitlesBackgroundColor');
|
|
291
309
|
}
|
|
292
310
|
|
|
293
311
|
return true;
|
|
294
312
|
}
|
|
295
|
-
case '
|
|
313
|
+
case 'extraSubtitlesOutlineColor': {
|
|
296
314
|
if (typeof propValue === 'string') {
|
|
297
|
-
|
|
315
|
+
try {
|
|
316
|
+
outlineColor = Color(propValue).rgb().string();
|
|
317
|
+
} catch (error) {
|
|
318
|
+
// eslint-disable-next-line no-console
|
|
319
|
+
console.error('withHTMLSubtitles', error);
|
|
320
|
+
}
|
|
321
|
+
|
|
298
322
|
renderSubtitles();
|
|
299
|
-
onPropChanged('
|
|
323
|
+
onPropChanged('extraSubtitlesOutlineColor');
|
|
300
324
|
}
|
|
301
325
|
|
|
302
326
|
return true;
|
|
@@ -361,7 +385,7 @@ function withHTMLSubtitles(Video) {
|
|
|
361
385
|
onPropChanged('extraSubtitlesOffset');
|
|
362
386
|
onPropChanged('extraSubtitlesTextColor');
|
|
363
387
|
onPropChanged('extraSubtitlesBackgroundColor');
|
|
364
|
-
onPropChanged('
|
|
388
|
+
onPropChanged('extraSubtitlesOutlineColor');
|
|
365
389
|
video.dispatch({ type: 'command', commandName: 'destroy' });
|
|
366
390
|
events.removeAllListeners();
|
|
367
391
|
containerElement.removeChild(subtitlesElement);
|
|
@@ -423,7 +447,7 @@ function withHTMLSubtitles(Video) {
|
|
|
423
447
|
VideoWithHTMLSubtitles.manifest = {
|
|
424
448
|
name: Video.manifest.name + 'WithHTMLSubtitles',
|
|
425
449
|
external: Video.manifest.external,
|
|
426
|
-
props: Video.manifest.props.concat(['extraSubtitlesTracks', 'selectedExtraSubtitlesTrackId', 'extraSubtitlesDelay', 'extraSubtitlesSize', 'extraSubtitlesOffset', 'extraSubtitlesTextColor', 'extraSubtitlesBackgroundColor', '
|
|
450
|
+
props: Video.manifest.props.concat(['extraSubtitlesTracks', 'selectedExtraSubtitlesTrackId', 'extraSubtitlesDelay', 'extraSubtitlesSize', 'extraSubtitlesOffset', 'extraSubtitlesTextColor', 'extraSubtitlesBackgroundColor', 'extraSubtitlesOutlineColor'])
|
|
427
451
|
.filter(function(value, index, array) { return array.indexOf(value) === index; }),
|
|
428
452
|
commands: Video.manifest.commands.concat(['load', 'unload', 'destroy', 'addExtraSubtitlesTracks'])
|
|
429
453
|
.filter(function(value, index, array) { return array.indexOf(value) === index; }),
|
|
@@ -20,7 +20,11 @@ function inferTorrentFileIdx(streamingServerURL, infoHash, sources, seriesInfo)
|
|
|
20
20
|
}
|
|
21
21
|
})
|
|
22
22
|
}).then(function(resp) {
|
|
23
|
-
|
|
23
|
+
if (resp.ok) {
|
|
24
|
+
return resp.json();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
throw new Error(resp.status + ' (' + resp.statusText + ')');
|
|
24
28
|
}).then(function(resp) {
|
|
25
29
|
if (!resp || !Array.isArray(resp.files) || resp.files.some(function(file) { return !file || typeof file.path !== 'string' || file.length === null || !isFinite(file.length); })) {
|
|
26
30
|
throw new Error('No files found in the torrent');
|
|
@@ -113,8 +113,8 @@ function withStreamingServer(Video) {
|
|
|
113
113
|
if (commandArgs.forceTranscoding) {
|
|
114
114
|
queryParams.set('forceTranscoding', '1');
|
|
115
115
|
}
|
|
116
|
-
if (commandArgs.
|
|
117
|
-
queryParams.set('
|
|
116
|
+
if (commandArgs.maxAudioChannels !== null && isFinite(commandArgs.maxAudioChannels)) {
|
|
117
|
+
queryParams.set('maxAudioChannels', commandArgs.maxAudioChannels);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
return {
|