eyevinn-channel-engine 4.3.1 → 4.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine/memcached_state_store.d.ts +1 -0
- package/dist/engine/memcached_state_store.js +8 -0
- package/dist/engine/memory_state_store.d.ts +2 -0
- package/dist/engine/memory_state_store.js +29 -0
- package/dist/engine/redis_state_store.d.ts +1 -0
- package/dist/engine/redis_state_store.js +17 -2
- package/dist/engine/server.d.ts +3 -0
- package/dist/engine/server.js +82 -3
- package/dist/engine/session.d.ts +1 -1
- package/dist/engine/session.js +12 -3
- package/dist/engine/shared_state_store.d.ts +1 -0
- package/dist/engine/shared_state_store.js +12 -0
- package/dist/package.json +1 -1
- package/engine/memcached_state_store.js +4 -0
- package/engine/memory_state_store.js +18 -2
- package/engine/redis_state_store.js +12 -7
- package/engine/server.ts +75 -3
- package/engine/session.js +7 -3
- package/engine/shared_state_store.js +4 -0
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ declare class MemcachedStateStore {
|
|
|
5
5
|
client: any;
|
|
6
6
|
initAsync(id: any, initData: any): Promise<{}>;
|
|
7
7
|
resetAsync(id: any, initData: any): Promise<void>;
|
|
8
|
+
resetAllAsync(): Promise<void>;
|
|
8
9
|
getAsync(id: any, key: any): Promise<any>;
|
|
9
10
|
setAsync(id: any, key: any, value: any): Promise<any>;
|
|
10
11
|
setVolatileAsync(id: any, key: any, value: any): Promise<void>;
|
|
@@ -112,6 +112,14 @@ var MemcachedStateStore = /** @class */ (function () {
|
|
|
112
112
|
});
|
|
113
113
|
});
|
|
114
114
|
};
|
|
115
|
+
MemcachedStateStore.prototype.resetAllAsync = function () {
|
|
116
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
117
|
+
return __generator(this, function (_a) {
|
|
118
|
+
console.error("Shared Storage Reset Failed.\nMemcache-client: Flush All Command Not Implemented Yet");
|
|
119
|
+
return [2 /*return*/];
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
};
|
|
115
123
|
MemcachedStateStore.prototype.getAsync = function (id, key) {
|
|
116
124
|
return __awaiter(this, void 0, void 0, function () {
|
|
117
125
|
var storeKey, data;
|
|
@@ -4,6 +4,8 @@ declare class MemoryStateStore {
|
|
|
4
4
|
sharedStates: {};
|
|
5
5
|
globalSharedStates: {};
|
|
6
6
|
initAsync(id: any, initData: any): Promise<any>;
|
|
7
|
+
resetAsync(id: any, initData: any): Promise<void>;
|
|
8
|
+
resetAllAsync(): Promise<void>;
|
|
7
9
|
getAsync(id: any, key: any): Promise<any>;
|
|
8
10
|
setAsync(id: any, key: any, value: any): Promise<any>;
|
|
9
11
|
setVolatileAsync(id: any, key: any, value: any): Promise<any>;
|
|
@@ -54,6 +54,29 @@ var MemoryStateStore = /** @class */ (function () {
|
|
|
54
54
|
});
|
|
55
55
|
});
|
|
56
56
|
};
|
|
57
|
+
MemoryStateStore.prototype.resetAsync = function (id, initData) {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
59
|
+
return __generator(this, function (_a) {
|
|
60
|
+
switch (_a.label) {
|
|
61
|
+
case 0:
|
|
62
|
+
this.sharedStates[id] = null;
|
|
63
|
+
return [4 /*yield*/, this.initAsync(id, initData)];
|
|
64
|
+
case 1:
|
|
65
|
+
_a.sent();
|
|
66
|
+
return [2 /*return*/];
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
MemoryStateStore.prototype.resetAllAsync = function () {
|
|
72
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
73
|
+
return __generator(this, function (_a) {
|
|
74
|
+
this.sharedStates = {};
|
|
75
|
+
this.globalSharedStates = {};
|
|
76
|
+
return [2 /*return*/];
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
};
|
|
57
80
|
MemoryStateStore.prototype.getAsync = function (id, key) {
|
|
58
81
|
return __awaiter(this, void 0, void 0, function () {
|
|
59
82
|
var value;
|
|
@@ -62,6 +85,9 @@ var MemoryStateStore = /** @class */ (function () {
|
|
|
62
85
|
value = this.globalSharedStates[key];
|
|
63
86
|
}
|
|
64
87
|
else {
|
|
88
|
+
if (!this.sharedStates[id]) {
|
|
89
|
+
return [2 /*return*/, null];
|
|
90
|
+
}
|
|
65
91
|
value = this.sharedStates[id][key];
|
|
66
92
|
}
|
|
67
93
|
return [2 /*return*/, value];
|
|
@@ -75,6 +101,9 @@ var MemoryStateStore = /** @class */ (function () {
|
|
|
75
101
|
this.globalSharedStates[key] = value;
|
|
76
102
|
}
|
|
77
103
|
else {
|
|
104
|
+
if (!this.sharedStates[id]) {
|
|
105
|
+
this.sharedStates[id] = {};
|
|
106
|
+
}
|
|
78
107
|
this.sharedStates[id][key] = value;
|
|
79
108
|
return [2 /*return*/, this.sharedStates[id][key]];
|
|
80
109
|
}
|
|
@@ -6,6 +6,7 @@ declare class RedisStateStore {
|
|
|
6
6
|
client: any;
|
|
7
7
|
initAsync(id: any, initData: any): Promise<{}>;
|
|
8
8
|
resetAsync(id: any, initData: any): Promise<void>;
|
|
9
|
+
resetAllAsync(): Promise<void>;
|
|
9
10
|
getAsync(id: any, key: any): Promise<any>;
|
|
10
11
|
setAsync(id: any, key: any, value: any): Promise<any>;
|
|
11
12
|
setVolatileAsync(id: any, key: any, value: any): Promise<any>;
|
|
@@ -104,6 +104,21 @@ var RedisStateStore = /** @class */ (function () {
|
|
|
104
104
|
});
|
|
105
105
|
};
|
|
106
106
|
RedisStateStore.prototype.resetAsync = function (id, initData) {
|
|
107
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
108
|
+
return __generator(this, function (_a) {
|
|
109
|
+
switch (_a.label) {
|
|
110
|
+
case 0: return [4 /*yield*/, this.setAsync(id, "_initiated", false)];
|
|
111
|
+
case 1:
|
|
112
|
+
_a.sent();
|
|
113
|
+
return [4 /*yield*/, this.initAsync(id, initData)];
|
|
114
|
+
case 2:
|
|
115
|
+
_a.sent();
|
|
116
|
+
return [2 /*return*/];
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
RedisStateStore.prototype.resetAllAsync = function () {
|
|
107
122
|
return __awaiter(this, void 0, void 0, function () {
|
|
108
123
|
var resetAsync;
|
|
109
124
|
var _this = this;
|
|
@@ -170,7 +185,7 @@ var RedisStateStore = /** @class */ (function () {
|
|
|
170
185
|
setAsync = new Promise(function (resolve, reject) {
|
|
171
186
|
_this.client.set(storeKey, JSON.stringify(value), function (err, res) {
|
|
172
187
|
var ioTimeMs = Date.now() - startMs;
|
|
173
|
-
debug("REDIS set ".concat(storeKey, ": ").concat(res, " (").concat(ioTimeMs, "ms) ").concat(ioTimeMs > 1000 ?
|
|
188
|
+
debug("REDIS set ".concat(storeKey, ": ").concat(res, " (").concat(ioTimeMs, "ms) ").concat(ioTimeMs > 1000 ? "REDISSLOW!" : ""));
|
|
174
189
|
if (!err) {
|
|
175
190
|
resolve(value);
|
|
176
191
|
}
|
|
@@ -226,7 +241,7 @@ var RedisStateStore = /** @class */ (function () {
|
|
|
226
241
|
delAsync = new Promise(function (resolve, reject) {
|
|
227
242
|
_this.client.del(storeKey, function (err, res) {
|
|
228
243
|
var ioTimeMs = Date.now() - startMs;
|
|
229
|
-
debug("REDIS remove ".concat(storeKey, ": (").concat(ioTimeMs, "ms) ").concat(ioTimeMs > 1000 ?
|
|
244
|
+
debug("REDIS remove ".concat(storeKey, ": (").concat(ioTimeMs, "ms) ").concat(ioTimeMs > 1000 ? "REDISSLOW!" : ""));
|
|
230
245
|
if (!err) {
|
|
231
246
|
resolve();
|
|
232
247
|
}
|
package/dist/engine/server.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export interface ChannelEngineOpts {
|
|
|
29
29
|
noSessionDataTags?: boolean;
|
|
30
30
|
volatileKeyTTL?: number;
|
|
31
31
|
autoCreateSession?: boolean;
|
|
32
|
+
sessionResetKey?: string;
|
|
32
33
|
}
|
|
33
34
|
export interface VodRequest {
|
|
34
35
|
sessionId: string;
|
|
@@ -147,6 +148,7 @@ export declare class ChannelEngine {
|
|
|
147
148
|
private adCopyMgrUri?;
|
|
148
149
|
private adXchangeUri?;
|
|
149
150
|
private autoCreateSession;
|
|
151
|
+
private sessionResetKey;
|
|
150
152
|
constructor(assetMgr: IAssetManager, options?: ChannelEngineOpts);
|
|
151
153
|
updateStreamSwitchAsync(): Promise<void>;
|
|
152
154
|
updateChannelsAsync(channelMgr: any, options: any): Promise<void>;
|
|
@@ -172,6 +174,7 @@ export declare class ChannelEngine {
|
|
|
172
174
|
_handleStatus(req: any, res: any, next: any): Promise<void>;
|
|
173
175
|
_handleAggregatedSessionHealth(req: any, res: any, next: any): Promise<void>;
|
|
174
176
|
_handleSessionHealth(req: any, res: any, next: any): Promise<void>;
|
|
177
|
+
_handleSessionsReset(req: any, res: any, next: any): Promise<void>;
|
|
175
178
|
_handleSessionReset(req: any, res: any, next: any): Promise<void>;
|
|
176
179
|
_gracefulErrorHandler(errMsg: any): any;
|
|
177
180
|
_errorHandler(errMsg: any): any;
|
package/dist/engine/server.js
CHANGED
|
@@ -71,6 +71,7 @@ var ChannelEngine = /** @class */ (function () {
|
|
|
71
71
|
function ChannelEngine(assetMgr, options) {
|
|
72
72
|
var _this = this;
|
|
73
73
|
this.autoCreateSession = false;
|
|
74
|
+
this.sessionResetKey = "";
|
|
74
75
|
this.options = options;
|
|
75
76
|
if (options && options.adCopyMgrUri) {
|
|
76
77
|
this.adCopyMgrUri = options.adCopyMgrUri;
|
|
@@ -85,6 +86,10 @@ var ChannelEngine = /** @class */ (function () {
|
|
|
85
86
|
this.useVTTSubtitles = (options && options.useVTTSubtitles) ? options.useVTTSubtitles : false;
|
|
86
87
|
this.dummySubtitleEndpoint = (options && options.dummySubtitleEndpoint) ? options.dummySubtitleEndpoint : DefaultDummySubtitleEndpointPath;
|
|
87
88
|
this.subtitleSliceEndpoint = (options && options.subtitleSliceEndpoint) ? options.subtitleSliceEndpoint : DefaultSubtitleSpliceEndpointPath;
|
|
89
|
+
this.sessionResetKey = "";
|
|
90
|
+
if (options && options.sessionResetKey) {
|
|
91
|
+
this.sessionResetKey = options.sessionResetKey;
|
|
92
|
+
}
|
|
88
93
|
this.alwaysNewSegments = false;
|
|
89
94
|
if (options && options.alwaysNewSegments) {
|
|
90
95
|
this.alwaysNewSegments = true;
|
|
@@ -262,7 +267,8 @@ var ChannelEngine = /** @class */ (function () {
|
|
|
262
267
|
this.server.get('/status/:sessionId', this._handleStatus.bind(this));
|
|
263
268
|
this.server.get('/health', this._handleAggregatedSessionHealth.bind(this));
|
|
264
269
|
this.server.get('/health/:sessionId', this._handleSessionHealth.bind(this));
|
|
265
|
-
this.server.get('/reset', this.
|
|
270
|
+
this.server.get('/reset', this._handleSessionsReset.bind(this));
|
|
271
|
+
this.server.get('/reset/:sessionId', this._handleSessionReset.bind(this));
|
|
266
272
|
this.server.get(DefaultDummySubtitleEndpointPath, this._handleDummySubtitleEndpoint.bind(this));
|
|
267
273
|
this.server.get(DefaultSubtitleSpliceEndpointPath, this._handleSubtitleSliceEndpoint.bind(this));
|
|
268
274
|
this.server.on('NotFound', function (req, res, err, next) {
|
|
@@ -445,6 +451,7 @@ var ChannelEngine = /** @class */ (function () {
|
|
|
445
451
|
subtitleSliceEndpoint: this.subtitleSliceEndpoint,
|
|
446
452
|
useVTTSubtitles: this.useVTTSubtitles,
|
|
447
453
|
alwaysNewSegments: options.alwaysNewSegments,
|
|
454
|
+
sessionResetKey: options.sessionResetKey,
|
|
448
455
|
partialStoreHLSVod: options.partialStoreHLSVod,
|
|
449
456
|
alwaysMapBandwidthByNearest: options.alwaysMapBandwidthByNearest,
|
|
450
457
|
noSessionDataTags: options.noSessionDataTags,
|
|
@@ -1013,7 +1020,7 @@ var ChannelEngine = /** @class */ (function () {
|
|
|
1013
1020
|
return __generator(this, function (_a) {
|
|
1014
1021
|
debug("req.url=".concat(req.url));
|
|
1015
1022
|
try {
|
|
1016
|
-
body = "WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:0,LOCAL:00:00:00.000";
|
|
1023
|
+
body = "WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:0,LOCAL:00:00:00.000\n\n";
|
|
1017
1024
|
res.sendRaw(200, Buffer.from(body, 'utf8'), {
|
|
1018
1025
|
"Content-Type": "text/vtt",
|
|
1019
1026
|
"Access-Control-Allow-Origin": "*",
|
|
@@ -1275,13 +1282,22 @@ var ChannelEngine = /** @class */ (function () {
|
|
|
1275
1282
|
});
|
|
1276
1283
|
});
|
|
1277
1284
|
};
|
|
1278
|
-
ChannelEngine.prototype.
|
|
1285
|
+
ChannelEngine.prototype._handleSessionsReset = function (req, res, next) {
|
|
1279
1286
|
return __awaiter(this, void 0, void 0, function () {
|
|
1280
1287
|
var sessionResets, _i, _a, sessionId, session, sessionLive, err;
|
|
1281
1288
|
return __generator(this, function (_b) {
|
|
1282
1289
|
switch (_b.label) {
|
|
1283
1290
|
case 0:
|
|
1284
1291
|
debug("req.url=".concat(req.url));
|
|
1292
|
+
if (this.sessionResetKey && req.query.key !== this.sessionResetKey) {
|
|
1293
|
+
res.sendRaw(403, JSON.stringify({ "message": "Invalid Session-Reset-Key" }), {
|
|
1294
|
+
"Content-Type": "application/json",
|
|
1295
|
+
"Access-Control-Allow-Origin": "*",
|
|
1296
|
+
"Cache-Control": "no-cache",
|
|
1297
|
+
});
|
|
1298
|
+
next();
|
|
1299
|
+
return [2 /*return*/];
|
|
1300
|
+
}
|
|
1285
1301
|
sessionResets = [];
|
|
1286
1302
|
_i = 0, _a = Object.keys(sessions);
|
|
1287
1303
|
_b.label = 1;
|
|
@@ -1314,6 +1330,69 @@ var ChannelEngine = /** @class */ (function () {
|
|
|
1314
1330
|
});
|
|
1315
1331
|
});
|
|
1316
1332
|
};
|
|
1333
|
+
ChannelEngine.prototype._handleSessionReset = function (req, res, next) {
|
|
1334
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1335
|
+
var sessionId, sessionResets, session, sessionLive, e_1, err;
|
|
1336
|
+
return __generator(this, function (_a) {
|
|
1337
|
+
switch (_a.label) {
|
|
1338
|
+
case 0:
|
|
1339
|
+
debug("req.url=".concat(req.url));
|
|
1340
|
+
if (this.sessionResetKey && req.query.key !== this.sessionResetKey) {
|
|
1341
|
+
res.sendRaw(403, JSON.stringify({ "message": "Invalid Session-Reset-Key" }), {
|
|
1342
|
+
"Content-Type": "application/json",
|
|
1343
|
+
"Access-Control-Allow-Origin": "*",
|
|
1344
|
+
"Cache-Control": "no-cache",
|
|
1345
|
+
});
|
|
1346
|
+
next();
|
|
1347
|
+
return [2 /*return*/];
|
|
1348
|
+
}
|
|
1349
|
+
_a.label = 1;
|
|
1350
|
+
case 1:
|
|
1351
|
+
_a.trys.push([1, 5, , 6]);
|
|
1352
|
+
sessionId = void 0;
|
|
1353
|
+
if (req.params && req.params.sessionId) {
|
|
1354
|
+
sessionId = req.params.sessionId;
|
|
1355
|
+
}
|
|
1356
|
+
sessionResets = [];
|
|
1357
|
+
session = sessions[sessionId];
|
|
1358
|
+
sessionLive = sessionsLive[sessionId];
|
|
1359
|
+
if (!(session && sessionLive)) return [3 /*break*/, 3];
|
|
1360
|
+
return [4 /*yield*/, session.resetAsync(sessionId)];
|
|
1361
|
+
case 2:
|
|
1362
|
+
_a.sent();
|
|
1363
|
+
sessionResets.push(sessionId);
|
|
1364
|
+
return [3 /*break*/, 4];
|
|
1365
|
+
case 3:
|
|
1366
|
+
res.sendRaw(400, JSON.stringify({ "message": "Invalid Session ID" }), {
|
|
1367
|
+
"Content-Type": "application/json",
|
|
1368
|
+
"Access-Control-Allow-Origin": "*",
|
|
1369
|
+
"Cache-Control": "no-cache",
|
|
1370
|
+
});
|
|
1371
|
+
next();
|
|
1372
|
+
return [2 /*return*/];
|
|
1373
|
+
case 4:
|
|
1374
|
+
res.sendRaw(200, JSON.stringify({ "status": "ok", "instanceId": this.instanceId, "resets": sessionResets }), {
|
|
1375
|
+
"Content-Type": "application/json",
|
|
1376
|
+
"Access-Control-Allow-Origin": "*",
|
|
1377
|
+
"Cache-Control": "no-cache",
|
|
1378
|
+
});
|
|
1379
|
+
next();
|
|
1380
|
+
return [3 /*break*/, 6];
|
|
1381
|
+
case 5:
|
|
1382
|
+
e_1 = _a.sent();
|
|
1383
|
+
res.sendRaw(500, JSON.stringify({ "error": e_1 }), {
|
|
1384
|
+
"Content-Type": "application/json",
|
|
1385
|
+
"Access-Control-Allow-Origin": "*",
|
|
1386
|
+
"Cache-Control": "no-cache",
|
|
1387
|
+
});
|
|
1388
|
+
err = new errs.NotFoundError('Session Reset Failed');
|
|
1389
|
+
next(err);
|
|
1390
|
+
return [3 /*break*/, 6];
|
|
1391
|
+
case 6: return [2 /*return*/];
|
|
1392
|
+
}
|
|
1393
|
+
});
|
|
1394
|
+
});
|
|
1395
|
+
};
|
|
1317
1396
|
ChannelEngine.prototype._gracefulErrorHandler = function (errMsg) {
|
|
1318
1397
|
console.error(errMsg);
|
|
1319
1398
|
var err = new errs.NotFoundError(errMsg);
|
package/dist/engine/session.d.ts
CHANGED
|
@@ -83,7 +83,7 @@ declare class Session {
|
|
|
83
83
|
} | {
|
|
84
84
|
sessionId: any;
|
|
85
85
|
}>;
|
|
86
|
-
resetAsync(): Promise<void>;
|
|
86
|
+
resetAsync(id: any): Promise<void>;
|
|
87
87
|
getSessionState(): Promise<any>;
|
|
88
88
|
getTruncatedVodSegments(vodUri: any, duration: any): Promise<any>;
|
|
89
89
|
setCurrentMediaSequenceSegments(segments: any, mSeqOffset: any, reloadBehind: any): Promise<void>;
|
package/dist/engine/session.js
CHANGED
|
@@ -454,15 +454,24 @@ var Session = /** @class */ (function () {
|
|
|
454
454
|
});
|
|
455
455
|
});
|
|
456
456
|
};
|
|
457
|
-
Session.prototype.resetAsync = function () {
|
|
457
|
+
Session.prototype.resetAsync = function (id) {
|
|
458
458
|
return __awaiter(this, void 0, void 0, function () {
|
|
459
459
|
return __generator(this, function (_a) {
|
|
460
460
|
switch (_a.label) {
|
|
461
|
-
case 0:
|
|
461
|
+
case 0:
|
|
462
|
+
if (!id) return [3 /*break*/, 3];
|
|
463
|
+
return [4 /*yield*/, this._sessionStateStore.reset(id)];
|
|
462
464
|
case 1:
|
|
463
465
|
_a.sent();
|
|
464
|
-
return [4 /*yield*/, this._playheadStateStore.reset(
|
|
466
|
+
return [4 /*yield*/, this._playheadStateStore.reset(id)];
|
|
465
467
|
case 2:
|
|
468
|
+
_a.sent();
|
|
469
|
+
_a.label = 3;
|
|
470
|
+
case 3: return [4 /*yield*/, this._sessionStateStore.resetAll()];
|
|
471
|
+
case 4:
|
|
472
|
+
_a.sent();
|
|
473
|
+
return [4 /*yield*/, this._playheadStateStore.resetAll()];
|
|
474
|
+
case 5:
|
|
466
475
|
_a.sent();
|
|
467
476
|
return [2 /*return*/];
|
|
468
477
|
}
|
|
@@ -10,6 +10,7 @@ declare class SharedStateStore {
|
|
|
10
10
|
isShared(): boolean;
|
|
11
11
|
init(id: any): Promise<void>;
|
|
12
12
|
reset(id: any): Promise<void>;
|
|
13
|
+
resetAll(): Promise<void>;
|
|
13
14
|
get(id: any, key: any): Promise<any>;
|
|
14
15
|
set(id: any, key: any, value: any): Promise<any>;
|
|
15
16
|
setVolatile(id: any, key: any, value: any): Promise<any>;
|
|
@@ -87,6 +87,18 @@ var SharedStateStore = /** @class */ (function () {
|
|
|
87
87
|
});
|
|
88
88
|
});
|
|
89
89
|
};
|
|
90
|
+
SharedStateStore.prototype.resetAll = function () {
|
|
91
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
92
|
+
return __generator(this, function (_a) {
|
|
93
|
+
switch (_a.label) {
|
|
94
|
+
case 0: return [4 /*yield*/, this.store.resetAllAsync()];
|
|
95
|
+
case 1:
|
|
96
|
+
_a.sent();
|
|
97
|
+
return [2 /*return*/];
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
};
|
|
90
102
|
SharedStateStore.prototype.get = function (id, key) {
|
|
91
103
|
return __awaiter(this, void 0, void 0, function () {
|
|
92
104
|
var data;
|
package/dist/package.json
CHANGED
|
@@ -36,6 +36,10 @@ class MemcachedStateStore {
|
|
|
36
36
|
await this.initAsync(id, initData);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
async resetAllAsync() {
|
|
40
|
+
console.error("Shared Storage Reset Failed.\nMemcache-client: Flush All Command Not Implemented Yet");
|
|
41
|
+
}
|
|
42
|
+
|
|
39
43
|
async getAsync(id, key) {
|
|
40
44
|
const storeKey = "" + this.keyPrefix + id + key;
|
|
41
45
|
const data = await this.client.get(storeKey);
|
|
@@ -9,18 +9,31 @@ class MemoryStateStore {
|
|
|
9
9
|
async initAsync(id, initData) {
|
|
10
10
|
if (!this.sharedStates[id]) {
|
|
11
11
|
this.sharedStates[id] = {};
|
|
12
|
-
Object.keys(initData).forEach(key => {
|
|
12
|
+
Object.keys(initData).forEach((key) => {
|
|
13
13
|
this.sharedStates[id][key] = initData[key];
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
16
|
return this.sharedStates[id];
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
async resetAsync(id, initData) {
|
|
20
|
+
this.sharedStates[id] = null;
|
|
21
|
+
await this.initAsync(id, initData);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async resetAllAsync() {
|
|
25
|
+
this.sharedStates = {};
|
|
26
|
+
this.globalSharedStates = {};
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
async getAsync(id, key) {
|
|
20
30
|
let value;
|
|
21
31
|
if (id === "" || id === null) {
|
|
22
32
|
value = this.globalSharedStates[key];
|
|
23
33
|
} else {
|
|
34
|
+
if (!this.sharedStates[id]) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
24
37
|
value = this.sharedStates[id][key];
|
|
25
38
|
}
|
|
26
39
|
return value;
|
|
@@ -30,6 +43,9 @@ class MemoryStateStore {
|
|
|
30
43
|
if (id === "" || id === null) {
|
|
31
44
|
this.globalSharedStates[key] = value;
|
|
32
45
|
} else {
|
|
46
|
+
if (!this.sharedStates[id]) {
|
|
47
|
+
this.sharedStates[id] = {};
|
|
48
|
+
}
|
|
33
49
|
this.sharedStates[id][key] = value;
|
|
34
50
|
return this.sharedStates[id][key];
|
|
35
51
|
}
|
|
@@ -41,7 +57,7 @@ class MemoryStateStore {
|
|
|
41
57
|
|
|
42
58
|
async removeAsync(id, key) {
|
|
43
59
|
delete this.sharedStates[id][key];
|
|
44
|
-
}
|
|
60
|
+
}
|
|
45
61
|
}
|
|
46
62
|
|
|
47
63
|
module.exports = MemoryStateStore;
|
|
@@ -14,7 +14,7 @@ class RedisStateStore {
|
|
|
14
14
|
this.volatileKeyTTL = DEFAULT_VOLATILE_KEY_TTL;
|
|
15
15
|
if (opts.volatileKeyTTL) {
|
|
16
16
|
debug(`Overriding default, volatileKeyTTL=${opts.volatileKeyTTL}s`);
|
|
17
|
-
this.volatileKeyTTL = opts.volatileKeyTTL;
|
|
17
|
+
this.volatileKeyTTL = opts.volatileKeyTTL;
|
|
18
18
|
}
|
|
19
19
|
this.client = createClient(opts.redisUrl);
|
|
20
20
|
}
|
|
@@ -23,14 +23,14 @@ class RedisStateStore {
|
|
|
23
23
|
const isInitiated = await this.getAsync(id, "_initiated");
|
|
24
24
|
let data = {};
|
|
25
25
|
if (!isInitiated) {
|
|
26
|
-
for(const key of Object.keys(initData)) {
|
|
26
|
+
for (const key of Object.keys(initData)) {
|
|
27
27
|
debug(`${this.keyPrefix}:${id}: Initiating key ${key} with init data`);
|
|
28
28
|
data[key] = await this.setAsync(id, key, initData[key]);
|
|
29
29
|
}
|
|
30
30
|
await this.setAsync(id, "_initiated", true);
|
|
31
31
|
} else {
|
|
32
32
|
debug(`${this.keyPrefix}:${id}: Already initiated, not initiating with init data`);
|
|
33
|
-
for(const key of Object.keys(initData)) {
|
|
33
|
+
for (const key of Object.keys(initData)) {
|
|
34
34
|
debug(`${this.keyPrefix}:${id}: Initiating key ${key} with data from store`);
|
|
35
35
|
data[key] = await this.getAsync(id, key);
|
|
36
36
|
}
|
|
@@ -39,8 +39,13 @@ class RedisStateStore {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
async resetAsync(id, initData) {
|
|
42
|
+
await this.setAsync(id, "_initiated", false);
|
|
43
|
+
await this.initAsync(id, initData);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async resetAllAsync() {
|
|
42
47
|
const resetAsync = new Promise((resolve, reject) => {
|
|
43
|
-
this.client.flushall((err, reply) => {
|
|
48
|
+
this.client.flushall((err, reply) => {
|
|
44
49
|
if (!err) {
|
|
45
50
|
console.log("Flushed Redis db: ", reply);
|
|
46
51
|
resolve();
|
|
@@ -64,7 +69,7 @@ class RedisStateStore {
|
|
|
64
69
|
} else {
|
|
65
70
|
reject(err);
|
|
66
71
|
}
|
|
67
|
-
|
|
72
|
+
});
|
|
68
73
|
});
|
|
69
74
|
const data = await getAsync;
|
|
70
75
|
return data;
|
|
@@ -76,7 +81,7 @@ class RedisStateStore {
|
|
|
76
81
|
const setAsync = new Promise((resolve, reject) => {
|
|
77
82
|
this.client.set(storeKey, JSON.stringify(value), (err, res) => {
|
|
78
83
|
const ioTimeMs = Date.now() - startMs;
|
|
79
|
-
debug(`REDIS set ${storeKey}: ${res} (${ioTimeMs}ms) ${ioTimeMs > 1000 ?
|
|
84
|
+
debug(`REDIS set ${storeKey}: ${res} (${ioTimeMs}ms) ${ioTimeMs > 1000 ? "REDISSLOW!" : ""}`);
|
|
80
85
|
if (!err) {
|
|
81
86
|
resolve(value);
|
|
82
87
|
} else {
|
|
@@ -110,7 +115,7 @@ class RedisStateStore {
|
|
|
110
115
|
const delAsync = new Promise((resolve, reject) => {
|
|
111
116
|
this.client.del(storeKey, (err, res) => {
|
|
112
117
|
const ioTimeMs = Date.now() - startMs;
|
|
113
|
-
debug(`REDIS remove ${storeKey}: (${ioTimeMs}ms) ${ioTimeMs > 1000 ?
|
|
118
|
+
debug(`REDIS remove ${storeKey}: (${ioTimeMs}ms) ${ioTimeMs > 1000 ? "REDISSLOW!" : ""}`);
|
|
114
119
|
if (!err) {
|
|
115
120
|
resolve();
|
|
116
121
|
} else {
|
package/engine/server.ts
CHANGED
|
@@ -58,6 +58,7 @@ export interface ChannelEngineOpts {
|
|
|
58
58
|
noSessionDataTags?: boolean;
|
|
59
59
|
volatileKeyTTL?: number;
|
|
60
60
|
autoCreateSession?: boolean;
|
|
61
|
+
sessionResetKey?:string;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
interface StreamerOpts {
|
|
@@ -203,6 +204,7 @@ export class ChannelEngine {
|
|
|
203
204
|
private adCopyMgrUri?: string;
|
|
204
205
|
private adXchangeUri?: string;
|
|
205
206
|
private autoCreateSession: boolean = false;
|
|
207
|
+
private sessionResetKey: string = "";
|
|
206
208
|
|
|
207
209
|
constructor(assetMgr: IAssetManager, options?: ChannelEngineOpts) {
|
|
208
210
|
this.options = options;
|
|
@@ -221,6 +223,10 @@ export class ChannelEngine {
|
|
|
221
223
|
this.dummySubtitleEndpoint = (options && options.dummySubtitleEndpoint) ? options.dummySubtitleEndpoint : DefaultDummySubtitleEndpointPath;
|
|
222
224
|
this.subtitleSliceEndpoint = (options && options.subtitleSliceEndpoint) ? options.subtitleSliceEndpoint : DefaultSubtitleSpliceEndpointPath;
|
|
223
225
|
|
|
226
|
+
this.sessionResetKey = "";
|
|
227
|
+
if (options && options.sessionResetKey) {
|
|
228
|
+
this.sessionResetKey = options.sessionResetKey;
|
|
229
|
+
}
|
|
224
230
|
this.alwaysNewSegments = false;
|
|
225
231
|
if (options && options.alwaysNewSegments) {
|
|
226
232
|
this.alwaysNewSegments = true;
|
|
@@ -361,7 +367,8 @@ export class ChannelEngine {
|
|
|
361
367
|
this.server.get('/status/:sessionId', this._handleStatus.bind(this));
|
|
362
368
|
this.server.get('/health', this._handleAggregatedSessionHealth.bind(this));
|
|
363
369
|
this.server.get('/health/:sessionId', this._handleSessionHealth.bind(this));
|
|
364
|
-
this.server.get('/reset', this.
|
|
370
|
+
this.server.get('/reset', this._handleSessionsReset.bind(this));
|
|
371
|
+
this.server.get('/reset/:sessionId', this._handleSessionReset.bind(this));
|
|
365
372
|
this.server.get(DefaultDummySubtitleEndpointPath, this._handleDummySubtitleEndpoint.bind(this));
|
|
366
373
|
this.server.get(DefaultSubtitleSpliceEndpointPath, this._handleSubtitleSliceEndpoint.bind(this));
|
|
367
374
|
|
|
@@ -466,6 +473,7 @@ export class ChannelEngine {
|
|
|
466
473
|
subtitleSliceEndpoint: this.subtitleSliceEndpoint,
|
|
467
474
|
useVTTSubtitles: this.useVTTSubtitles,
|
|
468
475
|
alwaysNewSegments: options.alwaysNewSegments,
|
|
476
|
+
sessionResetKey: options.sessionResetKey,
|
|
469
477
|
partialStoreHLSVod: options.partialStoreHLSVod,
|
|
470
478
|
alwaysMapBandwidthByNearest: options.alwaysMapBandwidthByNearest,
|
|
471
479
|
noSessionDataTags: options.noSessionDataTags,
|
|
@@ -820,7 +828,7 @@ export class ChannelEngine {
|
|
|
820
828
|
async _handleDummySubtitleEndpoint(req,res,next) {
|
|
821
829
|
debug(`req.url=${req.url}`);
|
|
822
830
|
try {
|
|
823
|
-
const body = `WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:0,LOCAL:00:00:00.000`;
|
|
831
|
+
const body = `WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:0,LOCAL:00:00:00.000\n\n`;
|
|
824
832
|
res.sendRaw(200, Buffer.from(body, 'utf8'), {
|
|
825
833
|
"Content-Type": "text/vtt",
|
|
826
834
|
"Access-Control-Allow-Origin": "*",
|
|
@@ -1005,8 +1013,18 @@ export class ChannelEngine {
|
|
|
1005
1013
|
}
|
|
1006
1014
|
}
|
|
1007
1015
|
|
|
1008
|
-
async
|
|
1016
|
+
async _handleSessionsReset(req, res, next) {
|
|
1009
1017
|
debug(`req.url=${req.url}`);
|
|
1018
|
+
if (this.sessionResetKey && req.query.key !== this.sessionResetKey) {
|
|
1019
|
+
res.sendRaw(403, JSON.stringify({ "message": "Invalid Session-Reset-Key" }),
|
|
1020
|
+
{
|
|
1021
|
+
"Content-Type": "application/json",
|
|
1022
|
+
"Access-Control-Allow-Origin": "*",
|
|
1023
|
+
"Cache-Control": "no-cache",
|
|
1024
|
+
});
|
|
1025
|
+
next();
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1010
1028
|
let sessionResets = [];
|
|
1011
1029
|
for (const sessionId of Object.keys(sessions)) {
|
|
1012
1030
|
const session = sessions[sessionId];
|
|
@@ -1027,6 +1045,60 @@ export class ChannelEngine {
|
|
|
1027
1045
|
});
|
|
1028
1046
|
}
|
|
1029
1047
|
|
|
1048
|
+
async _handleSessionReset(req, res, next) {
|
|
1049
|
+
debug(`req.url=${req.url}`);
|
|
1050
|
+
if (this.sessionResetKey && req.query.key !== this.sessionResetKey) {
|
|
1051
|
+
res.sendRaw(403, JSON.stringify({ "message": "Invalid Session-Reset-Key" }),
|
|
1052
|
+
{
|
|
1053
|
+
"Content-Type": "application/json",
|
|
1054
|
+
"Access-Control-Allow-Origin": "*",
|
|
1055
|
+
"Cache-Control": "no-cache",
|
|
1056
|
+
});
|
|
1057
|
+
next();
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
try {
|
|
1061
|
+
let sessionId;
|
|
1062
|
+
if (req.params && req.params.sessionId) {
|
|
1063
|
+
sessionId = req.params.sessionId;
|
|
1064
|
+
}
|
|
1065
|
+
let sessionResets = [];
|
|
1066
|
+
const session = sessions[sessionId];
|
|
1067
|
+
const sessionLive = sessionsLive[sessionId];
|
|
1068
|
+
if (session && sessionLive) {
|
|
1069
|
+
await session.resetAsync(sessionId);
|
|
1070
|
+
sessionResets.push(sessionId);
|
|
1071
|
+
} else {
|
|
1072
|
+
res.sendRaw(400, JSON.stringify({ "message": "Invalid Session ID" }),
|
|
1073
|
+
{
|
|
1074
|
+
"Content-Type": "application/json",
|
|
1075
|
+
"Access-Control-Allow-Origin": "*",
|
|
1076
|
+
"Cache-Control": "no-cache",
|
|
1077
|
+
});
|
|
1078
|
+
next();
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
res.sendRaw(200, JSON.stringify({ "status": "ok", "instanceId": this.instanceId, "resets": sessionResets }),
|
|
1083
|
+
{
|
|
1084
|
+
"Content-Type": "application/json",
|
|
1085
|
+
"Access-Control-Allow-Origin": "*",
|
|
1086
|
+
"Cache-Control": "no-cache",
|
|
1087
|
+
});
|
|
1088
|
+
next();
|
|
1089
|
+
} catch (e) {
|
|
1090
|
+
res.sendRaw(500, JSON.stringify({ "error": e}),
|
|
1091
|
+
{
|
|
1092
|
+
"Content-Type": "application/json",
|
|
1093
|
+
"Access-Control-Allow-Origin": "*",
|
|
1094
|
+
"Cache-Control": "no-cache",
|
|
1095
|
+
});
|
|
1096
|
+
const err = new errs.NotFoundError('Session Reset Failed');
|
|
1097
|
+
next(err);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
|
|
1030
1102
|
_gracefulErrorHandler(errMsg) {
|
|
1031
1103
|
console.error(errMsg);
|
|
1032
1104
|
const err = new errs.NotFoundError(errMsg);
|
package/engine/session.js
CHANGED
|
@@ -341,9 +341,13 @@ class Session {
|
|
|
341
341
|
}
|
|
342
342
|
}
|
|
343
343
|
|
|
344
|
-
async resetAsync() {
|
|
345
|
-
|
|
346
|
-
|
|
344
|
+
async resetAsync(id) {
|
|
345
|
+
if (id) {
|
|
346
|
+
await this._sessionStateStore.reset(id);
|
|
347
|
+
await this._playheadStateStore.reset(id);
|
|
348
|
+
}
|
|
349
|
+
await this._sessionStateStore.resetAll();
|
|
350
|
+
await this._playheadStateStore.resetAll();
|
|
347
351
|
}
|
|
348
352
|
|
|
349
353
|
async getSessionState() {
|
|
@@ -38,6 +38,10 @@ class SharedStateStore {
|
|
|
38
38
|
await this.store.resetAsync(id, this.initData);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
async resetAll() {
|
|
42
|
+
await this.store.resetAllAsync();
|
|
43
|
+
}
|
|
44
|
+
|
|
41
45
|
async get(id, key) {
|
|
42
46
|
//debug(`${this.type}:${id}:${key} Reading from shared store`);
|
|
43
47
|
let data = await this.store.getAsync(id, key);
|