eyevinn-channel-engine 3.3.10 → 3.4.0

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.
Files changed (54) hide show
  1. package/.github/workflows/publish.yml +5 -3
  2. package/dist/engine/ad_request.d.ts +14 -0
  3. package/dist/engine/ad_request.js +121 -0
  4. package/dist/engine/chaos_monkey.d.ts +1 -0
  5. package/dist/engine/chaos_monkey.js +55 -0
  6. package/dist/engine/event_stream.d.ts +6 -0
  7. package/dist/engine/event_stream.js +20 -0
  8. package/dist/engine/index.d.ts +0 -0
  9. package/dist/engine/index.js +0 -0
  10. package/dist/engine/memcached_state_store.d.ts +12 -0
  11. package/dist/engine/memcached_state_store.js +164 -0
  12. package/dist/engine/memory_state_store.d.ts +11 -0
  13. package/dist/engine/memory_state_store.js +105 -0
  14. package/dist/engine/playhead_state.d.ts +26 -0
  15. package/dist/engine/playhead_state.js +156 -0
  16. package/dist/engine/redis_state_store.d.ts +12 -0
  17. package/dist/engine/redis_state_store.js +236 -0
  18. package/dist/engine/server.d.ts +125 -0
  19. package/dist/engine/server.js +1113 -0
  20. package/dist/engine/session.d.ts +109 -0
  21. package/dist/engine/session.js +2194 -0
  22. package/dist/engine/session_live.d.ts +119 -0
  23. package/dist/engine/session_live.js +1723 -0
  24. package/dist/engine/session_live_state.d.ts +24 -0
  25. package/dist/engine/session_live_state.js +231 -0
  26. package/dist/engine/session_state.d.ts +45 -0
  27. package/dist/engine/session_state.js +365 -0
  28. package/dist/engine/shared_state_store.d.ts +21 -0
  29. package/dist/engine/shared_state_store.js +170 -0
  30. package/dist/engine/stream_switcher.d.ts +29 -0
  31. package/dist/engine/stream_switcher.js +813 -0
  32. package/dist/engine/util.d.ts +19 -0
  33. package/dist/engine/util.js +281 -0
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.js +5 -0
  36. package/dist/package.json +47 -0
  37. package/dist/server-demux.d.ts +1 -0
  38. package/dist/server-demux.js +103 -0
  39. package/dist/server-livemix.d.ts +1 -0
  40. package/dist/server-livemix.js +196 -0
  41. package/dist/server.d.ts +1 -0
  42. package/dist/server.js +101 -0
  43. package/engine/index.ts +0 -0
  44. package/engine/{server.js → server.ts} +128 -6
  45. package/engine/session.js +189 -174
  46. package/index.ts +12 -0
  47. package/package.json +11 -7
  48. package/{server-demux.js → server-demux.ts} +16 -10
  49. package/{server-livemix.js → server-livemix.ts} +21 -13
  50. package/{server.js → server.ts} +13 -8
  51. package/spec/engine/engine_spec.js +1 -1
  52. package/spec/engine/interface_spec.js +1 -1
  53. package/tsconfig.json +10 -0
  54. package/index.js +0 -3
