@srgssr/pillarbox-web 1.12.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.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/dist/pillarbox-core.cjs.js +307 -0
  3. package/dist/pillarbox-core.cjs.min.js +2 -0
  4. package/dist/pillarbox-core.cjs.min.js.map +1 -0
  5. package/dist/pillarbox-core.es.js +305 -0
  6. package/dist/pillarbox-core.es.min.js +2 -0
  7. package/dist/pillarbox-core.es.min.js.map +1 -0
  8. package/dist/pillarbox-core.umd.js +68327 -0
  9. package/dist/pillarbox-core.umd.min.js +35 -0
  10. package/dist/pillarbox-core.umd.min.js.map +1 -0
  11. package/dist/pillarbox.cjs.js +3571 -0
  12. package/dist/pillarbox.cjs.min.js +2 -0
  13. package/dist/pillarbox.cjs.min.js.map +1 -0
  14. package/dist/pillarbox.es.js +3569 -0
  15. package/dist/pillarbox.es.min.js +2 -0
  16. package/dist/pillarbox.es.min.js.map +1 -0
  17. package/dist/pillarbox.min.css +1 -0
  18. package/dist/pillarbox.min.css.map +1 -0
  19. package/dist/pillarbox.umd.js +71591 -0
  20. package/dist/pillarbox.umd.min.js +35 -0
  21. package/dist/pillarbox.umd.min.js.map +1 -0
  22. package/dist/types/build.es.d.ts +5 -0
  23. package/dist/types/build.es.d.ts.map +1 -0
  24. package/dist/types/src/analytics/SRGAnalytics.d.ts +414 -0
  25. package/dist/types/src/analytics/SRGAnalytics.d.ts.map +1 -0
  26. package/dist/types/src/components/player.d.ts +116 -0
  27. package/dist/types/src/components/player.d.ts.map +1 -0
  28. package/dist/types/src/components/typedef.d.ts +14 -0
  29. package/dist/types/src/components/typedef.d.ts.map +1 -0
  30. package/dist/types/src/dataProvider/model/MediaComposition.d.ts +154 -0
  31. package/dist/types/src/dataProvider/model/MediaComposition.d.ts.map +1 -0
  32. package/dist/types/src/dataProvider/model/typedef.d.ts +1485 -0
  33. package/dist/types/src/dataProvider/model/typedef.d.ts.map +1 -0
  34. package/dist/types/src/dataProvider/services/DataProvider.d.ts +40 -0
  35. package/dist/types/src/dataProvider/services/DataProvider.d.ts.map +1 -0
  36. package/dist/types/src/lang/de.d.ts +2 -0
  37. package/dist/types/src/lang/de.d.ts.map +1 -0
  38. package/dist/types/src/lang/en.d.ts +2 -0
  39. package/dist/types/src/lang/en.d.ts.map +1 -0
  40. package/dist/types/src/lang/fr.d.ts +2 -0
  41. package/dist/types/src/lang/fr.d.ts.map +1 -0
  42. package/dist/types/src/lang/it.d.ts +2 -0
  43. package/dist/types/src/lang/it.d.ts.map +1 -0
  44. package/dist/types/src/lang/rm.d.ts +2 -0
  45. package/dist/types/src/lang/rm.d.ts.map +1 -0
  46. package/dist/types/src/middleware/srgssr.d.ts +271 -0
  47. package/dist/types/src/middleware/srgssr.d.ts.map +1 -0
  48. package/dist/types/src/middleware/typedef.d.ts +67 -0
  49. package/dist/types/src/middleware/typedef.d.ts.map +1 -0
  50. package/dist/types/src/pillarbox.d.ts +11 -0
  51. package/dist/types/src/pillarbox.d.ts.map +1 -0
  52. package/dist/types/src/utils/AkamaiTokenService.d.ts +73 -0
  53. package/dist/types/src/utils/AkamaiTokenService.d.ts.map +1 -0
  54. package/dist/types/src/utils/Drm.d.ts +31 -0
  55. package/dist/types/src/utils/Drm.d.ts.map +1 -0
  56. package/dist/types/src/utils/Image.d.ts +33 -0
  57. package/dist/types/src/utils/Image.d.ts.map +1 -0
  58. package/dist/types/src/utils/PlayerEvents.d.ts +177 -0
  59. package/dist/types/src/utils/PlayerEvents.d.ts.map +1 -0
  60. package/dist/types/src/utils/typedef.d.ts +17 -0
  61. package/dist/types/src/utils/typedef.d.ts.map +1 -0
  62. package/package.json +109 -0
  63. package/scss/components/_big-play.scss +30 -0
  64. package/scss/components/_captions-settings.scss +71 -0
  65. package/scss/components/_control-bar.scss +15 -0
  66. package/scss/components/_control-spacer.scss +9 -0
  67. package/scss/components/_control.scss +8 -0
  68. package/scss/components/_error.scss +10 -0
  69. package/scss/components/_layout.scss +63 -0
  70. package/scss/components/_loading.scss +34 -0
  71. package/scss/components/_progress.scss +70 -0
  72. package/scss/components/_slider.scss +9 -0
  73. package/scss/components/_text-track.scss +8 -0
  74. package/scss/components/_time.scss +29 -0
  75. package/scss/components/_title-bar.scss +9 -0
  76. package/scss/components/_volume.scss +16 -0
  77. package/scss/components/menu/_menu-popup.scss +30 -0
  78. package/scss/components/menu/_menu.scss +26 -0
  79. package/scss/pillarbox.scss +26 -0
