@stremio/stremio-video 0.0.8 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -5
- package/src/ChromecastSenderVideo/ChromecastSenderVideo.js +15 -8
- package/src/HTMLVideo/HTMLVideo.js +7 -17
- package/src/HTMLVideo/getContentType.js +16 -0
- package/src/HTMLVideo/hlsConfig.js +15 -0
- package/src/IFrameVideo/IFrameVideo.js +130 -0
- package/src/IFrameVideo/index.js +3 -0
- package/src/StremioVideo/StremioVideo.js +11 -5
- package/src/StremioVideo/selectVideoImplementation.js +36 -0
- package/src/YouTubeVideo/YouTubeVideo.js +12 -9
- package/src/error.js +5 -25
- package/src/index.js +1 -13
- package/src/withHTMLSubtitles/subtitlesParser.js +69 -40
- package/src/withHTMLSubtitles/withHTMLSubtitles.js +59 -50
- package/src/withStreamingServer/convertStream.js +61 -0
- package/src/withStreamingServer/inferTorrentFileIdx.js +63 -0
- package/src/withStreamingServer/withStreamingServer.js +101 -189
- package/src/withHTMLSubtitles/fetchSubtitles.js +0 -27
- package/src/withStreamingServer/convertStreamToURL.js +0 -142
- package/src/withStreamingServer/createTranscoder.js +0 -46
- package/src/withStreamingServer/transcodeNextSegment.js +0 -17
|
@@ -1,92 +1,42 @@
|
|
|
1
1
|
var EventEmitter = require('eventemitter3');
|
|
2
|
+
var url = require('url');
|
|
3
|
+
var hat = require('hat');
|
|
2
4
|
var cloneDeep = require('lodash.clonedeep');
|
|
3
5
|
var deepFreeze = require('deep-freeze');
|
|
4
|
-
var
|
|
5
|
-
var createTranscoder = require('./createTranscoder');
|
|
6
|
-
var transcodeNextSegment = require('./transcodeNextSegment');
|
|
6
|
+
var convertStream = require('./convertStream');
|
|
7
7
|
var ERROR = require('../error');
|
|
8
8
|
|
|
9
|
-
var STARVATION_THRESHOLD = 25000;
|
|
10
|
-
var STARVATION_TIMEOUT = 1000;
|
|
11
|
-
|
|
12
9
|
function withStreamingServer(Video) {
|
|
13
10
|
function VideoWithStreamingServer(options) {
|
|
14
11
|
options = options || {};
|
|
15
12
|
|
|
16
13
|
var video = new Video(options);
|
|
17
14
|
video.on('error', onVideoError);
|
|
18
|
-
video.on('
|
|
19
|
-
video.on('
|
|
15
|
+
video.on('propValue', onVideoPropEvent.bind(null, 'propValue'));
|
|
16
|
+
video.on('propChanged', onVideoPropEvent.bind(null, 'propChanged'));
|
|
20
17
|
Video.manifest.events
|
|
21
18
|
.filter(function(eventName) {
|
|
22
|
-
return !['error', '
|
|
19
|
+
return !['error', 'propValue', 'propChanged'].includes(eventName);
|
|
23
20
|
})
|
|
24
21
|
.forEach(function(eventName) {
|
|
25
|
-
video.on(eventName,
|
|
22
|
+
video.on(eventName, onOtherVideoEvent(eventName));
|
|
26
23
|
});
|
|
27
24
|
|
|
28
|
-
var
|
|
29
|
-
time: null,
|
|
30
|
-
duration: null
|
|
31
|
-
};
|
|
25
|
+
var self = this;
|
|
32
26
|
var loadArgs = null;
|
|
33
|
-
var
|
|
34
|
-
var
|
|
35
|
-
var starvationHandlerTimeoutId = null;
|
|
36
|
-
var lastStarvationDuration = null;
|
|
27
|
+
var loaded = false;
|
|
28
|
+
var actionsQueue = [];
|
|
37
29
|
var events = new EventEmitter();
|
|
38
30
|
var destroyed = false;
|
|
39
31
|
var observedProps = {
|
|
40
32
|
stream: false
|
|
41
33
|
};
|
|
42
34
|
|
|
43
|
-
function
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
videoState.time !== null &&
|
|
49
|
-
videoState.duration !== null &&
|
|
50
|
-
videoState.duration !== lastStarvationDuration &&
|
|
51
|
-
videoState.time + STARVATION_THRESHOLD > videoState.duration;
|
|
52
|
-
}
|
|
53
|
-
function onStarving() {
|
|
54
|
-
transcodingNextSegment = true;
|
|
55
|
-
lastStarvationDuration = videoState.duration;
|
|
56
|
-
var loadingТranscoder = transcoder;
|
|
57
|
-
transcodeNextSegment(transcoder.streamingServerURL, transcoder.hash)
|
|
58
|
-
.then(function(resp) {
|
|
59
|
-
if (loadingТranscoder !== transcoder) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (resp.error) {
|
|
64
|
-
if (resp.error.code !== 21) {
|
|
65
|
-
throw resp.error;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
command('load', Object.assign({}, loadArgs, {
|
|
69
|
-
time: videoState.time
|
|
70
|
-
}));
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
transcoder.ended = resp.ended;
|
|
75
|
-
transcodingNextSegment = false;
|
|
76
|
-
if (isStarving()) {
|
|
77
|
-
onStarving();
|
|
78
|
-
}
|
|
79
|
-
})
|
|
80
|
-
.catch(function(error) {
|
|
81
|
-
if (loadingТranscoder !== transcoder) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
onError(Object.assign({}, ERROR.WITH_STREAMING_SERVER.TRANSCODING_FAILED, {
|
|
86
|
-
critical: true,
|
|
87
|
-
error: error
|
|
88
|
-
}));
|
|
89
|
-
});
|
|
35
|
+
function flushActionsQueue() {
|
|
36
|
+
while (actionsQueue.length > 0) {
|
|
37
|
+
var action = actionsQueue.shift();
|
|
38
|
+
self.dispatch.call(self, action);
|
|
39
|
+
}
|
|
90
40
|
}
|
|
91
41
|
function onVideoError(error) {
|
|
92
42
|
events.emit('error', error);
|
|
@@ -94,41 +44,17 @@ function withStreamingServer(Video) {
|
|
|
94
44
|
command('unload');
|
|
95
45
|
}
|
|
96
46
|
}
|
|
97
|
-
function
|
|
98
|
-
switch (propName) {
|
|
99
|
-
case 'time': {
|
|
100
|
-
videoState.time = propValue;
|
|
101
|
-
if (isStarving()) {
|
|
102
|
-
onStarving();
|
|
103
|
-
}
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
106
|
-
case 'duration': {
|
|
107
|
-
videoState.duration = propValue;
|
|
108
|
-
clearTimeout(starvationHandlerTimeoutId);
|
|
109
|
-
starvationHandlerTimeoutId = !transcodingNextSegment ?
|
|
110
|
-
setTimeout(function() {
|
|
111
|
-
starvationHandlerTimeoutId = null;
|
|
112
|
-
if (isStarving()) {
|
|
113
|
-
onStarving();
|
|
114
|
-
}
|
|
115
|
-
}, STARVATION_TIMEOUT)
|
|
116
|
-
:
|
|
117
|
-
null;
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
47
|
+
function onVideoPropEvent(eventName, propName, propValue) {
|
|
122
48
|
events.emit(eventName, propName, getProp(propName, propValue));
|
|
123
49
|
}
|
|
124
|
-
function
|
|
50
|
+
function onOtherVideoEvent(eventName) {
|
|
125
51
|
return function() {
|
|
126
52
|
events.emit.apply(events, [eventName].concat(Array.from(arguments)));
|
|
127
53
|
};
|
|
128
54
|
}
|
|
129
55
|
function onPropChanged(propName) {
|
|
130
56
|
if (observedProps[propName]) {
|
|
131
|
-
events.emit('propChanged', propName, getProp(propName));
|
|
57
|
+
events.emit('propChanged', propName, getProp(propName, null));
|
|
132
58
|
}
|
|
133
59
|
}
|
|
134
60
|
function onError(error) {
|
|
@@ -143,24 +69,6 @@ function withStreamingServer(Video) {
|
|
|
143
69
|
case 'stream': {
|
|
144
70
|
return loadArgs !== null ? loadArgs.stream : null;
|
|
145
71
|
}
|
|
146
|
-
case 'time': {
|
|
147
|
-
return videoPropValue !== null && transcoder !== null ?
|
|
148
|
-
videoPropValue + transcoder.timeOffset
|
|
149
|
-
:
|
|
150
|
-
videoPropValue;
|
|
151
|
-
}
|
|
152
|
-
case 'duration': {
|
|
153
|
-
return transcoder !== null ?
|
|
154
|
-
transcoder.duration
|
|
155
|
-
:
|
|
156
|
-
videoPropValue;
|
|
157
|
-
}
|
|
158
|
-
case 'buffered': {
|
|
159
|
-
return videoPropValue !== null && transcoder !== null ?
|
|
160
|
-
videoPropValue + transcoder.timeOffset
|
|
161
|
-
:
|
|
162
|
-
videoPropValue;
|
|
163
|
-
}
|
|
164
72
|
default: {
|
|
165
73
|
return videoPropValue;
|
|
166
74
|
}
|
|
@@ -169,7 +77,7 @@ function withStreamingServer(Video) {
|
|
|
169
77
|
function observeProp(propName) {
|
|
170
78
|
switch (propName) {
|
|
171
79
|
case 'stream': {
|
|
172
|
-
events.emit('propValue', propName, getProp(propName));
|
|
80
|
+
events.emit('propValue', propName, getProp(propName, null));
|
|
173
81
|
observedProps[propName] = true;
|
|
174
82
|
return true;
|
|
175
83
|
}
|
|
@@ -178,27 +86,6 @@ function withStreamingServer(Video) {
|
|
|
178
86
|
}
|
|
179
87
|
}
|
|
180
88
|
}
|
|
181
|
-
function setProp(propName, propValue) {
|
|
182
|
-
switch (propName) {
|
|
183
|
-
case 'time': {
|
|
184
|
-
if (transcoder !== null) {
|
|
185
|
-
if (propValue !== null && isFinite(propValue)) {
|
|
186
|
-
var commandArgs = Object.assign({}, loadArgs, {
|
|
187
|
-
time: parseInt(propValue, 10)
|
|
188
|
-
});
|
|
189
|
-
command('load', commandArgs);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return true;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return false;
|
|
196
|
-
}
|
|
197
|
-
default: {
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
89
|
function command(commandName, commandArgs) {
|
|
203
90
|
switch (commandName) {
|
|
204
91
|
case 'load': {
|
|
@@ -207,84 +94,106 @@ function withStreamingServer(Video) {
|
|
|
207
94
|
video.dispatch({ type: 'command', commandName: 'unload' });
|
|
208
95
|
loadArgs = commandArgs;
|
|
209
96
|
onPropChanged('stream');
|
|
210
|
-
|
|
97
|
+
convertStream(commandArgs.streamingServerURL, commandArgs.stream, commandArgs.seriesInfo)
|
|
211
98
|
.then(function(mediaURL) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (canPlay) {
|
|
221
|
-
return {
|
|
222
|
-
transcoder: null,
|
|
223
|
-
loadArgsExt: {
|
|
224
|
-
stream: {
|
|
225
|
-
url: mediaURL
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
}
|
|
99
|
+
var id = hat();
|
|
100
|
+
var queryParams = new URLSearchParams([['mediaURL', mediaURL]]);
|
|
101
|
+
if (commandArgs.forceTranscoding) {
|
|
102
|
+
queryParams.set('forceTranscoding', '1');
|
|
103
|
+
}
|
|
104
|
+
if (commandArgs.audioChannels !== null && isFinite(commandArgs.audioChannels)) {
|
|
105
|
+
queryParams.set('audioChannels', commandArgs.audioChannels);
|
|
106
|
+
}
|
|
230
107
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
behaviorHints: {
|
|
241
|
-
headers: {
|
|
242
|
-
'content-type': 'application/vnd.apple.mpegurl'
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
};
|
|
108
|
+
return {
|
|
109
|
+
url: url.resolve(commandArgs.streamingServerURL, '/hlsv2/' + id + '/master.m3u8?' + queryParams.toString()),
|
|
110
|
+
subtitles: Array.isArray(commandArgs.stream.subtitles) ?
|
|
111
|
+
commandArgs.stream.subtitles.map(function(track) {
|
|
112
|
+
return Object.assign({}, track, {
|
|
113
|
+
url: typeof track.url === 'string' ?
|
|
114
|
+
url.resolve(commandArgs.streamingServerURL, '/subtitles.vtt?' + new URLSearchParams([['from', track.url]]).toString())
|
|
115
|
+
:
|
|
116
|
+
track.url
|
|
248
117
|
});
|
|
249
|
-
|
|
118
|
+
})
|
|
119
|
+
:
|
|
120
|
+
[],
|
|
121
|
+
behaviorHints: {
|
|
122
|
+
headers: {
|
|
123
|
+
'content-type': 'application/vnd.apple.mpegurl'
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
250
127
|
})
|
|
251
|
-
.then(function(
|
|
128
|
+
.then(function(stream) {
|
|
252
129
|
if (commandArgs !== loadArgs) {
|
|
253
130
|
return;
|
|
254
131
|
}
|
|
255
132
|
|
|
256
|
-
transcoder = result.transcoder;
|
|
257
133
|
video.dispatch({
|
|
258
134
|
type: 'command',
|
|
259
135
|
commandName: 'load',
|
|
260
|
-
commandArgs: Object.assign({}, commandArgs,
|
|
136
|
+
commandArgs: Object.assign({}, commandArgs, {
|
|
137
|
+
stream: stream
|
|
138
|
+
})
|
|
261
139
|
});
|
|
140
|
+
loaded = true;
|
|
141
|
+
flushActionsQueue();
|
|
262
142
|
})
|
|
263
143
|
.catch(function(error) {
|
|
264
144
|
if (commandArgs !== loadArgs) {
|
|
265
145
|
return;
|
|
266
146
|
}
|
|
267
147
|
|
|
268
|
-
onError(Object.assign({},
|
|
269
|
-
|
|
148
|
+
onError(Object.assign({}, ERROR.WITH_STREAMING_SERVER.CONVERT_FAILED, {
|
|
149
|
+
error: error,
|
|
150
|
+
critical: true,
|
|
151
|
+
stream: commandArgs.stream,
|
|
152
|
+
streamingServerURL: commandArgs.streamingServerURL
|
|
270
153
|
}));
|
|
271
154
|
});
|
|
272
155
|
} else {
|
|
273
156
|
onError(Object.assign({}, ERROR.UNSUPPORTED_STREAM, {
|
|
274
157
|
critical: true,
|
|
275
|
-
stream: commandArgs ? commandArgs.stream : null
|
|
158
|
+
stream: commandArgs ? commandArgs.stream : null,
|
|
159
|
+
streamingServerURL: commandArgs && typeof commandArgs.streamingServerURL === 'string' ? commandArgs.streamingServerURL : null
|
|
276
160
|
}));
|
|
277
161
|
}
|
|
278
162
|
|
|
279
163
|
return true;
|
|
280
164
|
}
|
|
165
|
+
case 'addExtraSubtitlesTracks': {
|
|
166
|
+
if (loadArgs && commandArgs && Array.isArray(commandArgs.tracks)) {
|
|
167
|
+
if (loaded) {
|
|
168
|
+
video.dispatch({
|
|
169
|
+
type: 'command',
|
|
170
|
+
commandName: 'addExtraSubtitlesTracks',
|
|
171
|
+
commandArgs: Object.assign({}, commandArgs, {
|
|
172
|
+
tracks: commandArgs.tracks.map(function(track) {
|
|
173
|
+
return Object.assign({}, track, {
|
|
174
|
+
url: typeof track.url === 'string' ?
|
|
175
|
+
url.resolve(loadArgs.streamingServerURL, '/subtitles.vtt?' + new URLSearchParams([['from', track.url]]).toString())
|
|
176
|
+
:
|
|
177
|
+
track.url
|
|
178
|
+
});
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
});
|
|
182
|
+
} else {
|
|
183
|
+
actionsQueue.push({
|
|
184
|
+
type: 'command',
|
|
185
|
+
commandName: 'addExtraSubtitlesTracks',
|
|
186
|
+
commandArgs: commandArgs
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
281
193
|
case 'unload': {
|
|
282
|
-
clearTimeout(starvationHandlerTimeoutId);
|
|
283
194
|
loadArgs = null;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
starvationHandlerTimeoutId = null;
|
|
287
|
-
lastStarvationDuration = null;
|
|
195
|
+
loaded = false;
|
|
196
|
+
actionsQueue = [];
|
|
288
197
|
onPropChanged('stream');
|
|
289
198
|
return false;
|
|
290
199
|
}
|
|
@@ -296,6 +205,16 @@ function withStreamingServer(Video) {
|
|
|
296
205
|
return true;
|
|
297
206
|
}
|
|
298
207
|
default: {
|
|
208
|
+
if (!loaded) {
|
|
209
|
+
actionsQueue.push({
|
|
210
|
+
type: 'command',
|
|
211
|
+
commandName: commandName,
|
|
212
|
+
commandArgs: commandArgs
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
|
|
299
218
|
return false;
|
|
300
219
|
}
|
|
301
220
|
}
|
|
@@ -323,13 +242,6 @@ function withStreamingServer(Video) {
|
|
|
323
242
|
|
|
324
243
|
break;
|
|
325
244
|
}
|
|
326
|
-
case 'setProp': {
|
|
327
|
-
if (setProp(action.propName, action.propValue)) {
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
break;
|
|
332
|
-
}
|
|
333
245
|
case 'command': {
|
|
334
246
|
if (command(action.commandName, action.commandArgs)) {
|
|
335
247
|
return;
|
|
@@ -353,9 +265,9 @@ function withStreamingServer(Video) {
|
|
|
353
265
|
external: Video.manifest.external,
|
|
354
266
|
props: Video.manifest.props.concat(['stream'])
|
|
355
267
|
.filter(function(value, index, array) { return array.indexOf(value) === index; }),
|
|
356
|
-
commands: Video.manifest.commands.concat(['load', 'unload', 'destroy'])
|
|
268
|
+
commands: Video.manifest.commands.concat(['load', 'unload', 'destroy', 'addExtraSubtitlesTracks'])
|
|
357
269
|
.filter(function(value, index, array) { return array.indexOf(value) === index; }),
|
|
358
|
-
events: Video.manifest.events.concat(['error'])
|
|
270
|
+
events: Video.manifest.events.concat(['propValue', 'propChanged', 'error'])
|
|
359
271
|
.filter(function(value, index, array) { return array.indexOf(value) === index; })
|
|
360
272
|
};
|
|
361
273
|
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
var ERROR = require('../error');
|
|
2
|
-
var subtitlesParser = require('./subtitlesParser');
|
|
3
|
-
|
|
4
|
-
function fetchSubtitles(track) {
|
|
5
|
-
return fetch(track.url)
|
|
6
|
-
.then(function(resp) {
|
|
7
|
-
return resp.text();
|
|
8
|
-
})
|
|
9
|
-
.catch(function(error) {
|
|
10
|
-
throw Object.assign({}, ERROR.WITH_HTML_SUBTITLES.FETCH_FAILED, {
|
|
11
|
-
track: track,
|
|
12
|
-
error: error
|
|
13
|
-
});
|
|
14
|
-
})
|
|
15
|
-
.then(function(text) {
|
|
16
|
-
var cuesByTime = subtitlesParser.parse(text);
|
|
17
|
-
if (cuesByTime.times.length === 0) {
|
|
18
|
-
throw Object.assign({}, ERROR.WITH_HTML_SUBTITLES.PARSE_FAILED, {
|
|
19
|
-
track: track
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return cuesByTime;
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
module.exports = fetchSubtitles;
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
var url = require('url');
|
|
2
|
-
var magnet = require('magnet-uri');
|
|
3
|
-
var parseVideoName = require('video-name-parser');
|
|
4
|
-
var ERROR = require('../error');
|
|
5
|
-
|
|
6
|
-
var MEDIA_FILE_EXTENTIONS = /.mkv$|.avi$|.mp4$|.wmv$|.vp8$|.mov$|.mpg$|.ts$|.m3u8$|.webm$|.flac$|.mp3$|.wav$|.wma$|.aac$|.ogg$/i;
|
|
7
|
-
|
|
8
|
-
function inferTorrentFileIdx(streamingServerURL, infoHash, sources, seriesInfo) {
|
|
9
|
-
return fetch(url.resolve(streamingServerURL, '/' + encodeURIComponent(infoHash) + '/create'), {
|
|
10
|
-
method: 'POST',
|
|
11
|
-
headers: {
|
|
12
|
-
'content-type': 'application/json'
|
|
13
|
-
},
|
|
14
|
-
body: JSON.stringify({
|
|
15
|
-
torrent: {
|
|
16
|
-
infoHash: infoHash,
|
|
17
|
-
peerSearch: {
|
|
18
|
-
sources: ['dht:' + infoHash].concat(Array.isArray(sources) ? sources : []),
|
|
19
|
-
min: 40,
|
|
20
|
-
max: 150
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
})
|
|
24
|
-
}).then(function(resp) {
|
|
25
|
-
return resp.json();
|
|
26
|
-
}).catch(function(error) {
|
|
27
|
-
throw Object.assign({}, ERROR.WITH_STREAMING_SERVER.TORRENT_CREATE_FAILED, {
|
|
28
|
-
infoHash: infoHash,
|
|
29
|
-
sources: sources,
|
|
30
|
-
error: error
|
|
31
|
-
});
|
|
32
|
-
}).then(function(resp) {
|
|
33
|
-
if (!resp || !Array.isArray(resp.files) || resp.files.some(function(file) { return !file || typeof file.path !== 'string' || file.length === null || !isFinite(file.length); })) {
|
|
34
|
-
throw Object.assign({}, ERROR.WITH_STREAMING_SERVER.TORRENT_CREATE_FAILED, {
|
|
35
|
-
infoHash: infoHash,
|
|
36
|
-
sources: sources,
|
|
37
|
-
files: resp.files,
|
|
38
|
-
error: new Error('Invalid files')
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
var mediaFiles = resp.files.filter(function(file) {
|
|
43
|
-
return file.path.match(MEDIA_FILE_EXTENTIONS);
|
|
44
|
-
});
|
|
45
|
-
if (mediaFiles.length === 0) {
|
|
46
|
-
throw Object.assign({}, ERROR.WITH_STREAMING_SERVER.NO_MEDIA_FILES_FOUND, {
|
|
47
|
-
infoHash: infoHash,
|
|
48
|
-
sources: sources,
|
|
49
|
-
files: resp.files
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
var mediaFilesForEpisode = seriesInfo ?
|
|
54
|
-
mediaFiles.filter(function(file) {
|
|
55
|
-
try {
|
|
56
|
-
var info = parseVideoName(file.path);
|
|
57
|
-
return info.season !== null &&
|
|
58
|
-
isFinite(info.season) &&
|
|
59
|
-
info.season === seriesInfo.season &&
|
|
60
|
-
Array.isArray(info.episode) &&
|
|
61
|
-
info.episode.indexOf(seriesInfo.episode) !== -1;
|
|
62
|
-
} catch (e) {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
})
|
|
66
|
-
:
|
|
67
|
-
[];
|
|
68
|
-
var selectedFile = (mediaFilesForEpisode.length > 0 ? mediaFilesForEpisode : mediaFiles)
|
|
69
|
-
.reduce(function(result, file) {
|
|
70
|
-
if (!result || file.length > result.length) {
|
|
71
|
-
return file;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return result;
|
|
75
|
-
}, null);
|
|
76
|
-
return resp.files.indexOf(selectedFile);
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function convertStreamToURL(streamingServerURL, stream, seriesInfo) {
|
|
81
|
-
return new Promise(function(resolve, reject) {
|
|
82
|
-
if (typeof stream.url === 'string') {
|
|
83
|
-
if (stream.url.indexOf('magnet:') === 0) {
|
|
84
|
-
var parsedMagnetURI;
|
|
85
|
-
try {
|
|
86
|
-
parsedMagnetURI = magnet.decode(stream.url);
|
|
87
|
-
} catch (error) {
|
|
88
|
-
reject(Object.assign({}, ERROR.WITH_STREAMING_SERVER.STREAM_CONVERT_FAILED, {
|
|
89
|
-
stream: stream,
|
|
90
|
-
error: error
|
|
91
|
-
}));
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
if (parsedMagnetURI && typeof parsedMagnetURI.infoHash === 'string') {
|
|
95
|
-
var sources = Array.isArray(parsedMagnetURI.announce) ?
|
|
96
|
-
parsedMagnetURI.announce.map(function(source) {
|
|
97
|
-
return 'tracker:' + source;
|
|
98
|
-
})
|
|
99
|
-
:
|
|
100
|
-
[];
|
|
101
|
-
inferTorrentFileIdx(streamingServerURL, parsedMagnetURI.infoHash, sources, seriesInfo)
|
|
102
|
-
.then(function(fileIdx) {
|
|
103
|
-
resolve(url.resolve(streamingServerURL, '/' + encodeURIComponent(stream.infoHash) + '/' + encodeURIComponent(fileIdx)));
|
|
104
|
-
})
|
|
105
|
-
.catch(function(error) {
|
|
106
|
-
reject(Object.assign({}, error, {
|
|
107
|
-
stream: stream
|
|
108
|
-
}));
|
|
109
|
-
});
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
} else {
|
|
113
|
-
resolve(stream.url);
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (typeof stream.infoHash === 'string') {
|
|
119
|
-
if (stream.fileIdx !== null && isFinite(stream.fileIdx)) {
|
|
120
|
-
resolve(url.resolve(streamingServerURL, '/' + encodeURIComponent(stream.infoHash) + '/' + encodeURIComponent(stream.fileIdx)));
|
|
121
|
-
return;
|
|
122
|
-
} else {
|
|
123
|
-
inferTorrentFileIdx(streamingServerURL, stream.infoHash, stream.announce, seriesInfo)
|
|
124
|
-
.then(function(fileIdx) {
|
|
125
|
-
resolve(url.resolve(streamingServerURL, '/' + encodeURIComponent(stream.infoHash) + '/' + encodeURIComponent(fileIdx)));
|
|
126
|
-
})
|
|
127
|
-
.catch(function(error) {
|
|
128
|
-
reject(Object.assign({}, error, {
|
|
129
|
-
stream: stream
|
|
130
|
-
}));
|
|
131
|
-
});
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
reject(Object.assign({}, ERROR.WITH_STREAMING_SERVER.STREAM_CONVERT_FAILED, {
|
|
137
|
-
stream: stream
|
|
138
|
-
}));
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
module.exports = convertStreamToURL;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
var url = require('url');
|
|
2
|
-
var ERROR = require('../error');
|
|
3
|
-
|
|
4
|
-
function createTranscoder(streamingServerURL, mediaURL, time, audioChannels) {
|
|
5
|
-
var queryParams = new URLSearchParams([
|
|
6
|
-
['url', mediaURL],
|
|
7
|
-
['time', time]
|
|
8
|
-
]);
|
|
9
|
-
if (audioChannels !== null && isFinite(audioChannels)) {
|
|
10
|
-
queryParams.set('audioChannels', audioChannels);
|
|
11
|
-
}
|
|
12
|
-
return fetch(url.resolve(streamingServerURL, '/transcode/create') + '?' + queryParams.toString())
|
|
13
|
-
.then(function(resp) {
|
|
14
|
-
return resp.json();
|
|
15
|
-
})
|
|
16
|
-
.then(function(resp) {
|
|
17
|
-
if (resp.error) {
|
|
18
|
-
throw resp.error;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (typeof resp.hash !== 'string' || (resp.duration !== null && !isFinite(resp.duration)) || typeof resp.ended !== 'boolean') {
|
|
22
|
-
throw new Error('Inavalid response: ' + JSON.stringify(resp));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return Object.assign({}, resp, {
|
|
26
|
-
streamingServerURL: streamingServerURL,
|
|
27
|
-
timeOffset: resp.videoTimeOffset !== null && isFinite(resp.videoTimeOffset) ?
|
|
28
|
-
resp.videoTimeOffset
|
|
29
|
-
:
|
|
30
|
-
resp.audioTimeOffset !== null && isFinite(resp.audioTimeOffset) ?
|
|
31
|
-
resp.audioTimeOffset
|
|
32
|
-
:
|
|
33
|
-
0,
|
|
34
|
-
url: url.resolve(streamingServerURL, '/transcode/' + encodeURIComponent(resp.hash) + '/playlist.m3u8')
|
|
35
|
-
});
|
|
36
|
-
})
|
|
37
|
-
.catch(function(error) {
|
|
38
|
-
throw Object.assign({}, ERROR.WITH_STREAMING_SERVER.TRANSCODER_CREATE_FAILED, {
|
|
39
|
-
error: error,
|
|
40
|
-
mediaURL: mediaURL,
|
|
41
|
-
time: time
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
module.exports = createTranscoder;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
var url = require('url');
|
|
2
|
-
|
|
3
|
-
function transcodeNextSegment(streamingServerURL, hash) {
|
|
4
|
-
return fetch(url.resolve(streamingServerURL, '/transcode/next') + '?' + new URLSearchParams([['hash', hash]]).toString())
|
|
5
|
-
.then(function(resp) {
|
|
6
|
-
return resp.json();
|
|
7
|
-
})
|
|
8
|
-
.then(function(resp) {
|
|
9
|
-
if (!resp.error && typeof resp.ended !== 'boolean') {
|
|
10
|
-
throw new Error('Inavalid response: ' + JSON.stringify(resp));
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
return resp;
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
module.exports = transcodeNextSegment;
|