eyevinn-channel-engine 4.0.16 → 4.0.18

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,62 @@
1
+ # Submitting Issues
2
+
3
+ We use GitHub issues to track public bugs. If you are submitting a bug, please provide
4
+ as much info as possible to make it easier to reproduce the issue and update unit tests.
5
+
6
+ # Contributing Code
7
+
8
+ This project uses [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary).
9
+
10
+ We follow the [GitHub Flow](https://guides.github.com/introduction/flow/index.html) so all contributions happen through pull requests. We actively welcome your pull requests:
11
+
12
+ 1. Fork the repo and create your branch from master.
13
+ 2. If you've added code that should be tested, add tests.
14
+ 3. If you've changed APIs, update the documentation.
15
+ 4. Ensure the test suite passes.
16
+ 5. Issue that pull request!
17
+
18
+ When submitting code changes your submissions are understood to be under the same MIT License that covers the project. Feel free to contact Eyevinn Technology if that's a concern.
19
+
20
+ # Code of Conduct
21
+
22
+ ## Our Pledge
23
+
24
+ In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
25
+
26
+ ## Our Standards
27
+
28
+ Examples of behavior that contributes to creating a positive environment include:
29
+
30
+ - Using welcoming and inclusive language
31
+ - Being respectful of differing viewpoints and experiences
32
+ - Gracefully accepting constructive criticism
33
+ - Focusing on what is best for the community
34
+ - Showing empathy towards other community members
35
+
36
+ Examples of unacceptable behavior by participants include:
37
+
38
+ - The use of sexualized language or imagery and unwelcome sexual attention or advances
39
+ - Trolling, insulting/derogatory comments, and personal or political attacks
40
+ - Public or private harassment
41
+ - Publishing others' private information, such as a physical or electronic address, without explicit permission
42
+ - Other conduct which could reasonably be considered inappropriate in a professional setting
43
+
44
+ ## Our Responsibilities
45
+
46
+ Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
47
+
48
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
49
+
50
+ ## Scope
51
+
52
+ This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
53
+
54
+ ## Enforcement
55
+
56
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
57
+
58
+ Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
59
+
60
+ ## Attribution
61
+
62
+ This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at http://contributor-covenant.org/version/1/4
package/README.md CHANGED
@@ -91,6 +91,68 @@ and this means that it is not possible to mix muxed TS with demuxed CMAF.
91
91
 
92
92
  A breaking change was introduced in v3.4.0 when Typescript types were introduced. The library no longer exports a default. This means that you need to change `const ChannelEngine = require('eyevinn-channel-engine')` to `const { ChannelEngine } = require('eyevinn-channel-engine')`;
93
93
 
94
+ ## Contributing
95
+
96
+ Read [CONTRIBUTING](CONTRIBUTING.md) first and then follow the instructions below to get started with the devolpment environment.
97
+
98
+ ### Development
99
+
100
+ Ensure that you are running node version 16 as version 18 is not supported yet.
101
+
102
+ ```
103
+ node --version
104
+ v16.17.0
105
+ ```
106
+
107
+ Then install the dependencies:
108
+
109
+ ```
110
+ npm install
111
+ ```
112
+
113
+ Build the app:
114
+
115
+ ```
116
+ npm run build
117
+ ```
118
+
119
+ To start the reference implementation in `server.ts` run:
120
+
121
+ ```
122
+ npm start
123
+ ```
124
+
125
+ For full debug mode you set the environment variable `DEBUG`.
126
+
127
+ ```
128
+ DEBUG=* npm start
129
+ ```
130
+
131
+ or if you want be more specific for example:
132
+
133
+ ```
134
+ DEBUG=engine-* npm start
135
+ ```
136
+
137
+ In addition there other reference implemetations that can be used:
138
+ - `server-demux.ts` : example with demuxed audio
139
+ - `server-livemix.ts` : example with live mimxing
140
+ - `server-multicodec.ts` : example with multicodec
141
+
142
+ ### Source linking hls-vodtolive library
143
+
144
+ It is not uncommon that you need to test an local branch of the hls-vodtolive library and in order to do that you can use `npm link`.
145
+
146
+ 1. Go to the directory where you have the source code for the hls-vodtolive library and run `npm link`
147
+ 2. Then go to the channel engine source code directory and run `npm link @eyevinn/hls-vodtolive`
148
+
149
+ To restore and use the npm published version run the following in the channel engine source code directory:
150
+
151
+ ```
152
+ npm uninstall --no-save @eyevinn/hls-vodtolive
153
+ npm install
154
+ ```
155
+
94
156
  ## Support
95
157
 
96
158
  Join our [community on Slack](http://slack.streamingtech.se) where you can post any questions regarding any of our open source projects. Eyevinn's consulting business can also offer you:
@@ -50,6 +50,7 @@ export interface VodResponse {
50
50
  type?: string;
51
51
  currentMetadata?: VodResponseMetadata;
52
52
  timedMetadata?: VodTimedMetadata;
53
+ unixTs?: number;
53
54
  }
54
55
  export interface IAssetManager {
55
56
  getNextVod: (vodRequest: VodRequest) => Promise<VodResponse>;
@@ -952,7 +952,7 @@ var Session = /** @class */ (function () {
952
952
  };
953
953
  Session.prototype.incrementAsync = function () {
954
954
  return __awaiter(this, void 0, void 0, function () {
955
- var isLeader, sessionState, playheadState, currentVod, _a, leaderAction, _b, m3u8_3, _c, audioIncrement, position, currentVod_1, sessionState_1, posDiff, incrementValue, index, audioSeqLastIdx, thresh, audioPosition, _d, _e, _f, _g, _h, _j, _k, _l, _m, mediaSequenceValue, diffMs, m3u8;
955
+ var isLeader, sessionState, playheadState, currentVod, _a, leaderAction, _b, m3u8_3, _c, audioIncrement, positionV, positionA, position, currentVod_1, sessionState_1, posDiff, incrementValue, index, audioSeqLastIdx, thresh, audioPosition, _d, _e, _f, _g, _h, _j, _k, _l, _m, mediaSequenceValue, diffMs, m3u8;
956
956
  return __generator(this, function (_o) {
957
957
  switch (_o.label) {
958
958
  case 0: return [4 /*yield*/, this._tickAsync()];
@@ -1020,9 +1020,12 @@ var Session = /** @class */ (function () {
1020
1020
  _c.vodMediaSeqVideo = _o.sent();
1021
1021
  audioIncrement = void 0;
1022
1022
  if (!this.use_demuxed_audio) return [3 /*break*/, 23];
1023
+ positionV = 0;
1024
+ positionA = 0;
1023
1025
  return [4 /*yield*/, this._getCurrentPlayheadPosition()];
1024
1026
  case 16:
1025
1027
  position = (_o.sent()) * 1000;
1028
+ positionV = position / 1000;
1026
1029
  return [4 /*yield*/, this._sessionState.getCurrentVod()];
1027
1030
  case 17:
1028
1031
  currentVod_1 = _o.sent();
@@ -1039,6 +1042,7 @@ var Session = /** @class */ (function () {
1039
1042
  case 19: return [4 /*yield*/, this._getAudioPlayheadPosition(sessionState_1.vodMediaSeqAudio + index)];
1040
1043
  case 20:
1041
1044
  audioPosition = (_o.sent()) * 1000;
1045
+ positionA = audioPosition / 1000;
1042
1046
  posDiff = position - audioPosition;
1043
1047
  if (posDiff <= 0) {
1044
1048
  return [3 /*break*/, 22];
@@ -1060,6 +1064,7 @@ var Session = /** @class */ (function () {
1060
1064
  _o.label = 22;
1061
1065
  case 22:
1062
1066
  audioIncrement = index;
1067
+ debug("[".concat(this._sessionId, "]: Current VOD Playhead Positions are to be: [").concat(positionV.toFixed(3), "][").concat(positionA.toFixed(3), "] (").concat((positionA - positionV).toFixed(3), ")"));
1063
1068
  _o.label = 23;
1064
1069
  case 23:
1065
1070
  debug("[".concat(this._sessionId, "]: Will increment audio with ").concat(audioIncrement));
@@ -1628,7 +1633,7 @@ var Session = /** @class */ (function () {
1628
1633
  if (!vodResponse.type) {
1629
1634
  debug("[".concat(this._sessionId, "]: got first VOD uri=").concat(vodResponse.uri, ":").concat(vodResponse.offset || 0));
1630
1635
  hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments, forcedDemuxMode: this.use_demuxed_audio };
1631
- newVod = new HLSVod(vodResponse.uri, [], null, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
1636
+ newVod = new HLSVod(vodResponse.uri, [], vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
1632
1637
  if (vodResponse.timedMetadata) {
1633
1638
  Object.keys(vodResponse.timedMetadata).map(function (k) {
1634
1639
  newVod.addMetadata(k, vodResponse.timedMetadata[k]);
@@ -1836,7 +1841,7 @@ var Session = /** @class */ (function () {
1836
1841
  if (!vodResponse.type) {
1837
1842
  debug("[".concat(this._sessionId, "]: got next VOD uri=").concat(vodResponse.uri, ":").concat(vodResponse.offset));
1838
1843
  hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments, forcedDemuxMode: this.use_demuxed_audio };
1839
- newVod = new HLSVod(vodResponse.uri, null, null, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
1844
+ newVod = new HLSVod(vodResponse.uri, null, vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
1840
1845
  if (vodResponse.timedMetadata) {
1841
1846
  Object.keys(vodResponse.timedMetadata).map(function (k) {
1842
1847
  newVod.addMetadata(k, vodResponse.timedMetadata[k]);
@@ -2147,8 +2152,8 @@ var Session = /** @class */ (function () {
2147
2152
  slateVod_1.load()
2148
2153
  .then(function () {
2149
2154
  var hlsOpts = { sequenceAlwaysContainNewSegments: _this.alwaysNewSegments, forcedDemuxMode: _this.use_demuxed_audio };
2150
- hlsVod_1 = new HLSVod(_this.slateUri, null, null, null, m3u8Header(_this._instanceId), hlsOpts);
2151
2155
  var timestamp = Date.now();
2156
+ hlsVod_1 = new HLSVod(_this.slateUri, null, timestamp, null, m3u8Header(_this._instanceId), hlsOpts);
2152
2157
  hlsVod_1.addMetadata('id', "slate-".concat(timestamp));
2153
2158
  hlsVod_1.addMetadata('start-date', new Date(timestamp).toISOString());
2154
2159
  hlsVod_1.addMetadata('planned-duration', ((reps || _this.slateRepetitions) * _this.slateDuration) / 1000);
@@ -2210,8 +2215,8 @@ var Session = /** @class */ (function () {
2210
2215
  slateVod_2.load()
2211
2216
  .then(function () {
2212
2217
  var hlsOpts = { sequenceAlwaysContainNewSegments: _this.alwaysNewSegments, forcedDemuxMode: _this.use_demuxed_audio };
2213
- hlsVod_2 = new HLSVod(nexVodUri, null, null, null, m3u8Header(_this._instanceId), hlsOpts);
2214
2218
  var timestamp = Date.now();
2219
+ hlsVod_2 = new HLSVod(nexVodUri, null, timestamp, null, m3u8Header(_this._instanceId), hlsOpts);
2215
2220
  hlsVod_2.addMetadata('id', "slate-".concat(timestamp));
2216
2221
  hlsVod_2.addMetadata('start-date', new Date(timestamp).toISOString());
2217
2222
  hlsVod_2.addMetadata('planned-duration', requestedDuration);
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyevinn-channel-engine",
3
- "version": "4.0.16",
3
+ "version": "4.0.18",
4
4
  "description": "OTT TV Channel Engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -13,6 +13,9 @@
13
13
  "start-demux": "node dist/server-demux.js",
14
14
  "start-livemix": "node dist/server-livemix.js"
15
15
  },
16
+ "engines": {
17
+ "node": "<18"
18
+ },
16
19
  "author": "Eyevinn Technology AB <work@eyevinn.se>",
17
20
  "contributors": [
18
21
  "Jonas Birmé <jonas.birme@eyevinn.se> (Eyevinn Technology AB)",
@@ -26,7 +29,7 @@
26
29
  "dependencies": {
27
30
  "@eyevinn/hls-repeat": "^0.2.0",
28
31
  "@eyevinn/hls-truncate": "^0.2.0",
29
- "@eyevinn/hls-vodtolive": "^2.3.5",
32
+ "@eyevinn/hls-vodtolive": "^2.3.8",
30
33
  "@eyevinn/m3u8": "^0.5.3",
31
34
  "abort-controller": "^3.0.0",
32
35
  "debug": "^3.2.7",
package/engine/server.ts CHANGED
@@ -89,6 +89,9 @@ export interface VodResponse {
89
89
  type?: string;
90
90
  currentMetadata?: VodResponseMetadata;
91
91
  timedMetadata?: VodTimedMetadata;
92
+ // Wall-clock start time of the VOD as unix ts. If set
93
+ // the program-date-time will be added to each segment
94
+ unixTs?: number;
92
95
  }
93
96
 
94
97
  export interface IAssetManager {
package/engine/session.js CHANGED
@@ -726,7 +726,10 @@ class Session {
726
726
  sessionState.vodMediaSeqVideo = await this._sessionState.increment("vodMediaSeqVideo");
727
727
  let audioIncrement;
728
728
  if (this.use_demuxed_audio) {
729
+ let positionV = 0;
730
+ let positionA = 0;
729
731
  const position = (await this._getCurrentPlayheadPosition()) * 1000;
732
+ positionV = position / 1000;
730
733
  let currentVod = await this._sessionState.getCurrentVod();
731
734
  const sessionState = await this._sessionState.getValues(["vodMediaSeqAudio"]);
732
735
  let posDiff;
@@ -737,6 +740,7 @@ class Session {
737
740
  debug(`[${this._sessionId}]: About to determine audio increment`);
738
741
  do {
739
742
  const audioPosition = (await this._getAudioPlayheadPosition(sessionState.vodMediaSeqAudio + index)) * 1000;
743
+ positionA = audioPosition / 1000;
740
744
  posDiff = position - audioPosition;
741
745
  if (posDiff <= 0) {
742
746
  break;
@@ -753,6 +757,7 @@ class Session {
753
757
  }
754
758
  } while (!(-thresh < posDiff && posDiff < thresh));
755
759
  audioIncrement = index;
760
+ debug(`[${this._sessionId}]: Current VOD Playhead Positions are to be: [${positionV.toFixed(3)}][${positionA.toFixed(3)}] (${(positionA-positionV).toFixed(3)})`);
756
761
  }
757
762
  debug(`[${this._sessionId}]: Will increment audio with ${audioIncrement}`);
758
763
  sessionState.vodMediaSeqAudio = await this._sessionState.increment("vodMediaSeqAudio", audioIncrement);
@@ -1152,7 +1157,7 @@ class Session {
1152
1157
  if (!vodResponse.type) {
1153
1158
  debug(`[${this._sessionId}]: got first VOD uri=${vodResponse.uri}:${vodResponse.offset || 0}`);
1154
1159
  const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments, forcedDemuxMode: this.use_demuxed_audio };
1155
- newVod = new HLSVod(vodResponse.uri, [], null, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
1160
+ newVod = new HLSVod(vodResponse.uri, [], vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
1156
1161
  if (vodResponse.timedMetadata) {
1157
1162
  Object.keys(vodResponse.timedMetadata).map(k => {
1158
1163
  newVod.addMetadata(k, vodResponse.timedMetadata[k]);
@@ -1301,7 +1306,7 @@ class Session {
1301
1306
  if (!vodResponse.type) {
1302
1307
  debug(`[${this._sessionId}]: got next VOD uri=${vodResponse.uri}:${vodResponse.offset}`);
1303
1308
  const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments, forcedDemuxMode: this.use_demuxed_audio };
1304
- newVod = new HLSVod(vodResponse.uri, null, null, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
1309
+ newVod = new HLSVod(vodResponse.uri, null, vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
1305
1310
  if (vodResponse.timedMetadata) {
1306
1311
  Object.keys(vodResponse.timedMetadata).map(k => {
1307
1312
  newVod.addMetadata(k, vodResponse.timedMetadata[k]);
@@ -1532,8 +1537,8 @@ class Session {
1532
1537
  slateVod.load()
1533
1538
  .then(() => {
1534
1539
  const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments, forcedDemuxMode: this.use_demuxed_audio };
1535
- hlsVod = new HLSVod(this.slateUri, null, null, null, m3u8Header(this._instanceId), hlsOpts);
1536
1540
  const timestamp = Date.now();
1541
+ hlsVod = new HLSVod(this.slateUri, null, timestamp, null, m3u8Header(this._instanceId), hlsOpts);
1537
1542
  hlsVod.addMetadata('id', `slate-${timestamp}`);
1538
1543
  hlsVod.addMetadata('start-date', new Date(timestamp).toISOString());
1539
1544
  hlsVod.addMetadata('planned-duration', ((reps || this.slateRepetitions) * this.slateDuration) / 1000);
@@ -1592,8 +1597,8 @@ class Session {
1592
1597
  slateVod.load()
1593
1598
  .then(() => {
1594
1599
  const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments, forcedDemuxMode: this.use_demuxed_audio };
1595
- hlsVod = new HLSVod(nexVodUri, null, null, null, m3u8Header(this._instanceId), hlsOpts);
1596
1600
  const timestamp = Date.now();
1601
+ hlsVod = new HLSVod(nexVodUri, null, timestamp, null, m3u8Header(this._instanceId), hlsOpts);
1597
1602
  hlsVod.addMetadata('id', `slate-${timestamp}`);
1598
1603
  hlsVod.addMetadata('start-date', new Date(timestamp).toISOString());
1599
1604
  hlsVod.addMetadata('planned-duration', requestedDuration);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyevinn-channel-engine",
3
- "version": "4.0.16",
3
+ "version": "4.0.18",
4
4
  "description": "OTT TV Channel Engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -13,6 +13,9 @@
13
13
  "start-demux": "node dist/server-demux.js",
14
14
  "start-livemix": "node dist/server-livemix.js"
15
15
  },
16
+ "engines": {
17
+ "node": "<18"
18
+ },
16
19
  "author": "Eyevinn Technology AB <work@eyevinn.se>",
17
20
  "contributors": [
18
21
  "Jonas Birmé <jonas.birme@eyevinn.se> (Eyevinn Technology AB)",
@@ -26,7 +29,7 @@
26
29
  "dependencies": {
27
30
  "@eyevinn/hls-repeat": "^0.2.0",
28
31
  "@eyevinn/hls-truncate": "^0.2.0",
29
- "@eyevinn/hls-vodtolive": "^2.3.5",
32
+ "@eyevinn/hls-vodtolive": "^2.3.8",
30
33
  "@eyevinn/m3u8": "^0.5.3",
31
34
  "abort-controller": "^3.0.0",
32
35
  "debug": "^3.2.7",
@@ -468,6 +468,7 @@ describe("The initialize switching", () => {
468
468
  if (m) {
469
469
  mseqNo = Number(m[1]);
470
470
  }
471
+
471
472
  expect(mseqNo).toBe(10);
472
473
  const size = sessionCurrentSegs["1313000"].length;
473
474
  expect(sessionCurrentSegs["1313000"][0]).toEqual({
@@ -484,7 +485,7 @@ describe("The initialize switching", () => {
484
485
  const borderSegment = sessionCurrentSegs["1313000"][size - 1 - 3];
485
486
  expect(borderSegment.duration).toBe(10.880);
486
487
  expect(borderSegment.cue).toBe(null);
487
- expect(borderSegment.timelinePosition).toBe(null);
488
+ expect(borderSegment.timelinePosition).not.toBe(null);
488
489
  expect(borderSegment.uri).toBe("https://maitv-vod.lab.eyevinn.technology/MORBIUS_Trailer_2020.mp4/1000/1000-00000.ts");
489
490
  expect(borderSegment.daterange["id"]).not.toBe(null);
490
491
  expect(borderSegment.daterange["start-date"]).not.toBe(null);
@@ -750,7 +751,8 @@ describe("The initialize switching", () => {
750
751
 
751
752
  const expectedLastVODSegItem = {
752
753
  discontinuity: true,
753
- daterange: null
754
+ daterange: null,
755
+ vodTransition: true
754
756
  }
755
757
  const expectedFirstV2LSegItem = {
756
758
  duration: 7.2,
@@ -768,13 +770,11 @@ describe("The initialize switching", () => {
768
770
  }
769
771
 
770
772
  expect(newVodSegments["1313000"][0]).toEqual(expectedLastVODSegItem);
771
- expect(newVodSegments["1313000"][1]).toEqual({
772
- duration: 10.846444,
773
- timelinePosition: null,
774
- cue: null,
775
- uri: 'https://maitv-vod.lab.eyevinn.technology/VINN.mp4/600/600-00000.ts',
776
- byteRange: undefined,
777
- });
773
+ expect(newVodSegments["1313000"][1].duration).toEqual(10.846444);
774
+ expect(newVodSegments["1313000"][1].cue).toBeNull();
775
+ expect(newVodSegments["1313000"][1].uri).toEqual('https://maitv-vod.lab.eyevinn.technology/VINN.mp4/600/600-00000.ts');
776
+
777
+ newVodSegments["1313000"][2].timelinePosition = null; // disregard timeline position
778
778
  expect(newVodSegments["1313000"][2]).toEqual(expectedFirstV2LSegItem);
779
779
  // expect(newVodSegments["1313000"][5]).toEqual({
780
780
  // discontinuity: true,
@@ -784,9 +784,11 @@ describe("The initialize switching", () => {
784
784
  expect("start-date" in borderSegment.daterange).toBe(true);
785
785
  expect("id" in borderSegment.daterange).toBe(true);
786
786
  expect(borderSegment.duration).toEqual(11.3447);
787
- expect(borderSegment.timelinePosition).toEqual(null);
787
+ expect(borderSegment.timelinePosition).not.toEqual(null);
788
788
  expect(borderSegment.cue).toEqual(null);
789
789
  expect(borderSegment.uri).toEqual("https://maitv-vod.lab.eyevinn.technology/BECKY_Trailer_2020.mp4/1000/1000-00000.ts");
790
+
791
+ newVodSegments["1313000"][7].timelinePosition = null; // disregard timeline position
790
792
  expect(newVodSegments["1313000"][7]).toEqual(expectedSecondVODSegItem);
791
793
  });
792
794