eyevinn-channel-engine 4.3.5 → 4.3.7

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.
@@ -0,0 +1 @@
1
+ export function handler(req: any, res: any, next: any): any;
@@ -0,0 +1,43 @@
1
+ var constants = {
2
+ ALLOW_HEADERS: [
3
+ 'accept',
4
+ 'accept-version',
5
+ 'content-type',
6
+ 'request-id',
7
+ 'origin',
8
+ 'x-api-version',
9
+ 'x-request-id',
10
+ 'x-requested-with'
11
+ ],
12
+ EXPOSE_HEADERS: [
13
+ 'api-version',
14
+ 'content-length',
15
+ 'content-md5',
16
+ 'content-type',
17
+ 'date',
18
+ 'request-id',
19
+ 'response-time'
20
+ ],
21
+ AC_REQ_METHOD: 'access-control-request-method',
22
+ AC_REQ_HEADERS: 'access-control-request-headers',
23
+ AC_ALLOW_CREDS: 'access-control-allow-credentials',
24
+ AC_ALLOW_ORIGIN: 'access-control-allow-origin',
25
+ AC_ALLOW_HEADERS: 'access-control-allow-headers',
26
+ AC_ALLOW_METHODS: 'access-control-allow-methods',
27
+ AC_EXPOSE_HEADERS: 'access-control-expose-headers',
28
+ AC_MAX_AGE: 'access-control-max-age',
29
+ STR_VARY: 'vary',
30
+ STR_ORIGIN: 'origin',
31
+ HTTP_NO_CONTENT: 204
32
+ };
33
+ exports.handler = function (req, res, next) {
34
+ if (req.method !== 'OPTIONS')
35
+ return next();
36
+ res.once('header', function () {
37
+ res.header(constants.AC_ALLOW_ORIGIN, '*');
38
+ res.header(constants.AC_ALLOW_CREDS, true);
39
+ res.header(constants.AC_ALLOW_METHODS, ['GET', 'OPTIONS']);
40
+ res.header(constants.AC_ALLOW_HEADERS, ['x-health-key']);
41
+ });
42
+ res.send(constants.HTTP_NO_CONTENT);
43
+ };
@@ -32,6 +32,8 @@ export interface ChannelEngineOpts {
32
32
  autoCreateSession?: boolean;
33
33
  sessionResetKey?: string;
34
34
  keepAliveTimeout?: number;
35
+ sessionEventStream?: boolean;
36
+ sessionHealthKey?: string;
35
37
  }
