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,1723 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __generator = (this && this.__generator) || function (thisArg, body) {
11
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
12
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
13
+ function verb(n) { return function (v) { return step([n, v]); }; }
14
+ function step(op) {
15
+ if (f) throw new TypeError("Generator is already executing.");
16
+ while (_) try {
17
+ 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;
18
+ if (y = 0, t) op = [op[0] & 2, t.value];
19
+ switch (op[0]) {
20
+ case 0: case 1: t = op; break;
21
+ case 4: _.label++; return { value: op[1], done: false };
22
+ case 5: _.label++; y = op[1]; op = [0]; continue;
23
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
24
+ default:
25
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
26
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
27
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
28
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
29
+ if (t[2]) _.ops.pop();
30
+ _.trys.pop(); continue;
31
+ }
32
+ op = body.call(thisArg, _);
33
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
34
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35
+ }
36
+ };
37
+ var debug = require("debug")("engine-session-live");
38
+ var allSettled = require("promise.allsettled");
39
+ var crypto = require("crypto");
40
+ var m3u8 = require("@eyevinn/m3u8");
41
+ var url = require("url");
42
+ var fetch = require("node-fetch");
43
+ var m3u8Header = require("./util.js").m3u8Header;
44
+ var AbortController = require("abort-controller").AbortController;
45
+ var timer = function (ms) { return new Promise(function (res) { return setTimeout(res, ms); }); };
46
+ var daterangeAttribute = function (key, attr) {
47
+ if (key === "planned-duration" || key === "duration") {
48
+ return key.toUpperCase() + "=" + "".concat(attr.toFixed(3));
49
+ }
50
+ else {
51
+ return key.toUpperCase() + "=" + "\"".concat(attr, "\"");
52
+ }
53
+ };
54
+ var TARGET_PLAYLIST_DURATION_SEC = 60;
55
+ var RESET_DELAY = 5000;
56
+ var FAIL_TIMEOUT = 4000;
57
+ var DEFAULT_PLAYHEAD_INTERVAL_MS = 6 * 1000;
58
+ var PlayheadState = Object.freeze({
59
+ RUNNING: 1,
60
+ STOPPED: 2,
61
+ CRASHED: 3,
62
+ IDLE: 4,
63
+ });
64
+ var SessionLive = /** @class */ (function () {
65
+ function SessionLive(config, sessionLiveStore) {
66
+ this.sessionId = crypto.randomBytes(20).toString("hex");
67
+ this.sessionLiveStateStore = sessionLiveStore.sessionLiveStateStore;
68
+ this.instanceId = sessionLiveStore.instanceId;
69
+ this.mediaSeqCount = 0;
70
+ this.prevMediaSeqCount = 0;
71
+ this.discSeqCount = 0;
72
+ this.prevDiscSeqCount = 0;
73
+ this.targetDuration = 0;
74
+ this.masterManifestUri = null;
75
+ this.vodSegments = {};
76
+ this.mediaManifestURIs = {};
77
+ this.liveSegQueue = {};
78
+ this.lastRequestedMediaSeqRaw = null;
79
+ this.liveSourceM3Us = {};
80
+ this.playheadState = PlayheadState.IDLE;
81
+ this.liveSegsForFollowers = {};
82
+ this.timerCompensation = null;
83
+ this.firstTime = true;
84
+ this.allowedToSet = false;
85
+ this.pushAmount = 0;
86
+ this.restAmount = 0;
87
+ this.waitForPlayhead = true;
88
+ this.blockGenerateManifest = false;
89
+ if (config) {
90
+ if (config.sessionId) {
91
+ this.sessionId = config.sessionId;
92
+ }
93
+ if (config.useDemuxedAudio) {
94
+ this.useDemuxedAudio = true;
95
+ }
96
+ if (config.cloudWatchMetrics) {
97
+ this.cloudWatchLogging = true;
98
+ }
99
+ if (config.profile) {
100
+ this.sessionLiveProfile = config.profile;
101
+ }
102
+ }
103
+ }
104
+ SessionLive.prototype.initAsync = function () {
105
+ return __awaiter(this, void 0, void 0, function () {
106
+ var _a;
107
+ return __generator(this, function (_b) {
108
+ switch (_b.label) {
109
+ case 0:
110
+ _a = this;
111
+ return [4 /*yield*/, this.sessionLiveStateStore.create(this.sessionId, this.instanceId)];
112
+ case 1:
113
+ _a.sessionLiveState = _b.sent();
114
+ return [2 /*return*/];
115
+ }
116
+ });
117
+ });
118
+ };
119
+ /**
120
+ *
121
+ * @param {number} resetDelay The amount of time to wait before resetting the session.
122
+ *
123
+ */
124
+ SessionLive.prototype.resetLiveStoreAsync = function (resetDelay) {
125
+ return __awaiter(this, void 0, void 0, function () {
126
+ var isLeader;
127
+ return __generator(this, function (_a) {
128
+ switch (_a.label) {
129
+ case 0: return [4 /*yield*/, this.sessionLiveStateStore.isLeader(this.instanceId)];
130
+ case 1:
131
+ isLeader = _a.sent();
132
+ if (!isLeader) {
133
+ return [2 /*return*/];
134
+ }
135
+ if (resetDelay === null || resetDelay < 0) {
136
+ resetDelay = RESET_DELAY;
137
+ }
138
+ debug("[".concat(this.instanceId, "][").concat(this.sessionId, "]: LEADER: Resetting SessionLive values in Store ").concat(resetDelay === 0 ? "Immediately" : "after a delay=(".concat(resetDelay, "ms)")));
139
+ return [4 /*yield*/, timer(resetDelay)];
140
+ case 2:
141
+ _a.sent();
142
+ return [4 /*yield*/, this.sessionLiveState.set("liveSegsForFollowers", null)];
143
+ case 3:
144
+ _a.sent();
145
+ return [4 /*yield*/, this.sessionLiveState.set("lastRequestedMediaSeqRaw", null)];
146
+ case 4:
147
+ _a.sent();
148
+ return [4 /*yield*/, this.sessionLiveState.set("transitSegs", null)];
149
+ case 5:
150
+ _a.sent();
151
+ return [4 /*yield*/, this.sessionLiveState.set("firstCounts", {
152
+ liveSourceMseqCount: null,
153
+ mediaSeqCount: null,
154
+ discSeqCount: null,
155
+ })];
156
+ case 6:
157
+ _a.sent();
158
+ debug("[".concat(this.instanceId, "][").concat(this.sessionId, "]: LEADER: SessionLive values in Store have now been reset!"));
159
+ return [2 /*return*/];
160
+ }
161
+ });
162
+ });
163
+ };
164
+ SessionLive.prototype.resetSession = function () {
165
+ return __awaiter(this, void 0, void 0, function () {
166
+ return __generator(this, function (_a) {
167
+ switch (_a.label) {
168
+ case 0:
169
+ if (!this.waitForPlayhead) return [3 /*break*/, 2];
170
+ debug("[".concat(this.sessionId, "]: SessionLive RESET requested. Waiting for Playhead to finish a parse job."));
171
+ return [4 /*yield*/, timer(1000)];
172
+ case 1:
173
+ _a.sent();
174
+ return [3 /*break*/, 0];
175
+ case 2:
176
+ this.mediaSeqCount = 0;
177
+ this.prevMediaSeqCount = 0;
178
+ this.discSeqCount = 0;
179
+ this.targetDuration = 0;
180
+ this.masterManifestUri = null;
181
+ this.vodSegments = {};
182
+ this.mediaManifestURIs = {};
183
+ this.liveSegQueue = {};
184
+ this.lastRequestedMediaSeqRaw = null;
185
+ this.liveSourceM3Us = {};
186
+ this.liveSegsForFollowers = {};
187
+ this.timerCompensation = null;
188
+ this.firstTime = true;
189
+ this.pushAmount = 0;
190
+ this.allowedToSet = false;
191
+ this.waitForPlayhead = true;
192
+ this.blockGenerateManifest = false;
193
+ debug("[".concat(this.instanceId, "][").concat(this.sessionId, "]: Resetting all property values in sessionLive"));
194
+ return [2 /*return*/];
195
+ }
196
+ });
197
+ });
198
+ };
199
+ SessionLive.prototype.startPlayheadAsync = function () {
200
+ return __awaiter(this, void 0, void 0, function () {
201
+ var tsIncrementBegin, tsIncrementEnd, liveSegmentDurationMs, timerValueMs, isLeader, incrementDuration, err_1;
202
+ return __generator(this, function (_a) {
203
+ switch (_a.label) {
204
+ case 0:
205
+ debug("[".concat(this.sessionId, "]: SessionLive-Playhead consumer started"));
206
+ this.playheadState = PlayheadState.RUNNING;
207
+ _a.label = 1;
208
+ case 1:
209
+ if (!(this.playheadState !== PlayheadState.CRASHED)) return [3 /*break*/, 14];
210
+ _a.label = 2;
211
+ case 2:
212
+ _a.trys.push([2, 12, , 13]);
213
+ this.timerCompensation = true;
214
+ if (!!this.masterManifestUri) return [3 /*break*/, 4];
215
+ return [4 /*yield*/, timer(3000)];
216
+ case 3:
217
+ _a.sent();
218
+ return [3 /*break*/, 1];
219
+ case 4:
220
+ if (!(this.playheadState === PlayheadState.STOPPED)) return [3 /*break*/, 6];
221
+ debug("[".concat(this.sessionId, "]: Playhead has Stopped, clearing local session and store."));
222
+ this.waitForPlayhead = false;
223
+ return [4 /*yield*/, this.resetSession()];
224
+ case 5:
225
+ _a.sent();
226
+ this.resetLiveStoreAsync(RESET_DELAY);
227
+ return [2 /*return*/];
228
+ case 6:
229
+ // Fetch Live-Source Segments, and get ready for on-the-fly manifest generation
230
+ // And also compensate for processing time
231
+ this.waitForPlayhead = true;
232
+ tsIncrementBegin = Date.now();
233
+ return [4 /*yield*/, this._loadAllMediaManifests()];
234
+ case 7:
235
+ _a.sent();
236
+ tsIncrementEnd = Date.now();
237
+ this.waitForPlayhead = false;
238
+ liveSegmentDurationMs = this._getAnyFirstSegmentDurationMs() || DEFAULT_PLAYHEAD_INTERVAL_MS;
239
+ timerValueMs = 0;
240
+ if (!this.timerCompensation) return [3 /*break*/, 9];
241
+ return [4 /*yield*/, this.sessionLiveStateStore.isLeader(this.instanceId)];
242
+ case 8:
243
+ isLeader = _a.sent();
244
+ incrementDuration = tsIncrementEnd - tsIncrementBegin;
245
+ if (incrementDuration >= liveSegmentDurationMs * 0.5 && isLeader) {
246
+ timerValueMs = liveSegmentDurationMs;
247
+ }
248
+ else {
249
+ timerValueMs = liveSegmentDurationMs - (tsIncrementEnd - tsIncrementBegin);
250
+ }
251
+ return [3 /*break*/, 10];
252
+ case 9:
253
+ // DO NOT compensate if manifest fetching was out-of-sync
254
+ // It means that Live Source and Channel-Engine were awkwardly time-synced
255
+ timerValueMs = liveSegmentDurationMs;
256
+ _a.label = 10;
257
+ case 10:
258
+ debug("[".concat(this.sessionId, "]: SessionLive-Playhead going to ping again after ").concat(timerValueMs, "ms"));
259
+ return [4 /*yield*/, timer(timerValueMs)];
260
+ case 11:
261
+ _a.sent();
262
+ return [3 /*break*/, 13];
263
+ case 12:
264
+ err_1 = _a.sent();
265
+ debug("[".concat(this.sessionId, "]: SessionLive-Playhead consumer crashed"));
266
+ console.error("[".concat(this.sessionId, "]: ").concat(err_1.message));
267
+ debug(err_1);
268
+ this.playheadState = PlayheadState.CRASHED;
269
+ return [3 /*break*/, 13];
270
+ case 13: return [3 /*break*/, 1];
271
+ case 14: return [2 /*return*/];
272
+ }
273
+ });
274
+ });
275
+ };
276
+ SessionLive.prototype.restartPlayheadAsync = function () {
277
+ return __awaiter(this, void 0, void 0, function () {
278
+ return __generator(this, function (_a) {
279
+ switch (_a.label) {
280
+ case 0:
281
+ debug("[".concat(this.sessionId, "]: Restarting sessionLive-playhead consumer"));
282
+ return [4 /*yield*/, this.startPlayheadAsync()];
283
+ case 1:
284
+ _a.sent();
285
+ return [2 /*return*/];
286
+ }
287
+ });
288
+ });
289
+ };
290
+ SessionLive.prototype.stopPlayheadAsync = function () {
291
+ return __awaiter(this, void 0, void 0, function () {
292
+ return __generator(this, function (_a) {
293
+ debug("[".concat(this.sessionId, "]: Stopping sessionLive-playhead consumer"));
294
+ this.playheadState = PlayheadState.STOPPED;
295
+ return [2 /*return*/];
296
+ });
297
+ });
298
+ };
299
+ /**
300
+ * This function sets the master manifest URI in sessionLive.
301
+ * @param {string} masterManifestUri The master manifest URI.
302
+ * @returns a boolean indicating whether the master manifest URI is reachable or not.
303
+ */
304
+ SessionLive.prototype.setLiveUri = function (masterManifestUri) {
305
+ return __awaiter(this, void 0, void 0, function () {
306
+ var attempts, err_2;
307
+ return __generator(this, function (_a) {
308
+ switch (_a.label) {
309
+ case 0:
310
+ if (masterManifestUri === null) {
311
+ debug("[".concat(this.sessionId, "]: No Live URI provided."));
312
+ return [2 /*return*/, false];
313
+ }
314
+ attempts = 3;
315
+ _a.label = 1;
316
+ case 1:
317
+ if (!(!this.masterManifestUri && attempts > 0)) return [3 /*break*/, 7];
318
+ attempts--;
319
+ _a.label = 2;
320
+ case 2:
321
+ _a.trys.push([2, 4, , 6]);
322
+ debug("[".concat(this.instanceId, "][").concat(this.sessionId, "]: Going to fetch Live Master Manifest!"));
323
+ // Load & Parse all Media Manifest URIs from Master
324
+ return [4 /*yield*/, this._loadMasterManifest(masterManifestUri)];
325
+ case 3:
326
+ // Load & Parse all Media Manifest URIs from Master
327
+ _a.sent();
328
+ this.masterManifestUri = masterManifestUri;
329
+ if (this.sessionLiveProfile) {
330
+ this._filterLiveProfiles();
331
+ debug("[".concat(this.sessionId, "]: Filtered Live profiles! (").concat(Object.keys(this.mediaManifestURIs).length, ") profiles left!"));
332
+ }
333
+ return [3 /*break*/, 6];
334
+ case 4:
335
+ err_2 = _a.sent();
336
+ this.masterManifestUri = null;
337
+ debug("[".concat(this.instanceId, "][").concat(this.sessionId, "]: Failed to fetch Live Master Manifest! ").concat(err_2));
338
+ debug("[".concat(this.instanceId, "][").concat(this.sessionId, "]: Will try again in 1000ms! (tries left=").concat(attempts, ")"));
339
+ return [4 /*yield*/, timer(1000)];
340
+ case 5:
341
+ _a.sent();
342
+ return [3 /*break*/, 6];
343
+ case 6:
344
+ // To make sure certain operations only occur once.
345
+ this.firstTime = true;
346
+ return [3 /*break*/, 1];
347
+ case 7:
348
+ // Return whether job was successful or not.
349
+ if (!this.masterManifestUri) {
350
+ return [2 /*return*/, false];
351
+ }
352
+ return [2 /*return*/, true];
353
+ }
354
+ });
355
+ });
356
+ };
357
+ SessionLive.prototype.setCurrentMediaSequenceSegments = function (segments) {
358
+ return __awaiter(this, void 0, void 0, function () {
359
+ var allBws, i, bw, cueInExists, segIdx, v2lSegment, endIdx, finalSegItem, isLeader;
360
+ return __generator(this, function (_a) {
361
+ switch (_a.label) {
362
+ case 0:
363
+ if (segments === null) {
364
+ debug("[".concat(this.sessionId, "]: No segments provided."));
365
+ return [2 /*return*/, false];
366
+ }
367
+ // Make it possible to add & share new segments
368
+ this.allowedToSet = true;
369
+ allBws = Object.keys(segments);
370
+ if (this._isEmpty(this.vodSegments)) {
371
+ for (i = 0; i < allBws.length; i++) {
372
+ bw = allBws[i];
373
+ if (!this.vodSegments[bw]) {
374
+ this.vodSegments[bw] = [];
375
+ }
376
+ if (segments[bw][0].discontinuity) {
377
+ segments[bw].shift();
378
+ }
379
+ cueInExists = null;
380
+ for (segIdx = 0; segIdx < segments[bw].length; segIdx++) {
381
+ v2lSegment = segments[bw][segIdx];
382
+ if (v2lSegment.cue) {
383
+ if (v2lSegment.cue["in"]) {
384
+ cueInExists = true;
385
+ }
386
+ else {
387
+ cueInExists = false;
388
+ }
389
+ }
390
+ this.vodSegments[bw].push(v2lSegment);
391
+ }
392
+ endIdx = segments[bw].length - 1;
393
+ if (!segments[bw][endIdx].discontinuity) {
394
+ finalSegItem = { discontinuity: true };
395
+ if (!cueInExists) {
396
+ finalSegItem["cue"] = { in: true };
397
+ }
398
+ this.vodSegments[bw].push(finalSegItem);
399
+ }
400
+ else {
401
+ if (!cueInExists) {
402
+ segments[bw][endIdx]["cue"] = { in: true };
403
+ }
404
+ }
405
+ }
406
+ }
407
+ else {
408
+ debug("[".concat(this.sessionId, "]: 'vodSegments' not empty = Using 'transitSegs'"));
409
+ }
410
+ debug("[".concat(this.sessionId, "]: Setting CurrentMediaSequenceSegments. First seg is: [").concat(this.vodSegments[Object.keys(this.vodSegments)[0]][0].uri, "]"));
411
+ return [4 /*yield*/, this.sessionLiveStateStore.isLeader(this.instanceId)];
412
+ case 1:
413
+ isLeader = _a.sent();
414
+ if (!isLeader) return [3 /*break*/, 3];
415
+ //debug(`[${this.sessionId}]: LEADER: I am adding 'transitSegs'=${JSON.stringify(this.vodSegments)} to Store for future followers`);
416
+ return [4 /*yield*/, this.sessionLiveState.set("transitSegs", this.vodSegments)];
417
+ case 2:
418
+ //debug(`[${this.sessionId}]: LEADER: I am adding 'transitSegs'=${JSON.stringify(this.vodSegments)} to Store for future followers`);
419
+ _a.sent();
420
+ debug("[".concat(this.sessionId, "]: LEADER: I am adding 'transitSegs' to Store for future followers"));
421
+ _a.label = 3;
422
+ case 3: return [2 /*return*/];
423
+ }
424
+ });
425
+ });
426
+ };
427
+ SessionLive.prototype.setCurrentMediaAndDiscSequenceCount = function (mediaSeq, discSeq) {
428
+ return __awaiter(this, void 0, void 0, function () {
429
+ var isLeader, liveCounts, leadersMediaSeqCount, leadersDiscSeqCount, transitSegs;
430
+ return __generator(this, function (_a) {
431
+ switch (_a.label) {
432
+ case 0:
433
+ if (mediaSeq === null || discSeq === null) {
434
+ debug("[".concat(this.sessionId, "]: No media or disc sequence provided"));
435
+ return [2 /*return*/, false];
436
+ }
437
+ debug("[".concat(this.sessionId, "]: Setting mediaSeqCount and discSeqCount to: [").concat(mediaSeq, "]:[").concat(discSeq, "]"));
438
+ this.mediaSeqCount = mediaSeq;
439
+ this.discSeqCount = discSeq;
440
+ return [4 /*yield*/, this.sessionLiveStateStore.isLeader(this.instanceId)];
441
+ case 1:
442
+ isLeader = _a.sent();
443
+ return [4 /*yield*/, this.sessionLiveState.get("firstCounts")];
444
+ case 2:
445
+ liveCounts = _a.sent();
446
+ if (liveCounts === null) {
447
+ liveCounts = {
448
+ liveSourceMseqCount: null,
449
+ mediaSeqCount: null,
450
+ discSeqCount: null,
451
+ };
452
+ }
453
+ if (!isLeader) return [3 /*break*/, 4];
454
+ liveCounts.discSeqCount = this.discSeqCount;
455
+ return [4 /*yield*/, this.sessionLiveState.set("firstCounts", liveCounts)];
456
+ case 3:
457
+ _a.sent();
458
+ return [3 /*break*/, 7];
459
+ case 4:
460
+ leadersMediaSeqCount = liveCounts.mediaSeqCount;
461
+ leadersDiscSeqCount = liveCounts.discSeqCount;
462
+ if (!(leadersMediaSeqCount !== null)) return [3 /*break*/, 6];
463
+ this.mediaSeqCount = leadersMediaSeqCount;
464
+ debug("[".concat(this.sessionId, "]: Setting mediaSeqCount to: [").concat(this.mediaSeqCount, "]"));
465
+ return [4 /*yield*/, this.sessionLiveState.get("transitSegs")];
466
+ case 5:
467
+ transitSegs = _a.sent();
468
+ if (!this._isEmpty(transitSegs)) {
469
+ debug("[".concat(this.sessionId, "]: Getting and loading 'transitSegs'"));
470
+ this.vodSegments = transitSegs;
471
+ }
472
+ _a.label = 6;
473
+ case 6:
474
+ if (leadersDiscSeqCount !== null) {
475
+ this.discSeqCount = leadersDiscSeqCount;
476
+ debug("[".concat(this.sessionId, "]: Setting discSeqCount to: [").concat(this.discSeqCount, "]"));
477
+ }
478
+ _a.label = 7;
479
+ case 7: return [2 /*return*/];
480
+ }
481
+ });
482
+ });
483
+ };
484
+ SessionLive.prototype.getTransitionalSegments = function () {
485
+ return __awaiter(this, void 0, void 0, function () {
486
+ return __generator(this, function (_a) {
487
+ return [2 /*return*/, this.vodSegments];
488
+ });
489
+ });
490
+ };
491
+ SessionLive.prototype.getCurrentMediaSequenceSegments = function () {
492
+ return __awaiter(this, void 0, void 0, function () {
493
+ var isLeader, leadersMediaSeqRaw, _a, currentMediaSequenceSegments, segmentCount, increment, i, bw, liveTargetBandwidth, vodTargetBandwidth;
494
+ return __generator(this, function (_b) {
495
+ switch (_b.label) {
496
+ case 0:
497
+ /**
498
+ * Might be possible that a follower sends segments to Session
499
+ * BEFORE Leader finished fetching new segs and sending segs himself.
500
+ * As long as Leader sends same segs to session as Follower even though Leader
501
+ * is trying to get new segs, it should be fine!
502
+ **/
503
+ this.allowedToSet = false;
504
+ return [4 /*yield*/, this.sessionLiveStateStore.isLeader(this.instanceId)];
505
+ case 1:
506
+ isLeader = _b.sent();
507
+ if (!!isLeader) return [3 /*break*/, 4];
508
+ return [4 /*yield*/, this.sessionLiveState.get("lastRequestedMediaSeqRaw")];
509
+ case 2:
510
+ leadersMediaSeqRaw = _b.sent();
511
+ if (!(leadersMediaSeqRaw > this.lastRequestedMediaSeqRaw)) return [3 /*break*/, 4];
512
+ this.lastRequestedMediaSeqRaw = leadersMediaSeqRaw;
513
+ _a = this;
514
+ return [4 /*yield*/, this.sessionLiveState.get("liveSegsForFollowers")];
515
+ case 3:
516
+ _a.liveSegsForFollowers = _b.sent();
517
+ this._updateLiveSegQueue();
518
+ _b.label = 4;
519
+ case 4:
520
+ currentMediaSequenceSegments = {};
521
+ segmentCount = 0;
522
+ increment = 0;
523
+ for (i = 0; i < Object.keys(this.mediaManifestURIs).length; i++) {
524
+ bw = Object.keys(this.mediaManifestURIs)[i];
525
+ liveTargetBandwidth = this._findNearestBw(bw, Object.keys(this.mediaManifestURIs));
526
+ vodTargetBandwidth = this._getNearestBandwidth(bw, Object.keys(this.vodSegments));
527
+ // Remove segments and disc-tag if they are on top
528
+ if (this.vodSegments[vodTargetBandwidth].length > 0 && this.vodSegments[vodTargetBandwidth][0].discontinuity) {
529
+ this.vodSegments[vodTargetBandwidth].shift();
530
+ increment = 1;
531
+ }
532
+ segmentCount = this.vodSegments[vodTargetBandwidth].length;
533
+ currentMediaSequenceSegments[liveTargetBandwidth] = [];
534
+ // In case we switch back before we've depleted all transitional segments
535
+ currentMediaSequenceSegments[liveTargetBandwidth] = this.vodSegments[vodTargetBandwidth].concat(this.liveSegQueue[liveTargetBandwidth]);
536
+ currentMediaSequenceSegments[liveTargetBandwidth].push({ discontinuity: true, cue: { in: true } });
537
+ debug("[".concat(this.sessionId, "]: Getting current media segments for bw=").concat(bw));
538
+ }
539
+ this.discSeqCount += increment;
540
+ return [2 /*return*/, {
541
+ currMseqSegs: currentMediaSequenceSegments,
542
+ segCount: segmentCount,
543
+ }];
544
+ }
545
+ });
546
+ });
547
+ };
548
+ SessionLive.prototype.getCurrentMediaAndDiscSequenceCount = function () {
549
+ return __awaiter(this, void 0, void 0, function () {
550
+ return __generator(this, function (_a) {
551
+ return [2 /*return*/, {
552
+ mediaSeq: this.mediaSeqCount,
553
+ discSeq: this.discSeqCount,
554
+ }];
555
+ });
556
+ });
557
+ };
558
+ SessionLive.prototype.getStatus = function () {
559
+ var playheadStateMap = {};
560
+ playheadStateMap[PlayheadState.IDLE] = "idle";
561
+ playheadStateMap[PlayheadState.RUNNING] = "running";
562
+ playheadStateMap[PlayheadState.CRASHED] = "crashed";
563
+ playheadStateMap[PlayheadState.STOPPED] = "stopped";
564
+ var status = {
565
+ sessionId: this.sessionId,
566
+ playhead: {
567
+ state: playheadStateMap[this.playheadState],
568
+ },
569
+ };
570
+ return status;
571
+ };
572
+ // Generate manifest to give to client
573
+ SessionLive.prototype.getCurrentMediaManifestAsync = function (bw) {
574
+ return __awaiter(this, void 0, void 0, function () {
575
+ var attempts, m3u8, exc_1;
576
+ return __generator(this, function (_a) {
577
+ switch (_a.label) {
578
+ case 0:
579
+ if (!this.sessionLiveState) {
580
+ throw new Error("SessionLive not ready");
581
+ }
582
+ if (bw === null) {
583
+ debug("[".concat(this.sessionId, "]: No bandwidth provided"));
584
+ return [2 /*return*/, null];
585
+ }
586
+ debug("[".concat(this.sessionId, "]: ...Loading the selected Live Media Manifest"));
587
+ attempts = 10;
588
+ m3u8 = null;
589
+ _a.label = 1;
590
+ case 1:
591
+ if (!(!m3u8 && attempts > 0)) return [3 /*break*/, 8];
592
+ attempts--;
593
+ _a.label = 2;
594
+ case 2:
595
+ _a.trys.push([2, 6, , 7]);
596
+ return [4 /*yield*/, this._GenerateLiveManifest(bw)];
597
+ case 3:
598
+ m3u8 = _a.sent();
599
+ if (!!m3u8) return [3 /*break*/, 5];
600
+ debug("[".concat(this.sessionId, "]: No manifest available yet, will try again after 1000ms"));
601
+ return [4 /*yield*/, timer(1000)];
602
+ case 4:
603
+ _a.sent();
604
+ _a.label = 5;
605
+ case 5: return [3 /*break*/, 7];
606
+ case 6:
607
+ exc_1 = _a.sent();
608
+ throw new Error("[".concat(this.instanceId, "][").concat(this.sessionId, "]: Failed to generate manifest. Live Session might have ended already. \n").concat(exc_1));
609
+ case 7: return [3 /*break*/, 1];
610
+ case 8:
611
+ if (!m3u8) {
612
+ throw new Error("[".concat(this.instanceId, "][").concat(this.sessionId, "]: Failed to generate manifest after 10000ms"));
613
+ }
614
+ return [2 /*return*/, m3u8];
615
+ }
616
+ });
617
+ });
618
+ };
619
+ // TODO: Implement this later
620
+ SessionLive.prototype.getCurrentAudioManifestAsync = function (audioGroupId, audioLanguage) {
621
+ return __awaiter(this, void 0, void 0, function () {
622
+ return __generator(this, function (_a) {
623
+ debug("[".concat(this.sessionId, "]: getCurrentAudioManifestAsync is NOT Implemented"));
624
+ return [2 /*return*/, "Not Implemented"];
625
+ });
626
+ });
627
+ };
628
+ /**
629
+ *
630
+ * @param {string} masterManifestURI The master manifest URI.
631
+ * @returns Loads the URIs to the different media playlists from the given master playlist.
632
+ *
633
+ */
634
+ SessionLive.prototype._loadMasterManifest = function (masterManifestURI) {
635
+ return __awaiter(this, void 0, void 0, function () {
636
+ var parser, controller, timeout, response;
637
+ var _this = this;
638
+ return __generator(this, function (_a) {
639
+ switch (_a.label) {
640
+ case 0:
641
+ if (masterManifestURI === null) {
642
+ throw new Error("[".concat(this.instanceId, "][").concat(this.sessionId, "]: No masterManifestURI provided"));
643
+ }
644
+ parser = m3u8.createStream();
645
+ controller = new AbortController();
646
+ timeout = setTimeout(function () {
647
+ debug("[".concat(_this.sessionId, "]: Request Timeout! Aborting Request to ").concat(masterManifestURI));
648
+ controller.abort();
649
+ }, FAIL_TIMEOUT);
650
+ return [4 /*yield*/, fetch(masterManifestURI, { signal: controller.signal })];
651
+ case 1:
652
+ response = _a.sent();
653
+ try {
654
+ response.body.pipe(parser);
655
+ }
656
+ catch (err) {
657
+ debug("[".concat(this.sessionId, "]: Error when piping response to parser! ").concat(JSON.stringify(err)));
658
+ return [2 /*return*/, Promise.reject(err)];
659
+ }
660
+ finally {
661
+ clearTimeout(timeout);
662
+ }
663
+ return [2 /*return*/, new Promise(function (resolve, reject) {
664
+ parser.on("m3u", function (m3u) {
665
+ debug("[".concat(_this.sessionId, "]: ...Fetched a New Live Master Manifest from:\n").concat(masterManifestURI));
666
+ var baseUrl = "";
667
+ var m = masterManifestURI.match(/^(.*)\/.*?$/);
668
+ if (m) {
669
+ baseUrl = m[1] + "/";
670
+ }
671
+ // Get all Profile manifest URIs in the Live Master Manifest
672
+ for (var i = 0; i < m3u.items.StreamItem.length; i++) {
673
+ var streamItem = m3u.items.StreamItem[i];
674
+ var streamItemBW = streamItem.get("bandwidth");
675
+ var mediaManifestUri = url.resolve(baseUrl, streamItem.get("uri"));
676
+ if (!_this.mediaManifestURIs[streamItemBW]) {
677
+ _this.mediaManifestURIs[streamItemBW] = "";
678
+ }
679
+ _this.mediaManifestURIs[streamItemBW] = mediaManifestUri;
680
+ }
681
+ debug("[".concat(_this.sessionId, "]: All Live Media Manifest URIs have been collected. (").concat(Object.keys(_this.mediaManifestURIs).length, ") profiles found!"));
682
+ resolve();
683
+ parser.on("error", function (exc) {
684
+ debug("Parser Error: ".concat(JSON.stringify(exc)));
685
+ reject(exc);
686
+ });
687
+ });
688
+ })];
689
+ }
690
+ });
691
+ });
692
+ };
693
+ // FOLLOWER only function
694
+ SessionLive.prototype._updateLiveSegQueue = function () {
695
+ if (Object.keys(this.liveSegsForFollowers).length === 0) {
696
+ debug("[".concat(this.sessionId, "]: FOLLOWER: Error No Segments found at all."));
697
+ }
698
+ var liveBws = Object.keys(this.liveSegsForFollowers);
699
+ var size = this.liveSegsForFollowers[liveBws[0]].length;
700
+ // Push the New Live Segments to All Variants
701
+ for (var segIdx = 0; segIdx < size; segIdx++) {
702
+ for (var i = 0; i < liveBws.length; i++) {
703
+ var liveBw = liveBws[i];
704
+ var liveSegFromLeader = this.liveSegsForFollowers[liveBw][segIdx];
705
+ if (!this.liveSegQueue[liveBw]) {
706
+ this.liveSegQueue[liveBw] = [];
707
+ }
708
+ // Do not push duplicates
709
+ var liveSegURIs = this.liveSegQueue[liveBw].filter(function (seg) { return seg.uri; }).map(function (seg) { return seg.uri; });
710
+ if (liveSegFromLeader.uri && liveSegURIs.includes(liveSegFromLeader.uri)) {
711
+ debug("[".concat(this.sessionId, "]: FOLLOWER: Found duplicate live segment. Skip push! (").concat(liveBw, ")"));
712
+ }
713
+ else {
714
+ this.liveSegQueue[liveBw].push(liveSegFromLeader);
715
+ debug("[".concat(this.sessionId, "]: FOLLOWER: Pushed segment (").concat(liveSegFromLeader.uri ? liveSegFromLeader.uri : "Disc-tag", ") to 'liveSegQueue' (").concat(liveBw, ")"));
716
+ }
717
+ }
718
+ }
719
+ // Remove older segments and update counts
720
+ var newTotalDuration = this._incrementAndShift("FOLLOWER");
721
+ if (newTotalDuration) {
722
+ debug("[".concat(this.sessionId, "]: FOLLOWER: New Adjusted Playlist Duration=").concat(newTotalDuration, "s"));
723
+ }
724
+ };
725
+ /**
726
+ * This function adds new live segments to the node from which it can
727
+ * generate new manifests from. Method for attaining new segments differ
728
+ * depending on node Rank. The Leader collects from live source and
729
+ * Followers collect from shared storage.
730
+ *
731
+ * @returns Nothing, but gives data to certain class-variables
732
+ */
733
+ SessionLive.prototype._loadAllMediaManifests = function () {
734
+ return __awaiter(this, void 0, void 0, function () {
735
+ var currentMseqRaw, isLeader, leadersMediaSeqRaw, attempts, segDur, waitTimeMs, liveSegsInStore, segDur, waitTimeMs, FETCH_ATTEMPTS, bandwidthsToSkipOnRetry, _loop_1, this_1, state_1, leadersCurrentMseqRaw, counts, leadersFirstMseqRaw, _a, pushPromises, i, bw, leaderORFollower, newTotalDuration, liveBws, segListSize, firstCounts;
736
+ var _this = this;
737
+ return __generator(this, function (_b) {
738
+ switch (_b.label) {
739
+ case 0:
740
+ debug("[".concat(this.sessionId, "]: Attempting to load all media manifest URIs in=").concat(Object.keys(this.mediaManifestURIs)));
741
+ currentMseqRaw = null;
742
+ return [4 /*yield*/, this.sessionLiveStateStore.isLeader(this.instanceId)];
743
+ case 1:
744
+ isLeader = _b.sent();
745
+ if (!(!isLeader && this.lastRequestedMediaSeqRaw !== null)) return [3 /*break*/, 17];
746
+ debug("[".concat(this.sessionId, "]: FOLLOWER: Reading data from store!"));
747
+ return [4 /*yield*/, this.sessionLiveState.get("lastRequestedMediaSeqRaw")];
748
+ case 2:
749
+ leadersMediaSeqRaw = _b.sent();
750
+ if (!leadersMediaSeqRaw < this.lastRequestedMediaSeqRaw && this.blockGenerateManifest) {
751
+ this.blockGenerateManifest = false;
752
+ }
753
+ attempts = 10;
754
+ _b.label = 3;
755
+ case 3:
756
+ if (!(!leadersMediaSeqRaw && attempts > 0)) return [3 /*break*/, 8];
757
+ if (!!leadersMediaSeqRaw) return [3 /*break*/, 5];
758
+ return [4 /*yield*/, this.sessionLiveStateStore.isLeader(this.instanceId)];
759
+ case 4:
760
+ isLeader = _b.sent();
761
+ if (isLeader) {
762
+ debug("[".concat(this.instanceId, "]: I'm the new leader"));
763
+ return [2 /*return*/];
764
+ }
765
+ _b.label = 5;
766
+ case 5:
767
+ if (!this.allowedToSet) {
768
+ debug("[".concat(this.sessionId, "]: We are about to switch away from LIVE. Abort fetching from Store"));
769
+ return [3 /*break*/, 8];
770
+ }
771
+ segDur = this._getAnyFirstSegmentDurationMs() || DEFAULT_PLAYHEAD_INTERVAL_MS;
772
+ waitTimeMs = parseInt(segDur / 3, 10);
773
+ debug("[".concat(this.sessionId, "]: FOLLOWER: Leader has not put anything in store... Will check again in ").concat(waitTimeMs, "ms (Tries left=[").concat(attempts, "])"));
774
+ return [4 /*yield*/, timer(waitTimeMs)];
775
+ case 6:
776
+ _b.sent();
777
+ this.timerCompensation = false;
778
+ return [4 /*yield*/, this.sessionLiveState.get("lastRequestedMediaSeqRaw")];
779
+ case 7:
780
+ leadersMediaSeqRaw = _b.sent();
781
+ attempts--;
782
+ return [3 /*break*/, 3];
783
+ case 8:
784
+ if (!leadersMediaSeqRaw) {
785
+ debug("[".concat(this.instanceId, "]: The leader is still alive"));
786
+ return [2 /*return*/];
787
+ }
788
+ return [4 /*yield*/, this.sessionLiveState.get("liveSegsForFollowers")];
789
+ case 9:
790
+ liveSegsInStore = _b.sent();
791
+ attempts = 10;
792
+ _b.label = 10;
793
+ case 10:
794
+ if (!((leadersMediaSeqRaw <= this.lastRequestedMediaSeqRaw && attempts > 0) || (this._containsSegment(this.liveSegsForFollowers, liveSegsInStore) && attempts > 0))) return [3 /*break*/, 16];
795
+ if (!this.allowedToSet) {
796
+ debug("[".concat(this.sessionId, "]: We are about to switch away from LIVE. Abort fetching from Store"));
797
+ return [3 /*break*/, 16];
798
+ }
799
+ if (!(leadersMediaSeqRaw <= this.lastRequestedMediaSeqRaw)) return [3 /*break*/, 12];
800
+ return [4 /*yield*/, this.sessionLiveStateStore.isLeader(this.instanceId)];
801
+ case 11:
802
+ isLeader = _b.sent();
803
+ if (isLeader) {
804
+ debug("[".concat(this.instanceId, "][").concat(this.sessionId, "]: I'm the new leader"));
805
+ return [2 /*return*/];
806
+ }
807
+ _b.label = 12;
808
+ case 12:
809
+ if (this._containsSegment(this.liveSegsForFollowers, liveSegsInStore)) {
810
+ debug("[".concat(this.sessionId, "]: FOLLOWER: _containsSegment=true,").concat(leadersMediaSeqRaw, ",").concat(this.lastRequestedMediaSeqRaw));
811
+ }
812
+ segDur = this._getAnyFirstSegmentDurationMs() || DEFAULT_PLAYHEAD_INTERVAL_MS;
813
+ waitTimeMs = parseInt(segDur / 3, 10);
814
+ debug("[".concat(this.sessionId, "]: FOLLOWER: Cannot find anything NEW in store... Will check again in ").concat(waitTimeMs, "ms (Tries left=[").concat(attempts, "])"));
815
+ return [4 /*yield*/, timer(waitTimeMs)];
816
+ case 13:
817
+ _b.sent();
818
+ this.timerCompensation = false;
819
+ return [4 /*yield*/, this.sessionLiveState.get("lastRequestedMediaSeqRaw")];
820
+ case 14:
821
+ leadersMediaSeqRaw = _b.sent();
822
+ return [4 /*yield*/, this.sessionLiveState.get("liveSegsForFollowers")];
823
+ case 15:
824
+ liveSegsInStore = _b.sent();
825
+ attempts--;
826
+ return [3 /*break*/, 10];
827
+ case 16:
828
+ // FINALLY
829
+ if (leadersMediaSeqRaw <= this.lastRequestedMediaSeqRaw) {
830
+ debug("[".concat(this.instanceId, "][").concat(this.sessionId, "]: The leader is still alive"));
831
+ return [2 /*return*/];
832
+ }
833
+ // Follower updates its manifest building blocks (segment holders & counts)
834
+ this.lastRequestedMediaSeqRaw = leadersMediaSeqRaw;
835
+ this.liveSegsForFollowers = liveSegsInStore;
836
+ debug("[".concat(this.sessionId, "]: These are the segments from store: [").concat(JSON.stringify(this.liveSegsForFollowers), "]"));
837
+ this._updateLiveSegQueue();
838
+ return [2 /*return*/];
839
+ case 17:
840
+ FETCH_ATTEMPTS = 10;
841
+ this.liveSegsForFollowers = {};
842
+ bandwidthsToSkipOnRetry = [];
843
+ _loop_1 = function () {
844
+ var livePromises, manifestList, i, bw, err_3, allStoredMediaSeqCounts, higestMediaSeqCount_1, retryDelayMs, firstBw, lastIdx, leadersFirstSeqCounts, tries, transitSegs, transitSegs;
845
+ return __generator(this, function (_c) {
846
+ switch (_c.label) {
847
+ case 0:
848
+ if (isLeader) {
849
+ debug("[".concat(this_1.sessionId, "]: LEADER: Trying to fetch manifests for all bandwidths\n Attempts left=[").concat(FETCH_ATTEMPTS, "]"));
850
+ }
851
+ else {
852
+ debug("[".concat(this_1.sessionId, "]: NEW FOLLOWER: Trying to fetch manifests for all bandwidths\n Attempts left=[").concat(FETCH_ATTEMPTS, "]"));
853
+ }
854
+ if (!this_1.allowedToSet) {
855
+ debug("[".concat(this_1.sessionId, "]: We are about to switch away from LIVE. Abort fetching from Live-Source"));
856
+ return [2 /*return*/, "break"];
857
+ }
858
+ livePromises = [];
859
+ manifestList = [];
860
+ this_1.pushAmount = 0;
861
+ _c.label = 1;
862
+ case 1:
863
+ _c.trys.push([1, 3, , 4]);
864
+ if (bandwidthsToSkipOnRetry.length > 0) {
865
+ debug("[".concat(this_1.sessionId, "]: (X) Skipping loadMedia promises for bws ").concat(JSON.stringify(bandwidthsToSkipOnRetry)));
866
+ }
867
+ // Collect Live Source Requesting Promises
868
+ for (i = 0; i < Object.keys(this_1.mediaManifestURIs).length; i++) {
869
+ bw = Object.keys(this_1.mediaManifestURIs)[i];
870
+ if (bandwidthsToSkipOnRetry.includes(bw)) {
871
+ continue;
872
+ }
873
+ livePromises.push(this_1._loadMediaManifest(bw));
874
+ debug("[".concat(this_1.sessionId, "]: Pushed loadMedia promise for bw=[").concat(bw, "]"));
875
+ }
876
+ // Fetch From Live Source
877
+ debug("[".concat(this_1.sessionId, "]: Executing Promises I: Fetch From Live Source"));
878
+ return [4 /*yield*/, allSettled(livePromises)];
879
+ case 2:
880
+ manifestList = _c.sent();
881
+ livePromises = [];
882
+ return [3 /*break*/, 4];
883
+ case 3:
884
+ err_3 = _c.sent();
885
+ debug("[".concat(this_1.sessionId, "]: Promises I: FAILURE!\n").concat(err_3));
886
+ return [2 /*return*/, { value: void 0 }];
887
+ case 4:
888
+ if (!manifestList.some(function (result) { return result.status === "rejected"; })) return [3 /*break*/, 6];
889
+ FETCH_ATTEMPTS--;
890
+ debug("[".concat(this_1.sessionId, "]: ALERT! Promises I: Failed, Rejection Found! Trying again in 1000ms..."));
891
+ return [4 /*yield*/, timer(1000)];
892
+ case 5:
893
+ _c.sent();
894
+ return [2 /*return*/, "continue"];
895
+ case 6:
896
+ // Store the results locally
897
+ manifestList.forEach(function (variantItem) {
898
+ var bw = variantItem.value.bandwidth;
899
+ if (!_this.liveSourceM3Us[bw]) {
900
+ _this.liveSourceM3Us[bw] = {};
901
+ }
902
+ _this.liveSourceM3Us[bw] = variantItem.value;
903
+ });
904
+ allStoredMediaSeqCounts = Object.keys(this_1.liveSourceM3Us).map(function (variant) { return _this.liveSourceM3Us[variant].mediaSeq; });
905
+ if (!!allStoredMediaSeqCounts.every(function (val, i, arr) { return val === arr[0]; })) return [3 /*break*/, 8];
906
+ debug("[".concat(this_1.sessionId, "]: Live Mseq counts=[").concat(allStoredMediaSeqCounts, "]"));
907
+ higestMediaSeqCount_1 = Math.max.apply(Math, allStoredMediaSeqCounts);
908
+ bandwidthsToSkipOnRetry = Object.keys(this_1.liveSourceM3Us).filter(function (bw) {
909
+ if (_this.liveSourceM3Us[bw].mediaSeq === higestMediaSeqCount_1) {
910
+ return true;
911
+ }
912
+ return false;
913
+ });
914
+ // Decrement fetch counter
915
+ FETCH_ATTEMPTS--;
916
+ retryDelayMs = 1000;
917
+ if (Object.keys(this_1.liveSegQueue).length > 0) {
918
+ firstBw = Object.keys(this_1.liveSegQueue)[0];
919
+ lastIdx = this_1.liveSegQueue[firstBw].length - 1;
920
+ if (this_1.liveSegQueue[firstBw][lastIdx].duration) {
921
+ retryDelayMs = this_1.liveSegQueue[firstBw][lastIdx].duration * 1000 * 0.25;
922
+ }
923
+ }
924
+ // Wait a little before trying again
925
+ debug("[".concat(this_1.sessionId, "]: ALERT! Live Source Data NOT in sync! Will try again after ").concat(retryDelayMs, "ms"));
926
+ return [4 /*yield*/, timer(retryDelayMs)];
927
+ case 7:
928
+ _c.sent();
929
+ if (isLeader) {
930
+ this_1.timerCompensation = false;
931
+ }
932
+ return [2 /*return*/, "continue"];
933
+ case 8:
934
+ currentMseqRaw = allStoredMediaSeqCounts[0];
935
+ if (!!isLeader) return [3 /*break*/, 26];
936
+ return [4 /*yield*/, this_1.sessionLiveState.get("firstCounts")];
937
+ case 9:
938
+ leadersFirstSeqCounts = _c.sent();
939
+ tries = 20;
940
+ _c.label = 10;
941
+ case 10:
942
+ if (!((!isLeader && !leadersFirstSeqCounts.liveSourceMseqCount && tries > 0) || leadersFirstSeqCounts.liveSourceMseqCount === 0)) return [3 /*break*/, 14];
943
+ debug("[".concat(this_1.sessionId, "]: NEW FOLLOWER: Waiting for LEADER to add 'firstCounts' in store! Will look again after 1000ms (tries left=").concat(tries, ")"));
944
+ return [4 /*yield*/, timer(1000)];
945
+ case 11:
946
+ _c.sent();
947
+ return [4 /*yield*/, this_1.sessionLiveState.get("firstCounts")];
948
+ case 12:
949
+ leadersFirstSeqCounts = _c.sent();
950
+ tries--;
951
+ return [4 /*yield*/, this_1.sessionLiveStateStore.isLeader(this_1.instanceId)];
952
+ case 13:
953
+ // Might take over as Leader if Leader is not setting data due to being down.
954
+ isLeader = _c.sent();
955
+ if (isLeader) {
956
+ debug("[".concat(this_1.sessionId, "][").concat(this_1.instanceId, "]: I'm the new leader, and now I am going to add 'firstCounts' in store"));
957
+ }
958
+ return [3 /*break*/, 10];
959
+ case 14:
960
+ if (!(tries === 0)) return [3 /*break*/, 18];
961
+ return [4 /*yield*/, this_1.sessionLiveStateStore.isLeader(this_1.instanceId)];
962
+ case 15:
963
+ isLeader = _c.sent();
964
+ if (!isLeader) return [3 /*break*/, 16];
965
+ debug("[".concat(this_1.sessionId, "][").concat(this_1.instanceId, "]: I'm the new leader, and now I am going to add 'firstCounts' in store"));
966
+ return [2 /*return*/, "break"];
967
+ case 16:
968
+ debug("[".concat(this_1.sessionId, "][").concat(this_1.instanceId, "]: The leader is still alive"));
969
+ return [4 /*yield*/, this_1.sessionLiveState.get("firstCounts")];
970
+ case 17:
971
+ leadersFirstSeqCounts = _c.sent();
972
+ if (!leadersFirstSeqCounts.liveSourceMseqCount) {
973
+ debug("[".concat(this_1.sessionId, "][").concat(this_1.instanceId, "]: Could not find 'firstCounts' in store. Abort Executing Promises II & Returning to Playhead."));
974
+ return [2 /*return*/, { value: void 0 }];
975
+ }
976
+ _c.label = 18;
977
+ case 18:
978
+ if (!isLeader) return [3 /*break*/, 20];
979
+ debug("[".concat(this_1.sessionId, "]: NEW LEADER: Original Leader went missing, I am retrying live source fetch..."));
980
+ return [4 /*yield*/, this_1.sessionLiveState.set("transitSegs", this_1.vodSegments)];
981
+ case 19:
982
+ _c.sent();
983
+ debug("[".concat(this_1.sessionId, "]: NEW LEADER: I am adding 'transitSegs' to Store for future followers"));
984
+ return [2 /*return*/, "continue"];
985
+ case 20:
986
+ // Respawners never do this, only starter followers.
987
+ // Edge Case: FOLLOWER transitioned from session with different segments from LEADER
988
+ if (leadersFirstSeqCounts.discSeqCount !== this_1.discSeqCount) {
989
+ this_1.discSeqCount = leadersFirstSeqCounts.discSeqCount;
990
+ }
991
+ if (!(leadersFirstSeqCounts.mediaSeqCount !== this_1.mediaSeqCount)) return [3 /*break*/, 22];
992
+ this_1.mediaSeqCount = leadersFirstSeqCounts.mediaSeqCount;
993
+ debug("[".concat(this_1.sessionId, "]: FOLLOWER transistioned with wrong V2L segments, updating counts to [").concat(this_1.mediaSeqCount, "][").concat(this_1.discSeqCount, "], and reading 'transitSegs' from store"));
994
+ return [4 /*yield*/, this_1.sessionLiveState.get("transitSegs")];
995
+ case 21:
996
+ transitSegs = _c.sent();
997
+ if (!this_1._isEmpty(transitSegs)) {
998
+ this_1.vodSegments = transitSegs;
999
+ }
1000
+ _c.label = 22;
1001
+ case 22:
1002
+ // Prepare to load segments...
1003
+ debug("[".concat(this_1.instanceId, "][").concat(this_1.sessionId, "]: Newest mseq from LIVE=").concat(currentMseqRaw, " First mseq in store=").concat(leadersFirstSeqCounts.liveSourceMseqCount));
1004
+ if (!(currentMseqRaw === leadersFirstSeqCounts.liveSourceMseqCount)) return [3 /*break*/, 23];
1005
+ this_1.pushAmount = 1; // Follower from start
1006
+ return [3 /*break*/, 25];
1007
+ case 23:
1008
+ // TODO: To support and account for past discontinuity tags in the Live Source stream,
1009
+ // we will need to get the real 'current' discontinuity-sequence count from Leader somehow.
1010
+ // RESPAWNED NODES
1011
+ this_1.pushAmount = currentMseqRaw - leadersFirstSeqCounts.liveSourceMseqCount + 1;
1012
+ return [4 /*yield*/, this_1.sessionLiveState.get("transitSegs")];
1013
+ case 24:
1014
+ transitSegs = _c.sent();
1015
+ //debug(`[${this.sessionId}]: NEW FOLLOWER: I tried to get 'transitSegs'. This is what I found ${JSON.stringify(transitSegs)}`);
1016
+ if (!this_1._isEmpty(transitSegs)) {
1017
+ this_1.vodSegments = transitSegs;
1018
+ }
1019
+ _c.label = 25;
1020
+ case 25:
1021
+ debug("[".concat(this_1.sessionId, "]: ...pushAmount=").concat(this_1.pushAmount));
1022
+ return [3 /*break*/, 27];
1023
+ case 26:
1024
+ // LEADER calculates pushAmount differently...
1025
+ if (this_1.firstTime) {
1026
+ this_1.pushAmount = 1; // Leader from start
1027
+ }
1028
+ else {
1029
+ this_1.pushAmount = currentMseqRaw - this_1.lastRequestedMediaSeqRaw;
1030
+ debug("[".concat(this_1.sessionId, "]: ...calculating pushAmount=").concat(currentMseqRaw, "-").concat(this_1.lastRequestedMediaSeqRaw, "=").concat(this_1.pushAmount));
1031
+ }
1032
+ debug("[".concat(this_1.sessionId, "]: ...pushAmount=").concat(this_1.pushAmount));
1033
+ return [2 /*return*/, "break"];
1034
+ case 27: return [2 /*return*/, "break"];
1035
+ }
1036
+ });
1037
+ };
1038
+ this_1 = this;
1039
+ _b.label = 18;
1040
+ case 18:
1041
+ if (!(FETCH_ATTEMPTS > 0)) return [3 /*break*/, 20];
1042
+ return [5 /*yield**/, _loop_1()];
1043
+ case 19:
1044
+ state_1 = _b.sent();
1045
+ if (typeof state_1 === "object")
1046
+ return [2 /*return*/, state_1.value];
1047
+ if (state_1 === "break")
1048
+ return [3 /*break*/, 20];
1049
+ return [3 /*break*/, 18];
1050
+ case 20:
1051
+ if (FETCH_ATTEMPTS === 0) {
1052
+ debug("[".concat(this.sessionId, "]: Fetching from Live-Source did not work! Returning to Playhead Loop..."));
1053
+ return [2 /*return*/];
1054
+ }
1055
+ return [4 /*yield*/, this.sessionLiveStateStore.isLeader(this.instanceId)];
1056
+ case 21:
1057
+ isLeader = _b.sent();
1058
+ if (!!isLeader) return [3 /*break*/, 26];
1059
+ return [4 /*yield*/, this.sessionLiveState.get("lastRequestedMediaSeqRaw")];
1060
+ case 22:
1061
+ leadersCurrentMseqRaw = _b.sent();
1062
+ return [4 /*yield*/, this.sessionLiveState.get("firstCounts")];
1063
+ case 23:
1064
+ counts = _b.sent();
1065
+ leadersFirstMseqRaw = counts.liveSourceMseqCount;
1066
+ if (!(leadersCurrentMseqRaw !== null && leadersCurrentMseqRaw > currentMseqRaw)) return [3 /*break*/, 26];
1067
+ if (!(leadersFirstMseqRaw !== null && leadersFirstMseqRaw === leadersCurrentMseqRaw)) return [3 /*break*/, 25];
1068
+ // Follower updates it's manifest ingedients (segment holders & counts)
1069
+ this.lastRequestedMediaSeqRaw = leadersCurrentMseqRaw;
1070
+ _a = this;
1071
+ return [4 /*yield*/, this.sessionLiveState.get("liveSegsForFollowers")];
1072
+ case 24:
1073
+ _a.liveSegsForFollowers = _b.sent();
1074
+ debug("[".concat(this.sessionId, "]: NEW FOLLOWER: Leader is ahead or behind me! Clearing Queue and Getting latest segments from store."));
1075
+ this._updateLiveSegQueue();
1076
+ this.firstTime = false;
1077
+ debug("[".concat(this.sessionId, "]: Got all needed segments from live-source (read from store).\nWe are now able to build Live Manifest: [").concat(this.mediaSeqCount, "]"));
1078
+ return [2 /*return*/];
1079
+ case 25:
1080
+ if (leadersCurrentMseqRaw < this.lastRequestedMediaSeqRaw) {
1081
+ // WE ARE A RESPAWN-NODE, and we are ahead of leader.
1082
+ this.blockGenerateManifest = true;
1083
+ }
1084
+ _b.label = 26;
1085
+ case 26:
1086
+ if (!this.allowedToSet) return [3 /*break*/, 28];
1087
+ pushPromises = [];
1088
+ for (i = 0; i < Object.keys(this.mediaManifestURIs).length; i++) {
1089
+ bw = Object.keys(this.mediaManifestURIs)[i];
1090
+ // will add new segments to live seg queue
1091
+ pushPromises.push(this._parseMediaManifest(this.liveSourceM3Us[bw].M3U, this.mediaManifestURIs[bw], bw, isLeader));
1092
+ debug("[".concat(this.sessionId, "]: Pushed pushPromise for bw=").concat(bw));
1093
+ }
1094
+ // Segment Pushing
1095
+ debug("[".concat(this.sessionId, "]: Executing Promises II: Segment Pushing"));
1096
+ return [4 /*yield*/, allSettled(pushPromises)];
1097
+ case 27:
1098
+ _b.sent();
1099
+ leaderORFollower = isLeader ? "LEADER" : "NEW FOLLOWER";
1100
+ newTotalDuration = this._incrementAndShift(leaderORFollower);
1101
+ if (newTotalDuration) {
1102
+ debug("[".concat(this.sessionId, "]: New Adjusted Playlist Duration=").concat(newTotalDuration, "s"));
1103
+ }
1104
+ _b.label = 28;
1105
+ case 28:
1106
+ if (!isLeader) return [3 /*break*/, 35];
1107
+ if (!this.allowedToSet) return [3 /*break*/, 31];
1108
+ liveBws = Object.keys(this.liveSegsForFollowers);
1109
+ segListSize = this.liveSegsForFollowers[liveBws[0]].length;
1110
+ if (!(segListSize > 0)) return [3 /*break*/, 31];
1111
+ debug("[".concat(this.sessionId, "]: LEADER: Adding data to store!"));
1112
+ return [4 /*yield*/, this.sessionLiveState.set("lastRequestedMediaSeqRaw", this.lastRequestedMediaSeqRaw)];
1113
+ case 29:
1114
+ _b.sent();
1115
+ return [4 /*yield*/, this.sessionLiveState.set("liveSegsForFollowers", this.liveSegsForFollowers)];
1116
+ case 30:
1117
+ _b.sent();
1118
+ _b.label = 31;
1119
+ case 31:
1120
+ if (!(this.firstTime && this.allowedToSet)) return [3 /*break*/, 34];
1121
+ // Buy some time for followers (NOT Respawned) to fetch their own L.S m3u8.
1122
+ return [4 /*yield*/, timer(1000)];
1123
+ case 32:
1124
+ // Buy some time for followers (NOT Respawned) to fetch their own L.S m3u8.
1125
+ _b.sent(); // maybe remove
1126
+ firstCounts = {
1127
+ liveSourceMseqCount: this.lastRequestedMediaSeqRaw,
1128
+ mediaSeqCount: this.prevMediaSeqCount,
1129
+ discSeqCount: this.prevDiscSeqCount,
1130
+ };
1131
+ debug("[".concat(this.sessionId, "]: LEADER: I am adding 'firstCounts'=").concat(JSON.stringify(firstCounts), " to Store for future followers"));
1132
+ return [4 /*yield*/, this.sessionLiveState.set("firstCounts", firstCounts)];
1133
+ case 33:
1134
+ _b.sent();
1135
+ _b.label = 34;
1136
+ case 34:
1137
+ debug("[".concat(this.sessionId, "]: LEADER: I am using segs from Mseq=").concat(this.lastRequestedMediaSeqRaw));
1138
+ return [3 /*break*/, 36];
1139
+ case 35:
1140
+ debug("[".concat(this.sessionId, "]: NEW FOLLOWER: I am using segs from Mseq=").concat(this.lastRequestedMediaSeqRaw));
1141
+ _b.label = 36;
1142
+ case 36:
1143
+ this.firstTime = false;
1144
+ debug("[".concat(this.sessionId, "]: Got all needed segments from live-source (from all bandwidths).\nWe are now able to build Live Manifest: [").concat(this.mediaSeqCount, "]"));
1145
+ return [2 /*return*/];
1146
+ }
1147
+ });
1148
+ });
1149
+ };
1150
+ SessionLive.prototype._shiftSegments = function (opt) {
1151
+ var _totalDur = 0;
1152
+ var _segments = {};
1153
+ var _name = "";
1154
+ var _removedSegments = 0;
1155
+ var _removedDiscontinuities = 0;
1156
+ if (opt && opt.totalDur) {
1157
+ _totalDur = opt.totalDur;
1158
+ }
1159
+ if (opt && opt.segments) {
1160
+ _segments = JSON.parse(JSON.stringify(opt.segments)); // clone it
1161
+ }
1162
+ if (opt && opt.name) {
1163
+ _name = opt.name || "NONE";
1164
+ }
1165
+ if (opt && opt.removedSegments) {
1166
+ _removedSegments = opt.removedSegments;
1167
+ }
1168
+ if (opt && opt.removedDiscontinuities) {
1169
+ _removedDiscontinuities = opt.removedDiscontinuities;
1170
+ }
1171
+ var bws = Object.keys(_segments);
1172
+ /* When Total Duration is past the Limit, start Shifting V2L|LIVE segments if found */
1173
+ while (_totalDur > TARGET_PLAYLIST_DURATION_SEC) {
1174
+ // Skip loop if there are no more segments to remove...
1175
+ if (_segments[bws[0]].length === 0) {
1176
+ return { totalDuration: _totalDur, removedSegments: _removedSegments, removedDiscontinuities: _removedDiscontinuities, shiftedSegments: _segments };
1177
+ }
1178
+ debug("[".concat(this.sessionId, "]: ").concat(_name, ": (").concat(_totalDur, ")s/(").concat(TARGET_PLAYLIST_DURATION_SEC, ")s - Playlist Duration is Over the Target. Shift needed!"));
1179
+ var timeToRemove = 0;
1180
+ var incrementDiscSeqCount = false;
1181
+ // Shift Segments for each variant...
1182
+ for (var i = 0; i < bws.length; i++) {
1183
+ var seg = _segments[bws[i]].shift();
1184
+ if (i === 0) {
1185
+ debug("[".concat(this.sessionId, "]: ").concat(_name, ": (").concat(bws[i], ") Ejected from playlist->: ").concat(JSON.stringify(seg, null, 2)));
1186
+ }
1187
+ if (seg && seg.discontinuity) {
1188
+ incrementDiscSeqCount = true;
1189
+ if (_segments[bws[i]].length > 0) {
1190
+ seg = _segments[bws[i]].shift();
1191
+ if (i === 0) {
1192
+ debug("[".concat(this.sessionId, "]: ").concat(_name, ": (").concat(bws[i], ") Ejected from playlist->: ").concat(JSON.stringify(seg, null, 2)));
1193
+ }
1194
+ }
1195
+ }
1196
+ if (seg && seg.duration) {
1197
+ timeToRemove = seg.duration;
1198
+ }
1199
+ }
1200
+ if (timeToRemove) {
1201
+ _totalDur -= timeToRemove;
1202
+ // Increment number of removed segments...
1203
+ _removedSegments++;
1204
+ }
1205
+ if (incrementDiscSeqCount) {
1206
+ // Update Session Live Discontinuity Sequence Count
1207
+ _removedDiscontinuities++;
1208
+ }
1209
+ }
1210
+ return { totalDuration: _totalDur, removedSegments: _removedSegments, removedDiscontinuities: _removedDiscontinuities, shiftedSegments: _segments };
1211
+ };
1212
+ /**
1213
+ * Shifts V2L or LIVE items if total segment duration (V2L+LIVE) are over the target duration.
1214
+ * It will also update and increment SessionLive's MediaSeqCount and DiscSeqCount based
1215
+ * on what was shifted.
1216
+ * @param {string} instanceName Name of instance "LEADER" | "FOLLOWER"
1217
+ * @returns {number} The new total duration in seconds
1218
+ */
1219
+ SessionLive.prototype._incrementAndShift = function (instanceName) {
1220
+ if (!instanceName) {
1221
+ instanceName = "UNKNOWN";
1222
+ }
1223
+ var vodBws = Object.keys(this.vodSegments);
1224
+ var liveBws = Object.keys(this.liveSegQueue);
1225
+ var vodTotalDur = 0;
1226
+ var liveTotalDur = 0;
1227
+ var totalDur = 0;
1228
+ var removedSegments = 0;
1229
+ var removedDiscontinuities = 0;
1230
+ // Calculate Playlist Total Duration
1231
+ this.vodSegments[vodBws[0]].forEach(function (seg) {
1232
+ if (seg.duration) {
1233
+ vodTotalDur += seg.duration;
1234
+ }
1235
+ });
1236
+ this.liveSegQueue[liveBws[0]].forEach(function (seg) {
1237
+ if (seg.duration) {
1238
+ liveTotalDur += seg.duration;
1239
+ }
1240
+ });
1241
+ totalDur = vodTotalDur + liveTotalDur;
1242
+ debug("[".concat(this.sessionId, "]: ").concat(instanceName, ": L2L dur->: ").concat(liveTotalDur, "s | V2L dur->: ").concat(vodTotalDur, "s | Total dur->: ").concat(totalDur, "s"));
1243
+ /** --- SHIFT then INCREMENT --- **/
1244
+ // Shift V2L Segments
1245
+ var outputV2L = this._shiftSegments({
1246
+ name: instanceName,
1247
+ totalDur: totalDur,
1248
+ segments: this.vodSegments,
1249
+ removedSegments: removedSegments,
1250
+ removedDiscontinuities: removedDiscontinuities,
1251
+ });
1252
+ // Update V2L Segments
1253
+ this.vodSegments = outputV2L.shiftedSegments;
1254
+ // Update values
1255
+ totalDur = outputV2L.totalDuration;
1256
+ removedSegments = outputV2L.removedSegments;
1257
+ removedDiscontinuities = outputV2L.removedDiscontinuities;
1258
+ // Shift LIVE Segments
1259
+ var outputLIVE = this._shiftSegments({
1260
+ name: instanceName,
1261
+ totalDur: totalDur,
1262
+ segments: this.liveSegQueue,
1263
+ removedSegments: removedSegments,
1264
+ removedDiscontinuities: removedDiscontinuities,
1265
+ });
1266
+ // Update LIVE Segments
1267
+ this.liveSegQueue = outputLIVE.shiftedSegments;
1268
+ // Update values
1269
+ totalDur = outputLIVE.totalDuration;
1270
+ removedSegments = outputLIVE.removedSegments;
1271
+ removedDiscontinuities = outputLIVE.removedDiscontinuities;
1272
+ // Update Session Live Discontinuity Sequence Count...
1273
+ this.prevDiscSeqCount = this.discSeqCount;
1274
+ this.discSeqCount += removedDiscontinuities;
1275
+ // Update Session Live Media Sequence Count...
1276
+ this.prevMediaSeqCount = this.mediaSeqCount;
1277
+ this.mediaSeqCount += removedSegments;
1278
+ if (this.restAmount) {
1279
+ this.mediaSeqCount += this.restAmount;
1280
+ debug("[".concat(this.sessionId, "]: ").concat(instanceName, ": Added restAmount=[").concat(this.restAmount, "] to 'mediaSeqCount'"));
1281
+ this.restAmount = 0;
1282
+ }
1283
+ if (this.discSeqCount !== this.prevDiscSeqCount) {
1284
+ debug("[".concat(this.sessionId, "]: ").concat(instanceName, ": Incrementing Dseq Count from {").concat(this.prevDiscSeqCount, "} -> {").concat(this.discSeqCount, "}"));
1285
+ }
1286
+ debug("[".concat(this.sessionId, "]: ").concat(instanceName, ": Incrementing Mseq Count from [").concat(this.prevMediaSeqCount, "] -> [").concat(this.mediaSeqCount, "]"));
1287
+ debug("[".concat(this.sessionId, "]: ").concat(instanceName, ": Finished updating all Counts and Segment Queues!"));
1288
+ return totalDur;
1289
+ };
1290
+ SessionLive.prototype._loadMediaManifest = function (bw) {
1291
+ return __awaiter(this, void 0, void 0, function () {
1292
+ var liveTargetBandwidth, mediaManifestUri, parser, controller, timeout, response;
1293
+ var _this = this;
1294
+ return __generator(this, function (_a) {
1295
+ switch (_a.label) {
1296
+ case 0:
1297
+ if (!this.sessionLiveState) {
1298
+ throw new Error("SessionLive not ready");
1299
+ }
1300
+ liveTargetBandwidth = this._findNearestBw(bw, Object.keys(this.mediaManifestURIs));
1301
+ debug("[".concat(this.sessionId, "]: Requesting bw=(").concat(bw, "), Nearest Bandwidth is: ").concat(liveTargetBandwidth));
1302
+ mediaManifestUri = this.mediaManifestURIs[liveTargetBandwidth];
1303
+ parser = m3u8.createStream();
1304
+ controller = new AbortController();
1305
+ timeout = setTimeout(function () {
1306
+ debug("[".concat(_this.sessionId, "]: Request Timeout! Aborting Request to ").concat(mediaManifestUri));
1307
+ controller.abort();
1308
+ }, FAIL_TIMEOUT);
1309
+ return [4 /*yield*/, fetch(mediaManifestUri, { signal: controller.signal })];
1310
+ case 1:
1311
+ response = _a.sent();
1312
+ try {
1313
+ response.body.pipe(parser);
1314
+ }
1315
+ catch (err) {
1316
+ debug("[".concat(this.sessionId, "]: Error when piping response to parser! ").concat(JSON.stringify(err)));
1317
+ return [2 /*return*/, Promise.reject(err)];
1318
+ }
1319
+ finally {
1320
+ clearTimeout(timeout);
1321
+ }
1322
+ return [2 /*return*/, new Promise(function (resolve, reject) {
1323
+ parser.on("m3u", function (m3u) {
1324
+ try {
1325
+ var resolveObj = {
1326
+ M3U: m3u,
1327
+ mediaSeq: m3u.get("mediaSequence"),
1328
+ bandwidth: liveTargetBandwidth,
1329
+ };
1330
+ resolve(resolveObj);
1331
+ }
1332
+ catch (exc) {
1333
+ debug("[".concat(_this.sessionId, "]: Error when parsing latest manifest"));
1334
+ reject(exc);
1335
+ }
1336
+ });
1337
+ parser.on("error", function (exc) {
1338
+ debug("Parser Error: ".concat(JSON.stringify(exc)));
1339
+ reject(exc);
1340
+ });
1341
+ })];
1342
+ }
1343
+ });
1344
+ });
1345
+ };
1346
+ SessionLive.prototype._parseMediaManifest = function (m3u, mediaManifestUri, liveTargetBandwidth, isLeader) {
1347
+ var _this = this;
1348
+ return new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
1349
+ var baseUrl, m, startIdx;
1350
+ return __generator(this, function (_a) {
1351
+ try {
1352
+ if (!this.liveSegQueue[liveTargetBandwidth]) {
1353
+ this.liveSegQueue[liveTargetBandwidth] = [];
1354
+ }
1355
+ if (!this.liveSegsForFollowers[liveTargetBandwidth]) {
1356
+ this.liveSegsForFollowers[liveTargetBandwidth] = [];
1357
+ }
1358
+ baseUrl = "";
1359
+ m = mediaManifestUri.match(/^(.*)\/.*?$/);
1360
+ if (m) {
1361
+ baseUrl = m[1] + "/";
1362
+ }
1363
+ //debug(`[${this.sessionId}]: Current RAW Mseq: [${m3u.get("mediaSequence")}]`);
1364
+ //debug(`[${this.sessionId}]: Previous RAW Mseq: [${this.lastRequestedMediaSeqRaw}]`);
1365
+ if (this.pushAmount >= 0) {
1366
+ this.lastRequestedMediaSeqRaw = m3u.get("mediaSequence");
1367
+ }
1368
+ this.targetDuration = m3u.get("targetDuration");
1369
+ startIdx = m3u.items.PlaylistItem.length - this.pushAmount;
1370
+ if (startIdx < 0) {
1371
+ this.restAmount = startIdx * -1;
1372
+ startIdx = 0;
1373
+ }
1374
+ if (mediaManifestUri) {
1375
+ // push segments
1376
+ this._addLiveSegmentsToQueue(startIdx, m3u.items.PlaylistItem, baseUrl, liveTargetBandwidth, isLeader);
1377
+ }
1378
+ resolve();
1379
+ }
1380
+ catch (exc) {
1381
+ console.error("ERROR: " + exc);
1382
+ reject(exc);
1383
+ }
1384
+ return [2 /*return*/];
1385
+ });
1386
+ }); });
1387
+ };
1388
+ /**
1389
+ * Collects 'new' PlaylistItems and converts them into custom SegmentItems,
1390
+ * then Pushes them to the LiveSegQueue for all variants.
1391
+ * @param {number} startIdx
1392
+ * @param {m3u8.Item.PlaylistItem} playlistItems
1393
+ * @param {string} baseUrl
1394
+ * @param {string} liveTargetBandwidth
1395
+ */
1396
+ SessionLive.prototype._addLiveSegmentsToQueue = function (startIdx, playlistItems, baseUrl, liveTargetBandwidth, isLeader) {
1397
+ var leaderOrFollower = isLeader ? "LEADER" : "NEW FOLLOWER";
1398
+ var _loop_2 = function (i) {
1399
+ var seg = {};
1400
+ var playlistItem = playlistItems[i];
1401
+ var segmentUri = void 0;
1402
+ var cueData = null;
1403
+ var daterangeData = null;
1404
+ var attributes = playlistItem["attributes"].attributes;
1405
+ if (playlistItem.properties.discontinuity) {
1406
+ this_2.liveSegQueue[liveTargetBandwidth].push({ discontinuity: true });
1407
+ this_2.liveSegsForFollowers[liveTargetBandwidth].push({ discontinuity: true });
1408
+ }
1409
+ if ("cuein" in attributes) {
1410
+ if (!cueData) {
1411
+ cueData = {};
1412
+ }
1413
+ cueData["in"] = true;
1414
+ }
1415
+ if ("cueout" in attributes) {
1416
+ if (!cueData) {
1417
+ cueData = {};
1418
+ }
1419
+ cueData["out"] = true;
1420
+ cueData["duration"] = attributes["cueout"];
1421
+ }
1422
+ if ("cuecont" in attributes) {
1423
+ if (!cueData) {
1424
+ cueData = {};
1425
+ }
1426
+ cueData["cont"] = true;
1427
+ }
1428
+ if ("scteData" in attributes) {
1429
+ if (!cueData) {
1430
+ cueData = {};
1431
+ }
1432
+ cueData["scteData"] = attributes["scteData"];
1433
+ }
1434
+ if ("assetData" in attributes) {
1435
+ if (!cueData) {
1436
+ cueData = {};
1437
+ }
1438
+ cueData["assetData"] = attributes["assetData"];
1439
+ }
1440
+ if ("daterange" in attributes) {
1441
+ if (!daterangeData) {
1442
+ daterangeData = {};
1443
+ }
1444
+ var allDaterangeAttributes = Object.keys(attributes["daterange"]);
1445
+ allDaterangeAttributes.forEach(function (attr) {
1446
+ if (attr.match(/DURATION$/)) {
1447
+ daterangeData[attr.toLowerCase()] = parseFloat(attributes["daterange"][attr]);
1448
+ }
1449
+ else {
1450
+ daterangeData[attr.toLowerCase()] = attributes["daterange"][attr];
1451
+ }
1452
+ });
1453
+ }
1454
+ if (playlistItem.properties.uri) {
1455
+ if (playlistItem.properties.uri.match("^http")) {
1456
+ segmentUri = playlistItem.properties.uri;
1457
+ }
1458
+ else {
1459
+ segmentUri = url.resolve(baseUrl, playlistItem.properties.uri);
1460
+ }
1461
+ seg["duration"] = playlistItem.properties.duration;
1462
+ seg["uri"] = segmentUri;
1463
+ seg["cue"] = cueData;
1464
+ if (daterangeData) {
1465
+ seg["daterange"] = daterangeData;
1466
+ }
1467
+ // Push new Live Segments! But do not push duplicates
1468
+ var liveSegURIs = this_2.liveSegQueue[liveTargetBandwidth].filter(function (seg) { return seg.uri; }).map(function (seg) { return seg.uri; });
1469
+ if (seg.uri && liveSegURIs.includes(seg.uri)) {
1470
+ debug("[".concat(this_2.sessionId, "]: ").concat(leaderOrFollower, ": Found duplicate live segment. Skip push! (").concat(liveTargetBandwidth, ")"));
1471
+ }
1472
+ else {
1473
+ this_2.liveSegQueue[liveTargetBandwidth].push(seg);
1474
+ this_2.liveSegsForFollowers[liveTargetBandwidth].push(seg);
1475
+ debug("[".concat(this_2.sessionId, "]: ").concat(leaderOrFollower, ": Pushed segment (").concat(seg.uri ? seg.uri : "Disc-tag", ") to 'liveSegQueue' (").concat(liveTargetBandwidth, ")"));
1476
+ }
1477
+ }
1478
+ };
1479
+ var this_2 = this;
1480
+ for (var i = startIdx; i < playlistItems.length; i++) {
1481
+ _loop_2(i);
1482
+ }
1483
+ };
1484
+ /*
1485
+ ----------------------
1486
+ GENERATE MANIFEST
1487
+ ----------------------
1488
+ * Should be called independently from _loadAll...,_loadMedia...
1489
+ * So long Nodes are in sync!
1490
+ *
1491
+ * (returning null will cause the engine to try again after 1000ms)
1492
+ */
1493
+ SessionLive.prototype._GenerateLiveManifest = function (bw) {
1494
+ return __awaiter(this, void 0, void 0, function () {
1495
+ var liveTargetBandwidth, vodTargetBandwidth, segAmounts, i, vodSeg, m3u8;
1496
+ var _this = this;
1497
+ return __generator(this, function (_a) {
1498
+ if (bw === null) {
1499
+ throw new Error("No bandwidth provided");
1500
+ }
1501
+ liveTargetBandwidth = this._findNearestBw(bw, Object.keys(this.mediaManifestURIs));
1502
+ vodTargetBandwidth = this._getNearestBandwidth(bw, Object.keys(this.vodSegments));
1503
+ debug("[".concat(this.sessionId, "]: Client requesting manifest for bw=(").concat(bw, "). Nearest LiveBw=(").concat(liveTargetBandwidth, ")"));
1504
+ if (this.blockGenerateManifest) {
1505
+ debug("[".concat(this.sessionId, "]: FOLLOWER: Cannot Generate Manifest! Waiting to sync-up with Leader..."));
1506
+ return [2 /*return*/, null];
1507
+ }
1508
+ // Uncomment below to guarantee that node always return the most current m3u8,
1509
+ // But it will cost an extra trip to store for every client request...
1510
+ /*
1511
+ // DO NOT GENERATE MANIFEST CASE: Node is NOT in sync with Leader. (Store has new segs, but node hasn't read them yet)
1512
+ const isLeader = await this.sessionLiveStateStore.isLeader(this.instanceId);
1513
+ if (!isLeader) {
1514
+ let leadersMediaSeqRaw = await this.sessionLiveState.get("lastRequestedMediaSeqRaw");
1515
+ if (leadersMediaSeqRaw !== this.lastRequestedMediaSeqRaw) {
1516
+ debug(`[${this.sessionId}]: FOLLOWER: Cannot Generate Manifest! <${this.instanceId}> New segments need to be collected first!...`);
1517
+ return null;
1518
+ }
1519
+ }
1520
+ */
1521
+ // DO NOT GENERATE MANIFEST CASE: Node has not found anything in store OR Node has not even check yet.
1522
+ if (Object.keys(this.liveSegQueue).length === 0 || (this.liveSegQueue[liveTargetBandwidth] && this.liveSegQueue[liveTargetBandwidth].length === 0)) {
1523
+ debug("[".concat(this.sessionId, "]: Cannot Generate Manifest! <").concat(this.instanceId, "> Not yet collected ANY segments from Live Source..."));
1524
+ return [2 /*return*/, null];
1525
+ }
1526
+ // DO NOT GENERATE MANIFEST CASE: Node is in the middle of gathering segs of all variants.
1527
+ if (Object.keys(this.liveSegQueue).length !== 0) {
1528
+ segAmounts = Object.keys(this.liveSegQueue).map(function (bw) { return _this.liveSegQueue[bw].length; });
1529
+ if (!segAmounts.every(function (val, i, arr) { return val === arr[0]; })) {
1530
+ debug("[".concat(this.sessionId, "]: Cannot Generate Manifest! <").concat(this.instanceId, "> Not yet collected ALL segments from Live Source..."));
1531
+ return [2 /*return*/, null];
1532
+ }
1533
+ }
1534
+ if (!this._isEmpty(this.liveSegQueue) && this.liveSegQueue[Object.keys(this.liveSegQueue)[0]].length !== 0) {
1535
+ this.targetDuration = this._getMaxDuration(this.liveSegQueue[Object.keys(this.liveSegQueue)[0]]);
1536
+ }
1537
+ // Determine if VOD segments influence targetDuration
1538
+ for (i = 0; i < this.vodSegments[vodTargetBandwidth].length; i++) {
1539
+ vodSeg = this.vodSegments[vodTargetBandwidth][i];
1540
+ // Get max duration amongst segments
1541
+ if (vodSeg.duration > this.targetDuration) {
1542
+ this.targetDuration = vodSeg.duration;
1543
+ }
1544
+ }
1545
+ debug("[".concat(this.sessionId, "]: Started Generating the Manifest File:[").concat(this.mediaSeqCount, "]..."));
1546
+ m3u8 = "#EXTM3U\n";
1547
+ m3u8 += "#EXT-X-VERSION:6\n";
1548
+ m3u8 += m3u8Header(this.instanceId);
1549
+ m3u8 += "#EXT-X-INDEPENDENT-SEGMENTS\n";
1550
+ m3u8 += "#EXT-X-TARGETDURATION:" + Math.round(this.targetDuration) + "\n";
1551
+ m3u8 += "#EXT-X-MEDIA-SEQUENCE:" + this.mediaSeqCount + "\n";
1552
+ m3u8 += "#EXT-X-DISCONTINUITY-SEQUENCE:" + this.discSeqCount + "\n";
1553
+ if (Object.keys(this.vodSegments).length !== 0) {
1554
+ // Add transitional segments if there are any left.
1555
+ debug("[".concat(this.sessionId, "]: Adding a Total of (").concat(this.vodSegments[vodTargetBandwidth].length, ") VOD segments to manifest"));
1556
+ m3u8 = this._setMediaManifestTags(this.vodSegments, m3u8, vodTargetBandwidth);
1557
+ // Add live-source segments
1558
+ m3u8 = this._setMediaManifestTags(this.liveSegQueue, m3u8, liveTargetBandwidth);
1559
+ }
1560
+ debug("[".concat(this.sessionId, "]: Manifest Generation Complete!"));
1561
+ return [2 /*return*/, m3u8];
1562
+ });
1563
+ });
1564
+ };
1565
+ SessionLive.prototype._setMediaManifestTags = function (segments, m3u8, bw) {
1566
+ var _loop_3 = function (i) {
1567
+ var seg = segments[bw][i];
1568
+ if (seg.discontinuity) {
1569
+ m3u8 += "#EXT-X-DISCONTINUITY\n";
1570
+ }
1571
+ if (seg.cue) {
1572
+ if (seg.cue.out) {
1573
+ if (seg.cue.scteData) {
1574
+ m3u8 += "#EXT-OATCLS-SCTE35:" + seg.cue.scteData + "\n";
1575
+ }
1576
+ if (seg.cue.assetData) {
1577
+ m3u8 += "#EXT-X-ASSET:" + seg.cue.assetData + "\n";
1578
+ }
1579
+ m3u8 += "#EXT-X-CUE-OUT:DURATION=" + seg.cue.duration + "\n";
1580
+ }
1581
+ if (seg.cue.cont) {
1582
+ if (seg.cue.scteData) {
1583
+ m3u8 += "#EXT-X-CUE-OUT-CONT:ElapsedTime=" + seg.cue.cont + ",Duration=" + seg.cue.duration + ",SCTE35=" + seg.cue.scteData + "\n";
1584
+ }
1585
+ else {
1586
+ m3u8 += "#EXT-X-CUE-OUT-CONT:" + seg.cue.cont + "/" + seg.cue.duration + "\n";
1587
+ }
1588
+ }
1589
+ }
1590
+ if (seg.datetime) {
1591
+ m3u8 += "#EXT-X-PROGRAM-DATE-TIME:".concat(seg.datetime, "\n");
1592
+ }
1593
+ if (seg.daterange) {
1594
+ var dateRangeAttributes = Object.keys(seg.daterange)
1595
+ .map(function (key) { return daterangeAttribute(key, seg.daterange[key]); })
1596
+ .join(",");
1597
+ if (!seg.datetime && seg.daterange["start-date"]) {
1598
+ m3u8 += "#EXT-X-PROGRAM-DATE-TIME:" + seg.daterange["start-date"] + "\n";
1599
+ }
1600
+ m3u8 += "#EXT-X-DATERANGE:" + dateRangeAttributes + "\n";
1601
+ }
1602
+ // Mimick logic used in hls-vodtolive
1603
+ if (seg.cue && seg.cue.in) {
1604
+ m3u8 += "#EXT-X-CUE-IN" + "\n";
1605
+ }
1606
+ if (seg.uri) {
1607
+ m3u8 += "#EXTINF:" + seg.duration.toFixed(3) + ",\n";
1608
+ m3u8 += seg.uri + "\n";
1609
+ }
1610
+ };
1611
+ for (var i = 0; i < segments[bw].length; i++) {
1612
+ _loop_3(i);
1613
+ }
1614
+ return m3u8;
1615
+ };
1616
+ SessionLive.prototype._findNearestBw = function (bw, array) {
1617
+ var sorted = array.sort(function (a, b) { return b - a; });
1618
+ return sorted.reduce(function (a, b) {
1619
+ return Math.abs(b - bw) < Math.abs(a - bw) ? b : a;
1620
+ });
1621
+ };
1622
+ SessionLive.prototype._getNearestBandwidth = function (bandwidthToMatch, array) {
1623
+ var sortedBandwidths = array.sort(function (a, b) { return a - b; });
1624
+ var exactMatch = sortedBandwidths.find(function (a) { return a == bandwidthToMatch; });
1625
+ if (exactMatch) {
1626
+ return exactMatch;
1627
+ }
1628
+ for (var i = 0; i < sortedBandwidths.length; i++) {
1629
+ if (Number(bandwidthToMatch) <= Number(sortedBandwidths[i])) {
1630
+ return sortedBandwidths[i];
1631
+ }
1632
+ }
1633
+ return sortedBandwidths[sortedBandwidths.length - 1];
1634
+ };
1635
+ SessionLive.prototype._getFirstBwWithSegmentsInList = function (allSegments) {
1636
+ var bandwidths = Object.keys(allSegments);
1637
+ for (var i = 0; i < bandwidths.length; i++) {
1638
+ var bw = bandwidths[i];
1639
+ if (allSegments[bw].length > 0) {
1640
+ return bw;
1641
+ }
1642
+ }
1643
+ debug("[".concat(this.sessionId, "]: ERROR Could not find any bandwidth with segments"));
1644
+ return null;
1645
+ };
1646
+ SessionLive.prototype._getMaxDuration = function (segments) {
1647
+ if (!segments) {
1648
+ debug("[".concat(this.sessionId, "]: ERROR segments is: ").concat(segments));
1649
+ }
1650
+ var max = 0;
1651
+ for (var i = 0; i < segments.length; i++) {
1652
+ var seg = segments[i];
1653
+ if (!seg.discontinuity) {
1654
+ if (seg.duration > max) {
1655
+ max = seg.duration;
1656
+ }
1657
+ }
1658
+ }
1659
+ return max;
1660
+ };
1661
+ // To only use profiles that the channel will actually need.
1662
+ SessionLive.prototype._filterLiveProfiles = function () {
1663
+ var _this = this;
1664
+ var profiles = this.sessionLiveProfile;
1665
+ var toKeep = new Set();
1666
+ var newItem = {};
1667
+ profiles.forEach(function (profile) {
1668
+ var bwToKeep = _this._getNearestBandwidth(profile.bw, Object.keys(_this.mediaManifestURIs));
1669
+ toKeep.add(bwToKeep);
1670
+ });
1671
+ toKeep.forEach(function (bw) {
1672
+ newItem[bw] = _this.mediaManifestURIs[bw];
1673
+ });
1674
+ this.mediaManifestURIs = newItem;
1675
+ };
1676
+ SessionLive.prototype._getAnyFirstSegmentDurationMs = function () {
1677
+ if (this._isEmpty(this.liveSegQueue)) {
1678
+ return null;
1679
+ }
1680
+ var bw0 = Object.keys(this.liveSegQueue)[0];
1681
+ if (this.liveSegQueue[bw0].length === 0) {
1682
+ return null;
1683
+ }
1684
+ for (var i = 0; i < this.liveSegQueue[bw0].length; i++) {
1685
+ var segment = this.liveSegQueue[bw0][i];
1686
+ if (!segment.duration) {
1687
+ continue;
1688
+ }
1689
+ return segment.duration * 1000;
1690
+ }
1691
+ return null;
1692
+ };
1693
+ SessionLive.prototype._isEmpty = function (obj) {
1694
+ if (!obj) {
1695
+ return true;
1696
+ }
1697
+ for (var key in obj) {
1698
+ if (obj.hasOwnProperty(key)) {
1699
+ return false;
1700
+ }
1701
+ }
1702
+ return true;
1703
+ };
1704
+ SessionLive.prototype._containsSegment = function (segments, newSegments) {
1705
+ if (!segments || !newSegments) {
1706
+ return false;
1707
+ }
1708
+ if (Object.keys(segments).length === 0 || Object.keys(newSegments).length === 0) {
1709
+ return false;
1710
+ }
1711
+ var someBw = Object.keys(segments)[0];
1712
+ var segList = segments[someBw];
1713
+ var mostRecentSegment = segList[segList.length - 1];
1714
+ var segListNew = newSegments[someBw];
1715
+ var mostRecentSegmentNew = segListNew[segListNew.length - 1];
1716
+ if (mostRecentSegmentNew.uri === mostRecentSegment.uri) {
1717
+ return true;
1718
+ }
1719
+ return false;
1720
+ };
1721
+ return SessionLive;
1722
+ }());
1723
+ module.exports = SessionLive;