@@ -0,0 +1,1113 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (_) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.ChannelEngine = exports.ScheduleStreamType = void 0;
40
+ var restify = require('restify');
41
+ var errs = require('restify-errors');
42
+ var uuidv4 = require('uuid').v4;
43
+ var debug = require('debug')('engine-server');
44
+ var verbose = require('debug')('engine-server-verbose');
45
+ var Session = require('./session.js');
46
+ var SessionLive = require('./session_live.js');
47
+ var StreamSwitcher = require('./stream_switcher.js');
48
+ var EventStream = require('./event_stream.js');
49
+ var SessionStateStore = require('./session_state.js').SessionStateStore;
50
+ var SessionLiveStateStore = require('./session_live_state.js').SessionLiveStateStore;
51
+ var PlayheadStateStore = require('./playhead_state.js').PlayheadStateStore;
52
+ var _a = require('./util.js'), filterQueryParser = _a.filterQueryParser, toHHMMSS = _a.toHHMMSS, WaitTimeGenerator = _a.WaitTimeGenerator;
53
+ var version = require('../package.json').version;
54
+ var timer = function (ms) { return new Promise(function (res) { return setTimeout(res, ms); }); };
55
+ var sessions = {}; // Should be a persistent store...
56
+ var sessionsLive = {}; // Should be a persistent store...
57
+ var sessionSwitchers = {}; // Should be a persistent store...
58
+ var switcherStatus = {}; // Should be a persistent store...
59
+ var eventStreams = {};
60
+ var ScheduleStreamType;
61
+ (function (ScheduleStreamType) {
62
+ ScheduleStreamType[ScheduleStreamType["LIVE"] = 1] = "LIVE";
63
+ ScheduleStreamType[ScheduleStreamType["VOD"] = 2] = "VOD";
64
+ })(ScheduleStreamType = exports.ScheduleStreamType || (exports.ScheduleStreamType = {}));
65
+ ;
66
+ var ChannelEngine = /** @class */ (function () {
67
+ function ChannelEngine(assetMgr, options) {
68
+ var _this = this;
69
+ this.options = options;
70
+ if (options && options.adCopyMgrUri) {
71
+ this.adCopyMgrUri = options.adCopyMgrUri;
72
+ }
73
+ if (options && options.adXchangeUri) {
74
+ this.adXchangeUri = options.adXchangeUri;
75
+ }
76
+ this.useDemuxedAudio = false;
77
+ if (options && options.useDemuxedAudio) {
78
+ this.useDemuxedAudio = true;
79
+ }
80
+ this.alwaysNewSegments = false;
81
+ if (options && options.alwaysNewSegments) {
82
+ this.alwaysNewSegments = true;
83
+ }
84
+ if (options && options.defaultSlateUri) {
85
+ this.defaultSlateUri = options.defaultSlateUri;
86
+ this.slateRepetitions = options.slateRepetitions || 10;
87
+ this.slateDuration = options.slateDuration || 4000;
88
+ }
89
+ if (options && options.streamSwitchManager) {
90
+ this.streamSwitchManager = options.streamSwitchManager;
91
+ }
92
+ this.assetMgr = assetMgr;
93
+ this.monitorTimer = {};
94
+ this.server = restify.createServer();
95
+ this.server.use(restify.plugins.queryParser());
96
+ this.serverStartTime = Date.now();
97
+ this.instanceId = uuidv4();
98
+ this.streamSwitchTimeIntervalMs = 3000;
99
+ this.sessionStore = {
100
+ sessionStateStore: new SessionStateStore({
101
+ redisUrl: options.redisUrl,
102
+ memcachedUrl: options.memcachedUrl,
103
+ cacheTTL: options.sharedStoreCacheTTL,
104
+ }),
105
+ playheadStateStore: new PlayheadStateStore({
106
+ redisUrl: options.redisUrl,
107
+ memcachedUrl: options.memcachedUrl,
108
+ cacheTTL: options.sharedStoreCacheTTL,
109
+ }),
110
+ instanceId: this.instanceId,
111
+ };
112
+ this.sessionLiveStore = {
113
+ sessionLiveStateStore: new SessionLiveStateStore({
114
+ redisUrl: options.redisUrl,
115
+ memcachedUrl: options.memcachedUrl,
116
+ cacheTTL: options.sharedStoreCacheTTL,
117
+ }),
118
+ instanceId: this.instanceId,
119
+ };
120
+ if (options && options.staticDirectory) {
121
+ this.server.get('/', restify.plugins.serveStatic({
122
+ directory: options.staticDirectory,
123
+ default: 'index.html'
124
+ }));
125
+ }
126
+ this.streamerOpts = {};
127
+ if (options && options.averageSegmentDuration) {
128
+ this.streamerOpts.defaultAverageSegmentDuration = options.averageSegmentDuration;
129
+ }
130
+ if (options && options.cacheTTL) {
131
+ this.streamerOpts.cacheTTL = options.cacheTTL;
132
+ }
133
+ this.logCloudWatchMetrics = false;
134
+ if (options && options.cloudWatchMetrics) {
135
+ this.logCloudWatchMetrics = true;
136
+ }
137
+ if (options && options.playheadDiffThreshold) {
138
+ this.streamerOpts.defaultPlayheadDiffThreshold = options.playheadDiffThreshold;
139
+ }
140
+ if (options && options.maxTickInterval) {
141
+ this.streamerOpts.defaultMaxTickInterval = options.maxTickInterval;
142
+ }
143
+ if (options && options.targetDurationPadding) {
144
+ this.streamerOpts.targetDurationPadding = options.targetDurationPadding;
145
+ }
146
+ if (options && options.forceTargetDuration) {
147
+ this.streamerOpts.forceTargetDuration = options.forceTargetDuration;
148
+ }
149
+ if (options && options.diffCompensationRate) {
150
+ this.streamerOpts.diffCompensationRate = options.diffCompensationRate;
151
+ }
152
+ var handleMasterRoute = function (req, res, next) { return __awaiter(_this, void 0, void 0, function () {
153
+ var m;
154
+ return __generator(this, function (_a) {
155
+ switch (_a.label) {
156
+ case 0:
157
+ debug(req.params);
158
+ if (!req.params.file.match(/master.m3u8/)) return [3 /*break*/, 2];
159
+ return [4 /*yield*/, this._handleMasterManifest(req, res, next)];
160
+ case 1:
161
+ _a.sent();
162
+ return [3 /*break*/, 6];
163
+ case 2:
164
+ if (!(m = req.params.file.match(/master(\d+).m3u8;session=(.*)$/))) return [3 /*break*/, 4];
165
+ req.params[0] = m[1];
166
+ req.params[1] = m[2];
167
+ return [4 /*yield*/, this._handleMediaManifest(req, res, next)];
168
+ case 3:
169
+ _a.sent();
170
+ return [3 /*break*/, 6];
171
+ case 4:
172
+ if (!(m = req.params.file.match(/master-(\S+)_(\S+).m3u8;session=(.*)$/))) return [3 /*break*/, 6];
173
+ req.params[0] = m[1];
174
+ req.params[1] = m[2];
175
+ req.params[2] = m[3];
176
+ return [4 /*yield*/, this._handleAudioManifest(req, res, next)];
177
+ case 5:
178
+ _a.sent();
179
+ _a.label = 6;
180
+ case 6: return [2 /*return*/];
181
+ }
182
+ });
183
+ }); };
184
+ this.server.get('/live/:file', function (req, res, next) { return __awaiter(_this, void 0, void 0, function () {
185
+ return __generator(this, function (_a) {
186
+ switch (_a.label) {
187
+ case 0: return [4 /*yield*/, handleMasterRoute(req, res, next)];
188
+ case 1:
189
+ _a.sent();
190
+ return [2 /*return*/];
191
+ }
192
+ });
193
+ }); });
194
+ this.server.get('/channels/:channelId/:file', function (req, res, next) { return __awaiter(_this, void 0, void 0, function () {
195
+ return __generator(this, function (_a) {
196
+ switch (_a.label) {
197
+ case 0:
198
+ req.query['channel'] = req.params.channelId;
199
+ return [4 /*yield*/, handleMasterRoute(req, res, next)];
200
+ case 1:
201
+ _a.sent();
202
+ return [2 /*return*/];
203
+ }
204
+ });
205
+ }); });
206
+ this.server.get('/eventstream/:sessionId', this._handleEventStream.bind(this));
207
+ this.server.get('/status/:sessionId', this._handleStatus.bind(this));
208
+ this.server.get('/health', this._handleAggregatedSessionHealth.bind(this));
209
+ this.server.get('/health/:sessionId', this._handleSessionHealth.bind(this));
210
+ this.server.get('/reset', this._handleSessionReset.bind(this));
211
+ this.server.on('NotFound', function (req, res, err, next) {
212
+ res.header("X-Instance-Id", _this.instanceId + "<".concat(version, ">"));
213
+ return next();
214
+ });
215
+ this.server.on('InternalServer', function (req, res, err, next) {
216
+ res.header("X-Instance-Id", _this.instanceId + "<".concat(version, ">"));
217
+ return next();
218
+ });
219
+ if (options && options.heartbeat) {
220
+ this.server.get(options.heartbeat, this._handleHeartbeat.bind(this));
221
+ }
222
+ if (options && options.channelManager) {
223
+ var t = setInterval(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
224
+ switch (_a.label) {
225
+ case 0: return [4 /*yield*/, this.updateChannelsAsync(options.channelManager, options)];
226
+ case 1:
227
+ _a.sent();
228
+ return [2 /*return*/];
229
+ }
230
+ }); }); }, 60 * 1000);
231
+ }
232
+ var LeaderSyncSessionTypes = function () { return __awaiter(_this, void 0, void 0, function () {
233
+ var isLeader;
234
+ return __generator(this, function (_a) {
235
+ switch (_a.label) {
236
+ case 0: return [4 /*yield*/, timer(10 * 1000)];
237
+ case 1:
238
+ _a.sent();
239
+ return [4 /*yield*/, this.sessionStore.sessionStateStore.isLeader(this.instanceId)];
240
+ case 2:
241
+ isLeader = _a.sent();
242
+ if (!isLeader) return [3 /*break*/, 4];
243
+ return [4 /*yield*/, this.sessionLiveStore.sessionLiveStateStore.setLeader(this.instanceId)];
244
+ case 3:
245
+ _a.sent();
246
+ _a.label = 4;
247
+ case 4: return [2 /*return*/];
248
+ }
249
+ });
250
+ }); };
251
+ LeaderSyncSessionTypes();
252
+ var ping = setInterval(function () { return __awaiter(_this, void 0, void 0, function () {
253
+ return __generator(this, function (_a) {
254
+ switch (_a.label) {
255
+ case 0: return [4 /*yield*/, this.sessionStore.sessionStateStore.ping(this.instanceId)];
256
+ case 1:
257
+ _a.sent();
258
+ return [4 /*yield*/, this.sessionLiveStore.sessionLiveStateStore.ping(this.instanceId)];
259
+ case 2:
260
+ _a.sent();
261
+ return [2 /*return*/];
262
+ }
263
+ });
264
+ }); }, 3000);
265
+ var StreamSwitchLoop = function (timeIntervalMs) { return __awaiter(_this, void 0, void 0, function () {
266
+ var minIntervalMs, WTG, ts_1, ts_2, interval, tickInterval, err_1;
267
+ return __generator(this, function (_a) {
268
+ switch (_a.label) {
269
+ case 0:
270
+ minIntervalMs = 50;
271
+ WTG = new WaitTimeGenerator(timeIntervalMs, minIntervalMs);
272
+ _a.label = 1;
273
+ case 1:
274
+ if (!true) return [3 /*break*/, 9];
275
+ _a.label = 2;
276
+ case 2:
277
+ _a.trys.push([2, 6, , 8]);
278
+ ts_1 = Date.now();
279
+ return [4 /*yield*/, this.updateStreamSwitchAsync()];
280
+ case 3:
281
+ _a.sent();
282
+ ts_2 = Date.now();
283
+ interval = (timeIntervalMs - (ts_2 - ts_1)) < 0 ? minIntervalMs : (timeIntervalMs - (ts_2 - ts_1));
284
+ return [4 /*yield*/, WTG.getWaitTime(interval)];
285
+ case 4:
286
+ tickInterval = _a.sent();
287
+ return [4 /*yield*/, timer(tickInterval)];
288
+ case 5:
289
+ _a.sent();
290
+ debug("StreamSwitchLoop waited for all channels. Next tick in: ".concat(tickInterval, "ms"));
291
+ return [3 /*break*/, 8];
292
+ case 6:
293
+ err_1 = _a.sent();
294
+ console.error(err_1);
295
+ debug("StreamSwitchLoop iteration failed. Trying Again in 1000ms!");
296
+ return [4 /*yield*/, timer(1000)];
297
+ case 7:
298
+ _a.sent();
299
+ return [3 /*break*/, 8];
300
+ case 8: return [3 /*break*/, 1];
301
+ case 9: return [2 /*return*/];
302
+ }
303
+ });
304
+ }); };
305
+ StreamSwitchLoop(this.streamSwitchTimeIntervalMs);
306
+ }
307
+ ChannelEngine.prototype.updateStreamSwitchAsync = function () {
308
+ return __awaiter(this, void 0, void 0, function () {
309
+ var channels, getSwitchStatusAndPerformSwitch, err_2;
310
+ var _this = this;
311
+ return __generator(this, function (_a) {
312
+ switch (_a.label) {
313
+ case 0:
314
+ channels = Object.keys(sessionSwitchers);
315
+ getSwitchStatusAndPerformSwitch = function (channel) { return __awaiter(_this, void 0, void 0, function () {
316
+ var switcher, prevStatus, status_1, err_3;
317
+ return __generator(this, function (_a) {
318
+ switch (_a.label) {
319
+ case 0:
320
+ if (!sessionSwitchers[channel]) return [3 /*break*/, 5];
321
+ switcher = sessionSwitchers[channel];
322
+ prevStatus = switcherStatus[channel] !== null ? switcherStatus[channel] : null;
323
+ switcherStatus[channel] = null;
324
+ status_1 = null;
325
+ _a.label = 1;
326
+ case 1:
327
+ _a.trys.push([1, 3, , 4]);
328
+ return [4 /*yield*/, switcher.streamSwitcher(sessions[channel], sessionsLive[channel])];
329
+ case 2:
330
+ status_1 = _a.sent();
331
+ debug("[".concat(channel, "]: streamSwitcher returned switchstatus=").concat(status_1));
332
+ if (status_1 === undefined) {
333
+ debug("[WARNING]: switcherStatus->".concat(status_1, ". Setting value to previous status->").concat(prevStatus));
334
+ status_1 = prevStatus;
335
+ }
336
+ switcherStatus[channel] = status_1;
337
+ return [3 /*break*/, 4];
338
+ case 3:
339
+ err_3 = _a.sent();
340
+ throw new Error(err_3);
341
+ case 4: return [3 /*break*/, 6];
342
+ case 5:
343
+ debug("Tried to switch stream on a non-existing channel=[".concat(channel, "]. Switching Ignored!)"));
344
+ _a.label = 6;
345
+ case 6: return [2 /*return*/];
346
+ }
347
+ });
348
+ }); };
349
+ _a.label = 1;
350
+ case 1:
351
+ _a.trys.push([1, 3, , 4]);
352
+ return [4 /*yield*/, Promise.all(channels.map(function (channel) { return getSwitchStatusAndPerformSwitch(channel); }))];
353
+ case 2:
354
+ _a.sent();
355
+ return [3 /*break*/, 4];
356
+ case 3:
357
+ err_2 = _a.sent();
358
+ debug('Problem occured when updating streamSwitchers');
359
+ throw new Error(err_2);
360
+ case 4: return [2 /*return*/];
361
+ }
362
+ });
363
+ });
364
+ };
365
+ ChannelEngine.prototype.updateChannelsAsync = function (channelMgr, options) {
366
+ return __awaiter(this, void 0, void 0, function () {
367
+ var newChannels, addAsync, addLiveAsync, removedChannels, removeAsync;
368
+ var _this = this;
369
+ return __generator(this, function (_a) {
370
+ switch (_a.label) {
371
+ case 0:
372
+ debug("Do we have any new channels?");
373
+ newChannels = channelMgr.getChannels().filter(function (channel) { return !sessions[channel.id]; });
374
+ addAsync = function (channel) { return __awaiter(_this, void 0, void 0, function () {
375
+ var _this = this;
376
+ return __generator(this, function (_a) {
377
+ switch (_a.label) {
378
+ case 0:
379
+ debug("Adding channel with ID ".concat(channel.id));
380
+ sessions[channel.id] = new Session(this.assetMgr, {
381
+ sessionId: channel.id,
382
+ averageSegmentDuration: channel.options && channel.options.averageSegmentDuration ? channel.options.averageSegmentDuration : this.streamerOpts.defaultAverageSegmentDuration,
383
+ useDemuxedAudio: options.useDemuxedAudio,
384
+ alwaysNewSegments: options.alwaysNewSegments,
385
+ playheadDiffThreshold: channel.options && channel.options.playheadDiffThreshold ? channel.options.playheadDiffThreshold : this.streamerOpts.defaultPlayheadDiffThreshold,
386
+ maxTickInterval: channel.options && channel.options.maxTickInterval ? channel.options.maxTickInterval : this.streamerOpts.defaultMaxTickInterval,
387
+ targetDurationPadding: channel.options && channel.options.targetDurationPadding ? channel.options.targetDurationPadding : this.streamerOpts.targetDurationPadding,
388
+ forceTargetDuration: channel.options && channel.options.forceTargetDuration ? channel.options.forceTargetDuration : this.streamerOpts.forceTargetDuration,
389
+ diffCompensationRate: channel.options && channel.options.diffCompensationRate ? channel.options.diffCompensationRate : this.streamerOpts.diffCompensationRate,
390
+ profile: channel.profile,
391
+ audioTracks: channel.audioTracks,
392
+ closedCaptions: channel.closedCaptions,
393
+ slateUri: channel.slate && channel.slate.uri ? channel.slate.uri : this.defaultSlateUri,
394
+ slateRepetitions: channel.slate && channel.slate.repetitions ? channel.slate.repetitions : this.slateRepetitions,
395
+ slateDuration: channel.slate && channel.slate.duration ? channel.slate.duration : this.slateDuration,
396
+ cloudWatchMetrics: this.logCloudWatchMetrics,
397
+ }, this.sessionStore);
398
+ sessionsLive[channel.id] = new SessionLive({
399
+ sessionId: channel.id,
400
+ useDemuxedAudio: options.useDemuxedAudio,
401
+ cloudWatchMetrics: this.logCloudWatchMetrics,
402
+ profile: channel.profile,
403
+ }, this.sessionLiveStore);
404
+ sessionSwitchers[channel.id] = new StreamSwitcher({
405
+ sessionId: channel.id,
406
+ streamSwitchManager: this.streamSwitchManager ? this.streamSwitchManager : null
407
+ });
408
+ return [4 /*yield*/, sessions[channel.id].initAsync()];
409
+ case 1:
410
+ _a.sent();
411
+ return [4 /*yield*/, sessionsLive[channel.id].initAsync()];
412
+ case 2:
413
+ _a.sent();
414
+ if (!this.monitorTimer[channel.id]) {
415
+ this.monitorTimer[channel.id] = setInterval(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
416
+ switch (_a.label) {
417
+ case 0: return [4 /*yield*/, this._monitorAsync(sessions[channel.id], sessionsLive[channel.id])];
418
+ case 1:
419
+ _a.sent();
420
+ return [2 /*return*/];
421
+ }
422
+ }); }); }, 5000);
423
+ }
424
+ return [4 /*yield*/, sessions[channel.id].startPlayheadAsync()];
425
+ case 3:
426
+ _a.sent();
427
+ return [2 /*return*/];
428
+ }
429
+ });
430
+ }); };
431
+ addLiveAsync = function (channel) { return __awaiter(_this, void 0, void 0, function () {
432
+ return __generator(this, function (_a) {
433
+ switch (_a.label) {
434
+ case 0:
435
+ debug("Adding channel with ID ".concat(channel.id));
436
+ return [4 /*yield*/, sessionsLive[channel.id].initAsync()];
437
+ case 1:
438
+ _a.sent();
439
+ return [4 /*yield*/, sessionsLive[channel.id].startPlayheadAsync()];
440
+ case 2:
441
+ _a.sent();
442
+ return [2 /*return*/];
443
+ }
444
+ });
445
+ }); };
446
+ return [4 /*yield*/, Promise.all(newChannels.map(function (channel) { return addAsync(channel); }).concat(newChannels.map(function (channel) { return addLiveAsync(channel); })))];
447
+ case 1:
448
+ _a.sent();
449
+ debug("Have any channels been removed?");
450
+ removedChannels = Object.keys(sessions).filter(function (channelId) { return !channelMgr.getChannels().find(function (ch) { return ch.id == channelId; }); });
451
+ removeAsync = function (channelId) { return __awaiter(_this, void 0, void 0, function () {
452
+ return __generator(this, function (_a) {
453
+ switch (_a.label) {
454
+ case 0:
455
+ debug("Removing channel with ID ".concat(channelId));
456
+ clearInterval(this.monitorTimer[channelId]);
457
+ return [4 /*yield*/, sessions[channelId].stopPlayheadAsync()];
458
+ case 1:
459
+ _a.sent();
460
+ if (!sessionsLive[channelId]) return [3 /*break*/, 3];
461
+ return [4 /*yield*/, sessionsLive[channelId].stopPlayheadAsync()];
462
+ case 2:
463
+ _a.sent();
464
+ delete sessionsLive[channelId];
465
+ return [3 /*break*/, 4];
466
+ case 3:
467
+ debug("Cannot remove live session of channel that does not exist ".concat(channelId));
468
+ _a.label = 4;
469
+ case 4:
470
+ delete sessions[channelId];
471
+ delete sessionSwitchers[channelId];
472
+ delete switcherStatus[channelId];
473
+ return [2 /*return*/];
474
+ }
475
+ });
476
+ }); };
477
+ return [4 /*yield*/, Promise.all(removedChannels.map(function (channelId) { return removeAsync(channelId); }))];
478
+ case 2:
479
+ _a.sent();
480
+ return [2 /*return*/];
481
+ }
482
+ });
483
+ });
484
+ };
485
+ ChannelEngine.prototype.start = function () {
486
+ var _this = this;
487
+ var startAsync = function (channelId) { return __awaiter(_this, void 0, void 0, function () {
488
+ var session, sessionLive;
489
+ var _this = this;
490
+ return __generator(this, function (_a) {
491
+ switch (_a.label) {
492
+ case 0:
493
+ session = sessions[channelId];
494
+ sessionLive = sessionsLive[channelId];
495
+ if (!this.monitorTimer[channelId]) {
496
+ this.monitorTimer[channelId] = setInterval(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
497
+ switch (_a.label) {
498
+ case 0: return [4 /*yield*/, this._monitorAsync(session, sessionLive)];
499
+ case 1:
500
+ _a.sent();
501
+ return [2 /*return*/];
502
+ }
503
+ }); }); }, 5000);
504
+ }
505
+ session.startPlayheadAsync();
506
+ return [4 /*yield*/, sessionLive.startPlayheadAsync()];
507
+ case 1:
508
+ _a.sent();
509
+ return [2 /*return*/];
510
+ }
511
+ });
512
+ }); };
513
+ var startLiveAsync = function (channelId) { return __awaiter(_this, void 0, void 0, function () {
514
+ var sessionLive;
515
+ return __generator(this, function (_a) {
516
+ switch (_a.label) {
517
+ case 0:
518
+ sessionLive = sessionsLive[channelId];
519
+ return [4 /*yield*/, sessionLive.startPlayheadAsync()];
520
+ case 1:
521
+ _a.sent();
522
+ return [2 /*return*/];
523
+ }
524
+ });
525
+ }); };
526
+ (function () { return __awaiter(_this, void 0, void 0, function () {
527
+ return __generator(this, function (_a) {
528
+ switch (_a.label) {
529
+ case 0:
530
+ debug("Starting engine");
531
+ return [4 /*yield*/, this.updateChannelsAsync(this.options.channelManager, this.options)];
532
+ case 1:
533
+ _a.sent();
534
+ return [4 /*yield*/, Promise.all(Object.keys(sessions).map(function (channelId) { return startAsync(channelId); }).concat(Object.keys(sessionsLive).map(function (channelId) { return startLiveAsync(channelId); })))];
535
+ case 2:
536
+ _a.sent();
537
+ return [2 /*return*/];
538
+ }
539
+ });
540
+ }); })();
541
+ };
542
+ ChannelEngine.prototype.listen = function (port) {
543
+ var _this = this;
544
+ this.server.listen(port, function () {
545
+ debug('%s listening at %s', _this.server.name, _this.server.url);
546
+ });
547
+ };
548
+ ChannelEngine.prototype.getStatusForSessionAsync = function (sessionId) {
549
+ return __awaiter(this, void 0, void 0, function () {
550
+ return __generator(this, function (_a) {
551
+ switch (_a.label) {
552
+ case 0: return [4 /*yield*/, sessions[sessionId].getStatusAsync()];
553
+ case 1: return [2 /*return*/, _a.sent()];
554
+ }
555
+ });
556
+ });
557
+ };
558
+ ChannelEngine.prototype.getSessionCount = function () {
559
+ return Object.keys(sessions).length;
560
+ };
561
+ ChannelEngine.prototype.getPlayheadCount = function () {
562
+ return Object.keys(sessions).filter(function (sessionId) { return sessions[sessionId].hasPlayhead(); }).length;
563
+ };
564
+ ChannelEngine.prototype._monitorAsync = function (session, sessionLive) {
565
+ return __awaiter(this, void 0, void 0, function () {
566
+ var statusSessionLive, statusSession;
567
+ return __generator(this, function (_a) {
568
+ switch (_a.label) {
569
+ case 0:
570
+ statusSessionLive = sessionLive.getStatus();
571
+ return [4 /*yield*/, session.getStatusAsync()];
572
+ case 1:
573
+ statusSession = _a.sent();
574
+ debug("MONITOR: (".concat(new Date().toISOString(), ") [").concat(statusSession.sessionId, "]: playhead: ").concat(statusSession.playhead.state));
575
+ debug("MONITOR: (".concat(new Date().toISOString(), ") [").concat(statusSessionLive.sessionId, "]: live-playhead: ").concat(statusSessionLive.playhead.state));
576
+ if (!(statusSessionLive.playhead.state === 'crashed')) return [3 /*break*/, 3];
577
+ debug("[".concat(statusSessionLive.sessionId, "]: SessionLive-Playhead crashed, restarting"));
578
+ return [4 /*yield*/, sessionLive.restartPlayheadAsync()];
579
+ case 2:
580
+ _a.sent();
581
+ _a.label = 3;
582
+ case 3:
583
+ if (!(statusSession.playhead.state === 'crashed')) return [3 /*break*/, 5];
584
+ debug("[".concat(statusSession.sessionId, "]: Session-Playhead crashed, restarting"));
585
+ return [4 /*yield*/, session.restartPlayheadAsync()];
586
+ case 4:
587
+ _a.sent();
588
+ return [3 /*break*/, 7];
589
+ case 5:
590
+ if (!(statusSession.playhead.state === 'idle')) return [3 /*break*/, 7];
591
+ debug("[".concat(statusSession.sessionId, "]: Starting playhead"));
592
+ return [4 /*yield*/, session.startPlayheadAsync()];
593
+ case 6:
594
+ _a.sent();
595
+ _a.label = 7;
596
+ case 7: return [2 /*return*/];
597
+ }
598
+ });
599
+ });
600
+ };
601
+ ChannelEngine.prototype.getMasterManifest = function (channelId) {
602
+ return __awaiter(this, void 0, void 0, function () {
603
+ var session, masterM3U8, err;
604
+ return __generator(this, function (_a) {
605
+ switch (_a.label) {
606
+ case 0:
607
+ if (!sessions[channelId]) return [3 /*break*/, 2];
608
+ session = sessions[channelId];
609
+ return [4 /*yield*/, session.getMasterManifestAsync()];
610
+ case 1:
611
+ masterM3U8 = _a.sent();
612
+ return [2 /*return*/, masterM3U8];
613
+ case 2:
614
+ err = new errs.NotFoundError('Invalid session');
615
+ return [2 /*return*/, Promise.reject(err)];
616
+ }
617
+ });
618
+ });
619
+ };
620
+ ChannelEngine.prototype.getMediaManifests = function (channelId) {
621
+ return __awaiter(this, void 0, void 0, function () {
622
+ var allMediaM3U8_1, promises_1, session_1, bandwidths, addM3U8_1, err;
623
+ var _this = this;
624
+ return __generator(this, function (_a) {
625
+ switch (_a.label) {
626
+ case 0:
627
+ if (!sessions[channelId]) return [3 /*break*/, 2];
628
+ allMediaM3U8_1 = {};
629
+ promises_1 = [];
630
+ session_1 = sessions[channelId];
631
+ bandwidths = this.options.channelManager
632
+ .getChannels()
633
+ .filter(function (ch) { return ch.id === channelId; })
634
+ .pop()
635
+ .profile
636
+ .map(function (profile) { return profile.bw; });
637
+ addM3U8_1 = function (bw) { return __awaiter(_this, void 0, void 0, function () {
638
+ var _a, _b;
639
+ return __generator(this, function (_c) {
640
+ switch (_c.label) {
641
+ case 0:
642
+ _a = allMediaM3U8_1;
643
+ _b = bw;
644
+ return [4 /*yield*/, session_1.getCurrentMediaManifestAsync(bw)];
645
+ case 1:
646
+ _a[_b] = _c.sent();
647
+ return [2 /*return*/];
648
+ }
649
+ });
650
+ }); };
651
+ bandwidths.forEach(function (bw) {
652
+ promises_1.push(addM3U8_1(bw));
653
+ });
654
+ return [4 /*yield*/, Promise.all(promises_1)];
655
+ case 1:
656
+ _a.sent();
657
+ return [2 /*return*/, allMediaM3U8_1];
658
+ case 2:
659
+ err = new errs.NotFoundError('Invalid session');
660
+ return [2 /*return*/, Promise.reject(err)];
661
+ }
662
+ });
663
+ });
664
+ };
665
+ ChannelEngine.prototype.getAudioManifests = function (channelId) {
666
+ return __awaiter(this, void 0, void 0, function () {
667
+ var allAudioM3U8_1, promises_2, session_2, addM3U8_2, audioGroupsAndLangs, _loop_1, _i, _a, _b, audioGroup, languages, err;
668
+ var _this = this;
669
+ return __generator(this, function (_c) {
670
+ switch (_c.label) {
671
+ case 0:
672
+ if (!sessions[channelId]) return [3 /*break*/, 3];
673
+ allAudioM3U8_1 = {};
674
+ promises_2 = [];
675
+ session_2 = sessions[channelId];
676
+ addM3U8_2 = function (groupId, lang) { return __awaiter(_this, void 0, void 0, function () {
677
+ var audioM3U8;
678
+ return __generator(this, function (_a) {
679
+ switch (_a.label) {
680
+ case 0: return [4 /*yield*/, session_2.getCurrentAudioManifestAsync(groupId, lang)];
681
+ case 1:
682
+ audioM3U8 = _a.sent();
683
+ if (!allAudioM3U8_1[groupId]) {
684
+ allAudioM3U8_1[groupId] = {};
685
+ }
686
+ allAudioM3U8_1[groupId][lang] = audioM3U8;
687
+ return [2 /*return*/];
688
+ }
689
+ });
690
+ }); };
691
+ return [4 /*yield*/, session_2.getAudioGroupsAndLangs()];
692
+ case 1:
693
+ audioGroupsAndLangs = _c.sent();
694
+ _loop_1 = function (audioGroup, languages) {
695
+ languages.forEach(function (lang) {
696
+ promises_2.push(addM3U8_2(audioGroup, lang));
697
+ });
698
+ };
699
+ for (_i = 0, _a = Object.entries(audioGroupsAndLangs); _i < _a.length; _i++) {
700
+ _b = _a[_i], audioGroup = _b[0], languages = _b[1];
701
+ _loop_1(audioGroup, languages);
702
+ }
703
+ return [4 /*yield*/, Promise.all(promises_2)];
704
+ case 2:
705
+ _c.sent();
706
+ return [2 /*return*/, allAudioM3U8_1];
707
+ case 3:
708
+ err = new errs.NotFoundError('Invalid session');
709
+ return [2 /*return*/, Promise.reject(err)];
710
+ }
711
+ });
712
+ });
713
+ };
714
+ ChannelEngine.prototype._handleHeartbeat = function (req, res, next) {
715
+ debug('req.url=' + req.url);
716
+ res.send(200);
717
+ next();
718
+ };
719
+ ChannelEngine.prototype._handleMasterManifest = function (req, res, next) {
720
+ return __awaiter(this, void 0, void 0, function () {
721
+ var session, sessionLive, options, eventStream, filter, body, err_4;
722
+ return __generator(this, function (_a) {
723
+ switch (_a.label) {
724
+ case 0:
725
+ debug('req.url=' + req.url);
726
+ debug(req.query);
727
+ options = {};
728
+ if (req.query['playlist']) {
729
+ // Backward compatibility
730
+ options.category = req.query['playlist'];
731
+ }
732
+ if (req.query['category']) {
733
+ options.category = req.query['category'];
734
+ }
735
+ if (!(req.query['channel'] && sessions[req.query['channel']])) return [3 /*break*/, 1];
736
+ session = sessions[req.query['channel']];
737
+ return [3 /*break*/, 4];
738
+ case 1:
739
+ if (!(req.query['session'] && sessions[req.query['session']])) return [3 /*break*/, 2];
740
+ session = sessions[req.query['session']];
741
+ return [3 /*break*/, 4];
742
+ case 2:
743
+ options.adCopyMgrUri = this.adCopyMgrUri;
744
+ options.adXchangeUri = this.adXchangeUri;
745
+ options.averageSegmentDuration = this.streamerOpts.defaultAverageSegmentDuration;
746
+ options.useDemuxedAudio = this.useDemuxedAudio;
747
+ options.alwaysNewSegments = this.alwaysNewSegments;
748
+ options.playheadDiffThreshold = this.streamerOpts.defaultPlayheadDiffThreshold;
749
+ options.maxTickInterval = this.streamerOpts.defaultMaxTickInterval;
750
+ options.targetDurationPadding = this.streamerOpts.targetDurationPadding;
751
+ options.forceTargetDuration = this.streamerOpts.forceTargetDuration;
752
+ options.diffCompensationRate = this.streamerOpts.diffCompensationRate;
753
+ // if we are initiating a master manifest
754
+ // outside of specific Channel context,
755
+ // if slate options are set at the ChannelEngine level, then set these here
756
+ if (this.defaultSlateUri) {
757
+ options.slateUri = this.defaultSlateUri;
758
+ }
759
+ if (this.slateRepetitions) {
760
+ options.slateRepetitions = this.slateRepetitions;
761
+ }
762
+ if (this.slateDuration) {
763
+ options.slateDuration = this.slateDuration;
764
+ }
765
+ options.disabledPlayhead = true; // disable playhead for personalized streams
766
+ session = new Session(this.assetMgr, options, this.sessionStore);
767
+ return [4 /*yield*/, session.initAsync()];
768
+ case 3:
769
+ _a.sent();
770
+ sessions[session.sessionId] = session;
771
+ _a.label = 4;
772
+ case 4:
773
+ if (req.query['startWithId']) {
774
+ options.startWithId = req.query['startWithId'];
775
+ debug("New session to start with assetId=".concat(options.startWithId));
776
+ }
777
+ if (!session) return [3 /*break*/, 9];
778
+ eventStream = new EventStream(session);
779
+ eventStreams[session.sessionId] = eventStream;
780
+ filter = void 0;
781
+ if (req.query['filter']) {
782
+ debug("Applying filter on master manifest ".concat(req.query['filter']));
783
+ filter = filterQueryParser(req.query['filter']);
784
+ }
785
+ _a.label = 5;
786
+ case 5:
787
+ _a.trys.push([5, 7, , 8]);
788
+ return [4 /*yield*/, session.getMasterManifestAsync(filter)];
789
+ case 6:
790
+ body = _a.sent();
791
+ res.sendRaw(200, Buffer.from(body, 'utf8'), {
792
+ "Content-Type": "application/x-mpegURL;charset=UTF-8",
793
+ "Access-Control-Allow-Origin": "*",
794
+ "Access-Control-Allow-Headers": "X-Session-Id",
795
+ "Access-Control-Expose-Headers": "X-Session-Id",
796
+ "Cache-Control": "no-cache",
797
+ "X-Session-Id": session.sessionId,
798
+ "X-Instance-Id": this.instanceId + "<".concat(version, ">"),
799
+ });
800
+ next();
801
+ return [3 /*break*/, 8];
802
+ case 7:
803
+ err_4 = _a.sent();
804
+ next(this._errorHandler(err_4));
805
+ return [3 /*break*/, 8];
806
+ case 8: return [3 /*break*/, 10];
807
+ case 9:
808
+ next(this._gracefulErrorHandler("Could not find a valid session"));
809
+ _a.label = 10;
810
+ case 10: return [2 /*return*/];
811
+ }
812
+ });
813
+ });
814
+ };
815
+ ChannelEngine.prototype._handleAudioManifest = function (req, res, next) {
816
+ return __awaiter(this, void 0, void 0, function () {
817
+ var session, body, err_5, err;
818
+ return __generator(this, function (_a) {
819
+ switch (_a.label) {
820
+ case 0:
821
+ debug("req.url=".concat(req.url));
822
+ session = sessions[req.params[2]];
823
+ if (!session) return [3 /*break*/, 5];
824
+ _a.label = 1;
825
+ case 1:
826
+ _a.trys.push([1, 3, , 4]);
827
+ return [4 /*yield*/, session.getCurrentAudioManifestAsync(req.params[0], req.params[1], req.headers["x-playback-session-id"])];
828
+ case 2:
829
+ body = _a.sent();
830
+ res.sendRaw(200, Buffer.from(body, 'utf8'), {
831
+ "Content-Type": "application/x-mpegURL;charset=UTF-8",
832
+ "Access-Control-Allow-Origin": "*",
833
+ "Cache-Control": "max-age=".concat(this.streamerOpts.cacheTTL || '4'),
834
+ "X-Instance-Id": this.instanceId + "<".concat(version, ">"),
835
+ });
836
+ next();
837
+ return [3 /*break*/, 4];
838
+ case 3:
839
+ err_5 = _a.sent();
840
+ next(this._gracefulErrorHandler(err_5));
841
+ return [3 /*break*/, 4];
842
+ case 4: return [3 /*break*/, 6];
843
+ case 5:
844
+ err = new errs.NotFoundError('Invalid session');
845
+ next(err);
846
+ _a.label = 6;
847
+ case 6: return [2 /*return*/];
848
+ }
849
+ });
850
+ });
851
+ };
852
+ ChannelEngine.prototype._handleMediaManifest = function (req, res, next) {
853
+ return __awaiter(this, void 0, void 0, function () {
854
+ var session, sessionLive, body, err_6, err;
855
+ return __generator(this, function (_a) {
856
+ switch (_a.label) {
857
+ case 0:
858
+ debug("x-playback-session-id=".concat(req.headers["x-playback-session-id"], " req.url=").concat(req.url));
859
+ debug(req.params);
860
+ session = sessions[req.params[1]];
861
+ sessionLive = sessionsLive[req.params[1]];
862
+ if (!(session && sessionLive)) return [3 /*break*/, 11];
863
+ _a.label = 1;
864
+ case 1:
865
+ _a.trys.push([1, 9, , 10]);
866
+ _a.label = 2;
867
+ case 2:
868
+ if (!(switcherStatus[req.params[1]] === null || switcherStatus[req.params[1]] === undefined)) return [3 /*break*/, 4];
869
+ debug("[".concat(req.params[1], "]: (").concat(switcherStatus[req.params[1]], ") Waiting for streamSwitcher to respond"));
870
+ return [4 /*yield*/, timer(500)];
871
+ case 3:
872
+ _a.sent();
873
+ return [3 /*break*/, 2];
874
+ case 4:
875
+ body = null;
876
+ debug("switcherStatus[".concat(req.params[1], "]=[").concat(switcherStatus[req.params[1]], "]"));
877
+ if (!switcherStatus[req.params[1]]) return [3 /*break*/, 6];
878
+ debug("[".concat(req.params[1], "]: Responding with Live-stream manifest"));
879
+ return [4 /*yield*/, sessionLive.getCurrentMediaManifestAsync(req.params[0])];
880
+ case 5:
881
+ body = _a.sent();
882
+ return [3 /*break*/, 8];
883
+ case 6:
884
+ debug("[".concat(req.params[1], "]: Responding with VOD2Live manifest"));
885
+ return [4 /*yield*/, session.getCurrentMediaManifestAsync(req.params[0], req.headers["x-playback-session-id"])];
886
+ case 7:
887
+ body = _a.sent();
888
+ _a.label = 8;
889
+ case 8:
890
+ //verbose(`[${session.sessionId}] body=`);
891
+ //verbose(body);
892
+ res.sendRaw(200, Buffer.from(body, 'utf8'), {
893
+ "Content-Type": "application/x-mpegURL;charset=UTF-8",
894
+ "Access-Control-Allow-Origin": "*",
895
+ "Cache-Control": "max-age=".concat(this.streamerOpts.cacheTTL || '4'),
896
+ "X-Instance-Id": this.instanceId + "<".concat(version, ">"),
897
+ });
898
+ next();
899
+ return [3 /*break*/, 10];
900
+ case 9:
901
+ err_6 = _a.sent();
902
+ next(this._gracefulErrorHandler(err_6));
903
+ return [3 /*break*/, 10];
904
+ case 10: return [3 /*break*/, 12];
905
+ case 11:
906
+ err = new errs.NotFoundError('Invalid session(s)');
907
+ next(err);
908
+ _a.label = 12;
909
+ case 12: return [2 /*return*/];
910
+ }
911
+ });
912
+ });
913
+ };
914
+ ChannelEngine.prototype._handleEventStream = function (req, res, next) {
915
+ var _this = this;
916
+ debug("req.url=".concat(req.url));
917
+ var eventStream = eventStreams[req.params.sessionId];
918
+ if (eventStream) {
919
+ eventStream.poll().then(function (body) {
920
+ res.sendRaw(200, body, {
921
+ "Content-Type": "application/json",
922
+ "Access-Control-Allow-Origin": "*",
923
+ "Cache-Control": "max-age=4",
924
+ });
925
+ next();
926
+ }).catch(function (err) {
927
+ next(_this._errorHandler(err));
928
+ });
929
+ }
930
+ else {
931
+ // Silent error
932
+ debug("No event stream found for session=".concat(req.params.sessionId));
933
+ res.sendRaw(200, '{}', {
934
+ "Content-Type": "application/json",
935
+ "Access-Control-Allow-Origin": "*",
936
+ "Cache-Control": "max-age=4",
937
+ });
938
+ next();
939
+ }
940
+ };
941
+ ChannelEngine.prototype._handleStatus = function (req, res, next) {
942
+ return __awaiter(this, void 0, void 0, function () {
943
+ var session, body, err;
944
+ return __generator(this, function (_a) {
945
+ switch (_a.label) {
946
+ case 0:
947
+ debug("req.url=".concat(req.url));
948
+ session = sessions[req.params.sessionId];
949
+ if (!session) return [3 /*break*/, 2];
950
+ return [4 /*yield*/, session.getStatusAsync()];
951
+ case 1:
952
+ body = _a.sent();
953
+ res.sendRaw(200, JSON.stringify(body), {
954
+ "Content-Type": "application/json",
955
+ "Access-Control-Allow-Origin": "*",
956
+ "Cache-Control": "no-cache",
957
+ });
958
+ next();
959
+ return [3 /*break*/, 3];
960
+ case 2:
961
+ err = new errs.NotFoundError('Invalid session');
962
+ next(err);
963
+ _a.label = 3;
964
+ case 3: return [2 /*return*/];
965
+ }
966
+ });
967
+ });
968
+ };
969
+ ChannelEngine.prototype._handleAggregatedSessionHealth = function (req, res, next) {
970
+ return __awaiter(this, void 0, void 0, function () {
971
+ var failingSessions, endpoints, _i, _a, sessionId, session, status_2, engineStatus;
972
+ return __generator(this, function (_b) {
973
+ switch (_b.label) {
974
+ case 0:
975
+ debug("req.url=".concat(req.url));
976
+ failingSessions = [];
977
+ endpoints = [];
978
+ _i = 0, _a = Object.keys(sessions);
979
+ _b.label = 1;
980
+ case 1:
981
+ if (!(_i < _a.length)) return [3 /*break*/, 4];
982
+ sessionId = _a[_i];
983
+ session = sessions[sessionId];
984
+ if (!(session && session.hasPlayhead())) return [3 /*break*/, 3];
985
+ return [4 /*yield*/, session.getStatusAsync()];
986
+ case 2:
987
+ status_2 = _b.sent();
988
+ if (status_2.playhead && status_2.playhead.state !== "running") {
989
+ failingSessions.push(status_2);
990
+ }
991
+ endpoints.push({
992
+ health: '/health/' + sessionId,
993
+ status: '/status/' + sessionId,
994
+ playback: '/live/master.m3u8?channel=' + sessionId,
995
+ });
996
+ _b.label = 3;
997
+ case 3:
998
+ _i++;
999
+ return [3 /*break*/, 1];
1000
+ case 4:
1001
+ engineStatus = {
1002
+ startTime: new Date(this.serverStartTime).toISOString(),
1003
+ uptime: toHHMMSS((Date.now() - this.serverStartTime) / 1000),
1004
+ version: version,
1005
+ instanceId: this.instanceId,
1006
+ };
1007
+ if (failingSessions.length === 0) {
1008
+ res.sendRaw(200, JSON.stringify({ "health": "ok", "engine": engineStatus, "count": endpoints.length, "sessionEndpoints": endpoints }), {
1009
+ "Content-Type": "application/json",
1010
+ "Access-Control-Allow-Origin": "*",
1011
+ "Cache-Control": "no-cache",
1012
+ });
1013
+ }
1014
+ else {
1015
+ res.sendRaw(503, JSON.stringify({ "health": "unhealthy", "engine": engineStatus, "failed": failingSessions }), {
1016
+ "Content-Type": "application/json",
1017
+ "Access-Control-Allow-Origin": "*",
1018
+ "Cache-Control": "no-cache",
1019
+ });
1020
+ }
1021
+ return [2 /*return*/];
1022
+ }
1023
+ });
1024
+ });
1025
+ };
1026
+ ChannelEngine.prototype._handleSessionHealth = function (req, res, next) {
1027
+ return __awaiter(this, void 0, void 0, function () {
1028
+ var session, status_3, err;
1029
+ return __generator(this, function (_a) {
1030
+ switch (_a.label) {
1031
+ case 0:
1032
+ debug("req.url=".concat(req.url));
1033
+ session = sessions[req.params.sessionId];
1034
+ if (!session) return [3 /*break*/, 2];
1035
+ return [4 /*yield*/, session.getStatusAsync()];
1036
+ case 1:
1037
+ status_3 = _a.sent();
1038
+ if (status_3.playhead && status_3.playhead.state === "running") {
1039
+ res.sendRaw(200, JSON.stringify({ "health": "ok", "tick": status_3.playhead.tickMs, "mediaSeq": status_3.playhead.mediaSeq }), {
1040
+ "Content-Type": "application/json",
1041
+ "Access-Control-Allow-Origin": "*",
1042
+ "Cache-Control": "no-cache",
1043
+ });
1044
+ }
1045
+ else {
1046
+ res.sendRaw(503, JSON.stringify({ "health": "unhealthy" }), {
1047
+ "Content-Type": "application/json",
1048
+ "Access-Control-Allow-Origin": "*",
1049
+ "Cache-Control": "no-cache",
1050
+ });
1051
+ }
1052
+ return [3 /*break*/, 3];
1053
+ case 2:
1054
+ err = new errs.NotFoundError('Invalid session');
1055
+ next(err);
1056
+ _a.label = 3;
1057
+ case 3: return [2 /*return*/];
1058
+ }
1059
+ });
1060
+ });
1061
+ };
1062
+ ChannelEngine.prototype._handleSessionReset = function (req, res, next) {
1063
+ return __awaiter(this, void 0, void 0, function () {
1064
+ var sessionResets, _i, _a, sessionId, session, sessionLive, err;
1065
+ return __generator(this, function (_b) {
1066
+ switch (_b.label) {
1067
+ case 0:
1068
+ debug("req.url=".concat(req.url));
1069
+ sessionResets = [];
1070
+ _i = 0, _a = Object.keys(sessions);
1071
+ _b.label = 1;
1072
+ case 1:
1073
+ if (!(_i < _a.length)) return [3 /*break*/, 5];
1074
+ sessionId = _a[_i];
1075
+ session = sessions[sessionId];
1076
+ sessionLive = sessionsLive[sessionId];
1077
+ if (!(session && sessionLive)) return [3 /*break*/, 3];
1078
+ return [4 /*yield*/, session.resetAsync()];
1079
+ case 2:
1080
+ _b.sent();
1081
+ sessionResets.push(sessionId);
1082
+ return [3 /*break*/, 4];
1083
+ case 3:
1084
+ err = new errs.NotFoundError('Invalid session');
1085
+ next(err);
1086
+ _b.label = 4;
1087
+ case 4:
1088
+ _i++;
1089
+ return [3 /*break*/, 1];
1090
+ case 5:
1091
+ res.sendRaw(200, JSON.stringify({ "status": "ok", "instanceId": this.instanceId, "resets": sessionResets }), {
1092
+ "Content-Type": "application/json",
1093
+ "Access-Control-Allow-Origin": "*",
1094
+ "Cache-Control": "no-cache",
1095
+ });
1096
+ return [2 /*return*/];
1097
+ }
1098
+ });
1099
+ });
1100
+ };
1101
+ ChannelEngine.prototype._gracefulErrorHandler = function (errMsg) {
1102
+ console.error(errMsg);
1103
+ var err = new errs.NotFoundError(errMsg);
1104
+ return err;
1105
+ };
1106
+ ChannelEngine.prototype._errorHandler = function (errMsg) {
1107
+ console.error(errMsg);
1108
+ var err = new errs.InternalServerError(errMsg);
1109
+ return err;
1110
+ };
1111
+ return ChannelEngine;
1112
+ }());
1113
+ exports.ChannelEngine = ChannelEngine;