36
38
  export interface VodRequest {
37
39
  sessionId: string;
@@ -151,6 +153,8 @@ export declare class ChannelEngine {
151
153
  private adXchangeUri?;
152
154
  private autoCreateSession;
153
155
  private sessionResetKey;
156
+ private sessionEventStream;
157
+ private sessionHealthKey;
154
158
  constructor(assetMgr: IAssetManager, options?: ChannelEngineOpts);
155
159
  updateStreamSwitchAsync(): Promise<void>;
156
160
  updateChannelsAsync(channelMgr: any, options: any): Promise<void>;
@@ -52,6 +52,7 @@ var SessionStateStore = require('./session_state.js').SessionStateStore;
52
52
  var SessionLiveStateStore = require('./session_live_state.js').SessionLiveStateStore;
53
53
  var PlayheadStateStore = require('./playhead_state.js').PlayheadStateStore;
54
54
  var _a = require('./util.js'), filterQueryParser = _a.filterQueryParser, toHHMMSS = _a.toHHMMSS, WaitTimeGenerator = _a.WaitTimeGenerator;
55
+ var preflight = require('./preflight.js');
55
56
  var version = require('../package.json').version;
56
57
  var AUTO_CREATE_CHANNEL_TIMEOUT = 3000;
57
58
  var sessions = {}; // Should be a persistent store...
@@ -72,6 +73,8 @@ var ChannelEngine = /** @class */ (function () {
72
73
  var _this = this;
73
74
  this.autoCreateSession = false;
74
75
  this.sessionResetKey = "";
76
+ this.sessionEventStream = false;
77
+ this.sessionHealthKey = "";
75
78
  this.options = options;
76
79
  if (options && options.adCopyMgrUri) {
77
80
  this.adCopyMgrUri = options.adCopyMgrUri;
@@ -91,6 +94,9 @@ var ChannelEngine = /** @class */ (function () {
91
94
  if (options && options.sessionResetKey) {
92
95
  this.sessionResetKey = options.sessionResetKey;
93
96
  }
97
+ if (options && options.sessionHealthKey) {
98
+ this.sessionHealthKey = options.sessionHealthKey;
99
+ }
94
100
  this.alwaysNewSegments = false;
95
101
  if (options && options.alwaysNewSegments) {
96
102
  this.alwaysNewSegments = true;
@@ -121,7 +127,6 @@ var ChannelEngine = /** @class */ (function () {
121
127
  this.server.server.keepAliveTimeout = options.keepAliveTimeout;
122
128
  this.server.server.headersTimeout = options.keepAliveTimeout + 1000;
123
129
  }
124
- console.log(this.server);
125
130
  this.server.use(restify.plugins.queryParser());
126
131
  this.serverStartTime = Date.now();
127
132
  this.instanceId = uuidv4();
@@ -245,30 +250,7 @@ var ChannelEngine = /** @class */ (function () {
245
250
  }
246
251
  });
247
252
  }); });
248
- this.server.opts('/live/:file', function (req, res, next) { return __awaiter(_this, void 0, void 0, function () {
249
- return __generator(this, function (_a) {
250
- res.sendRaw(204, "", {
251
- headers: {
252
- "Access-Control-Allow-Origin": "*",
253
- "Access-Control-Allow-Methods": "GET, OPTIONS",
254
- },
255
- });
256
- next();
257
- return [2 /*return*/];
258
- });
259
- }); });
260
- this.server.opts('/channels/:channelId/:file', function (req, res, next) { return __awaiter(_this, void 0, void 0, function () {
261
- return __generator(this, function (_a) {
262
- res.sendRaw(204, "", {
263
- headers: {
264
- "Access-Control-Allow-Origin": "*",
265
- "Access-Control-Allow-Methods": "GET, OPTIONS",
266
- },
267
- });
268
- next();
269
- return [2 /*return*/];
270
- });
271
- }); });
253
+ this.server.pre(preflight.handler);
272
254
  this.server.get('/eventstream/:sessionId', this._handleEventStream.bind(this));
273
255
  this.server.get('/status/:sessionId', this._handleStatus.bind(this));
274
256
  this.server.get('/health', this._handleAggregatedSessionHealth.bind(this));
@@ -474,6 +456,7 @@ var ChannelEngine = /** @class */ (function () {
474
456
  slateRepetitions: channel.slate && channel.slate.repetitions ? channel.slate.repetitions : this.slateRepetitions,
475
457
  slateDuration: channel.slate && channel.slate.duration ? channel.slate.duration : this.slateDuration,
476
458
  cloudWatchMetrics: this.logCloudWatchMetrics,
459
+ sessionEventStream: options.sessionEventStream
477
460
  }, this.sessionStore);
478
461
  sessionsLive[channel.id] = new SessionLive({
479
462
  sessionId: channel.id,
@@ -1202,6 +1185,15 @@ var ChannelEngine = /** @class */ (function () {
1202
1185
  switch (_b.label) {
1203
1186
  case 0:
1204
1187
  debug("req.url=".concat(req.url));
1188
+ if (this.sessionHealthKey && this.sessionHealthKey !== req.headers['x-health-key']) {
1189
+ res.sendRaw(403, JSON.stringify({ "message": "Invalid Session-Health-Key" }), {
1190
+ "Content-Type": "application/json",
1191
+ "Access-Control-Allow-Origin": "*",
1192
+ "Cache-Control": "no-cache",
1193
+ });
1194
+ next();
1195
+ return [2 /*return*/];
1196
+ }
1205
1197
  failingSessions = [];
1206
1198
  endpoints = [];
1207
1199
  _i = 0, _a = Object.keys(sessions);
@@ -59,6 +59,7 @@ declare class Session {
59
59
  _subtitleTracks: any;
60
60
  _closedCaptions: any;
61
61
  _noSessionDataTags: any;
62
+ _sessionEventStream: any;
62
63
  slateUri: any;
63
64
  slateRepetitions: any;
64
65
  slateDuration: any;
@@ -56,6 +56,7 @@ var SessionState = require('./session_state.js').SessionState;
56
56
  var PlayheadState = require('./playhead_state.js').PlayheadState;
57
57
  var _a = require('./util.js'), applyFilter = _a.applyFilter, cloudWatchLog = _a.cloudWatchLog, m3u8Header = _a.m3u8Header, logerror = _a.logerror, codecsFromString = _a.codecsFromString;
58
58
  var ChaosMonkey = require('./chaos_monkey.js');
59
+ var EVENT_LIST_LIMIT = 100;
59
60
  var AVERAGE_SEGMENT_DURATION = 3000;
60
61
  var DEFAULT_PLAYHEAD_DIFF_THRESHOLD = 1000;
61
62
  var DEFAULT_MAX_TICK_INTERVAL = 10000;
@@ -164,6 +165,9 @@ var Session = /** @class */ (function () {
164
165
  if (config.noSessionDataTags) {
165
166
  this._noSessionDataTags = config.noSessionDataTags;
166
167
  }
168
+ if (config.sessionEventStream) {
169
+ this._sessionEventStream = config.sessionEventStream;
170
+ }
167
171
  if (config.slateUri) {
168
172
  this.slateUri = config.slateUri;
169
173
  this.slateRepetitions = config.slateRepetitions || 10;
@@ -1687,7 +1691,12 @@ var Session = /** @class */ (function () {
1687
1691
  return this._events.shift();
1688
1692
  };
1689
1693
  Session.prototype.produceEvent = function (event) {
1690
- this._events.push(event);
1694
+ if (this._sessionEventStream) {
1695
+ this._events.push(event);
1696
+ if (this._events.length > EVENT_LIST_LIMIT) {
1697
+ this.consumeEvent();
1698
+ }
1699
+ }
1691
1700
  };
1692
1701
  Session.prototype.hasPlayhead = function () {
1693
1702
  return !this.disabledPlayhead;
@@ -94,7 +94,8 @@ var engineOptions = {
94
94
  defaultSlateUri: "https://maitv-vod.lab.eyevinn.technology/slate-consuo.mp4/master.m3u8",
95
95
  slateRepetitions: 10,
96
96
  redisUrl: process.env.REDIS_URL,
97
- keepAliveTimeout: process.env.KEEP_ALIVE_TIMEOUT ? parseInt(process.env.KEEP_ALIVE_TIMEOUT) * 1000 : undefined
97
+ keepAliveTimeout: process.env.KEEP_ALIVE_TIMEOUT ? parseInt(process.env.KEEP_ALIVE_TIMEOUT) * 1000 : undefined,
98
+ sessionHealthKey: 'eyevinn'
98
99
  };
99
100
  var engine = new index_1.ChannelEngine(refAssetManager, engineOptions);
100
101
  engine.start();
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyevinn-channel-engine",
3
- "version": "4.3.5",
3
+ "version": "4.3.7",
4
4
  "description": "OTT TV Channel Engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -30,7 +30,7 @@
30
30
  "dependencies": {
31
31
  "@eyevinn/hls-repeat": "^0.2.0",
32
32
  "@eyevinn/hls-truncate": "^0.3.0",
33
- "@eyevinn/hls-vodtolive": "^4.0.1",
33
+ "@eyevinn/hls-vodtolive": "^4.1.0",
34
34
  "@eyevinn/m3u8": "^0.5.3",
35
35
  "abort-controller": "^3.0.0",
36
36
  "debug": "^3.2.7",
@@ -0,0 +1,44 @@
1
+ const constants = {
2
+ ALLOW_HEADERS: [
3
+ 'accept',
4
+ 'accept-version',
5
+ 'content-type',
6
+ 'request-id',
7
+ 'origin',
8
+ 'x-api-version',
9
+ 'x-request-id',
10
+ 'x-requested-with'
11
+ ],
12
+ EXPOSE_HEADERS: [
13
+ 'api-version',
14
+ 'content-length',
15
+ 'content-md5',
16
+ 'content-type',
17
+ 'date',
18
+ 'request-id',
19
+ 'response-time'
20
+ ],
21
+ AC_REQ_METHOD: 'access-control-request-method',
22
+ AC_REQ_HEADERS: 'access-control-request-headers',
23
+ AC_ALLOW_CREDS: 'access-control-allow-credentials',
24
+ AC_ALLOW_ORIGIN: 'access-control-allow-origin',
25
+ AC_ALLOW_HEADERS: 'access-control-allow-headers',
26
+ AC_ALLOW_METHODS: 'access-control-allow-methods',
27
+ AC_EXPOSE_HEADERS: 'access-control-expose-headers',
28
+ AC_MAX_AGE: 'access-control-max-age',
29
+ STR_VARY: 'vary',
30
+ STR_ORIGIN: 'origin',
31
+ HTTP_NO_CONTENT: 204
32
+ }
33
+
34
+ exports.handler = function (req, res, next) {
35
+ if (req.method !== 'OPTIONS') return next();
36
+ res.once('header', function () {
37
+ res.header(constants.AC_ALLOW_ORIGIN, '*');
38
+ res.header(constants.AC_ALLOW_CREDS, true);
39
+ res.header(constants.AC_ALLOW_METHODS, ['GET', 'OPTIONS']);
40
+ res.header(constants.AC_ALLOW_HEADERS, ['x-health-key']);
41
+ });
42
+
43
+ res.send(constants.HTTP_NO_CONTENT);
44
+ }
package/engine/server.ts CHANGED
@@ -15,6 +15,8 @@ const { SessionLiveStateStore } = require('./session_live_state.js');
15
15
  const { PlayheadStateStore } = require('./playhead_state.js');
16
16
 
17
17
  const { filterQueryParser, toHHMMSS, WaitTimeGenerator } = require('./util.js');
18
+ const preflight = require('./preflight.js');
19
+
18
20
  const { version } = require('../package.json');
19
21
 
20
22
  const AUTO_CREATE_CHANNEL_TIMEOUT = 3000;
@@ -61,6 +63,8 @@ export interface ChannelEngineOpts {
61
63
  autoCreateSession?: boolean;
62
64
  sessionResetKey?: string;
63
65
  keepAliveTimeout?: number;
66
+ sessionEventStream?: boolean;
67
+ sessionHealthKey?: string;
64
68
  }
65
69
 
66
70
  interface StreamerOpts {
@@ -207,6 +211,8 @@ export class ChannelEngine {
207
211
  private adXchangeUri?: string;
208
212
  private autoCreateSession: boolean = false;
209
213
  private sessionResetKey: string = "";
214
+ private sessionEventStream: boolean = false;
215
+ private sessionHealthKey: string = "";
210
216
 
211
217
  constructor(assetMgr: IAssetManager, options?: ChannelEngineOpts) {
212
218
  this.options = options;
@@ -230,6 +236,9 @@ export class ChannelEngine {
230
236
  if (options && options.sessionResetKey) {
231
237
  this.sessionResetKey = options.sessionResetKey;
232
238
  }
239
+ if (options && options.sessionHealthKey) {
240
+ this.sessionHealthKey = options.sessionHealthKey;
241
+ }
233
242
  this.alwaysNewSegments = false;
234
243
  if (options && options.alwaysNewSegments) {
235
244
  this.alwaysNewSegments = true;
@@ -260,7 +269,6 @@ export class ChannelEngine {
260
269
  this.server.server.keepAliveTimeout = options.keepAliveTimeout;
261
270
  this.server.server.headersTimeout = options.keepAliveTimeout + 1000;
262
271
  }
263
- console.log(this.server);
264
272
  this.server.use(restify.plugins.queryParser());
265
273
  this.serverStartTime = Date.now();
266
274
  this.instanceId = uuidv4();
@@ -352,25 +360,8 @@ export class ChannelEngine {
352
360
  this.server.get('/channels/:channelId/:file', async (req, res, next) => {
353
361
  req.query['channel'] = req.params.channelId;
354
362
  await handleMasterRoute(req, res, next);
355
- });
356
- this.server.opts('/live/:file', async (req, res, next) => {
357
- res.sendRaw(204, "", {
358
- headers: {
359
- "Access-Control-Allow-Origin": "*",
360
- "Access-Control-Allow-Methods": "GET, OPTIONS",
361
- },
362
- });
363
- next();
364
- });
365
- this.server.opts('/channels/:channelId/:file', async (req, res, next) => {
366
- res.sendRaw(204, "", {
367
- headers: {
368
- "Access-Control-Allow-Origin": "*",
369
- "Access-Control-Allow-Methods": "GET, OPTIONS",
370
- },
371
- });
372
- next();
373
363
  });
364
+ this.server.pre(preflight.handler);
374
365
  this.server.get('/eventstream/:sessionId', this._handleEventStream.bind(this));
375
366
  this.server.get('/status/:sessionId', this._handleStatus.bind(this));
376
367
  this.server.get('/health', this._handleAggregatedSessionHealth.bind(this));
@@ -498,6 +489,7 @@ export class ChannelEngine {
498
489
  slateRepetitions: channel.slate && channel.slate.repetitions ? channel.slate.repetitions : this.slateRepetitions,
499
490
  slateDuration: channel.slate && channel.slate.duration ? channel.slate.duration : this.slateDuration,
500
491
  cloudWatchMetrics: this.logCloudWatchMetrics,
492
+ sessionEventStream: options.sessionEventStream
501
493
  }, this.sessionStore);
502
494
 
503
495
  sessionsLive[channel.id] = new SessionLive({
@@ -955,6 +947,16 @@ export class ChannelEngine {
955
947
 
956
948
  async _handleAggregatedSessionHealth(req, res, next) {
957
949
  debug(`req.url=${req.url}`);
950
+ if (this.sessionHealthKey && this.sessionHealthKey !== req.headers['x-health-key']) {
951
+ res.sendRaw(403, JSON.stringify({ "message": "Invalid Session-Health-Key" }),
952
+ {
953
+ "Content-Type": "application/json",
954
+ "Access-Control-Allow-Origin": "*",
955
+ "Cache-Control": "no-cache",
956
+ });
957
+ next();
958
+ return;
959
+ }
958
960
  let failingSessions = [];
959
961
  let endpoints = [];
960
962
  for (const sessionId of Object.keys(sessions)) {
package/engine/session.js CHANGED
@@ -12,6 +12,7 @@ const { PlayheadState } = require('./playhead_state.js');
12
12
  const { applyFilter, cloudWatchLog, m3u8Header, logerror, codecsFromString } = require('./util.js');
13
13
  const ChaosMonkey = require('./chaos_monkey.js');
14
14
 
15
+ const EVENT_LIST_LIMIT = 100;
15
16
  const AVERAGE_SEGMENT_DURATION = 3000;
16
17
  const DEFAULT_PLAYHEAD_DIFF_THRESHOLD = 1000;
17
18
  const DEFAULT_MAX_TICK_INTERVAL = 10000;
@@ -128,6 +129,9 @@ class Session {
128
129
  if (config.noSessionDataTags) {
129
130
  this._noSessionDataTags = config.noSessionDataTags;
130
131
  }
132
+ if (config.sessionEventStream) {
133
+ this._sessionEventStream = config.sessionEventStream;
134
+ }
131
135
  if (config.slateUri) {
132
136
  this.slateUri = config.slateUri;
133
137
  this.slateRepetitions = config.slateRepetitions || 10;
@@ -1246,7 +1250,12 @@ class Session {
1246
1250
  }
1247
1251
 
1248
1252
  produceEvent(event) {
1249
- this._events.push(event);
1253
+ if (this._sessionEventStream) {
1254
+ this._events.push(event);
1255
+ if (this._events.length > EVENT_LIST_LIMIT) {
1256
+ this.consumeEvent();
1257
+ }
1258
+ }
1250
1259
  }
1251
1260
 
1252
1261
  hasPlayhead() {
@@ -106,7 +106,8 @@ const engineOptions: ChannelEngineOpts = {
106
106
  "https://maitv-vod.lab.eyevinn.technology/slate-consuo.mp4/master.m3u8",
107
107
  slateRepetitions: 10,
108
108
  redisUrl: process.env.REDIS_URL,
109
- keepAliveTimeout: process.env.KEEP_ALIVE_TIMEOUT ? parseInt(process.env.KEEP_ALIVE_TIMEOUT) * 1000: undefined
109
+ keepAliveTimeout: process.env.KEEP_ALIVE_TIMEOUT ? parseInt(process.env.KEEP_ALIVE_TIMEOUT) * 1000: undefined,
110
+ sessionHealthKey: 'eyevinn'
110
111
  };
111
112
 
112
113
  const engine = new ChannelEngine(refAssetManager, engineOptions);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyevinn-channel-engine",
3
- "version": "4.3.5",
3
+ "version": "4.3.7",
4
4
  "description": "OTT TV Channel Engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -30,7 +30,7 @@
30
30
  "dependencies": {
31
31
  "@eyevinn/hls-repeat": "^0.2.0",
32
32
  "@eyevinn/hls-truncate": "^0.3.0",
33
- "@eyevinn/hls-vodtolive": "^4.0.1",
33
+ "@eyevinn/hls-vodtolive": "^4.1.0",
34
34
  "@eyevinn/m3u8": "^0.5.3",
35
35
  "abort-controller": "^3.0.0",
36
36
  "debug": "^3.2.7",