@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.
- package/LICENSE +21 -0
- package/dist/pillarbox-core.cjs.js +307 -0
- package/dist/pillarbox-core.cjs.min.js +2 -0
- package/dist/pillarbox-core.cjs.min.js.map +1 -0
- package/dist/pillarbox-core.es.js +305 -0
- package/dist/pillarbox-core.es.min.js +2 -0
- package/dist/pillarbox-core.es.min.js.map +1 -0
- package/dist/pillarbox-core.umd.js +68327 -0
- package/dist/pillarbox-core.umd.min.js +35 -0
- package/dist/pillarbox-core.umd.min.js.map +1 -0
- package/dist/pillarbox.cjs.js +3571 -0
- package/dist/pillarbox.cjs.min.js +2 -0
- package/dist/pillarbox.cjs.min.js.map +1 -0
- package/dist/pillarbox.es.js +3569 -0
- package/dist/pillarbox.es.min.js +2 -0
- package/dist/pillarbox.es.min.js.map +1 -0
- package/dist/pillarbox.min.css +1 -0
- package/dist/pillarbox.min.css.map +1 -0
- package/dist/pillarbox.umd.js +71591 -0
- package/dist/pillarbox.umd.min.js +35 -0
- package/dist/pillarbox.umd.min.js.map +1 -0
- package/dist/types/build.es.d.ts +5 -0
- package/dist/types/build.es.d.ts.map +1 -0
- package/dist/types/src/analytics/SRGAnalytics.d.ts +414 -0
- package/dist/types/src/analytics/SRGAnalytics.d.ts.map +1 -0
- package/dist/types/src/components/player.d.ts +116 -0
- package/dist/types/src/components/player.d.ts.map +1 -0
- package/dist/types/src/components/typedef.d.ts +14 -0
- package/dist/types/src/components/typedef.d.ts.map +1 -0
- package/dist/types/src/dataProvider/model/MediaComposition.d.ts +154 -0
- package/dist/types/src/dataProvider/model/MediaComposition.d.ts.map +1 -0
- package/dist/types/src/dataProvider/model/typedef.d.ts +1485 -0
- package/dist/types/src/dataProvider/model/typedef.d.ts.map +1 -0
- package/dist/types/src/dataProvider/services/DataProvider.d.ts +40 -0
- package/dist/types/src/dataProvider/services/DataProvider.d.ts.map +1 -0
- package/dist/types/src/lang/de.d.ts +2 -0
- package/dist/types/src/lang/de.d.ts.map +1 -0
- package/dist/types/src/lang/en.d.ts +2 -0
- package/dist/types/src/lang/en.d.ts.map +1 -0
- package/dist/types/src/lang/fr.d.ts +2 -0
- package/dist/types/src/lang/fr.d.ts.map +1 -0
- package/dist/types/src/lang/it.d.ts +2 -0
- package/dist/types/src/lang/it.d.ts.map +1 -0
- package/dist/types/src/lang/rm.d.ts +2 -0
- package/dist/types/src/lang/rm.d.ts.map +1 -0
- package/dist/types/src/middleware/srgssr.d.ts +271 -0
- package/dist/types/src/middleware/srgssr.d.ts.map +1 -0
- package/dist/types/src/middleware/typedef.d.ts +67 -0
- package/dist/types/src/middleware/typedef.d.ts.map +1 -0
- package/dist/types/src/pillarbox.d.ts +11 -0
- package/dist/types/src/pillarbox.d.ts.map +1 -0
- package/dist/types/src/utils/AkamaiTokenService.d.ts +73 -0
- package/dist/types/src/utils/AkamaiTokenService.d.ts.map +1 -0
- package/dist/types/src/utils/Drm.d.ts +31 -0
- package/dist/types/src/utils/Drm.d.ts.map +1 -0
- package/dist/types/src/utils/Image.d.ts +33 -0
- package/dist/types/src/utils/Image.d.ts.map +1 -0
- package/dist/types/src/utils/PlayerEvents.d.ts +177 -0
- package/dist/types/src/utils/PlayerEvents.d.ts.map +1 -0
- package/dist/types/src/utils/typedef.d.ts +17 -0
- package/dist/types/src/utils/typedef.d.ts.map +1 -0
- package/package.json +109 -0
- package/scss/components/_big-play.scss +30 -0
- package/scss/components/_captions-settings.scss +71 -0
- package/scss/components/_control-bar.scss +15 -0
- package/scss/components/_control-spacer.scss +9 -0
- package/scss/components/_control.scss +8 -0
- package/scss/components/_error.scss +10 -0
- package/scss/components/_layout.scss +63 -0
- package/scss/components/_loading.scss +34 -0
- package/scss/components/_progress.scss +70 -0
- package/scss/components/_slider.scss +9 -0
- package/scss/components/_text-track.scss +8 -0
- package/scss/components/_time.scss +29 -0
- package/scss/components/_title-bar.scss +9 -0
- package/scss/components/_volume.scss +16 -0
- package/scss/components/menu/_menu-popup.scss +30 -0
- package/scss/components/menu/_menu.scss +26 -0
- 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;
|