@@ -0,0 +1,3571 @@
1
+ 'use strict';
2
+
3
+ var videojs = require('video.js');
4
+ require('videojs-contrib-eme');
5
+
6
+ function ownKeys(e, r) {
7
+ var t = Object.keys(e);
8
+ if (Object.getOwnPropertySymbols) {
9
+ var o = Object.getOwnPropertySymbols(e);
10
+ r && (o = o.filter(function (r) {
11
+ return Object.getOwnPropertyDescriptor(e, r).enumerable;
12
+ })), t.push.apply(t, o);
13
+ }
14
+ return t;
15
+ }
16
+ function _objectSpread2(e) {
17
+ for (var r = 1; r < arguments.length; r++) {
18
+ var t = null != arguments[r] ? arguments[r] : {};
19
+ r % 2 ? ownKeys(Object(t), !0).forEach(function (r) {
20
+ _defineProperty(e, r, t[r]);
21
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
22
+ Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
23
+ });
24
+ }
25
+ return e;
26
+ }
27
+ function _toPrimitive(t, r) {
28
+ if ("object" != typeof t || !t) return t;
29
+ var e = t[Symbol.toPrimitive];
30
+ if (void 0 !== e) {
31
+ var i = e.call(t, r || "default");
32
+ if ("object" != typeof i) return i;
33
+ throw new TypeError("@@toPrimitive must return a primitive value.");
34
+ }
35
+ return ("string" === r ? String : Number)(t);
36
+ }
37
+ function _toPropertyKey(t) {
38
+ var i = _toPrimitive(t, "string");
39
+ return "symbol" == typeof i ? i : i + "";
40
+ }
41
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
42
+ try {
43
+ var info = gen[key](arg);
44
+ var value = info.value;
45
+ } catch (error) {
46
+ reject(error);
47
+ return;
48
+ }
49
+ if (info.done) {
50
+ resolve(value);
51
+ } else {
52
+ Promise.resolve(value).then(_next, _throw);
53
+ }
54
+ }
55
+ function _asyncToGenerator(fn) {
56
+ return function () {
57
+ var self = this,
58
+ args = arguments;
59
+ return new Promise(function (resolve, reject) {
60
+ var gen = fn.apply(self, args);
61
+ function _next(value) {
62
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
63
+ }
64
+ function _throw(err) {
65
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
66
+ }
67
+ _next(undefined);
68
+ });
69
+ };
70
+ }
71
+ function _defineProperty(obj, key, value) {
72
+ key = _toPropertyKey(key);
73
+ if (key in obj) {
74
+ Object.defineProperty(obj, key, {
75
+ value: value,
76
+ enumerable: true,
77
+ configurable: true,
78
+ writable: true
79
+ });
80
+ } else {
81
+ obj[key] = value;
82
+ }
83
+ return obj;
84
+ }
85
+ function _objectWithoutPropertiesLoose(source, excluded) {
86
+ if (source == null) return {};
87
+ var target = {};
88
+ var sourceKeys = Object.keys(source);
89
+ var key, i;
90
+ for (i = 0; i < sourceKeys.length; i++) {
91
+ key = sourceKeys[i];
92
+ if (excluded.indexOf(key) >= 0) continue;
93
+ target[key] = source[key];
94
+ }
95
+ return target;
96
+ }
97
+ function _objectWithoutProperties(source, excluded) {
98
+ if (source == null) return {};
99
+ var target = _objectWithoutPropertiesLoose(source, excluded);
100
+ var key, i;
101
+ if (Object.getOwnPropertySymbols) {
102
+ var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
103
+ for (i = 0; i < sourceSymbolKeys.length; i++) {
104
+ key = sourceSymbolKeys[i];
105
+ if (excluded.indexOf(key) >= 0) continue;
106
+ if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
107
+ target[key] = source[key];
108
+ }
109
+ }
110
+ return target;
111
+ }
112
+
113
+ var version = "1.12.1";
114
+
115
+ /**
116
+ * @ignore
117
+ * @type {typeof import('video.js/dist/types/player').default}
118
+ */
119
+ const vjsPlayer = videojs.getComponent('player');
120
+
121
+ /**
122
+ * This class extends the video.js Player.
123
+ *
124
+ * @class Player
125
+ * @see https://docs.videojs.com/player
126
+ */
127
+ class Player extends vjsPlayer {
128
+ constructor(tag, options, ready) {
129
+ /**
130
+ * Configuration for plugins.
131
+ *
132
+ * @see [Video.js Plugins Option]{@link https://videojs.com/guides/options/#plugins}
133
+ * @type {Object}
134
+ * @property {boolean} eme - Enable the EME (Encrypted Media Extensions) plugin.
135
+ */
136
+ options = videojs.obj.merge(options, {
137
+ plugins: {
138
+ eme: true
139
+ }
140
+ });
141
+ super(tag, options, ready);
142
+ }
143
+
144
+ /**
145
+ * A getter/setter for the media's audio track.
146
+ * Activates the audio track according to the language and kind properties.
147
+ * Falls back on the first audio track found if the kind property is not satisfied.
148
+ *
149
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/AudioTrack/kind
150
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/AudioTrack/language
151
+ *
152
+ * @param {import('./typedef').TrackSelector} [trackSelector]
153
+ *
154
+ * @example
155
+ * // Get the current audio track
156
+ * player.audioTrack();
157
+ *
158
+ * @example
159
+ * // Activate an audio track based on language and kind properties
160
+ * player.audioTrack({language:'en', kind:'description'});
161
+ *
162
+ * @example
163
+ * // Activate first audio track found corresponding to language
164
+ * player.audioTrack({language:'fr'});
165
+ *
166
+ * @return {import('video.js/dist/types/tracks/audio-track').default | undefined} The
167
+ * currently enabled audio track. See {@link https://docs.videojs.com/audiotrack}.
168
+ */
169
+ audioTrack(trackSelector) {
170
+ const audioTracks = Array.from(this.player().audioTracks());
171
+ if (!trackSelector) {
172
+ return audioTracks.find(audioTrack => audioTrack.enabled);
173
+ }
174
+ const {
175
+ kind,
176
+ language
177
+ } = trackSelector;
178
+ const audioTrack = audioTracks.find(audioTrack => audioTrack.language === language && audioTrack.kind === kind) || audioTracks.find(audioTrack => audioTrack.language === language);
179
+ if (audioTrack) {
180
+ audioTrack.enabled = true;
181
+ }
182
+ return audioTrack;
183
+ }
184
+
185
+ /**
186
+ * Calculates an array of ranges based on the `buffered()` data.
187
+ *
188
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/buffered
189
+ *
190
+ * @returns {Array<{start: number, end: number}>} An array of objects representing start and end points of buffered ranges.
191
+ */
192
+ bufferedRanges() {
193
+ const ranges = [];
194
+ for (let i = 0; i < this.buffered().length; i++) {
195
+ const start = this.buffered().start(i);
196
+ const end = this.buffered().end(i);
197
+ ranges.push({
198
+ start,
199
+ end
200
+ });
201
+ }
202
+ return ranges;
203
+ }
204
+
205
+ /**
206
+ * Get the percent (as a decimal) of the media that's been played.
207
+ * This method is not a part of the native HTML video API.
208
+ *
209
+ * Live streams with DVR are not currently supported.
210
+ *
211
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#htmlmediaelement.played
212
+ *
213
+ * @return {number}
214
+ * A decimal between 0 and 1 representing the percent
215
+ * that is played 0 being 0% and 1 being 100%
216
+ */
217
+ playedPercent() {
218
+ if (!Number.isFinite(this.duration())) return NaN;
219
+ let timePlayed = 0;
220
+ for (let i = 0; i != this.played().length; i++) {
221
+ timePlayed += this.played().end(i) - this.played().start(i);
222
+ }
223
+ const percentPlayed = timePlayed / this.duration();
224
+ return percentPlayed;
225
+ }
226
+
227
+ /**
228
+ * Get an array of ranges based on the `played` data.
229
+ *
230
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#htmlmediaelement.played
231
+ *
232
+ * @returns {Array<{start: number, end: number}>} An array of objects representing start and end points of played ranges.
233
+ */
234
+ playedRanges() {
235
+ const ranges = [];
236
+ for (let i = 0; i < this.played().length; i++) {
237
+ const start = this.played().start(i);
238
+ const end = this.played().end(i);
239
+ ranges.push({
240
+ start,
241
+ end
242
+ });
243
+ }
244
+ return ranges;
245
+ }
246
+
247
+ /**
248
+ * Calculates an array of ranges based on the `seekable()` data.
249
+ *
250
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seekable
251
+ *
252
+ * @returns {Array<{start: number, end: number}>} An array of objects representing start and end points of seekable ranges.
253
+ */
254
+ seekableRanges() {
255
+ const ranges = [];
256
+ for (let i = 0; i < this.seekable().length; i++) {
257
+ const start = this.seekable().start(i);
258
+ const end = this.seekable().end(i);
259
+ ranges.push({
260
+ start,
261
+ end
262
+ });
263
+ }
264
+ return ranges;
265
+ }
266
+
267
+ /**
268
+ * A getter/setter for the media's text track.
269
+ * Activates the text track according to the language and kind properties.
270
+ * Falls back on the first text track found if the kind property is not satisfied.
271
+ * Disables all subtitle tracks that are `showing` if the `trackSelector` is truthy but does not satisfy any condition.
272
+ *
273
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TextTrack/kind
274
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/textTrack/language
275
+ *
276
+ * @param {import('./typedef').TrackSelector} [trackSelector]
277
+ *
278
+ * @example
279
+ * // Get the current text track
280
+ * player.textTrack();
281
+ *
282
+ * @example
283
+ * // Disable all text tracks has a side effect
284
+ * player.textTrack('off');
285
+ * player.textTrack({});
286
+ *
287
+ * @example
288
+ * // Activate an text track based on language and kind properties
289
+ * player.textTrack({language:'en', kind:'captions'});
290
+ *
291
+ * @example
292
+ * // Activate first text track found corresponding to language
293
+ * player.textTrack({language:'fr'});
294
+ *
295
+ * @return {import('video.js/dist/types/tracks/text-track').default | undefined} The
296
+ * currently enabled text track. See {@link https://docs.videojs.com/texttrack}.
297
+ */
298
+ textTrack(trackSelector) {
299
+ const textTracks = Array.from(this.player().textTracks()).filter(textTrack => !['chapters', 'metadata'].includes(textTrack.kind));
300
+ if (!trackSelector) {
301
+ return textTracks.find(textTrack => textTrack.mode === 'showing');
302
+ }
303
+ textTracks.forEach(textTrack => textTrack.mode = 'disabled');
304
+ const {
305
+ kind,
306
+ language
307
+ } = trackSelector;
308
+ const textTrack = textTracks.find(textTrack => {
309
+ if (textTrack.language === language && textTrack.kind === kind) {
310
+ textTrack.mode = 'showing';
311
+ }
312
+ return textTrack.mode === 'showing';
313
+ }) || textTracks.find(textTrack => {
314
+ if (textTrack.language === language) {
315
+ textTrack.mode = 'showing';
316
+ }
317
+ return textTrack.mode === 'showing';
318
+ });
319
+ return textTrack;
320
+ }
321
+ }
322
+ videojs.registerComponent('player', Player);
323
+
324
+ /**
325
+ * Pillarbox is an alias for the video.js namespace with additional options.
326
+ *
327
+ * @namespace
328
+ * @see https://docs.videojs.com/module-videojs-videojs
329
+ * @type {videojs}
330
+ */
331
+ const pillarbox = videojs;
332
+ pillarbox.VERSION = {
333
+ pillarbox: version,
334
+ videojs: videojs.VERSION,
335
+ [videojs.VhsSourceHandler.name]: videojs.VhsSourceHandler.VERSION,
336
+ eme: videojs.getPlugin('eme').VERSION
337
+ };
338
+
339
+ /**
340
+ * Enable smooth seeking for Pillarbox.
341
+ *
342
+ * @see [Video.js enableSmoothSeeking Option]{@link https://videojs.com/guides/options/#enablesmoothseeking}
343
+ * @type {boolean}
344
+ * @default true
345
+ */
346
+ pillarbox.options.enableSmoothSeeking = true;
347
+ /**
348
+ * Enable fill mode for the video player, allowing it to expand to fill the container.
349
+ *
350
+ * @see [Video.js Fill Option]{@link https://videojs.com/guides/layout/#fill-mode}
351
+ * @type {boolean}
352
+ * @default true
353
+ */
354
+ pillarbox.options.fill = true;
355
+ /**
356
+ * Configuration options for HTML5 settings in Pillarbox.
357
+ *
358
+ * @see [VHS useForcedSubtitles Option]{@link https://github.com/videojs/http-streaming/blob/main/README.md#useforcedsubtitles}
359
+ * @type {Object}
360
+ * @property {Object} vhs - Configuration for the Video.js HTTP Streaming.
361
+ * @property {boolean} useForcedSubtitles - Enables the player to display forced subtitles by default.
362
+ * Forced subtitles are pieces of information intended for display when no other text representation
363
+ * is selected. They are used to clarify dialogue, provide alternate languages, display texted graphics,
364
+ * or present location/person IDs that are not otherwise covered in the dubbed/localized audio.
365
+ */
366
+ pillarbox.options.html5 = {
367
+ vhs: {
368
+ useForcedSubtitles: true
369
+ }
370
+ };
371
+ /**
372
+ * Configuration for the live tracker.
373
+ *
374
+ * @see [Video.js liveTracker Option]{@link https://videojs.com/guides/options/#livetrackertrackingthreshold}
375
+ * @type {Object}
376
+ * @property {number} trackingThreshold - A threshold that controls when the liveui should be shown.
377
+ * @property {number} liveTolerance - An option that controls how far from the seekable end should be considered live playback.
378
+ */
379
+ pillarbox.options.liveTracker = {
380
+ trackingThreshold: 120,
381
+ liveTolerance: 15
382
+ };
383
+ /**
384
+ * Allows the player to use the live ui that includes:
385
+ *
386
+ * - A progress bar for seeking within the live window
387
+ * - A button that can be clicked to seek to the live edge with a circle indicating if you are at the live edge or not.
388
+ *
389
+ * @see [Video.js liveui Option]{@link https://videojs.com/guides/options/#liveui}
390
+ * @type {boolean}
391
+ */
392
+ pillarbox.options.liveui = true;
393
+ /**
394
+ * Indicates that the video is to be played "inline", that is within the element's playback area.
395
+ *
396
+ * @see [Video element playsinline attribute]{@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#playsinline}
397
+ * @type {boolean}
398
+ */
399
+ pillarbox.options.playsinline = true;
400
+ /**
401
+ * Enable responsive mode, this will cause the player to customize itself based on responsive breakpoints.
402
+ *
403
+ * @see [Video.js Responsive Option]{@link https://videojs.com/guides/options/#responsive}
404
+ * @type {boolean}
405
+ */
406
+ pillarbox.options.responsive = true;
407
+ /**
408
+ * A placeholder for accessing trackers directly from the player.
409
+ *
410
+ * @type {Object}
411
+ */
412
+ pillarbox.options.trackers = {};
413
+
414
+ /**
415
+ * Represents a data provider for constructing URLs and handling requests.
416
+ * @class
417
+ * @ignore
418
+ */
419
+ class DataProvider {
420
+ /**
421
+ * Creates an instance of DataProvider.
422
+ *
423
+ * @param {string} [hostName='il.srgssr.ch'] The base host name for constructing URLs
424
+ */
425
+ constructor(hostName = 'il.srgssr.ch') {
426
+ this.setIlHost(hostName);
427
+ }
428
+
429
+ /**
430
+ * Sets the integration layer host name.
431
+ *
432
+ * @param {string} hostName The host name to set
433
+ */
434
+ setIlHost(hostName) {
435
+ this.baseUrl = `${hostName}/integrationlayer/2.1/`;
436
+ }
437
+
438
+ /**
439
+ * Handles requests by constructing URLs and fetching data.
440
+ *
441
+ * This provides unified error handling, regardless of the urlHandler used.
442
+ *
443
+ * @param {Function} urlHandler A function that constructs the URL
444
+ *
445
+ * @returns {Promise<import('../model/MediaComposition.js').default>} A promise with the fetched data
446
+ */
447
+ handleRequest(urlHandler) {
448
+ var _this = this;
449
+ return /*#__PURE__*/function () {
450
+ var _ref = _asyncToGenerator(function* (urn) {
451
+ const url = typeof urlHandler === 'function' ? urlHandler(urn) : _this.mediaCompositionUrlHandler(urn);
452
+ const response = yield fetch(url);
453
+ if (!response.ok) {
454
+ throw response;
455
+ }
456
+
457
+ /** @type {import('../model/MediaComposition.js').default} */
458
+ const data = yield response.json();
459
+ return data;
460
+ });
461
+ return function (_x) {
462
+ return _ref.apply(this, arguments);
463
+ };
464
+ }();
465
+ }
466
+
467
+ /**
468
+ * Gets the media composition URL by URN.
469
+ *
470
+ * @param {string} urn The URN for the media composition
471
+ *
472
+ * @returns {string} The constructed URL
473
+ */
474
+ mediaCompositionUrlHandler(urn) {
475
+ return `https://${this.baseUrl}mediaComposition/byUrn/${urn}?onlyChapters=true&vector=portalplay`;
476
+ }
477
+ }
478
+
479
+ const SCALE = {
480
+ WIDTH_240: '240',
481
+ WIDTH_320: '320',
482
+ WIDTH_480: '480',
483
+ WIDTH_960: '960',
484
+ WIDTH_1920: '1920'
485
+ };
486
+ const FORMAT = {
487
+ JPG: 'jpg',
488
+ WEBP: 'webp',
489
+ PNG: 'png'
490
+ };
491
+ const IMAGE_SERVICE_URL = 'https://il.srgssr.ch/images/';
492
+
493
+ /**
494
+ * @class Image
495
+ */
496
+ class Image {
497
+ /**
498
+ * Generates the image scaling URL.
499
+ *
500
+ * @property {Object} image is the object representation of an image.
501
+ * @property {String} [image.url] is the image URL.
502
+ * @property {String} [image.width=960] is the width of the image, default value 960.
503
+ * @property {String} [image.format=jpg] is the format of the image, default value jpg.
504
+ * @property {String} [imageServiceUrl] Url of the image service that needs to comply with the specification defined by the IL.
505
+ *
506
+ * @see https://confluence.srg.beecollaboration.com/pages/viewpage.action?spaceKey=SRGPLAY&title=Project+-+Image+Service
507
+ *
508
+ * @returns {String|undefined} the image scaling URL.
509
+ */
510
+ static scale({
511
+ url,
512
+ width = SCALE.WIDTH_960,
513
+ format = FORMAT.JPG
514
+ } = {}, imageServiceUrl = IMAGE_SERVICE_URL) {
515
+ if (!url) return;
516
+ const scaleUrl = new URL(imageServiceUrl);
517
+ scaleUrl.searchParams.set('imageUrl', url);
518
+ scaleUrl.searchParams.set('format', format);
519
+ scaleUrl.searchParams.set('width', width);
520
+ return decodeURIComponent(scaleUrl.href);
521
+ }
522
+ static get JPG() {
523
+ return FORMAT.JPG;
524
+ }
525
+ static get PNG() {
526
+ return FORMAT.PNG;
527
+ }
528
+ static get WEBP() {
529
+ return FORMAT.WEBP;
530
+ }
531
+ static get WIDTH_240() {
532
+ return SCALE.WIDTH_240;
533
+ }
534
+ static get WIDTH_320() {
535
+ return SCALE.WIDTH_320;
536
+ }
537
+ static get WIDTH_480() {
538
+ return SCALE.WIDTH_480;
539
+ }
540
+ static get WIDTH_960() {
541
+ return SCALE.WIDTH_960;
542
+ }
543
+ static get WIDTH_1920() {
544
+ return SCALE.WIDTH_1920;
545
+ }
546
+ }
547
+
548
+ const DRM_VENDORS = {
549
+ WIDEVINE: 'com.widevine.alpha',
550
+ FAIRPLAY: 'com.apple.fps.1_0',
551
+ PLAYREADY: 'com.microsoft.playready'
552
+ };
553
+
554
+ /**
555
+ * @class Drm
556
+ */
557
+ class Drm {
558
+ /**
559
+ * Build the keySystems object according to the DRM vendor.
560
+ *
561
+ * @param {Array.<import('../dataProvider/model/typedef').DrmMetadata>} drmList The DRM list from the media composition.
562
+ *
563
+ * @returns {import('./typedef').KeySystems} The resulting keySystems.
564
+ */
565
+ static buildKeySystems(drmList = []) {
566
+ const keySystems = {};
567
+ drmList.forEach(drmVendor => {
568
+ const type = Drm.vendors[drmVendor.type];
569
+ if (Drm.vendors.FAIRPLAY === type) {
570
+ const {
571
+ certificateUrl: certificateUri,
572
+ licenseUrl: licenseUri
573
+ } = drmVendor;
574
+ keySystems[type] = {
575
+ certificateUri,
576
+ licenseUri
577
+ };
578
+ } else {
579
+ keySystems[type] = drmVendor.licenseUrl;
580
+ }
581
+ });
582
+ return {
583
+ keySystems
584
+ };
585
+ }
586
+
587
+ /**
588
+ * Check if some of the resources have DRM.
589
+ *
590
+ * @param {Array.<import('../dataProvider/model/typedef').MainResource>} resources
591
+ *
592
+ * @returns {boolean} true if some of the resources have DRM, false otherwise.
593
+ */
594
+ static hasDrm(resources) {
595
+ return resources.some(({
596
+ drmList
597
+ }) => drmList && drmList.length > 0);
598
+ }
599
+
600
+ /**
601
+ * Get DRM vendors.
602
+ */
603
+ static get vendors() {
604
+ return DRM_VENDORS;
605
+ }
606
+ }
607
+
608
+ const TOKEN_TYPES = {
609
+ AKAMAI: 'AKAMAI',
610
+ NONE: 'NONE'
611
+ };
612
+
613
+ /**
614
+ * @class AkamaiTokenService
615
+ */
616
+ class AkamaiTokenService {
617
+ /**
618
+ * Get the acl path.
619
+ *
620
+ * @param {URL} streamUrl
621
+ *
622
+ * @returns {String}
623
+ */
624
+ static aclPath(streamUrl) {
625
+ const path = streamUrl.pathname;
626
+ return `${path.substring(0, path.lastIndexOf('/') + 1)}*`;
627
+ }
628
+
629
+ /**
630
+ * AKAMAI
631
+ *
632
+ * @type {String}
633
+ */
634
+ static get AKAMAI() {
635
+ return TOKEN_TYPES.AKAMAI;
636
+ }
637
+
638
+ /**
639
+ * Check if the resources are protected by an Akamai token.
640
+ * Keep in mind, as we are using the 'some' function,
641
+ * if the resources have at least one resource
642
+ * protected by a token it returns true!
643
+ *
644
+ * @param {Array.<import('../dataProvider/model/typedef').MainResource>} resources
645
+ *
646
+ * @returns {Boolean}
647
+ */
648
+ static hasToken(resources) {
649
+ return resources.some(resource => AkamaiTokenService.isAkamai(resource.tokenType));
650
+ }
651
+
652
+ /**
653
+ * Check if the token type is AKAMAI.
654
+ *
655
+ * @param {String} tokentype
656
+ *
657
+ * @returns {Boolean}
658
+ */
659
+ static isAkamai(tokentype) {
660
+ return TOKEN_TYPES.AKAMAI === tokentype;
661
+ }
662
+
663
+ /**
664
+ * Check if the token type is NONE.
665
+ *
666
+ * @param {String} tokentype
667
+ *
668
+ * @returns {Boolean}
669
+ */
670
+ static isNone(tokentype) {
671
+ return TOKEN_TYPES.NONE === tokentype;
672
+ }
673
+
674
+ /**
675
+ * NONE
676
+ *
677
+ * @type {String}
678
+ */
679
+ static get NONE() {
680
+ return TOKEN_TYPES.NONE;
681
+ }
682
+
683
+ /**
684
+ * Generate the stream URL with the akamai token.
685
+ *
686
+ * @param {import('../dataProvider/model/typedef').MainResource} source
687
+ * @param {String} tokenServerUrl
688
+ *
689
+ * @returns {Promise.<import('../dataProvider/model/typedef').MainResource>}
690
+ */
691
+ static tokenize(source, tokenServerUrl) {
692
+ const streamUrlToTokenize = new URL(`${source.url}`);
693
+ const acl = AkamaiTokenService.aclPath(streamUrlToTokenize);
694
+ const url = `${tokenServerUrl}${encodeURIComponent(acl)}`;
695
+ return fetch(url).then(response => {
696
+ if (response.ok) {
697
+ return response.json();
698
+ }
699
+ return Promise.reject({
700
+ status: response.status,
701
+ statusText: response.statusText
702
+ });
703
+ }).then(({
704
+ token: {
705
+ authparams
706
+ }
707
+ }) => {
708
+ const akamaiAuthParams = new URLSearchParams(authparams);
709
+ akamaiAuthParams.forEach((v, k) => streamUrlToTokenize.searchParams.set(k, v));
710
+ return Object.assign({}, source, {
711
+ url: streamUrlToTokenize.toString()
712
+ });
713
+ }).catch(reason => {
714
+ return Promise.reject(reason);
715
+ });
716
+ }
717
+
718
+ /**
719
+ * Generate a token for each source
720
+ *
721
+ * @template {import('../dataProvider/model/typedef').MainResource} T
722
+ * @param {Array.<T>} sources
723
+ * @param {String} tokenServerUrl
724
+ *
725
+ * @returns {Promise.<Array.<T>>}
726
+ */
727
+ static tokenizeSources(sources, tokenServerUrl = 'https://tp.srgssr.ch/akahd/token?acl=') {
728
+ const tokenizedSources = [];
729
+ sources.forEach(source => {
730
+ const tokenizedSource = AkamaiTokenService.tokenize(source, tokenServerUrl);
731
+ tokenizedSources.push(tokenizedSource);
732
+ });
733
+ return Promise.all(tokenizedSources).then(values => values).catch(reason => Promise.reject(reason));
734
+ }
735
+ }
736
+
737
+ /**
738
+ * Exhaustive list of player events.
739
+ *
740
+ * See below the documentation related to the media events
741
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement#events
742
+ *
743
+ * @namespace {Object} PlayerEvents
744
+ */
745
+
746
+
747
+ /**
748
+ * Triggered when the media element is emptied (e.g., reset as part of the seeking process).
749
+ *
750
+ * @event PlayerEvents#EMPTIED
751
+ * @type {string}
752
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/emptied_event
753
+ */
754
+ const EMPTIED = 'emptied';
755
+
756
+ /**
757
+ * Triggered when the media playback has ended.
758
+ *
759
+ * @event PlayerEvents#ENDED
760
+ * @type {string}
761
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ended_event
762
+ */
763
+ const ENDED = 'ended';
764
+
765
+ /**
766
+ * Triggered when the media data has been loaded.
767
+ *
768
+ * @event PlayerEvents#LOADED_DATA
769
+ * @type {string}
770
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadeddata_event
771
+ */
772
+ const LOADED_DATA = 'loadeddata';
773
+
774
+ /**
775
+ * Triggered when the browser starts looking for media data.
776
+ *
777
+ * @event PlayerEvents#LOAD_START
778
+ * @type {string}
779
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadstart_event
780
+ */
781
+ const LOAD_START = 'loadstart';
782
+
783
+ /**
784
+ * Triggered when the media playback is paused.
785
+ *
786
+ * @event PlayerEvents#PAUSE
787
+ * @type {string}
788
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/pause_event
789
+ */
790
+ const PAUSE = 'pause';
791
+
792
+ /**
793
+ * Triggered when the media playback is resumed or started.
794
+ *
795
+ * @event PlayerEvents#PLAY
796
+ * @type {string}
797
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play_event
798
+ */
799
+ const PLAY = 'play';
800
+
801
+ /**
802
+ * Triggered when the media playback is in progress.
803
+ *
804
+ * @event PlayerEvents#PLAYING
805
+ * @type {string}
806
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/playing_event
807
+ */
808
+ const PLAYING = 'playing';
809
+
810
+ /**
811
+ * Triggered when the playback rate changes.
812
+ *
813
+ * @event PlayerEvents#RATE_CHANGE
814
+ * @type {string}
815
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ratechange_event
816
+ */
817
+ const RATE_CHANGE = 'ratechange';
818
+
819
+ /**
820
+ * Triggered when a seek operation is in progress.
821
+ *
822
+ * @event PlayerEvents#SEEKING
823
+ * @type {string}
824
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seeking_event
825
+ */
826
+ const SEEKING = 'seeking';
827
+
828
+ /**
829
+ * Triggered when the current playback position is updated.
830
+ *
831
+ * @event PlayerEvents#TIME_UPDATE
832
+ * @type {string}
833
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/timeupdate_event
834
+ */
835
+ const TIME_UPDATE = 'timeupdate';
836
+
837
+ /**
838
+ * Triggered when the media playback is waiting for data.
839
+ *
840
+ * @event PlayerEvents#WAITING
841
+ * @type {string}
842
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/waiting_event
843
+ */
844
+ const WAITING = 'waiting';
845
+
846
+ /* eslint max-lines-per-function: ["error", 200] */
847
+ /* eslint max-statements: ["error", 20]*/
848
+ /* eslint complexity: ["error", 10]*/
849
+ /**
850
+ * SRG analytics
851
+ * @class SRGAnalytics
852
+ * @ignore
853
+ *
854
+ * ### Script URL
855
+ * JS script : https://colibri-js.akamaized.net/penguin/tc_SRGGD_11.js
856
+ *
857
+ * ### Official documentation
858
+ * Variables list
859
+ * @see https://confluence.srg.beecollaboration.com/display/INTFORSCHUNG/Datalayer+for+media+players
860
+ *
861
+ * Standard event sequences
862
+ * @see https://confluence.srg.beecollaboration.com/display/INTFORSCHUNG/standard+streaming+events%3A+sequence+of+events+for+media+player+actions
863
+ *
864
+ * Review of Standard Media Actions
865
+ * @see https://confluence.srg.beecollaboration.com/display/INTFORSCHUNG/Implementation+Concept+-+draft
866
+ *
867
+ * ComScore Implementation Guide
868
+ * @see https://www.dropbox.com/sh/cdwuikq0abxi21m/AABmSyXYKUTWSAwRZgQA9Ujna/JavaScript%20Latest%20Version?dl=0&preview=Comscore_Library-JavaScript-Streaming_Tag-Implementation_Guide-International.pdf&subfolder_nav_tracking=1
869
+ *
870
+ * ### Variables list
871
+ * - 'event_id', // init | play | stop | pos | pause | seek | uptime | eof
872
+ * - 'event_timestamp', // Seems to be generated automatically from the documentation, but the TP overrides it
873
+ * - 'event_name', // NA TP seems to not sending this variable
874
+ * - 'event_source', // NA TP seems to not sending this variable
875
+ * - 'event_name', // NA TP seems to not sending this variable
876
+ * - 'event_value', // NA TP seems to not sending this variable
877
+ * - 'navigation_environment', // prod | preprod
878
+ * - 'media_subtitles_on', // string true | false
879
+ * - 'media_timeshift', // need better description
880
+ * - 'media_quality', // SD | HD ?
881
+ * - 'media_bandwidth', // NA for the web, 64000
882
+ * - 'media_volume', // from 0 to 100
883
+ * - 'media_embedding_url', //
884
+ * - 'media_player_name', // videojs | letterbox-web ?
885
+ * - 'media_chromecast_selected', // boolean true | false
886
+ * - 'media_player_version', // player's version
887
+ * - 'media_player_display', // is the player mode, on the TP : inline, embed etc..
888
+ * - 'media_audio_track', // NA
889
+ * - 'media_position_real', // NA
890
+ * - 'media_time_spent', // NA
891
+ * - 'device_id', // NA
892
+ * - 'user_id_log_in', // NA only RTS has log in today
893
+ * - 'media_thumbnail', // Not required by the spec but sended by the TP
894
+ * - 'media_bu_distributer', // Not required by the spec but sended by the TP
895
+ *
896
+ *
897
+ * ### Sequence stories
898
+ *
899
+ * __Story 1 (AoD/VOD-basics)__: A VoD is played. The user does not interact with the player. The VoD plays to its end.
900
+ *
901
+ * Hints:
902
+ * - Media sessions always start with PLAY. They end with STOP or EOF (or with PAUSE or last POS)
903
+ * - POS is sent ever 30s
904
+ *
905
+ *
906
+ * __Story 2 (livestream-basics A)__: A Livestream is played. The user does not interact with the player. After 61 seconds, playback is paused.
907
+ *
908
+ * Hints:
909
+ * - Media sessions always start with PLAY. They end with STOP (or, worse for data quailty, with PAUSE or last POS/UPTIME)
910
+ * - UPTIME is sent only for livestreams
911
+ * - POS is sent ever 30s, UPTIME every 60s with inital UPTIME after 30s.
912
+ * - This is the interval: 30s: POS + UPTIME; 60s: POS; 90s: POS + UPTIME; ...
913
+ *
914
+ *
915
+ * __Story 3 (Seeking a VoD/AoD)__: A VoD is played. User seeks in the VoD/AoD.
916
+ *
917
+ * Hints:
918
+ * - Once the Media Player slider is released (seek is over), another action to finish up the seeking is initiated. Typically this is PLAY. For that second PLAY, the media position has altered.
919
+ *
920
+ *
921
+ * __Story 4 (Seeking a livestream)__: A Livestream is played. User goes back in the livestream.
922
+ *
923
+ * Hints:
924
+ * - Once the Media Player slider is released (seek is over), another action to finish up the seeking is initiated. Typically this is PLAY. For that second PLAY, the a new variable, media_timeshift is passed.
925
+ * - For livestreams media_position is always the "time passed on your watch" - regardless of the SEEK event. So, if 1 second after PLAY the slider is moved back 600 seconds, then:
926
+ * 1. The the value of media_timeshift is '600'.
927
+ * 2. The value of media_position is '1'.
928
+ */
929
+ class SRGAnalytics {
930
+ constructor(player, {
931
+ debug = false,
932
+ environment = 'prod',
933
+ playerVersion = 'none',
934
+ tagCommanderScriptURL = '//colibri-js.akamaized.net/penguin/tc_SRGGD_11.js'
935
+ } = {}) {
936
+ this.isDebugEnabled = debug;
937
+ this.elapsedPlaybackTime = 0;
938
+ this.environment = environment;
939
+ this.hasStarted = false;
940
+ this.heartBeatIntervalId = undefined;
941
+ /* Set to true when 'init' event is sent or queued. */
942
+ this.initialized = false;
943
+ this.isSeeking = false;
944
+ this.isWaiting = false;
945
+ this.mediaSession = 0;
946
+ this.pendingQueue = [];
947
+ this.pendingTagCommanderReload = false;
948
+ this.player = player;
949
+ this.playerVersion = playerVersion;
950
+ this.srcMediaData = undefined;
951
+ this.startPlaybackSession = 0;
952
+ this.tagCommanderScriptURL = tagCommanderScriptURL;
953
+ this.trackedCurrentTime = 0;
954
+ this.uptimeIntervalId = undefined;
955
+ this.initScript();
956
+ this.initListeners();
957
+ }
958
+
959
+ /**
960
+ * Sent when the window, the document and its resources are about to be unloaded.
961
+ *
962
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
963
+ */
964
+ beforeunload() {
965
+ this.notify('stop');
966
+ }
967
+
968
+ /**
969
+ * Clear timers used to send uptime and heartbeat.
970
+ */
971
+ clearTimers() {
972
+ clearInterval(this.heartBeatIntervalId);
973
+ clearInterval(this.uptimeIntervalId);
974
+ clearTimeout(this.uptimeTimeoutId);
975
+ }
976
+
977
+ /**
978
+ * Get the tracked current time in seconds.
979
+ *
980
+ * @returns {Number} current time in seconds
981
+ */
982
+ currentTime() {
983
+ // see PLAYRTS-2771
984
+ return Math.round(this.trackedCurrentTime);
985
+ }
986
+
987
+ /**
988
+ * Get or set debug mode.
989
+ *
990
+ * @returns {Boolean|undefined}
991
+ */
992
+ debug(enabled) {
993
+ if (enabled === undefined) {
994
+ return this.isDebugEnabled || this.player.debug();
995
+ }
996
+ this.isDebugEnabled = Boolean(enabled);
997
+ }
998
+
999
+ /**
1000
+ * Destroy all properties and setIntervals to avoid mixing media sessions.
1001
+ */
1002
+ destroy() {
1003
+ this.clearTimers();
1004
+ if (!window.tc_vars) {
1005
+ window.tc_vars = {};
1006
+ }
1007
+ this.elapsedPlaybackTime = 0;
1008
+ this.hasStarted = false;
1009
+ this.heartBeatIntervalId = undefined;
1010
+ this.initialized = false;
1011
+ this.isWaiting = false;
1012
+ this.mediaSession = 0;
1013
+ this.pendingQueue = [];
1014
+ this.srcMediaData = undefined;
1015
+ this.startPlaybackSession = 0;
1016
+ this.trackedCurrentTime = 0;
1017
+ this.uptimeIntervalId = undefined;
1018
+ }
1019
+
1020
+ /**
1021
+ * Dispose all listeners used to send analytics data to TagCommander.
1022
+ *
1023
+ * Calls `beforeunload` to send a notify stop.
1024
+ * Clear intervals and timeouts.
1025
+ *
1026
+ * __Used events__
1027
+ * - beforeunload
1028
+ * - emptied
1029
+ * - ended
1030
+ * - loadstart
1031
+ * - loadeddata
1032
+ * - play
1033
+ * - pause
1034
+ * - timeupdate
1035
+ */
1036
+ dispose() {
1037
+ this.beforeunload();
1038
+ this.clearTimers();
1039
+ window.removeEventListener('beforeunload', this.beforeunloadListener);
1040
+ this.player.off(EMPTIED, this.emptiedListener);
1041
+ this.player.off(ENDED, this.endedListener);
1042
+ this.player.off(LOAD_START, this.loadstartListener);
1043
+ this.player.off(LOADED_DATA, this.loadeddataListener);
1044
+ this.player.off(PLAYING, this.playListener);
1045
+ this.player.off(PAUSE, this.pauseListener);
1046
+ this.player.off(RATE_CHANGE, this.rateChangeListener);
1047
+ this.player.off(SEEKING, this.seekingListener);
1048
+ this.player.off(TIME_UPDATE, this.timeUpdateListener);
1049
+ this.player.off(WAITING, this.waitingListener);
1050
+ }
1051
+
1052
+ /**
1053
+ * Sent before a new media is loading.
1054
+ * - Destroy all properties.
1055
+ * - Send a notify stop if the media is not ended and new media is about to be loaded.
1056
+ */
1057
+ emptied() {
1058
+ if (!this.player.ended()) {
1059
+ this.notify('stop');
1060
+ }
1061
+ }
1062
+
1063
+ /**
1064
+ * Sent when playback completes.
1065
+ *
1066
+ * @see https://docs.videojs.com/player#event:ended
1067
+ */
1068
+ ended() {
1069
+ this.notify('eof');
1070
+ this.mediaSession = 0;
1071
+ this.clearTimers();
1072
+ }
1073
+
1074
+ /**
1075
+ * Flush the queued events when tc event script is loaded.
1076
+ */
1077
+ flush() {
1078
+ if (this.isTrackerDisabled()) return;
1079
+ if (this.pendingTagCommanderReload && window.tC) {
1080
+ window.tC.container.reload();
1081
+ this.pendingTagCommanderReload = false;
1082
+ }
1083
+ if (window.tc_events_11 && this.pendingQueue.length > 0) {
1084
+ this.pendingQueue.forEach(notification => {
1085
+ window.tc_events_11(this.player.el(), notification.action, notification.labels);
1086
+ });
1087
+ this.pendingQueue = [];
1088
+ }
1089
+ }
1090
+
1091
+ /**
1092
+ * Get the language of the current audio track.
1093
+ *
1094
+ * @returns {String} empty string or uppercase language.
1095
+ */
1096
+ getCurrentAudioTrack() {
1097
+ const currentTrack = Array.from(this.player.audioTracks()).find(track => track.enabled);
1098
+ let language = 'und';
1099
+ if (currentTrack && !!currentTrack.language) {
1100
+ // eslint-disable-next-line prefer-destructuring
1101
+ language = currentTrack.language;
1102
+ }
1103
+ return currentTrack ? language.toUpperCase() : '';
1104
+ }
1105
+
1106
+ /**
1107
+ * Get the language of the current text track.
1108
+ *
1109
+ * @returns {String} empty string or uppercase language.
1110
+ */
1111
+ getCurrentTextTrack() {
1112
+ const currentTrack = this.player.textTrack();
1113
+ let language = 'und';
1114
+ if (currentTrack && !!currentTrack.language) {
1115
+ // eslint-disable-next-line prefer-destructuring
1116
+ language = currentTrack.language;
1117
+ }
1118
+ return currentTrack ? language.toUpperCase() : '';
1119
+ }
1120
+
1121
+ /**
1122
+ * Get the position inside the dvr window where the 0 represents the live edge
1123
+ *
1124
+ * @return {Number} 0 or the position in milliseconds
1125
+ */
1126
+ getDvrWindowPosition() {
1127
+ const {
1128
+ liveTracker
1129
+ } = this.player;
1130
+ const ct = this.currentTime() - liveTracker.seekableStart() | 0;
1131
+ const position = liveTracker.liveWindow() - ct;
1132
+ return position < 0 || position === Infinity ? 0 : position * 1000;
1133
+ }
1134
+
1135
+ /**
1136
+ * Get the size of the live DVR window in milliseconds.
1137
+ *
1138
+ * @return {Number} DVR window size in milliseconds
1139
+ */
1140
+ getDvrWindowSize() {
1141
+ const isInfinity = this.player.liveTracker.liveWindow() === Infinity;
1142
+ const windowSize = this.player.liveTracker.liveWindow() * 1000;
1143
+ return isInfinity ? 0 : windowSize;
1144
+ }
1145
+
1146
+ /**
1147
+ * Get the elapsed playback time in seconds.
1148
+ *
1149
+ * @returns {Number} elapsed time in seconds
1150
+ */
1151
+ getElapsedPlaybackTime() {
1152
+ if (this.startPlaybackSession) {
1153
+ return this.getElapsedPlayingTime();
1154
+ }
1155
+ return this.elapsedPlaybackTime;
1156
+ }
1157
+
1158
+ /**
1159
+ * Get the elapsed playing time in seconds.
1160
+ *
1161
+ * @returns {Number} elapsed time in seconds
1162
+ */
1163
+ getElapsedPlayingTime() {
1164
+ const playingSession = SRGAnalytics.now() - this.startPlaybackSession | 0;
1165
+ return this.elapsedPlaybackTime + playingSession;
1166
+ }
1167
+
1168
+ /**
1169
+ * Set all event labels to be sent to TagCommander. The event labels are updated whenever a new event occurs.
1170
+ *
1171
+ * @param {String} eventName init | play | stop | pos | pause | seek | uptime | eof
1172
+ *
1173
+ * @returns {Object} JSON to be sent to TagCommander
1174
+ */
1175
+ getEventLabels(eventName) {
1176
+ const labels = {
1177
+ event_id: eventName,
1178
+ event_timestamp: SRGAnalytics.now(),
1179
+ media_dvr_window_length: 0,
1180
+ media_dvr_window_offset: 0,
1181
+ media_is_dvr: false,
1182
+ media_is_live: false,
1183
+ media_mute: this.player.muted() ? '1' : '0',
1184
+ media_playback_rate: this.player.playbackRate(),
1185
+ media_position: this.currentTime(),
1186
+ media_quality: this.srcMediaData.mediaData.quality,
1187
+ // TODO use media_is_dvr, media_is_live to define peach media_stream_type
1188
+ media_subtitles_on: this.isTextTrackEnabled(),
1189
+ media_volume: (this.player.volume() * 100).toFixed(0),
1190
+ navigation_environment: this.environment
1191
+ };
1192
+ if (this.isAudioTrackEnabled()) {
1193
+ labels.media_audio_track = this.getCurrentAudioTrack();
1194
+ }
1195
+ if (this.isTextTrackEnabled()) {
1196
+ labels.media_subtitle_selection = this.getCurrentTextTrack();
1197
+ }
1198
+
1199
+ // DVR or Live related labels
1200
+ if (!this.isMediaOnDemand()) {
1201
+ labels.media_is_live = true;
1202
+ labels.media_position = this.getElapsedPlaybackTime();
1203
+ }
1204
+
1205
+ // DVR related labels
1206
+ if (this.isMediaDvr()) {
1207
+ labels.media_dvr_window_offset = this.getDvrWindowPosition() | 0;
1208
+ labels.media_dvr_window_length = this.getDvrWindowSize() | 0;
1209
+ labels.media_is_dvr = true;
1210
+ labels.media_timeshift = [PLAY, PAUSE].includes(eventName) ? this.timeShifted() : 0;
1211
+ }
1212
+ return labels;
1213
+ }
1214
+
1215
+ /**
1216
+ * Set all internal labels to be sent to TagCommander. Internal labels are assigned once at initialisation time.
1217
+ */
1218
+ getInternalLabels() {
1219
+ const data = {
1220
+ media_bu_distributer: this.srcMediaData.mediaData.vendor,
1221
+ media_chromecast_selected: Boolean(this.player.tech(true).isCasting),
1222
+ media_embedding_url: document.referrer,
1223
+ media_player_display: 'default',
1224
+ // TODO implement if it still relevant
1225
+ media_player_name: 'pillarbox-web',
1226
+ // TODO add a property playerName in the constructor with a default value ?
1227
+ media_player_version: this.playerVersion,
1228
+ media_url: this.srcMediaData.src
1229
+ };
1230
+ const analyticsMetadata = this.srcMediaData.mediaData.analyticsMetadata || {};
1231
+ window.tc_vars = Object.assign({}, window.tc_vars, data, analyticsMetadata);
1232
+ }
1233
+
1234
+ /**
1235
+ * Heart beat, current position of a AoD/VoD (every 30s).
1236
+ *
1237
+ * @description The action pos should be sent regularly every 30 seconds.
1238
+ * It is used for tracking the viewed chapters of a video and the last position of the video, in case the user ends the video by closing the browser tab/window.
1239
+ *
1240
+ * - pos should be sent when the media player is in "play mode".
1241
+ * - once the video is paused or stopped, the timer for sending these actions must be stopped.
1242
+ *
1243
+ * @see https://confluence.srg.beecollaboration.com/display/INTFORSCHUNG/standard+streaming+events%3A+sequence+of+events+for+media+player+actions#standardstreamingevents:sequenceofeventsformediaplayeractions-mandatoryplayerevents
1244
+ */
1245
+ heartBeat() {
1246
+ this.heartBeatIntervalId = setInterval(() => {
1247
+ // Send only when playing
1248
+ if (!this.player.paused()) {
1249
+ this.notify('pos');
1250
+ }
1251
+ }, 30000);
1252
+ }
1253
+
1254
+ /**
1255
+ * Initialize callbacks used to send analytics data to TagCommander.
1256
+ *
1257
+ * __Used events__
1258
+ * - beforeunload
1259
+ * - emptied
1260
+ * - ended
1261
+ * - loadstart
1262
+ * - loadeddata
1263
+ * - play
1264
+ * - pause
1265
+ * - ratechange
1266
+ * - seeking
1267
+ * - timeupdate
1268
+ * - waiting
1269
+ */
1270
+ initCallbacks() {
1271
+ this.beforeunloadListener = this.beforeunload.bind(this);
1272
+ this.emptiedListener = this.emptied.bind(this);
1273
+ this.endedListener = this.ended.bind(this);
1274
+ this.loadstartListener = this.loadstart.bind(this);
1275
+ this.loadeddataListener = this.loadeddata.bind(this);
1276
+ this.playListener = this.play.bind(this);
1277
+ this.pauseListener = this.pause.bind(this);
1278
+ this.rateChangeListener = this.rateChange.bind(this);
1279
+ this.seekingListener = this.seeking.bind(this);
1280
+ this.timeUpdateListener = this.timeUpdate.bind(this);
1281
+ this.waitingListener = this.waiting.bind(this);
1282
+ }
1283
+
1284
+ /**
1285
+ * Initialize all listeners used to send analytics data to TagCommander.
1286
+ *
1287
+ * __Used events__
1288
+ * - beforeunload
1289
+ * - dispose
1290
+ * - emptied
1291
+ * - ended
1292
+ * - loadstart
1293
+ * - loadeddata
1294
+ * - play
1295
+ * - pause
1296
+ * - timeupdate
1297
+ * - waiting
1298
+ */
1299
+ initListeners() {
1300
+ this.initCallbacks();
1301
+ window.addEventListener('beforeunload', this.beforeunloadListener);
1302
+ this.player.on(EMPTIED, this.emptiedListener);
1303
+ this.player.on(ENDED, this.endedListener);
1304
+ this.player.on(LOAD_START, this.loadstartListener);
1305
+ this.player.on(LOADED_DATA, this.loadeddataListener);
1306
+ this.player.on(PLAYING, this.playListener);
1307
+ this.player.on(PAUSE, this.pauseListener);
1308
+ this.player.on(RATE_CHANGE, this.rateChangeListener);
1309
+ this.player.on(SEEKING, this.seekingListener);
1310
+ this.player.on(TIME_UPDATE, this.timeUpdateListener);
1311
+ this.player.on(WAITING, this.waitingListener);
1312
+ this.player.one('dispose', this.dispose.bind(this));
1313
+ }
1314
+
1315
+ /**
1316
+ * Initialize TagCommander script dynamically and add it to the DOM
1317
+ */
1318
+ initScript() {
1319
+ const scriptId = 'tc_script__11';
1320
+ if (!document.querySelector(`#${scriptId}`)) {
1321
+ const script = document.createElement('script');
1322
+ const src = this.tagCommanderScriptURL;
1323
+ script.defer = true;
1324
+ script.id = scriptId;
1325
+ script.src = src;
1326
+ script.type = 'text/javascript';
1327
+ script.onload = _e => {
1328
+ this.flush();
1329
+ };
1330
+ document.body.appendChild(script);
1331
+ }
1332
+ }
1333
+
1334
+ /**
1335
+ * Check if the audio track is enabled.
1336
+ *
1337
+ * @returns {Boolean} __true__ if enabled __false__ otherwise.
1338
+ */
1339
+ isAudioTrackEnabled() {
1340
+ return !!this.getCurrentAudioTrack();
1341
+ }
1342
+
1343
+ /**
1344
+ * Check if the media is a live with DVR.
1345
+ *
1346
+ * @returns {Boolean} __true__ if it DVR __false__ otherwise.
1347
+ */
1348
+ isMediaDvr() {
1349
+ const {
1350
+ trackingThreshold
1351
+ } = this.player.liveTracker.options();
1352
+ return !this.isMediaOnDemand() && trackingThreshold < this.player.liveTracker.liveWindow();
1353
+ }
1354
+
1355
+ /**
1356
+ * Check if the media is a live.
1357
+ *
1358
+ * @returns {Boolean} __true__ if live __false__ otherwise.
1359
+ */
1360
+ isMediaLive() {
1361
+ const {
1362
+ trackingThreshold
1363
+ } = this.player.liveTracker.options();
1364
+ return !this.isMediaOnDemand() && trackingThreshold > this.player.liveTracker.liveWindow();
1365
+ }
1366
+
1367
+ /**
1368
+ * Check if the media is an on demand.
1369
+ *
1370
+ * @returns {Boolean} __true__ if on demand __false__ otherwise.
1371
+ */
1372
+ isMediaOnDemand() {
1373
+ return Number.isFinite(this.player.duration());
1374
+ }
1375
+
1376
+ /**
1377
+ * Check if the text track is enabled.
1378
+ *
1379
+ * @returns {Boolean} __true__ if enabled __false__ otherwise.
1380
+ */
1381
+ isTextTrackEnabled() {
1382
+ return !!this.getCurrentTextTrack();
1383
+ }
1384
+
1385
+ /**
1386
+ * Check if the tracker is disabled.
1387
+ *
1388
+ * @returns {Boolean} __true__ if disabled __false__ otherwise.
1389
+ */
1390
+ isTrackerDisabled() {
1391
+ if (!this.srcMediaData || !this.srcMediaData.mediaData) return true;
1392
+ if (!Array.isArray(this.srcMediaData.disableTrackers)) {
1393
+ return Boolean(this.srcMediaData.disableTrackers);
1394
+ }
1395
+ return Boolean(this.srcMediaData.disableTrackers.find(tracker => tracker.toLowerCase() === SRGAnalytics.name.toLowerCase()));
1396
+ }
1397
+
1398
+ /**
1399
+ * Sent when loading of the media begins.
1400
+ *
1401
+ * @see https://docs.videojs.com/player#event:loadstart
1402
+ */
1403
+ loadstart() {
1404
+ this.destroy();
1405
+ this.updateSrcMediaData(this.player.currentSource());
1406
+ if (this.isTrackerDisabled()) return;
1407
+ this.getInternalLabels();
1408
+ // Set ComScore labels
1409
+ this.reloadTagCommanderContainer();
1410
+ this.notify('buffer_start');
1411
+ this.hasStarted = false;
1412
+ }
1413
+
1414
+ /**
1415
+ * The first frame of the media has finished loading.
1416
+ *
1417
+ * @see https://docs.videojs.com/player#event:loadeddata
1418
+ */
1419
+ loadeddata() {
1420
+ this.notify('init');
1421
+ this.initialized = true;
1422
+ this.notify('buffer_stop');
1423
+ }
1424
+
1425
+ /**
1426
+ * Event logger that prints the current event, event labels and internal labels in the browser's console.
1427
+ *
1428
+ * @param {String} eventName init | play | stop | pos | pause | seek | uptime | eof
1429
+ * @param {Object} eventMetadata event metadata object
1430
+ * @param {String} severity log | warn | error
1431
+ */
1432
+ log(eventName, eventMetadata, severity = 'log') {
1433
+ if (this.debug()) {
1434
+ // eslint-disable-next-line
1435
+ console[severity](`SRGAnalytics:${eventName}`, eventMetadata, window.tc_vars);
1436
+ }
1437
+ }
1438
+
1439
+ /**
1440
+ * Notify TagCommander all event and internal labels. If tc script is not available it queues all pending events.
1441
+ *
1442
+ * @param {String} eventName init | play | stop | pos | pause | seek | uptime | eof
1443
+ */
1444
+ notify(eventName, eventMetadata) {
1445
+ if (this.isTrackerDisabled()) return;
1446
+ try {
1447
+ this.flush();
1448
+ } catch (error) {
1449
+ this.log(eventName, error, 'error');
1450
+ }
1451
+ const labels = Object.assign({}, this.getEventLabels(eventName), eventMetadata);
1452
+ this.log(eventName, labels);
1453
+ try {
1454
+ if (window.tc_events_11) {
1455
+ window.tc_events_11(this.player.el(), eventName, labels);
1456
+ } else {
1457
+ this.pendingQueue.push({
1458
+ action: eventName,
1459
+ labels
1460
+ });
1461
+ }
1462
+ } catch (error) {
1463
+ this.log(eventName, error, 'error');
1464
+ }
1465
+ }
1466
+
1467
+ /**
1468
+ * Return the current timestamp in seconds.
1469
+ *
1470
+ * @returns {Number} Timestamp in seconds
1471
+ */
1472
+ static now() {
1473
+ return (Date.now() / 1000).toFixed(0);
1474
+ }
1475
+
1476
+ /**
1477
+ * Sent when the playback state is no longer paused, as a result of the play method, or the autoplay attribute.
1478
+ *
1479
+ * @see https://docs.videojs.com/player#event:play
1480
+ */
1481
+ play() {
1482
+ if (!this.hasStarted) this.hasStarted = true;
1483
+ if (!this.startPlaybackSession && !this.isMediaOnDemand()) {
1484
+ this.startPlaybackSession = SRGAnalytics.now();
1485
+ }
1486
+ if (this.mediaSession === 0) {
1487
+ this.mediaSession = SRGAnalytics.now();
1488
+ this.heartBeat();
1489
+ this.uptime();
1490
+ }
1491
+ this.timeUpdate();
1492
+ this.notify('play');
1493
+ if (this.isSeeking) this.isSeeking = false;
1494
+ }
1495
+
1496
+ /**
1497
+ * Sent when the playback state is changed to paused (paused property is true).
1498
+ * Pause event is sent if :
1499
+ * - The player is not scrubbing
1500
+ * - The stream is not a live only
1501
+ * - The current time is strictly inferior to the duration
1502
+ *
1503
+ * @see https://docs.videojs.com/player#event:pause
1504
+ */
1505
+ pause() {
1506
+ if (!this.isMediaOnDemand()) {
1507
+ this.elapsedPlaybackTime = this.getElapsedPlayingTime();
1508
+ this.startPlaybackSession = 0;
1509
+ }
1510
+ if (!this.player.seeking() && !this.isMediaLive() && this.player.currentTime() < this.player.duration()) {
1511
+ this.notify('pause');
1512
+ return;
1513
+ }
1514
+ if (this.hasStarted && !this.isSeeking) {
1515
+ this.notify('seek');
1516
+ this.isSeeking = true;
1517
+ }
1518
+ }
1519
+
1520
+ /**
1521
+ * Sent to ComScore when the playback rate changes.
1522
+ *
1523
+ * @see https://github.com/SRGSSR/srgletterbox-web/issues/761
1524
+ * @see https://jira.srg.beecollaboration.com/browse/ADI-256
1525
+ */
1526
+ rateChange() {
1527
+ this.notify('change_playback_rate');
1528
+ }
1529
+
1530
+ /**
1531
+ * Reload the tagCommander container and set all ComScore labels
1532
+ */
1533
+ reloadTagCommanderContainer() {
1534
+ if (window.tC) {
1535
+ window.tC.container.reload();
1536
+ this.pendingTagCommanderReload = false;
1537
+ } else {
1538
+ this.pendingTagCommanderReload = true;
1539
+ }
1540
+ }
1541
+
1542
+ /**
1543
+ * Sent when the current time is modified by the player's currentTime API.
1544
+ *
1545
+ * @see https://docs.videojs.com/player#event:seeking
1546
+ */
1547
+ seeking() {
1548
+ if (this.hasStarted && !this.player.paused() && !this.isSeeking) {
1549
+ this.notify('seek');
1550
+ this.isSeeking = true;
1551
+ }
1552
+ }
1553
+
1554
+ /**
1555
+ * Track current time updates delayed by a tick.
1556
+ *
1557
+ * @see https://docs.videojs.com/player#event:timeupdate
1558
+ */
1559
+ timeUpdate() {
1560
+ if (!this.player.paused()) {
1561
+ this.trackedCurrentTime = this.player.currentTime();
1562
+ }
1563
+ }
1564
+
1565
+ /**
1566
+ * Gets the number of seconds that separate from the live edge that is represented by 0.
1567
+ *
1568
+ * @returns {String}
1569
+ */
1570
+ timeShifted() {
1571
+ const isAtLiveEdge = this.player.liveTracker.atLiveEdge();
1572
+ const liveCurrentTime = this.player.liveTracker.liveCurrentTime();
1573
+ const currentTime = this.player.currentTime();
1574
+ const timeShifted = isAtLiveEdge ? 0 : (liveCurrentTime - currentTime).toFixed(0);
1575
+ return timeShifted;
1576
+ }
1577
+
1578
+ /**
1579
+ * Update the src media data.
1580
+ */
1581
+ updateSrcMediaData(srcMediaData) {
1582
+ this.srcMediaData = srcMediaData;
1583
+ }
1584
+
1585
+ /**
1586
+ * Calculate the uptime when playing a live stream with or without DVR
1587
+ *
1588
+ * __Rules__:
1589
+ * - Send the first uptime after 30 seconds
1590
+ * - Send uptime each 60 seconds after the 30 seconds
1591
+ * - Uptime is sent only when playing
1592
+ */
1593
+ uptime() {
1594
+ const notifyUptime = () => {
1595
+ if (!this.player.paused() && !this.isMediaOnDemand()) {
1596
+ this.notify('uptime');
1597
+ }
1598
+ };
1599
+
1600
+ // Send the first uptime after 30 seconds
1601
+ this.uptimeTimeoutId = setTimeout(() => {
1602
+ // Send only when playing
1603
+ notifyUptime();
1604
+
1605
+ // Initialize the uptime interval after 30 seconds
1606
+ this.uptimeIntervalId = setInterval(() => {
1607
+ // Send only when playing
1608
+ notifyUptime();
1609
+ }, 60000);
1610
+ }, 30000);
1611
+ }
1612
+
1613
+ /**
1614
+ * __ComScore__:
1615
+ * It's expected notifyBufferStart() to be called when the player starts buffering
1616
+ * and a call to notifyBufferStop() when content resumes after buffering.
1617
+ *
1618
+ * @see Item 2: https://jira.srg.beecollaboration.com/browse/PLAY-2628
1619
+ *
1620
+ * After the issue PLAYRTS-321
1621
+ * @see Fix: https://jira.srg.beecollaboration.com/browse/PLAYRTS-321?focusedCommentId=201023&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-201023
1622
+ * @see Fix: https://jira.srg.beecollaboration.com/browse/PLAYRTS-3065
1623
+ */
1624
+ waiting() {
1625
+ if (!this.initialized || this.isWaiting) {
1626
+ return;
1627
+ }
1628
+ const bufferStop = () => {
1629
+ this.isWaiting = false;
1630
+ this.notify('buffer_stop');
1631
+ };
1632
+ this.isWaiting = true;
1633
+ this.notify('buffer_start');
1634
+
1635
+ // As Safari is not consistent with its playing event, it is better to use the timeupdate event.
1636
+ if (pillarbox.browser.IS_ANY_SAFARI) {
1637
+ this.player.one(TIME_UPDATE, bufferStop);
1638
+ } else {
1639
+ // As Chromium-based browsers are not consistent with their timeupdate event, it is better to use the playing event.
1640
+ // Firefox is consistent with its playing event.
1641
+ this.player.one(PLAYING, bufferStop);
1642
+ }
1643
+ }
1644
+ }
1645
+
1646
+ /**
1647
+ * Represents the composition of media content.
1648
+ *
1649
+ * @class MediaComposition
1650
+ * @property {string} chapterUrn URN (Uniform Resource Name) of the associated chapter.
1651
+ * @property {string} segmentUrn URN of the associated segment.
1652
+ * @property {Episode} episode Associated episode.
1653
+ * @property {Show} show Associated show.
1654
+ * @property {Channel} channel Associated channel.
1655
+ * @property {Array.<Chapter>} chapterList List of associated chapters.
1656
+ * @property {Array.<Topic>} topicList List of associated topics.
1657
+ * @property {Object.<String, String>} analyticsData Analytics data associated with the media composition.
1658
+ * @property {Object.<String, String>} analyticsMetadata Metadata associated with analytics for the media composition.
1659
+ */
1660
+ class MediaComposition {
1661
+ /**
1662
+ * Find a chapter by its URN.
1663
+ *
1664
+ * @param {String} urn
1665
+ *
1666
+ * @returns {Chapter} chapter
1667
+ */
1668
+ findChapterByUrn(urn) {
1669
+ if (this.chapterList) {
1670
+ const [chapter] = this.chapterList.filter(element => element.urn === urn);
1671
+ return chapter;
1672
+ }
1673
+ return undefined;
1674
+ }
1675
+
1676
+ /**
1677
+ * Return a segment from main chapter following segmentUrn in mediaComposition.
1678
+ *
1679
+ * @returns {Segment|undefined} main segment
1680
+ */
1681
+ findMainSegment() {
1682
+ if (!this.segmentUrn) {
1683
+ return undefined;
1684
+ }
1685
+ const segmentList = this.getMainSegments();
1686
+ const [segment] = segmentList.filter(element => element.urn === this.segmentUrn);
1687
+ return segment;
1688
+ }
1689
+
1690
+ /**
1691
+ * Find resource list by URN.
1692
+ *
1693
+ * @param {String} urn
1694
+ * @returns {Array.<Resource>|undefined} of resources
1695
+ */
1696
+ findResourceListByUrn(urn) {
1697
+ const chapterByUrn = this.findChapterByUrn(urn);
1698
+ if (chapterByUrn) {
1699
+ return chapterByUrn.resourceList || [];
1700
+ }
1701
+ return undefined;
1702
+ }
1703
+
1704
+ /**
1705
+ * A list of chapters.
1706
+ *
1707
+ * @returns {Array.<Chapter>} of chapters
1708
+ */
1709
+ getChapters() {
1710
+ const AUDIO = 'AUDIO';
1711
+ if (this.getMainChapter().mediaType === AUDIO) return [];
1712
+ return this.chapterList.filter(({
1713
+ mediaType
1714
+ }) => mediaType !== AUDIO);
1715
+ }
1716
+
1717
+ /**
1718
+ * Filter external text tracks that are already available internally.
1719
+ *
1720
+ * __Rules:__
1721
+ * 1. TTML format is filtered
1722
+ *
1723
+ * 2. If both are empty that means only internal text tracks will be displayed
1724
+ * to the user as they are automatically loaded by the player.
1725
+ *
1726
+ * 3. If subtitleInformationList is missing from the MediaComposition and subtitleList
1727
+ * is available but the media contains internal text tracks that are also available internally.
1728
+ * It will result on a duplication client side.
1729
+ *
1730
+ * 4. If subtitleList and subtitleInformationList a merge between both will be operated,
1731
+ * removing the external text tracks already available internally.
1732
+ *
1733
+ *
1734
+ * @returns {Array.<Subtitle>} external text tracks
1735
+ */
1736
+ getFilteredExternalSubtitles() {
1737
+ const {
1738
+ subtitleList
1739
+ } = this.getMainChapter();
1740
+ const [{
1741
+ subtitleInformationList
1742
+ } = {}] = this.getResourceList().filter(({
1743
+ subtitleInformationList
1744
+ }) => subtitleInformationList);
1745
+ const onlyHasExternalSubtitles = subtitleList && !subtitleInformationList;
1746
+ if (!subtitleList) {
1747
+ return [];
1748
+ }
1749
+
1750
+ // TTML format is not supported
1751
+ const subtitles = subtitleList.filter(subtitle => subtitle.format !== 'TTML');
1752
+ if (onlyHasExternalSubtitles) {
1753
+ return subtitles;
1754
+ }
1755
+ return subtitles.filter(subtitle => {
1756
+ const addSubtitle = !subtitleInformationList.find(subtitleInformation => subtitleInformation.locale === subtitle.locale && subtitle.type === subtitleInformation.type);
1757
+ return addSubtitle;
1758
+ });
1759
+ }
1760
+
1761
+ /**
1762
+ * Block reason for main chapter. This also uses current date for STARTDATE.
1763
+ *
1764
+ * @see BlockReason
1765
+ *
1766
+ * @returns {string | undefined} undefined if main chapter is not blocked
1767
+ */
1768
+ getMainBlockReason() {
1769
+ const mainChapter = this.getMainChapter();
1770
+ if (!mainChapter) {
1771
+ return undefined;
1772
+ }
1773
+ let {
1774
+ blockReason
1775
+ } = mainChapter;
1776
+ if (!blockReason && new Date() < this.getMainValidFromDate()) {
1777
+ blockReason = 'STARTDATE';
1778
+ }
1779
+ return blockReason;
1780
+ }
1781
+
1782
+ /**
1783
+ * Get blocked segments from the main chapter.
1784
+ *
1785
+ * @returns {Array.<Segment>} of blocked segments
1786
+ */
1787
+ getMainBlockedSegments() {
1788
+ return this.getMainSegments().filter(segment => segment.blockReason);
1789
+ }
1790
+
1791
+ /**
1792
+ * Get the mediaComposition's main chapter.
1793
+ *
1794
+ * @returns {Chapter}
1795
+ */
1796
+ getMainChapter() {
1797
+ if (!this.mainChapter) {
1798
+ this.mainChapter = this.findChapterByUrn(this.chapterUrn);
1799
+ }
1800
+ if (!this.mainChapter && this.chapterList && this.chapterList.length > 0) {
1801
+ [this.mainChapter] = this.chapterList;
1802
+ }
1803
+ return this.mainChapter;
1804
+ }
1805
+
1806
+ /**
1807
+ * Get the main chapter's image URL decorated with default width and format.
1808
+ *
1809
+ * @returns {String|undefined} image URL
1810
+ */
1811
+ getMainChapterImageUrl() {
1812
+ const mainChapter = this.getMainChapter();
1813
+ if (!mainChapter || !mainChapter.imageUrl) {
1814
+ return undefined;
1815
+ }
1816
+ return mainChapter.imageUrl;
1817
+ }
1818
+
1819
+ /**
1820
+ * Get main resources.
1821
+ *
1822
+ * @returns {Array.<MainResource>} array of sources.
1823
+ */
1824
+ // eslint-disable-next-line max-lines-per-function
1825
+ getMainResources() {
1826
+ const resourceList = this.getResourceList();
1827
+ if (!resourceList || !resourceList.length) {
1828
+ return undefined;
1829
+ }
1830
+ return resourceList.map(resource => ({
1831
+ analyticsData: this.getMergedAnalyticsData(resource.analyticsData),
1832
+ analyticsMetadata: this.getMergedAnalyticsMetadata(resource.analyticsMetadata),
1833
+ blockReason: this.getMainChapter().blockReason,
1834
+ blockedSegments: this.getMainBlockedSegments(),
1835
+ imageUrl: this.getMainChapterImageUrl(),
1836
+ chapters: this.getChapters(),
1837
+ drmList: resource.drmList,
1838
+ dvr: resource.dvr,
1839
+ eventData: this.getMainChapter().eventData,
1840
+ id: this.getMainChapter().id,
1841
+ imageCopyright: this.getMainChapter().imageCopyright,
1842
+ intervals: this.getMainTimeIntervals(),
1843
+ live: resource.live,
1844
+ mediaType: this.getMainChapter().mediaType,
1845
+ mimeType: resource.mimeType,
1846
+ presentation: resource.presentation,
1847
+ quality: resource.quality,
1848
+ streaming: resource.streaming,
1849
+ streamOffset: resource.streamOffset,
1850
+ subtitles: this.getFilteredExternalSubtitles(),
1851
+ title: this.getMainChapter().title,
1852
+ tokenType: resource.tokenType,
1853
+ url: resource.url,
1854
+ urn: this.chapterUrn,
1855
+ vendor: this.getMainChapter().vendor
1856
+ }));
1857
+ }
1858
+
1859
+ /**
1860
+ * Get segments of the main chapter ordered by markIn.
1861
+ *
1862
+ * @returns {Array.<Segment>} of segments
1863
+ */
1864
+ getMainSegments() {
1865
+ const mainChapter = this.getMainChapter();
1866
+ if (!this.mainSegments && mainChapter && mainChapter.segmentList) {
1867
+ this.mainSegments = mainChapter.segmentList;
1868
+ }
1869
+ return this.mainSegments || [];
1870
+ }
1871
+
1872
+ /**
1873
+ * Retrieves an array of time intervals associated with the main chapter.
1874
+ *
1875
+ * @returns {Array.<TimeInterval>} An array of time intervals.
1876
+ */
1877
+ getMainTimeIntervals() {
1878
+ const {
1879
+ timeIntervalList = []
1880
+ } = this.getMainChapter() || {};
1881
+ return timeIntervalList;
1882
+ }
1883
+
1884
+ /**
1885
+ * Compute a date from which this content is valid. Always return a date object.
1886
+ *
1887
+ * @returns {Date} date specified in media composition or EPOCH when no date present.
1888
+ */
1889
+ getMainValidFromDate() {
1890
+ const mainChapter = this.getMainChapter();
1891
+ if (!mainChapter) {
1892
+ return new Date(0);
1893
+ }
1894
+ const {
1895
+ validFrom
1896
+ } = mainChapter;
1897
+ if (validFrom) {
1898
+ return new Date(validFrom);
1899
+ }
1900
+ }
1901
+
1902
+ /**
1903
+ * Get merged analytics data.
1904
+ *
1905
+ * @param {Object.<string, string>} analyticsData
1906
+ * @returns {Object.<string, string>} Merged analytics data.
1907
+ */
1908
+ getMergedAnalyticsData(analyticsData) {
1909
+ return _objectSpread2(_objectSpread2(_objectSpread2({}, this.analyticsData), this.getMainChapter().analyticsData), analyticsData);
1910
+ }
1911
+
1912
+ /**
1913
+ * Get merged analytics metadata.
1914
+ *
1915
+ * @param {Object.<string, string>} analyticsMetadata
1916
+ * @returns {Object.<string, string>} Merged analytics metadata.
1917
+ */
1918
+ getMergedAnalyticsMetadata(analyticsMetadata) {
1919
+ return _objectSpread2(_objectSpread2(_objectSpread2({}, this.analyticsMetadata), this.getMainChapter().analyticsMetadata), analyticsMetadata);
1920
+ }
1921
+
1922
+ /**
1923
+ * Get the chapter's resource list
1924
+ * @returns {Array.<Resource>} of resources
1925
+ */
1926
+ getResourceList() {
1927
+ const {
1928
+ resourceList
1929
+ } = this.getMainChapter();
1930
+ return resourceList || [];
1931
+ }
1932
+ }
1933
+
1934
+ /**
1935
+ * @typedef {import('./typedef').Channel} Channel
1936
+ * @typedef {import('./typedef').Chapter} Chapter
1937
+ * @typedef {import('./typedef').Episode} Episode
1938
+ * @typedef {import('./typedef').Resource} Resource
1939
+ * @typedef {import('./typedef').Segment} Segment
1940
+ * @typedef {import('./typedef').Show} Show
1941
+ * @typedef {import('./typedef').Subtitle} Subtitle
1942
+ * @typedef {import('./typedef').TimeInterval} TimeInterval
1943
+ * @typedef {import('./typedef').Topic} Topic
1944
+ * @typedef {import('./typedef').MainResource} MainResource
1945
+ */
1946
+
1947
+ var Play$4 = "Wiedergabe";
1948
+ var Pause$4 = "Pause";
1949
+ var Replay$4 = "Erneut abspielen";
1950
+ var Duration$4 = "Dauer";
1951
+ var LIVE$4 = "LIVE";
1952
+ var Loaded$4 = "Geladen";
1953
+ var Progress$4 = "Status";
1954
+ var Fullscreen$4 = "Vollbild";
1955
+ var Mute$4 = "Stumm schalten";
1956
+ var Unmute$4 = "Ton einschalten";
1957
+ var Subtitles$4 = "Untertitel";
1958
+ var Captions$4 = "Untertitel";
1959
+ var Chapters$4 = "Kapitel";
1960
+ var Close$4 = "Schließen";
1961
+ var Descriptions$4 = "Beschreibungen";
1962
+ var Text$4 = "Schrift";
1963
+ var White$4 = "Weiß";
1964
+ var Black$4 = "Schwarz";
1965
+ var Red$4 = "Rot";
1966
+ var Green$4 = "Grün";
1967
+ var Blue$4 = "Blau";
1968
+ var Yellow$4 = "Gelb";
1969
+ var Magenta$4 = "Magenta";
1970
+ var Cyan$4 = "Türkis";
1971
+ var Background$4 = "Hintergrund";
1972
+ var Window$4 = "Fenster";
1973
+ var Transparent$4 = "Durchsichtig";
1974
+ var Opaque$4 = "Undurchsichtig";
1975
+ var None$4 = "Kein";
1976
+ var Raised$3 = "Erhoben";
1977
+ var Depressed$3 = "Gedrückt";
1978
+ var Uniform$4 = "Uniform";
1979
+ var Casual$3 = "Zwanglos";
1980
+ var Script$3 = "Schreibschrift";
1981
+ var Reset$4 = "Zurücksetzen";
1982
+ var Done$4 = "Fertig";
1983
+ var Color$3 = "Farbe";
1984
+ var Opacity$3 = "Deckkraft";
1985
+ var de$1 = {
1986
+ Play: Play$4,
1987
+ Pause: Pause$4,
1988
+ Replay: Replay$4,
1989
+ "Current Time": "Aktueller Zeitpunkt",
1990
+ Duration: Duration$4,
1991
+ "Remaining Time": "Verbleibende Zeit",
1992
+ "Stream Type": "Streamtyp",
1993
+ LIVE: LIVE$4,
1994
+ Loaded: Loaded$4,
1995
+ Progress: Progress$4,
1996
+ Fullscreen: Fullscreen$4,
1997
+ "Exit Fullscreen": "Vollbildmodus beenden",
1998
+ Mute: Mute$4,
1999
+ Unmute: Unmute$4,
2000
+ "Playback Rate": "Wiedergabegeschwindigkeit",
2001
+ Subtitles: Subtitles$4,
2002
+ "subtitles off": "Untertitel aus",
2003
+ Captions: Captions$4,
2004
+ "captions off": "Untertitel aus",
2005
+ Chapters: Chapters$4,
2006
+ "You aborted the media playback": "Sie haben die Videowiedergabe abgebrochen.",
2007
+ "A network error caused the media download to fail part-way.": "Der Videodownload ist aufgrund eines Netzwerkfehlers fehlgeschlagen.",
2008
+ "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Das Video konnte nicht geladen werden, da entweder ein Server- oder Netzwerkfehler auftrat oder das Format nicht unterstützt wird.",
2009
+ "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Die Videowiedergabe wurde entweder wegen eines Problems mit einem beschädigten Video oder wegen verwendeten Funktionen, die vom Browser nicht unterstützt werden, abgebrochen.",
2010
+ "No compatible source was found for this media.": "Für dieses Video wurde keine kompatible Quelle gefunden.",
2011
+ "Play Video": "Video abspielen",
2012
+ Close: Close$4,
2013
+ "Modal Window": "Modales Fenster",
2014
+ "This is a modal window": "Dies ist ein modales Fenster",
2015
+ "This modal can be closed by pressing the Escape key or activating the close button.": "Durch Drücken der Esc-Taste bzw. Betätigung der Schaltfläche \"Schließen\" wird dieses modale Fenster geschlossen.",
2016
+ ", opens captions settings dialog": ", öffnet Einstellungen für Untertitel",
2017
+ ", opens subtitles settings dialog": ", öffnet Einstellungen für Untertitel",
2018
+ ", selected": ", ausgewählt",
2019
+ "captions settings": "Untertiteleinstellungen",
2020
+ "subtitles settings": "Untertiteleinstellungen",
2021
+ "descriptions settings": "Einstellungen für Beschreibungen",
2022
+ "Close Modal Dialog": "Modales Fenster schließen",
2023
+ Descriptions: Descriptions$4,
2024
+ "descriptions off": "Beschreibungen aus",
2025
+ "The media is encrypted and we do not have the keys to decrypt it.": "Die Entschlüsselungsschlüssel für den verschlüsselten Medieninhalt sind nicht verfügbar.",
2026
+ ", opens descriptions settings dialog": ", öffnet Einstellungen für Beschreibungen",
2027
+ "Audio Track": "Tonspur",
2028
+ Text: Text$4,
2029
+ White: White$4,
2030
+ Black: Black$4,
2031
+ Red: Red$4,
2032
+ Green: Green$4,
2033
+ Blue: Blue$4,
2034
+ Yellow: Yellow$4,
2035
+ Magenta: Magenta$4,
2036
+ Cyan: Cyan$4,
2037
+ Background: Background$4,
2038
+ Window: Window$4,
2039
+ Transparent: Transparent$4,
2040
+ "Semi-Transparent": "Halbdurchsichtig",
2041
+ Opaque: Opaque$4,
2042
+ "Font Size": "Schriftgröße",
2043
+ "Text Edge Style": "Textkantenstil",
2044
+ None: None$4,
2045
+ Raised: Raised$3,
2046
+ Depressed: Depressed$3,
2047
+ Uniform: Uniform$4,
2048
+ "Drop shadow": "Schlagschatten",
2049
+ "Font Family": "Schriftfamilie",
2050
+ "Proportional Sans-Serif": "Proportionale Sans-Serif",
2051
+ "Monospace Sans-Serif": "Monospace Sans-Serif",
2052
+ "Proportional Serif": "Proportionale Serif",
2053
+ "Monospace Serif": "Monospace Serif",
2054
+ Casual: Casual$3,
2055
+ Script: Script$3,
2056
+ "Small Caps": "Small-Caps",
2057
+ Reset: Reset$4,
2058
+ "restore all settings to the default values": "Alle Einstellungen auf die Standardwerte zurücksetzen",
2059
+ Done: Done$4,
2060
+ "Caption Settings Dialog": "Einstellungsdialog für Untertitel",
2061
+ "Beginning of dialog window. Escape will cancel and close the window.": "Anfang des Dialogfensters. Esc bricht ab und schließt das Fenster.",
2062
+ "End of dialog window.": "Ende des Dialogfensters.",
2063
+ "Audio Player": "Audio-Player",
2064
+ "Video Player": "Video-Player",
2065
+ "Progress Bar": "Fortschrittsbalken",
2066
+ "progress bar timing: currentTime={1} duration={2}": "{1} von {2}",
2067
+ "Volume Level": "Lautstärke",
2068
+ "{1} is loading.": "{1} wird geladen.",
2069
+ "Seek to live, currently behind live": "Zur Live-Übertragung wechseln. Aktuell wird es nicht live abgespielt.",
2070
+ "Seek to live, currently playing live": "Zur Live-Übertragung wechseln. Es wird aktuell live abgespielt.",
2071
+ "Exit Picture-in-Picture": "Bild-im-Bild-Modus beenden",
2072
+ "Picture-in-Picture": "Bild-im-Bild-Modus",
2073
+ "No content": "Kein Inhalt",
2074
+ Color: Color$3,
2075
+ Opacity: Opacity$3,
2076
+ "Text Background": "Texthintergrund",
2077
+ "Caption Area Background": "Hintergrund des Untertitelbereichs",
2078
+ "Playing in Picture-in-Picture": "Wird im Bild-im-Bild-Modus wiedergegeben",
2079
+ "Skip forward {1} seconds": "{1} Sekunden vorwärts",
2080
+ "Skip backward {1} seconds": "{1} Sekunden zurück"
2081
+ };
2082
+
2083
+ var vjsLang$3 = /*#__PURE__*/Object.freeze({
2084
+ __proto__: null,
2085
+ Background: Background$4,
2086
+ Black: Black$4,
2087
+ Blue: Blue$4,
2088
+ Captions: Captions$4,
2089
+ Casual: Casual$3,
2090
+ Chapters: Chapters$4,
2091
+ Close: Close$4,
2092
+ Color: Color$3,
2093
+ Cyan: Cyan$4,
2094
+ Depressed: Depressed$3,
2095
+ Descriptions: Descriptions$4,
2096
+ Done: Done$4,
2097
+ Duration: Duration$4,
2098
+ Fullscreen: Fullscreen$4,
2099
+ Green: Green$4,
2100
+ LIVE: LIVE$4,
2101
+ Loaded: Loaded$4,
2102
+ Magenta: Magenta$4,
2103
+ Mute: Mute$4,
2104
+ None: None$4,
2105
+ Opacity: Opacity$3,
2106
+ Opaque: Opaque$4,
2107
+ Pause: Pause$4,
2108
+ Play: Play$4,
2109
+ Progress: Progress$4,
2110
+ Raised: Raised$3,
2111
+ Red: Red$4,
2112
+ Replay: Replay$4,
2113
+ Reset: Reset$4,
2114
+ Script: Script$3,
2115
+ Subtitles: Subtitles$4,
2116
+ Text: Text$4,
2117
+ Transparent: Transparent$4,
2118
+ Uniform: Uniform$4,
2119
+ Unmute: Unmute$4,
2120
+ White: White$4,
2121
+ Window: Window$4,
2122
+ Yellow: Yellow$4,
2123
+ default: de$1
2124
+ });
2125
+
2126
+ var AGERATING12$4 = "Aus Gründen des Jugendschutzes steht dieser Inhalt nur zwischen 20:00 und 06:00 Uhr zur Verfügung.";
2127
+ var AGERATING18$4 = "Aus Gründen des Jugendschutzes steht dieser Inhalt nur zwischen 23:00 und 05:00 Uhr zur Verfügung.";
2128
+ var COMMERCIAL$4 = "Die Werbung wurde übersprungen.";
2129
+ var ENDDATE$4 = "Dieser Inhalt ist nicht mehr verfügbar.";
2130
+ var GEOBLOCK$4 = "Dieser Inhalt ist ausserhalb der Schweiz nicht verfügbar.";
2131
+ var LEGAL$4 = "Dieser Inhalt ist aus rechtlichen Gründen nicht verfügbar.";
2132
+ var STARTDATE$4 = "Dieser Inhalt ist noch nicht verfügbar. Bitte probieren Sie es später noch einmal.";
2133
+ var UNKNOWN$4 = "Dieser Inhalt ist nicht verfügbar.";
2134
+ var de = {
2135
+ AGERATING12: AGERATING12$4,
2136
+ AGERATING18: AGERATING18$4,
2137
+ COMMERCIAL: COMMERCIAL$4,
2138
+ ENDDATE: ENDDATE$4,
2139
+ GEOBLOCK: GEOBLOCK$4,
2140
+ LEGAL: LEGAL$4,
2141
+ STARTDATE: STARTDATE$4,
2142
+ UNKNOWN: UNKNOWN$4
2143
+ };
2144
+
2145
+ var pillarboxLang$4 = /*#__PURE__*/Object.freeze({
2146
+ __proto__: null,
2147
+ AGERATING12: AGERATING12$4,
2148
+ AGERATING18: AGERATING18$4,
2149
+ COMMERCIAL: COMMERCIAL$4,
2150
+ ENDDATE: ENDDATE$4,
2151
+ GEOBLOCK: GEOBLOCK$4,
2152
+ LEGAL: LEGAL$4,
2153
+ STARTDATE: STARTDATE$4,
2154
+ UNKNOWN: UNKNOWN$4,
2155
+ default: de
2156
+ });
2157
+
2158
+ pillarbox.addLanguage('de', _objectSpread2(_objectSpread2({}, vjsLang$3), pillarboxLang$4));
2159
+
2160
+ var Play$3 = "Play";
2161
+ var Pause$3 = "Pause";
2162
+ var Replay$3 = "Replay";
2163
+ var Duration$3 = "Duration";
2164
+ var LIVE$3 = "LIVE";
2165
+ var Loaded$3 = "Loaded";
2166
+ var Progress$3 = "Progress";
2167
+ var Fullscreen$3 = "Fullscreen";
2168
+ var Mute$3 = "Mute";
2169
+ var Unmute$3 = "Unmute";
2170
+ var Subtitles$3 = "Subtitles";
2171
+ var Captions$3 = "Captions";
2172
+ var Chapters$3 = "Chapters";
2173
+ var Descriptions$3 = "Descriptions";
2174
+ var Close$3 = "Close";
2175
+ var Text$3 = "Text";
2176
+ var White$3 = "White";
2177
+ var Black$3 = "Black";
2178
+ var Red$3 = "Red";
2179
+ var Green$3 = "Green";
2180
+ var Blue$3 = "Blue";
2181
+ var Yellow$3 = "Yellow";
2182
+ var Magenta$3 = "Magenta";
2183
+ var Cyan$3 = "Cyan";
2184
+ var Background$3 = "Background";
2185
+ var Window$3 = "Window";
2186
+ var Transparent$3 = "Transparent";
2187
+ var Opaque$3 = "Opaque";
2188
+ var None$3 = "None";
2189
+ var Raised$2 = "Raised";
2190
+ var Depressed$2 = "Depressed";
2191
+ var Uniform$3 = "Uniform";
2192
+ var Casual$2 = "Casual";
2193
+ var Script$2 = "Script";
2194
+ var Reset$3 = "Reset";
2195
+ var Done$3 = "Done";
2196
+ var Color$2 = "Color";
2197
+ var Opacity$2 = "Opacity";
2198
+ var en$1 = {
2199
+ "Audio Player": "Audio Player",
2200
+ "Video Player": "Video Player",
2201
+ Play: Play$3,
2202
+ Pause: Pause$3,
2203
+ Replay: Replay$3,
2204
+ "Current Time": "Current Time",
2205
+ Duration: Duration$3,
2206
+ "Remaining Time": "Remaining Time",
2207
+ "Stream Type": "Stream Type",
2208
+ LIVE: LIVE$3,
2209
+ "Seek to live, currently behind live": "Seek to live, currently behind live",
2210
+ "Seek to live, currently playing live": "Seek to live, currently playing live",
2211
+ Loaded: Loaded$3,
2212
+ Progress: Progress$3,
2213
+ "Progress Bar": "Progress Bar",
2214
+ "progress bar timing: currentTime={1} duration={2}": "{1} of {2}",
2215
+ Fullscreen: Fullscreen$3,
2216
+ "Exit Fullscreen": "Exit Fullscreen",
2217
+ Mute: Mute$3,
2218
+ Unmute: Unmute$3,
2219
+ "Playback Rate": "Playback Rate",
2220
+ Subtitles: Subtitles$3,
2221
+ "subtitles off": "subtitles off",
2222
+ Captions: Captions$3,
2223
+ "captions off": "captions off",
2224
+ Chapters: Chapters$3,
2225
+ Descriptions: Descriptions$3,
2226
+ "descriptions off": "descriptions off",
2227
+ "Audio Track": "Audio Track",
2228
+ "Volume Level": "Volume Level",
2229
+ "You aborted the media playback": "You aborted the media playback",
2230
+ "A network error caused the media download to fail part-way.": "A network error caused the media download to fail part-way.",
2231
+ "The media could not be loaded, either because the server or network failed or because the format is not supported.": "The media could not be loaded, either because the server or network failed or because the format is not supported.",
2232
+ "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.",
2233
+ "No compatible source was found for this media.": "No compatible source was found for this media.",
2234
+ "The media is encrypted and we do not have the keys to decrypt it.": "The media is encrypted and we do not have the keys to decrypt it.",
2235
+ "Play Video": "Play Video",
2236
+ Close: Close$3,
2237
+ "Close Modal Dialog": "Close Modal Dialog",
2238
+ "Modal Window": "Modal Window",
2239
+ "This is a modal window": "This is a modal window",
2240
+ "This modal can be closed by pressing the Escape key or activating the close button.": "This modal can be closed by pressing the Escape key or activating the close button.",
2241
+ ", opens captions settings dialog": ", opens captions settings dialog",
2242
+ ", opens subtitles settings dialog": ", opens subtitles settings dialog",
2243
+ ", opens descriptions settings dialog": ", opens descriptions settings dialog",
2244
+ ", selected": ", selected",
2245
+ "captions settings": "captions settings",
2246
+ "subtitles settings": "subtitles settings",
2247
+ "descriptions settings": "descriptions settings",
2248
+ Text: Text$3,
2249
+ White: White$3,
2250
+ Black: Black$3,
2251
+ Red: Red$3,
2252
+ Green: Green$3,
2253
+ Blue: Blue$3,
2254
+ Yellow: Yellow$3,
2255
+ Magenta: Magenta$3,
2256
+ Cyan: Cyan$3,
2257
+ Background: Background$3,
2258
+ Window: Window$3,
2259
+ Transparent: Transparent$3,
2260
+ "Semi-Transparent": "Semi-Transparent",
2261
+ Opaque: Opaque$3,
2262
+ "Font Size": "Font Size",
2263
+ "Text Edge Style": "Text Edge Style",
2264
+ None: None$3,
2265
+ Raised: Raised$2,
2266
+ Depressed: Depressed$2,
2267
+ Uniform: Uniform$3,
2268
+ "Drop shadow": "Drop shadow",
2269
+ "Font Family": "Font Family",
2270
+ "Proportional Sans-Serif": "Proportional Sans-Serif",
2271
+ "Monospace Sans-Serif": "Monospace Sans-Serif",
2272
+ "Proportional Serif": "Proportional Serif",
2273
+ "Monospace Serif": "Monospace Serif",
2274
+ Casual: Casual$2,
2275
+ Script: Script$2,
2276
+ "Small Caps": "Small Caps",
2277
+ Reset: Reset$3,
2278
+ "restore all settings to the default values": "restore all settings to the default values",
2279
+ Done: Done$3,
2280
+ "Caption Settings Dialog": "Caption Settings Dialog",
2281
+ "Beginning of dialog window. Escape will cancel and close the window.": "Beginning of dialog window. Escape will cancel and close the window.",
2282
+ "End of dialog window.": "End of dialog window.",
2283
+ "{1} is loading.": "{1} is loading.",
2284
+ "Exit Picture-in-Picture": "Exit Picture-in-Picture",
2285
+ "Picture-in-Picture": "Picture-in-Picture",
2286
+ "No content": "No content",
2287
+ Color: Color$2,
2288
+ Opacity: Opacity$2,
2289
+ "Text Background": "Text Background",
2290
+ "Caption Area Background": "Caption Area Background",
2291
+ "Playing in Picture-in-Picture": "Playing in Picture-in-Picture",
2292
+ "Skip backward {1} seconds": "Skip backward {1} seconds",
2293
+ "Skip forward {1} seconds": "Skip forward {1} seconds"
2294
+ };
2295
+
2296
+ var vjsLang$2 = /*#__PURE__*/Object.freeze({
2297
+ __proto__: null,
2298
+ Background: Background$3,
2299
+ Black: Black$3,
2300
+ Blue: Blue$3,
2301
+ Captions: Captions$3,
2302
+ Casual: Casual$2,
2303
+ Chapters: Chapters$3,
2304
+ Close: Close$3,
2305
+ Color: Color$2,
2306
+ Cyan: Cyan$3,
2307
+ Depressed: Depressed$2,
2308
+ Descriptions: Descriptions$3,
2309
+ Done: Done$3,
2310
+ Duration: Duration$3,
2311
+ Fullscreen: Fullscreen$3,
2312
+ Green: Green$3,
2313
+ LIVE: LIVE$3,
2314
+ Loaded: Loaded$3,
2315
+ Magenta: Magenta$3,
2316
+ Mute: Mute$3,
2317
+ None: None$3,
2318
+ Opacity: Opacity$2,
2319
+ Opaque: Opaque$3,
2320
+ Pause: Pause$3,
2321
+ Play: Play$3,
2322
+ Progress: Progress$3,
2323
+ Raised: Raised$2,
2324
+ Red: Red$3,
2325
+ Replay: Replay$3,
2326
+ Reset: Reset$3,
2327
+ Script: Script$2,
2328
+ Subtitles: Subtitles$3,
2329
+ Text: Text$3,
2330
+ Transparent: Transparent$3,
2331
+ Uniform: Uniform$3,
2332
+ Unmute: Unmute$3,
2333
+ White: White$3,
2334
+ Window: Window$3,
2335
+ Yellow: Yellow$3,
2336
+ default: en$1
2337
+ });
2338
+
2339
+ var AGERATING12$3 = "To protect children this content is only available between 8PM and 6AM.";
2340
+ var AGERATING18$3 = "To protect children this content is only available between 10PM and 5AM.";
2341
+ var COMMERCIAL$3 = "This commercial content is not available.";
2342
+ var ENDDATE$3 = "This content is not available anymore.";
2343
+ var GEOBLOCK$3 = "This content is not available outside Switzerland.";
2344
+ var LEGAL$3 = "This content is not available due to legal restrictions.";
2345
+ var STARTDATE$3 = "This content is not available yet.";
2346
+ var UNKNOWN$3 = "This content is not available.";
2347
+ var en = {
2348
+ AGERATING12: AGERATING12$3,
2349
+ AGERATING18: AGERATING18$3,
2350
+ COMMERCIAL: COMMERCIAL$3,
2351
+ ENDDATE: ENDDATE$3,
2352
+ GEOBLOCK: GEOBLOCK$3,
2353
+ LEGAL: LEGAL$3,
2354
+ STARTDATE: STARTDATE$3,
2355
+ UNKNOWN: UNKNOWN$3
2356
+ };
2357
+
2358
+ var pillarboxLang$3 = /*#__PURE__*/Object.freeze({
2359
+ __proto__: null,
2360
+ AGERATING12: AGERATING12$3,
2361
+ AGERATING18: AGERATING18$3,
2362
+ COMMERCIAL: COMMERCIAL$3,
2363
+ ENDDATE: ENDDATE$3,
2364
+ GEOBLOCK: GEOBLOCK$3,
2365
+ LEGAL: LEGAL$3,
2366
+ STARTDATE: STARTDATE$3,
2367
+ UNKNOWN: UNKNOWN$3,
2368
+ default: en
2369
+ });
2370
+
2371
+ pillarbox.addLanguage('en', _objectSpread2(_objectSpread2({}, vjsLang$2), pillarboxLang$3));
2372
+
2373
+ var Play$2 = "Lecture";
2374
+ var Pause$2 = "Pause";
2375
+ var Replay$2 = "Revoir";
2376
+ var Duration$2 = "Durée";
2377
+ var LIVE$2 = "EN DIRECT";
2378
+ var Loaded$2 = "Chargé";
2379
+ var Progress$2 = "Progression";
2380
+ var Fullscreen$2 = "Plein écran";
2381
+ var Mute$2 = "Mettre en sourdine";
2382
+ var Unmute$2 = "Activer le son";
2383
+ var Subtitles$2 = "Sous-titres";
2384
+ var Captions$2 = "Sous-titres transcrits";
2385
+ var Chapters$2 = "Chapitres";
2386
+ var Descriptions$2 = "Descriptions";
2387
+ var Close$2 = "Fermer";
2388
+ var Text$2 = "Texte";
2389
+ var White$2 = "Blanc";
2390
+ var Black$2 = "Noir";
2391
+ var Red$2 = "Rouge";
2392
+ var Green$2 = "Vert";
2393
+ var Blue$2 = "Bleu";
2394
+ var Yellow$2 = "Jaune";
2395
+ var Magenta$2 = "Magenta";
2396
+ var Cyan$2 = "Cyan";
2397
+ var Background$2 = "Arrière-plan";
2398
+ var Window$2 = "Fenêtre";
2399
+ var Transparent$2 = "Transparent";
2400
+ var Opaque$2 = "Opaque";
2401
+ var None$2 = "Aucun";
2402
+ var Raised$1 = "Élevé";
2403
+ var Depressed$1 = "Enfoncé";
2404
+ var Uniform$2 = "Uniforme";
2405
+ var Casual$1 = "Manuscrite";
2406
+ var Script$1 = "Scripte";
2407
+ var Reset$2 = "Réinitialiser";
2408
+ var Done$2 = "Terminé";
2409
+ var Color$1 = "Couleur";
2410
+ var Opacity$1 = "Opacité";
2411
+ var fr$1 = {
2412
+ "Audio Player": "Lecteur audio",
2413
+ "Video Player": "Lecteur vidéo",
2414
+ Play: Play$2,
2415
+ Pause: Pause$2,
2416
+ Replay: Replay$2,
2417
+ "Current Time": "Temps actuel",
2418
+ Duration: Duration$2,
2419
+ "Remaining Time": "Temps restant",
2420
+ "Stream Type": "Type de flux",
2421
+ LIVE: LIVE$2,
2422
+ "Seek to live, currently behind live": "Rechercher le direct, actuellement après le direct",
2423
+ "Seek to live, currently playing live": "Rechercher le direct, le direct actuellement en cours de lecture",
2424
+ Loaded: Loaded$2,
2425
+ Progress: Progress$2,
2426
+ "Progress Bar": "Barre de progression",
2427
+ "progress bar timing: currentTime={1} duration={2}": "{1} de {2}",
2428
+ Fullscreen: Fullscreen$2,
2429
+ "Exit Fullscreen": "Fenêtré",
2430
+ Mute: Mute$2,
2431
+ Unmute: Unmute$2,
2432
+ "Playback Rate": "Vitesse de lecture",
2433
+ Subtitles: Subtitles$2,
2434
+ "subtitles off": "Sous-titres désactivés",
2435
+ Captions: Captions$2,
2436
+ "captions off": "Sous-titres transcrits désactivés",
2437
+ Chapters: Chapters$2,
2438
+ Descriptions: Descriptions$2,
2439
+ "descriptions off": "descriptions désactivées",
2440
+ "Audio Track": "Piste audio",
2441
+ "Volume Level": "Niveau de volume",
2442
+ "You aborted the media playback": "Vous avez interrompu la lecture de la vidéo.",
2443
+ "A network error caused the media download to fail part-way.": "Une erreur de réseau a interrompu le téléchargement de la vidéo.",
2444
+ "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Cette vidéo n'a pas pu être chargée, soit parce que le serveur ou le réseau a échoué ou parce que le format n'est pas reconnu.",
2445
+ "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "La lecture de la vidéo a été interrompue à cause d'un problème de corruption ou parce que la vidéo utilise des fonctionnalités non prises en charge par votre navigateur.",
2446
+ "No compatible source was found for this media.": "Aucune source compatible n'a été trouvée pour cette vidéo.",
2447
+ "The media is encrypted and we do not have the keys to decrypt it.": "Le média est chiffré et nous n'avons pas les clés pour le déchiffrer.",
2448
+ "Play Video": "Lire la vidéo",
2449
+ Close: Close$2,
2450
+ "Close Modal Dialog": "Fermer la boîte de dialogue modale",
2451
+ "Modal Window": "Fenêtre modale",
2452
+ "This is a modal window": "Ceci est une fenêtre modale",
2453
+ "This modal can be closed by pressing the Escape key or activating the close button.": "Ce modal peut être fermé en appuyant sur la touche Échap ou activer le bouton de fermeture.",
2454
+ ", opens captions settings dialog": ", ouvrir les paramètres des sous-titres transcrits",
2455
+ ", opens subtitles settings dialog": ", ouvrir les paramètres des sous-titres",
2456
+ ", opens descriptions settings dialog": ", ouvrir les paramètres des descriptions",
2457
+ ", selected": ", sélectionné",
2458
+ "captions settings": "Paramètres des sous-titres transcrits",
2459
+ "subtitles settings": "Paramètres des sous-titres",
2460
+ "descriptions settings": "Paramètres des descriptions",
2461
+ Text: Text$2,
2462
+ White: White$2,
2463
+ Black: Black$2,
2464
+ Red: Red$2,
2465
+ Green: Green$2,
2466
+ Blue: Blue$2,
2467
+ Yellow: Yellow$2,
2468
+ Magenta: Magenta$2,
2469
+ Cyan: Cyan$2,
2470
+ Background: Background$2,
2471
+ Window: Window$2,
2472
+ Transparent: Transparent$2,
2473
+ "Semi-Transparent": "Semi-transparent",
2474
+ Opaque: Opaque$2,
2475
+ "Font Size": "Taille des caractères",
2476
+ "Text Edge Style": "Style des contours du texte",
2477
+ None: None$2,
2478
+ Raised: Raised$1,
2479
+ Depressed: Depressed$1,
2480
+ Uniform: Uniform$2,
2481
+ "Drop shadow": "Ombre portée",
2482
+ "Font Family": "Famille de polices",
2483
+ "Proportional Sans-Serif": "Polices à chasse variable sans empattement (Proportional Sans-Serif)",
2484
+ "Monospace Sans-Serif": "Polices à chasse fixe sans empattement (Monospace Sans-Serif)",
2485
+ "Proportional Serif": "Polices à chasse variable avec empattement (Proportional Serif)",
2486
+ "Monospace Serif": "Polices à chasse fixe avec empattement (Monospace Serif)",
2487
+ Casual: Casual$1,
2488
+ Script: Script$1,
2489
+ "Small Caps": "Petites capitales",
2490
+ Reset: Reset$2,
2491
+ "restore all settings to the default values": "Restaurer tous les paramètres aux valeurs par défaut",
2492
+ Done: Done$2,
2493
+ "Caption Settings Dialog": "Boîte de dialogue des paramètres des sous-titres transcrits",
2494
+ "Beginning of dialog window. Escape will cancel and close the window.": "Début de la fenêtre de dialogue. La touche d'échappement annulera et fermera la fenêtre.",
2495
+ "End of dialog window.": "Fin de la fenêtre de dialogue.",
2496
+ "Exit Picture-in-Picture": "Quitter le mode image dans l'image",
2497
+ "Picture-in-Picture": "Image dans l'image",
2498
+ "{1} is loading.": "{1} en cours de chargement.",
2499
+ "No content": "Aucun contenu",
2500
+ Color: Color$1,
2501
+ Opacity: Opacity$1,
2502
+ "Text Background": "Arrière-plan du texte",
2503
+ "Caption Area Background": "Arrière-plan de la zone de sous-titre",
2504
+ "Skip backward {1} seconds": "Reculer de {1} secondes",
2505
+ "Skip forward {1} seconds": "Avancer de {1} secondes"
2506
+ };
2507
+
2508
+ var vjsLang$1 = /*#__PURE__*/Object.freeze({
2509
+ __proto__: null,
2510
+ Background: Background$2,
2511
+ Black: Black$2,
2512
+ Blue: Blue$2,
2513
+ Captions: Captions$2,
2514
+ Casual: Casual$1,
2515
+ Chapters: Chapters$2,
2516
+ Close: Close$2,
2517
+ Color: Color$1,
2518
+ Cyan: Cyan$2,
2519
+ Depressed: Depressed$1,
2520
+ Descriptions: Descriptions$2,
2521
+ Done: Done$2,
2522
+ Duration: Duration$2,
2523
+ Fullscreen: Fullscreen$2,
2524
+ Green: Green$2,
2525
+ LIVE: LIVE$2,
2526
+ Loaded: Loaded$2,
2527
+ Magenta: Magenta$2,
2528
+ Mute: Mute$2,
2529
+ None: None$2,
2530
+ Opacity: Opacity$1,
2531
+ Opaque: Opaque$2,
2532
+ Pause: Pause$2,
2533
+ Play: Play$2,
2534
+ Progress: Progress$2,
2535
+ Raised: Raised$1,
2536
+ Red: Red$2,
2537
+ Replay: Replay$2,
2538
+ Reset: Reset$2,
2539
+ Script: Script$1,
2540
+ Subtitles: Subtitles$2,
2541
+ Text: Text$2,
2542
+ Transparent: Transparent$2,
2543
+ Uniform: Uniform$2,
2544
+ Unmute: Unmute$2,
2545
+ White: White$2,
2546
+ Window: Window$2,
2547
+ Yellow: Yellow$2,
2548
+ default: fr$1
2549
+ });
2550
+
2551
+ var AGERATING12$2 = "Pour protéger les enfants, ce contenu est accessible entre 20h et 6h.";
2552
+ var AGERATING18$2 = "Pour protéger les enfants, ce contenu est accessible entre 23h et 5h.";
2553
+ var COMMERCIAL$2 = "Ce contenu n'est actuellement pas disponible.";
2554
+ var ENDDATE$2 = "Ce contenu n'est plus disponible.";
2555
+ var GEOBLOCK$2 = "La RTS ne dispose pas des droits de diffusion en dehors de la Suisse.";
2556
+ var LEGAL$2 = "Pour des raisons juridiques, ce contenu n'est pas disponible.";
2557
+ var STARTDATE$2 = "Ce contenu n'est pas encore disponible. Veuillez réessayer plus tard.";
2558
+ var UNKNOWN$2 = "Ce contenu n'est actuellement pas disponible.";
2559
+ var fr = {
2560
+ AGERATING12: AGERATING12$2,
2561
+ AGERATING18: AGERATING18$2,
2562
+ COMMERCIAL: COMMERCIAL$2,
2563
+ ENDDATE: ENDDATE$2,
2564
+ GEOBLOCK: GEOBLOCK$2,
2565
+ LEGAL: LEGAL$2,
2566
+ STARTDATE: STARTDATE$2,
2567
+ UNKNOWN: UNKNOWN$2
2568
+ };
2569
+
2570
+ var pillarboxLang$2 = /*#__PURE__*/Object.freeze({
2571
+ __proto__: null,
2572
+ AGERATING12: AGERATING12$2,
2573
+ AGERATING18: AGERATING18$2,
2574
+ COMMERCIAL: COMMERCIAL$2,
2575
+ ENDDATE: ENDDATE$2,
2576
+ GEOBLOCK: GEOBLOCK$2,
2577
+ LEGAL: LEGAL$2,
2578
+ STARTDATE: STARTDATE$2,
2579
+ UNKNOWN: UNKNOWN$2,
2580
+ default: fr
2581
+ });
2582
+
2583
+ pillarbox.addLanguage('fr', _objectSpread2(_objectSpread2({}, vjsLang$1), pillarboxLang$2));
2584
+
2585
+ var Play$1 = "Play";
2586
+ var Pause$1 = "Pausa";
2587
+ var Replay$1 = "Replay";
2588
+ var Duration$1 = "Durata";
2589
+ var LIVE$1 = "LIVE";
2590
+ var Loaded$1 = "Caricato";
2591
+ var Progress$1 = "Stato";
2592
+ var Fullscreen$1 = "Schermo intero";
2593
+ var Mute$1 = "Disattiva l’audio";
2594
+ var Unmute$1 = "Attiva l’audio";
2595
+ var Subtitles$1 = "Sottotitoli";
2596
+ var Captions$1 = "Sottotitoli non udenti";
2597
+ var Chapters$1 = "Capitolo";
2598
+ var Descriptions$1 = "Descrizioni";
2599
+ var Close$1 = "Chiudi";
2600
+ var Text$1 = "Testo";
2601
+ var White$1 = "Bianco";
2602
+ var Black$1 = "Nero";
2603
+ var Red$1 = "Rosso";
2604
+ var Green$1 = "Verde";
2605
+ var Blue$1 = "Blu";
2606
+ var Yellow$1 = "Giallo";
2607
+ var Magenta$1 = "Magenta";
2608
+ var Cyan$1 = "Ciano";
2609
+ var Background$1 = "Sfondo";
2610
+ var Window$1 = "Finestra";
2611
+ var Transparent$1 = "Trasparente";
2612
+ var Opaque$1 = "Opaco";
2613
+ var None$1 = "Nessuno";
2614
+ var Uniform$1 = "Uniforme";
2615
+ var Reset$1 = "Reinizializza";
2616
+ var Done$1 = "Fatto";
2617
+ var Color = "Colore";
2618
+ var Opacity = "Opacità";
2619
+ var it$1 = {
2620
+ "Audio Player": "Lettore audio",
2621
+ "Video Player": "Lettore video",
2622
+ Play: Play$1,
2623
+ Pause: Pause$1,
2624
+ Replay: Replay$1,
2625
+ "Current Time": "Orario attuale",
2626
+ Duration: Duration$1,
2627
+ "Remaining Time": "Tempo rimanente",
2628
+ "Stream Type": "Tipo di streaming",
2629
+ LIVE: LIVE$1,
2630
+ Loaded: Loaded$1,
2631
+ Progress: Progress$1,
2632
+ "Progress Bar": "Barra di avanzamento",
2633
+ "progress bar timing: currentTime={1} duration={2}": "{1} di {2}",
2634
+ Fullscreen: Fullscreen$1,
2635
+ "Exit Fullscreen": "Chiudi Schermo intero",
2636
+ Mute: Mute$1,
2637
+ Unmute: Unmute$1,
2638
+ "Playback Rate": "Velocità di riproduzione",
2639
+ Subtitles: Subtitles$1,
2640
+ "subtitles off": "Senza sottotitoli",
2641
+ Captions: Captions$1,
2642
+ "captions off": "Senza sottotitoli non udenti",
2643
+ Chapters: Chapters$1,
2644
+ Descriptions: Descriptions$1,
2645
+ "descriptions off": "Descrizioni disattivate",
2646
+ "Audio Track": "Traccia audio",
2647
+ "Volume Level": "Livello del volume",
2648
+ "You aborted the media playback": "La riproduzione del contenuto multimediale è stata interrotta.",
2649
+ "A network error caused the media download to fail part-way.": "Il download del contenuto multimediale è stato interrotto a causa di un problema rete.",
2650
+ "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Il contenuto multimediale non può essere caricato a causa di un errore nel server o nella rete o perché il formato non viene supportato.",
2651
+ "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "La riproduzione del contenuto multimediale è stata interrotta a causa di un file danneggiato o per l’utilizzo di impostazioni non supportate dal browser.",
2652
+ "No compatible source was found for this media.": "Non ci sono fonti compatibili per questo contenuto multimediale.",
2653
+ "The media is encrypted and we do not have the keys to decrypt it.": "Il contenuto multimediale è criptato e non disponiamo delle chiavi per decifrarlo.",
2654
+ "Play Video": "Riproduci il video",
2655
+ Close: Close$1,
2656
+ "Close Modal Dialog": "Chiudi la finestra di dialogo",
2657
+ "Modal Window": "Finestra di dialogo",
2658
+ "This is a modal window": "Questa è una finestra di dialogo",
2659
+ "This modal can be closed by pressing the Escape key or activating the close button.": "Questa finestra di dialogo può essere chiusa premendo sul tasto Esc o attivando il pulsante di chiusura.",
2660
+ ", opens captions settings dialog": ", aprire i parametri della trascrizione dei sottotitoli",
2661
+ ", opens subtitles settings dialog": ", aprire i parametri dei sottotitoli",
2662
+ ", opens descriptions settings dialog": ", aprire i parametri delle descrizioni",
2663
+ ", selected": ", selezionato",
2664
+ "captions settings": "Parametri sottotitoli non udenti",
2665
+ "subtitles settings": "Parametri sottotitoli",
2666
+ "descriptions settings": "Parametri descrizioni",
2667
+ Text: Text$1,
2668
+ White: White$1,
2669
+ Black: Black$1,
2670
+ Red: Red$1,
2671
+ Green: Green$1,
2672
+ Blue: Blue$1,
2673
+ Yellow: Yellow$1,
2674
+ Magenta: Magenta$1,
2675
+ Cyan: Cyan$1,
2676
+ Background: Background$1,
2677
+ Window: Window$1,
2678
+ Transparent: Transparent$1,
2679
+ "Semi-Transparent": "Semi-Trasparente",
2680
+ Opaque: Opaque$1,
2681
+ "Font Size": "Dimensione dei caratteri",
2682
+ "Text Edge Style": "Stile dei bordi del testo",
2683
+ None: None$1,
2684
+ Uniform: Uniform$1,
2685
+ "Drop shadow": "Ombra",
2686
+ "Font Family": "Carattere",
2687
+ "Proportional Sans-Serif": "Sans-Serif proporzionale",
2688
+ "Monospace Sans-Serif": "Sans-Serif monospaziato",
2689
+ "Proportional Serif": "Serif proporzionale",
2690
+ "Monospace Serif": "Serif monospaziato",
2691
+ "Small Caps": "Maiuscoletto",
2692
+ Reset: Reset$1,
2693
+ "restore all settings to the default values": "Ripristina i valori predefiniti per tutti i parametri",
2694
+ Done: Done$1,
2695
+ "Caption Settings Dialog": "Finestra di dialogo dei parametri della trascrizione dei sottotitoli",
2696
+ "Beginning of dialog window. Escape will cancel and close the window.": "Inizio della finestra di dialogo. Il tasto Esc annullerà l’operazione e chiuderà la finestra.",
2697
+ "End of dialog window.": "Fine della finestra di dialogo.",
2698
+ "{1} is loading.": "{1} in fase di caricamento.",
2699
+ "Exit Picture-in-Picture": "Esci dalla modalità Picture-in-Picture",
2700
+ "Picture-in-Picture": "Picture-in-Picture",
2701
+ Color: Color,
2702
+ Opacity: Opacity,
2703
+ "Text Background": "Sfondo testo",
2704
+ "Caption Area Background": "Sfondo area sottotitoli",
2705
+ "Skip forward {1} seconds": "Avanti {1} secondi",
2706
+ "Skip backward {1} seconds": "Indietro {1} secondi"
2707
+ };
2708
+
2709
+ var vjsLang = /*#__PURE__*/Object.freeze({
2710
+ __proto__: null,
2711
+ Background: Background$1,
2712
+ Black: Black$1,
2713
+ Blue: Blue$1,
2714
+ Captions: Captions$1,
2715
+ Chapters: Chapters$1,
2716
+ Close: Close$1,
2717
+ Color: Color,
2718
+ Cyan: Cyan$1,
2719
+ Descriptions: Descriptions$1,
2720
+ Done: Done$1,
2721
+ Duration: Duration$1,
2722
+ Fullscreen: Fullscreen$1,
2723
+ Green: Green$1,
2724
+ LIVE: LIVE$1,
2725
+ Loaded: Loaded$1,
2726
+ Magenta: Magenta$1,
2727
+ Mute: Mute$1,
2728
+ None: None$1,
2729
+ Opacity: Opacity,
2730
+ Opaque: Opaque$1,
2731
+ Pause: Pause$1,
2732
+ Play: Play$1,
2733
+ Progress: Progress$1,
2734
+ Red: Red$1,
2735
+ Replay: Replay$1,
2736
+ Reset: Reset$1,
2737
+ Subtitles: Subtitles$1,
2738
+ Text: Text$1,
2739
+ Transparent: Transparent$1,
2740
+ Uniform: Uniform$1,
2741
+ Unmute: Unmute$1,
2742
+ White: White$1,
2743
+ Window: Window$1,
2744
+ Yellow: Yellow$1,
2745
+ default: it$1
2746
+ });
2747
+
2748
+ var AGERATING12$1 = "Per proteggere i bambini, questo media è disponibile solo fra le 20 e le 6.";
2749
+ var AGERATING18$1 = "Per proteggere i bambini, questo media è disponibile solo fra le 23 le 5.";
2750
+ var COMMERCIAL$1 = "Questo contenuto commerciale non è disponibile.";
2751
+ var ENDDATE$1 = "Questo media non è più disponibile.";
2752
+ var GEOBLOCK$1 = "Questo media non è disponibile fuori dalla Svizzera.";
2753
+ var LEGAL$1 = "Il contenuto non è fruibile a causa di restrizioni legali.";
2754
+ var STARTDATE$1 = "Il contenuto non è ancora disponibile. Per cortesia prova più tardi.";
2755
+ var UNKNOWN$1 = "Questo media non è disponibile.";
2756
+ var it = {
2757
+ AGERATING12: AGERATING12$1,
2758
+ AGERATING18: AGERATING18$1,
2759
+ COMMERCIAL: COMMERCIAL$1,
2760
+ ENDDATE: ENDDATE$1,
2761
+ GEOBLOCK: GEOBLOCK$1,
2762
+ LEGAL: LEGAL$1,
2763
+ STARTDATE: STARTDATE$1,
2764
+ UNKNOWN: UNKNOWN$1
2765
+ };
2766
+
2767
+ var pillarboxLang$1 = /*#__PURE__*/Object.freeze({
2768
+ __proto__: null,
2769
+ AGERATING12: AGERATING12$1,
2770
+ AGERATING18: AGERATING18$1,
2771
+ COMMERCIAL: COMMERCIAL$1,
2772
+ ENDDATE: ENDDATE$1,
2773
+ GEOBLOCK: GEOBLOCK$1,
2774
+ LEGAL: LEGAL$1,
2775
+ STARTDATE: STARTDATE$1,
2776
+ UNKNOWN: UNKNOWN$1,
2777
+ default: it
2778
+ });
2779
+
2780
+ pillarbox.addLanguage('it', _objectSpread2(_objectSpread2({}, vjsLang), pillarboxLang$1));
2781
+
2782
+ var Play = "Laschar ir";
2783
+ var Pause = "Pausa";
2784
+ var Replay = "Mussar danovamain";
2785
+ var Duration = "Durada";
2786
+ var LIVE = "LIVE";
2787
+ var Loaded = "Chargià";
2788
+ var Progress = "Progress";
2789
+ var Fullscreen = "Entir visur";
2790
+ var Mute = "Senza tun";
2791
+ var Unmute = "Cun tun";
2792
+ var Subtitles = "Suttitels";
2793
+ var Captions = "Suttitels";
2794
+ var Chapters = "Chapitels";
2795
+ var Descriptions = "Descripziuns";
2796
+ var Close = "Serrar";
2797
+ var Text = "Text";
2798
+ var White = "Alv";
2799
+ var Black = "Nair";
2800
+ var Red = "Cotschn";
2801
+ var Green = "Verd";
2802
+ var Blue = "Blau";
2803
+ var Yellow = "Mellen";
2804
+ var Magenta = "Magenta";
2805
+ var Cyan = "Cyan";
2806
+ var Background = "Fund";
2807
+ var Window = "Fanestra";
2808
+ var Transparent = "Transparent";
2809
+ var Opaque = "Betg transparent";
2810
+ var None = "Nagin";
2811
+ var Raised = "Auzà";
2812
+ var Depressed = "Sbassà";
2813
+ var Uniform = "Uniform";
2814
+ var Dropshadow = "Sumbriva";
2815
+ var Casual = "Casual";
2816
+ var Script = "Script";
2817
+ var Reset = "Da nov";
2818
+ var Done = "Fatg";
2819
+ var AGERATING12 = "Per proteger uffants, è quest cuntegn disponibel mo tranter las 20.00 e las 06.00.";
2820
+ var AGERATING18 = "Per proteger uffants, è quest cuntegn disponibel mo tranter las 23.00 e las 05.00.";
2821
+ var COMMERCIAL = "Quest medium commerzial n'è betg disponibel.";
2822
+ var ENDDATE = "Quest cuntegn n'è betg pli disponibel.";
2823
+ var GEOBLOCK = "Quest cuntegn n'è betg disponibel ordaifer la Svizra.";
2824
+ var LEGAL = "Quest cuntegn n'è betg disponibel perquai ch'el è scadì.";
2825
+ var STARTDATE = "Quest cuntegn n'è betg anc disponibel. Empruvai pli tard.";
2826
+ var UNKNOWN = "Quest cuntegn n'è betg disponibel.";
2827
+ var rm = {
2828
+ "Audio Player": "Audio-Player",
2829
+ "Video Player": "Video-Player",
2830
+ Play: Play,
2831
+ Pause: Pause,
2832
+ Replay: Replay,
2833
+ "Current Time": "Temp actual",
2834
+ Duration: Duration,
2835
+ "Remaining Time": "Temp restant",
2836
+ "Stream Type": "Tip dal stream",
2837
+ LIVE: LIVE,
2838
+ Loaded: Loaded,
2839
+ Progress: Progress,
2840
+ "Progress Bar": "Bar da progessiun",
2841
+ "progress bar timing: currentTime={1} duration={2}": "{1} da {2}",
2842
+ Fullscreen: Fullscreen,
2843
+ "Non-Fullscreen": "Betg entir visur",
2844
+ Mute: Mute,
2845
+ Unmute: Unmute,
2846
+ "Playback Rate": "Tempo ",
2847
+ Subtitles: Subtitles,
2848
+ "subtitles off": "senza suttitels",
2849
+ Captions: Captions,
2850
+ "captions off": "senza suttitels",
2851
+ Chapters: Chapters,
2852
+ Descriptions: Descriptions,
2853
+ "descriptions off": "senza descripziuns",
2854
+ "Audio Track": "Piese audio",
2855
+ "Volume Level": "Nivel dal volumen",
2856
+ "You aborted the media playback": "Vus avais interrut il vdieo",
2857
+ "A network error caused the media download to fail part-way.": "In sbagl en la rait ha impedì il download",
2858
+ "The media could not be loaded, either because the server or network failed or because the format is not supported.": "Il video n'è betg chargià - ubain per in sbagl da server / da la rait, ubain ch'il format n'è betg cumpatibel.",
2859
+ "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Il video è interrut: Ubain ch'il video è donnegià, ubain che funcziuns n'èn betg cumpatiblas.",
2860
+ "No compatible source was found for this media.": "Chattà nagina funtauna cumpatibla per quest video.",
2861
+ "The media is encrypted and we do not have the keys to decrypt it.": "Il video è codifitgà da moda nunenconuschenta.",
2862
+ "Play Video": "Aviar video",
2863
+ Close: Close,
2864
+ "Close Modal Dialog": "Serrar la fanestra modala",
2865
+ "Modal Window": "Fanestra modala",
2866
+ "This is a modal window": "Quai è ina fanestra modala",
2867
+ "This modal can be closed by pressing the Escape key or activating the close button.": "Questa fanestra modala pudais serrar cun la tasta \"Escape\" ubain cun il buttun.",
2868
+ ", opens captions settings dialog": ", avra opziuns per ils suttitels",
2869
+ ", opens subtitles settings dialog": ", avra opziuns per ils suttitels",
2870
+ ", opens descriptions settings dialog": ", avra opziuns per la descripziun",
2871
+ ", selected": ", selecziunà",
2872
+ "captions settings": "opziuns per ils suttitels",
2873
+ "subtitles settings": "opziuns per ils suttitels",
2874
+ "descriptions settings": "opziuns per la descripziun",
2875
+ Text: Text,
2876
+ White: White,
2877
+ Black: Black,
2878
+ Red: Red,
2879
+ Green: Green,
2880
+ Blue: Blue,
2881
+ Yellow: Yellow,
2882
+ Magenta: Magenta,
2883
+ Cyan: Cyan,
2884
+ Background: Background,
2885
+ Window: Window,
2886
+ Transparent: Transparent,
2887
+ "Semi-Transparent": "Mez transparent",
2888
+ Opaque: Opaque,
2889
+ "Font Size": "Grandezza dal text",
2890
+ "Text Edge Style": "Stil dal text",
2891
+ None: None,
2892
+ Raised: Raised,
2893
+ Depressed: Depressed,
2894
+ Uniform: Uniform,
2895
+ Dropshadow: Dropshadow,
2896
+ "Font Family": "Scrittira",
2897
+ "Proportional Sans-Serif": "Proportionale Sans-Serif",
2898
+ "Monospace Sans-Serif": "Monospace Sans-Serif",
2899
+ "Proportional Serif": "Proportionale Serif",
2900
+ "Monospace Serif": "Monospace Serif",
2901
+ Casual: Casual,
2902
+ Script: Script,
2903
+ "Small Caps": "Bustabs pitschens",
2904
+ Reset: Reset,
2905
+ "restore all settings to the default values": "Enavos tar las opziuns da standard",
2906
+ Done: Done,
2907
+ "Caption Settings Dialog": "Opziuns per suttitels",
2908
+ "Beginning of dialog window. Escape will cancel and close the window.": "Entschatta da la fanestra da dialog. Escape stizza e serra la fanestra.",
2909
+ "End of dialog window.": "Fin da la fanestra da dialog.",
2910
+ AGERATING12: AGERATING12,
2911
+ AGERATING18: AGERATING18,
2912
+ COMMERCIAL: COMMERCIAL,
2913
+ ENDDATE: ENDDATE,
2914
+ GEOBLOCK: GEOBLOCK,
2915
+ LEGAL: LEGAL,
2916
+ STARTDATE: STARTDATE,
2917
+ UNKNOWN: UNKNOWN
2918
+ };
2919
+
2920
+ var pillarboxLang = /*#__PURE__*/Object.freeze({
2921
+ __proto__: null,
2922
+ AGERATING12: AGERATING12,
2923
+ AGERATING18: AGERATING18,
2924
+ Background: Background,
2925
+ Black: Black,
2926
+ Blue: Blue,
2927
+ COMMERCIAL: COMMERCIAL,
2928
+ Captions: Captions,
2929
+ Casual: Casual,
2930
+ Chapters: Chapters,
2931
+ Close: Close,
2932
+ Cyan: Cyan,
2933
+ Depressed: Depressed,
2934
+ Descriptions: Descriptions,
2935
+ Done: Done,
2936
+ Dropshadow: Dropshadow,
2937
+ Duration: Duration,
2938
+ ENDDATE: ENDDATE,
2939
+ Fullscreen: Fullscreen,
2940
+ GEOBLOCK: GEOBLOCK,
2941
+ Green: Green,
2942
+ LEGAL: LEGAL,
2943
+ LIVE: LIVE,
2944
+ Loaded: Loaded,
2945
+ Magenta: Magenta,
2946
+ Mute: Mute,
2947
+ None: None,
2948
+ Opaque: Opaque,
2949
+ Pause: Pause,
2950
+ Play: Play,
2951
+ Progress: Progress,
2952
+ Raised: Raised,
2953
+ Red: Red,
2954
+ Replay: Replay,
2955
+ Reset: Reset,
2956
+ STARTDATE: STARTDATE,
2957
+ Script: Script,
2958
+ Subtitles: Subtitles,
2959
+ Text: Text,
2960
+ Transparent: Transparent,
2961
+ UNKNOWN: UNKNOWN,
2962
+ Uniform: Uniform,
2963
+ Unmute: Unmute,
2964
+ White: White,
2965
+ Window: Window,
2966
+ Yellow: Yellow,
2967
+ default: rm
2968
+ });
2969
+
2970
+ pillarbox.addLanguage('rm', _objectSpread2({}, pillarboxLang));
2971
+
2972
+ const _excluded = ["url", "mimeType", "keySystems"],
2973
+ _excluded2 = ["src"];
2974
+
2975
+ /**
2976
+ * @class SrgSsr
2977
+ */
2978
+ class SrgSsr {
2979
+ /**
2980
+ * Adds blocked segments to the player.
2981
+ *
2982
+ * @param {import('video.js/dist/types/player').default} player
2983
+ * @param {Array<import('../dataProvider/model/typedef').Segment>} [segments=[]]
2984
+ */
2985
+ static addBlockedSegments(player, segments = []) {
2986
+ const trackId = 'srgssr-blocked-segments';
2987
+ const removeTrack = player.textTracks().getTrackById(trackId);
2988
+ if (removeTrack) {
2989
+ player.textTracks().removeTrack(removeTrack);
2990
+ }
2991
+ if (!Array.isArray(segments) || !segments.length) return;
2992
+ const blockedSegments = segments.filter(segment => segment.blockReason);
2993
+ if (!blockedSegments.length) return;
2994
+ SrgSsr.createTextTrack(player, trackId).then(segmentTrack => {
2995
+ blockedSegments.forEach(segment => {
2996
+ SrgSsr.addTextTrackCue(segmentTrack, segment);
2997
+ });
2998
+ player.textTracks().addTrack(segmentTrack);
2999
+ });
3000
+ }
3001
+
3002
+ /**
3003
+ * Adds remote text tracks from an array of subtitles.
3004
+ *
3005
+ * @param {import('video.js/dist/types/player').default} player
3006
+ * @param {Array<import('../dataProvider/model/typedef').Subtitle>} [subtitles=[]]
3007
+ */
3008
+ static addRemoteTextTracks(player, subtitles = []) {
3009
+ if (!Array.isArray(subtitles)) return;
3010
+ subtitles.forEach(({
3011
+ type,
3012
+ language: label,
3013
+ locale: language,
3014
+ url: src
3015
+ }) => {
3016
+ player.addRemoteTextTrack({
3017
+ kind: type === 'SDH' ? 'captions' : 'subtitles',
3018
+ label,
3019
+ language,
3020
+ src
3021
+ });
3022
+ });
3023
+ }
3024
+
3025
+ /**
3026
+ * Add a new cue to a text track with the given data.
3027
+ *
3028
+ * @param {TextTrack} textTrack
3029
+ * @param {
3030
+ * import('../dataProvider/model/typedef').Segment |
3031
+ * import('../dataProvider/model/typedef').Chapter |
3032
+ * import('../dataProvider/model/typedef').TimeInterval
3033
+ * } data SRG SSR's cue-like representation
3034
+ */
3035
+ static addTextTrackCue(textTrack, data) {
3036
+ const startTime = (Number.isFinite(data.markIn) ? data.markIn : data.fullLengthMarkIn) / 1000;
3037
+ const endTime = (Number.isFinite(data.markOut) ? data.markOut : data.fullLengthMarkOut) / 1000;
3038
+ textTrack.addCue(new VTTCue(startTime, endTime, JSON.stringify(data)));
3039
+ }
3040
+
3041
+ /**
3042
+ * Add multiple text tracks to the player.
3043
+ *
3044
+ * @param {import('video.js/dist/types/player').default} player
3045
+ * @param {import('./typedef').ComposedSrcMediaData} srcMediaObj
3046
+ */
3047
+ static addTextTracks(player, {
3048
+ mediaData
3049
+ }) {
3050
+ SrgSsr.addRemoteTextTracks(player, mediaData.subtitles);
3051
+ SrgSsr.addChapters(player, mediaData.urn, mediaData.chapters);
3052
+ SrgSsr.addBlockedSegments(player, mediaData.blockedSegments);
3053
+ SrgSsr.addIntervals(player, mediaData.intervals);
3054
+ }
3055
+
3056
+ /**
3057
+ * Adds chapters to the player.
3058
+ *
3059
+ * @param {import('video.js/dist/types/player').default} player
3060
+ * @param {string} chapterUrn The URN of the main chapter.
3061
+ * @param {Array.<import('../dataProvider/model/typedef').Chapter>} [chapters=[]]
3062
+ */
3063
+ static addChapters(player, chapterUrn, chapters = []) {
3064
+ const trackId = 'srgssr-chapters';
3065
+ const removeTrack = player.textTracks().getTrackById(trackId);
3066
+ if (removeTrack) {
3067
+ player.textTracks().removeTrack(removeTrack);
3068
+ }
3069
+ if (!Array.isArray(chapters) || !chapters.length) return;
3070
+ SrgSsr.createTextTrack(player, trackId).then(chapterTrack => {
3071
+ chapters.forEach(chapter => {
3072
+ if (chapterUrn !== chapter.fullLengthUrn) return;
3073
+ SrgSsr.addTextTrackCue(chapterTrack, chapter);
3074
+ });
3075
+ player.textTracks().addTrack(chapterTrack);
3076
+ });
3077
+ }
3078
+
3079
+ /**
3080
+ * Adds intervals to the player.
3081
+ *
3082
+ * @param {import('video.js/dist/types/player').default} player
3083
+ * @param {Array.<import('../dataProvider/model/typedef').TimeInterval>} [intervals=[]]
3084
+ */
3085
+ static addIntervals(player, intervals = []) {
3086
+ const trackId = 'srgssr-intervals';
3087
+ const removeTrack = player.textTracks().getTrackById(trackId);
3088
+ if (removeTrack) {
3089
+ player.textTracks().removeTrack(removeTrack);
3090
+ }
3091
+ if (!Array.isArray(intervals) || !intervals.length) return;
3092
+ SrgSsr.createTextTrack(player, trackId).then(intervalTrack => {
3093
+ intervals.forEach(interval => {
3094
+ SrgSsr.addTextTrackCue(intervalTrack, interval);
3095
+ });
3096
+ player.textTracks().addTrack(intervalTrack);
3097
+ });
3098
+ }
3099
+
3100
+ /**
3101
+ * Set a blocking reason according to the block reason returned
3102
+ * by mediaData.
3103
+ *
3104
+ * @param {import('video.js/dist/types/player').default} player
3105
+ * @param {import('./typedef').ComposedSrcMediaData} srcMediaObj
3106
+ *
3107
+ * @returns {undefined|Boolean}
3108
+ */
3109
+ static blockingReason(player, srcMediaObj) {
3110
+ if (!srcMediaObj.mediaData.blockReason) return;
3111
+ const message = player.localize(srcMediaObj.mediaData.blockReason);
3112
+ SrgSsr.error(player, {
3113
+ code: MediaError.MEDIA_ERR_ABORTED,
3114
+ message,
3115
+ metadata: {
3116
+ errorType: srcMediaObj.mediaData.blockReason,
3117
+ src: srcMediaObj
3118
+ }
3119
+ });
3120
+ return true;
3121
+ }
3122
+
3123
+ /**
3124
+ * Add the Akamai token to all resources
3125
+ * if at least one of them has tokenType
3126
+ * set to Akamai.
3127
+ *
3128
+ * @param {Array.<import('./typedef').MainResourceWithKeySystems>} resources
3129
+ *
3130
+ * @returns {Promise<Array.<import('./typedef').MainResourceWithKeySystems>>}
3131
+ */
3132
+ static composeAkamaiResources(resources = []) {
3133
+ return _asyncToGenerator(function* () {
3134
+ if (!AkamaiTokenService.hasToken(resources)) {
3135
+ return Promise.resolve(resources);
3136
+ }
3137
+
3138
+ // TODO allow to modify the Akamai URL
3139
+ return AkamaiTokenService.tokenizeSources(resources);
3140
+ })();
3141
+ }
3142
+
3143
+ /**
3144
+ * Add the keySystems property to all resources
3145
+ * if at least one of them has DRM.
3146
+ *
3147
+ * @param {Array.<import('../dataProvider/model/typedef').MainResource>} resources
3148
+ *
3149
+ * @returns {Array.<import('./typedef').MainResourceWithKeySystems>}
3150
+ */
3151
+ static composeKeySystemsResources(resources = []) {
3152
+ if (!Drm.hasDrm(resources)) ;
3153
+ return resources.map(resource => _objectSpread2(_objectSpread2({}, resource), Drm.buildKeySystems(resource.drmList)));
3154
+ }
3155
+
3156
+ /**
3157
+ * Get the main resources from a mediaComposition.
3158
+ * May add an Akamai token or key systems if required by the resource.
3159
+ *
3160
+ * @param {MediaComposition} mediaComposition
3161
+ *
3162
+ * @returns {Promise<Array.<import('./typedef').MainResourceWithKeySystems>>}
3163
+ */
3164
+ static composeMainResources(mediaComposition) {
3165
+ return SrgSsr.composeAkamaiResources(SrgSsr.composeKeySystemsResources(SrgSsr.filterIncompatibleResources(mediaComposition.getMainResources())));
3166
+ }
3167
+
3168
+ /**
3169
+ * Compose source options with media data.
3170
+ * MediaData properties from source options overwrite mediaData from IL.
3171
+ *
3172
+ * @param {any} srcObj
3173
+ * @param {any} srcObj.mediaData overrides or adds metadata to the composed mediaData.
3174
+ * @param {boolean} srcObj.disableTrackers
3175
+ * @param {import('./typedef').MainResourceWithKeySystems} resource
3176
+ *
3177
+ * @returns {import('./typedef').ComposedSrcMediaData}
3178
+ */
3179
+ static composeSrcMediaData({
3180
+ mediaData: srcMediaData,
3181
+ disableTrackers
3182
+ }, resource) {
3183
+ const _Pillarbox$obj$merge = pillarbox.obj.merge(resource, srcMediaData),
3184
+ {
3185
+ url,
3186
+ mimeType,
3187
+ keySystems
3188
+ } = _Pillarbox$obj$merge,
3189
+ mediaData = _objectWithoutProperties(_Pillarbox$obj$merge, _excluded);
3190
+ return {
3191
+ src: url,
3192
+ type: mimeType,
3193
+ keySystems,
3194
+ disableTrackers,
3195
+ mediaData
3196
+ };
3197
+ }
3198
+
3199
+ /**
3200
+ * Create a new metadata text track.
3201
+ *
3202
+ * @param {import('video.js/dist/types/player').default} player
3203
+ * @param {String} trackId Text track unique ID
3204
+ *
3205
+ * @returns {Promise<TextTrack>}
3206
+ */
3207
+ static createTextTrack(player, trackId) {
3208
+ // See https://github.com/videojs/video.js/issues/8519
3209
+ return new Promise(resolve => {
3210
+ setTimeout(() => {
3211
+ resolve(new pillarbox.TextTrack({
3212
+ id: trackId,
3213
+ kind: 'metadata',
3214
+ label: trackId,
3215
+ tech: player.tech(true)
3216
+ }));
3217
+ }, 100);
3218
+ });
3219
+ }
3220
+
3221
+ /**
3222
+ * Proxy SRG SSR chapters and intervals cuechange events at player level.
3223
+ *
3224
+ * @param {import('video.js/dist/types/player').default} player
3225
+ */
3226
+ static cuechangeEventProxy(player) {
3227
+ player.textTracks().on('addtrack', ({
3228
+ track
3229
+ }) => {
3230
+ if (!['srgssr-chapters', 'srgssr-intervals'].includes(track.id)) return;
3231
+ track.on('cuechange', () => {
3232
+ const [cue] = Array.from(track.activeCues);
3233
+ const type = track.id.includes('srgssr-chapters') ? 'srgssr/chapter' : 'srgssr/interval';
3234
+ player.trigger({
3235
+ type,
3236
+ data: cue
3237
+ });
3238
+ });
3239
+ });
3240
+ }
3241
+
3242
+ /**
3243
+ * SRG SSR data provider singleton.
3244
+ *
3245
+ * @param {import('video.js/dist/types/player').default} player
3246
+ *
3247
+ * @returns {DataProvider}
3248
+ */
3249
+ static dataProvider(player) {
3250
+ if (!player.options().srgOptions.dataProvider) {
3251
+ const {
3252
+ dataProviderHost,
3253
+ dataProviderUrlHandler
3254
+ } = player.options().srgOptions;
3255
+ const dataProvider = new DataProvider(dataProviderHost);
3256
+ const requestHandler = dataProvider.handleRequest(dataProviderUrlHandler);
3257
+ player.options({
3258
+ srgOptions: {
3259
+ dataProvider: requestHandler
3260
+ }
3261
+ });
3262
+ }
3263
+ return player.options().srgOptions.dataProvider;
3264
+ }
3265
+
3266
+ /**
3267
+ * Set an error if something goes wrong with the data provider.
3268
+ *
3269
+ * @param {import('video.js/dist/types/player').default} player
3270
+ * @param {Object} error
3271
+ *
3272
+ * @returns {undefined|true}
3273
+ */
3274
+ static dataProviderError(player, error) {
3275
+ if (!error) return;
3276
+ const statusText = error.statusText ? error.statusText : error.message;
3277
+ SrgSsr.error(player, {
3278
+ code: 0,
3279
+ message: player.localize('UNKNOWN'),
3280
+ metadata: {
3281
+ errorType: 'UNKNOWN',
3282
+ urn: player.src(),
3283
+ status: error.status,
3284
+ statusText,
3285
+ url: error.url
3286
+ }
3287
+ });
3288
+ return true;
3289
+ }
3290
+
3291
+ /**
3292
+ * Set player error.
3293
+ *
3294
+ * @param {import('video.js/dist/types/player').default} player
3295
+ * @param {Object} error
3296
+ */
3297
+ static error(player, {
3298
+ code,
3299
+ message,
3300
+ metadata
3301
+ }) {
3302
+ player.error(null);
3303
+ player.error({
3304
+ code,
3305
+ message,
3306
+ metadata
3307
+ });
3308
+ }
3309
+
3310
+ /**
3311
+ * Filter out incompatible resources such as `RTMP` and `HDS`.
3312
+ *
3313
+ * @param {Array.<import('../dataProvider/model/typedef').MainResource>} resources Resources to filter
3314
+ *
3315
+ * @returns {Array.<import('../dataProvider/model/typedef').MainResource>} The filtered resources
3316
+ */
3317
+ static filterIncompatibleResources(resources = []) {
3318
+ return resources.filter(resource => !['RTMP', 'HDS'].includes(resource.streaming));
3319
+ }
3320
+
3321
+ /**
3322
+ * Get the current blocked segment from the player.
3323
+ *
3324
+ * @param {import('video.js/dist/types/player').default} player
3325
+ *
3326
+ * @returns {VTTCue|undefined} The blocked segment cue object, or undefined
3327
+ */
3328
+ static getBlockedSegment(player) {
3329
+ const trackId = 'srgssr-blocked-segments';
3330
+ const blockedSegmentsTrack = player.textTracks().getTrackById(trackId);
3331
+ if (!blockedSegmentsTrack) return;
3332
+
3333
+ /** @type {VTTCue} */
3334
+ const [blockedCue] = Array.from(blockedSegmentsTrack.activeCues);
3335
+ return blockedCue;
3336
+ }
3337
+
3338
+ /**
3339
+ * Get the VTT cue of a blocked segment.
3340
+ *
3341
+ * @param {import('video.js/dist/types/player').default} player
3342
+ * @param {Number} currentTime
3343
+ *
3344
+ * @returns {VTTCue|undefined} The VTT cue of a blocked segment, or undefined
3345
+ */
3346
+ static getBlockedSegmentByTime(player, currentTime) {
3347
+ const blockedSegment = SrgSsr.getBlockedSegment(player);
3348
+ if (!blockedSegment) return;
3349
+ const isBlocked = currentTime >= blockedSegment.startTime && currentTime < blockedSegment.endTime;
3350
+ return isBlocked ? blockedSegment : undefined;
3351
+ }
3352
+
3353
+ /**
3354
+ * Get mediaComposition from an URN.
3355
+ *
3356
+ * @param {String} urn
3357
+ * @param {Function} requestHandler
3358
+ *
3359
+ * @returns {Promise<MediaComposition>}
3360
+ */
3361
+ static getMediaComposition(urn, handleRequest = new DataProvider().handleRequest()) {
3362
+ return _asyncToGenerator(function* () {
3363
+ const data = yield handleRequest(urn);
3364
+ return Object.assign(new MediaComposition(), data);
3365
+ })();
3366
+ }
3367
+
3368
+ /**
3369
+ * Get the mediaData most likely to be compatible depending on the browser.
3370
+ *
3371
+ * @param {Array.<import('./typedef').MainResourceWithKeySystems>} resources
3372
+ *
3373
+ * @returns {import('./typedef').MainResourceWithKeySystems} By default, the first entry is used if none is compatible.
3374
+ */
3375
+ static getMediaData(resources = []) {
3376
+ if (AkamaiTokenService.hasToken(resources)) return resources[0];
3377
+ const type = pillarbox.browser.IS_ANY_SAFARI ? 'HLS' : 'DASH';
3378
+ const resource = resources.find(({
3379
+ streaming
3380
+ }) => streaming === type);
3381
+ return resource || resources[0];
3382
+ }
3383
+
3384
+ /**
3385
+ * Get the source media object.
3386
+ *
3387
+ * @param {import('video.js/dist/types/player').default} player
3388
+ * @param {any} srcObj
3389
+ *
3390
+ * @returns {Promise<import('./typedef').ComposedSrcMediaData>} - The composed source media data.
3391
+ */
3392
+ static getSrcMediaObj(player, srcObj) {
3393
+ return _asyncToGenerator(function* () {
3394
+ const {
3395
+ src: urn
3396
+ } = srcObj,
3397
+ srcOptions = _objectWithoutProperties(srcObj, _excluded2);
3398
+ const mediaComposition = yield SrgSsr.getMediaComposition(urn, SrgSsr.dataProvider(player));
3399
+ const mainResources = yield SrgSsr.composeMainResources(mediaComposition);
3400
+ const mediaData = SrgSsr.getMediaData(mainResources);
3401
+ return SrgSsr.composeSrcMediaData(srcOptions, mediaData);
3402
+ })();
3403
+ }
3404
+
3405
+ /**
3406
+ * Handles the middleware's currentTime function.
3407
+ * - If the current time is between the start and end of a blocked segment,
3408
+ * the blocked portion will be skipped.
3409
+ *
3410
+ * _Note_: This function should disappear as soon as this behavior is
3411
+ * supported on the packaging side.
3412
+ *
3413
+ * @param {import('video.js/dist/types/player').default} player
3414
+ * @param {number} currentTime
3415
+ *
3416
+ * @returns {number}
3417
+ */
3418
+ static handleCurrentTime(player, currentTime) {
3419
+ const blockedSegment = SrgSsr.getBlockedSegmentByTime(player, currentTime);
3420
+ if (!blockedSegment || !Number.isFinite(blockedSegment.endTime)) {
3421
+ return currentTime;
3422
+ }
3423
+
3424
+ // as a workaround, add 0.1 seconds to avoid getting stuck on endTime on
3425
+ // some safaris.
3426
+ const endTimeWithTolerance = blockedSegment.endTime + 0.1;
3427
+
3428
+ // proxy for handling cuechange events at the player level
3429
+ player.trigger({
3430
+ type: 'srgssr/blocked-segment',
3431
+ data: blockedSegment
3432
+ });
3433
+ player.currentTime(endTimeWithTolerance);
3434
+ return endTimeWithTolerance;
3435
+ }
3436
+
3437
+ /**
3438
+ * Handles the middleware's setCurrentTime function.
3439
+ * - Modify the current time if the value is between the start and end of a
3440
+ * blocked segment.
3441
+ *
3442
+ * _Note_: This function should disappear as soon as this behavior is
3443
+ * supported on the packaging side.
3444
+ *
3445
+ * @param {import('video.js/dist/types/player').default} player
3446
+ * @param {number} currentTime
3447
+ *
3448
+ * @returns {number}
3449
+ */
3450
+ static handleSetCurrentTime(player, currentTime) {
3451
+ const {
3452
+ endTime: blockedSegmentEndTime
3453
+ } = SrgSsr.getBlockedSegmentByTime(player, currentTime) || {};
3454
+ return Number.isFinite(blockedSegmentEndTime) ? blockedSegmentEndTime : currentTime;
3455
+ }
3456
+
3457
+ /**
3458
+ * Handles the middleware's setSource function.
3459
+ *
3460
+ * This function allows to:
3461
+ * - resolve a URN into media that can be played by the player
3462
+ * - initialize media playback tracking
3463
+ * - update the title bar
3464
+ * - handle blocking reasons
3465
+ * - add remote subtitles
3466
+ *
3467
+ * @param {import('video.js/dist/types/player').default} player
3468
+ * @param {any} srcObj
3469
+ * @param {function} next
3470
+ *
3471
+ * @returns {Promise<any>}
3472
+ */
3473
+ static handleSetSource(player, srcObj, next) {
3474
+ return _asyncToGenerator(function* () {
3475
+ try {
3476
+ const srcMediaObj = yield SrgSsr.getSrcMediaObj(player, srcObj);
3477
+ SrgSsr.srgAnalytics(player);
3478
+ SrgSsr.updateTitleBar(player, srcMediaObj);
3479
+ SrgSsr.updatePoster(player, srcMediaObj);
3480
+ if (SrgSsr.blockingReason(player, srcMediaObj)) return;
3481
+ SrgSsr.addTextTracks(player, srcMediaObj);
3482
+ return next(null, srcMediaObj);
3483
+ } catch (error) {
3484
+ if (SrgSsr.dataProviderError(player, error)) return;
3485
+ return next(error);
3486
+ }
3487
+ })();
3488
+ }
3489
+
3490
+ /**
3491
+ * SRG SSR analytics singleton.
3492
+ *
3493
+ * @param {import('video.js/dist/types/player').default} player
3494
+ */
3495
+ static srgAnalytics(player) {
3496
+ if (player.options().trackers.srgAnalytics === false) return;
3497
+ if (!player.options().trackers.srgAnalytics) {
3498
+ const srgAnalytics = new SRGAnalytics(player, {
3499
+ debug: player.debug(),
3500
+ playerVersion: pillarbox.VERSION.pillarbox,
3501
+ tagCommanderScriptURL: player.options().srgOptions.tagCommanderScriptURL
3502
+ });
3503
+ player.options({
3504
+ trackers: {
3505
+ srgAnalytics
3506
+ }
3507
+ });
3508
+ }
3509
+ }
3510
+
3511
+ /**
3512
+ * Update player's poster.
3513
+ *
3514
+ * @param {import('video.js/dist/types/player').default} player
3515
+ * @param {import('./typedef').ComposedSrcMediaData} srcMediaObj
3516
+ * @param {Image} imageService
3517
+ */
3518
+ static updatePoster(player, srcMediaObj, imageService = Image) {
3519
+ player.poster(imageService.scale({
3520
+ url: srcMediaObj.mediaData.imageUrl
3521
+ }));
3522
+ }
3523
+
3524
+ /**
3525
+ * Update player titleBar with title and description.
3526
+ *
3527
+ * @param {import('video.js/dist/types/player').default} player
3528
+ * @param {import('./typedef').ComposedSrcMediaData} srcMediaObj
3529
+ */
3530
+ static updateTitleBar(player, srcMediaObj) {
3531
+ if (!player.titleBar) return;
3532
+ player.titleBar.update({
3533
+ title: srcMediaObj.mediaData.vendor,
3534
+ description: srcMediaObj.mediaData.title
3535
+ });
3536
+ }
3537
+
3538
+ /**
3539
+ * Middleware to resolve SRG SSR URNs into playable media.
3540
+ *
3541
+ * @param {import('video.js/dist/types/player').default} player
3542
+ *
3543
+ * @returns {Object}
3544
+ */
3545
+ static middleware(player) {
3546
+ SrgSsr.cuechangeEventProxy(player);
3547
+ return {
3548
+ currentTime: currentTime => SrgSsr.handleCurrentTime(player, currentTime),
3549
+ setCurrentTime: currentTime => SrgSsr.handleSetCurrentTime(player, currentTime),
3550
+ setSource: function () {
3551
+ var _ref = _asyncToGenerator(function* (srcObj, next) {
3552
+ return SrgSsr.handleSetSource(player, srcObj, next);
3553
+ });
3554
+ return function setSource(_x, _x2) {
3555
+ return _ref.apply(this, arguments);
3556
+ };
3557
+ }()
3558
+ };
3559
+ }
3560
+ }
3561
+ pillarbox.use('srgssr/urn', SrgSsr.middleware);
3562
+
3563
+ // Add Middleware specific options
3564
+ pillarbox.options.srgOptions = {
3565
+ dataProvider: undefined,
3566
+ dataProviderHost: undefined,
3567
+ dataProviderUrlHandler: undefined,
3568
+ tagCommanderScriptURL: undefined
3569
+ };
3570
+
3571
+ module.exports = pillarbox;