@srgssr/pillarbox-web 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pillarbox-core.cjs.js +1 -1
- package/dist/pillarbox-core.cjs.min.js +1 -1
- package/dist/pillarbox-core.es.js +1 -1
- package/dist/pillarbox-core.es.min.js +1 -1
- package/dist/pillarbox-core.umd.js +1 -1
- package/dist/pillarbox-core.umd.min.js +1 -1
- package/dist/pillarbox.cjs.js +859 -3
- package/dist/pillarbox.cjs.min.js +1 -1
- package/dist/pillarbox.cjs.min.js.map +1 -1
- package/dist/pillarbox.es.js +859 -3
- package/dist/pillarbox.es.min.js +1 -1
- package/dist/pillarbox.es.min.js.map +1 -1
- package/dist/pillarbox.umd.js +861 -5
- package/dist/pillarbox.umd.min.js +11 -11
- package/dist/pillarbox.umd.min.js.map +1 -1
- package/dist/types/src/middleware/srgssr.d.ts +9 -0
- package/dist/types/src/middleware/srgssr.d.ts.map +1 -1
- package/dist/types/src/trackers/PillarboxMonitoring.d.ts +695 -0
- package/dist/types/src/trackers/PillarboxMonitoring.d.ts.map +1 -0
- package/dist/types/src/trackers/SRGAnalytics.d.ts.map +1 -0
- package/package.json +1 -1
- package/dist/types/src/analytics/SRGAnalytics.d.ts.map +0 -1
- /package/dist/types/src/{analytics → trackers}/SRGAnalytics.d.ts +0 -0
package/dist/pillarbox.cjs.js
CHANGED
|
@@ -110,7 +110,7 @@ function _objectWithoutProperties(source, excluded) {
|
|
|
110
110
|
return target;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
var version = "1.
|
|
113
|
+
var version = "1.13.1";
|
|
114
114
|
|
|
115
115
|
/**
|
|
116
116
|
* @ignore
|
|
@@ -1643,6 +1643,833 @@ class SRGAnalytics {
|
|
|
1643
1643
|
}
|
|
1644
1644
|
}
|
|
1645
1645
|
|
|
1646
|
+
/* eslint max-statements: ["error", 25]*/
|
|
1647
|
+
|
|
1648
|
+
/**
|
|
1649
|
+
* The PillarboxMonitoring class retrieves data about media playback.
|
|
1650
|
+
*
|
|
1651
|
+
* This data can be used to :
|
|
1652
|
+
* - help investigate playback problems
|
|
1653
|
+
* - measure the quality of our service
|
|
1654
|
+
*
|
|
1655
|
+
* The sending of this data tries to respect as much as possible the
|
|
1656
|
+
* specification described in the link below.
|
|
1657
|
+
*
|
|
1658
|
+
* However, some platforms may have certain limitations.
|
|
1659
|
+
* In this case, only the data available will be sent.
|
|
1660
|
+
*
|
|
1661
|
+
* @see https://github.com/SRGSSR/pillarbox-documentation/blob/main/Specifications/monitoring.md
|
|
1662
|
+
*/
|
|
1663
|
+
class PillarboxMonitoring {
|
|
1664
|
+
constructor(player, {
|
|
1665
|
+
playerName = 'none',
|
|
1666
|
+
playerVersion = 'none',
|
|
1667
|
+
platform = 'Web',
|
|
1668
|
+
schemaVersion = 1,
|
|
1669
|
+
heartbeatInterval = 30000,
|
|
1670
|
+
beaconUrl = 'https://monitoring.pillarbox.ch/api/events'
|
|
1671
|
+
} = {}) {
|
|
1672
|
+
/**
|
|
1673
|
+
* @type {import('video.js/dist/types/player').default}
|
|
1674
|
+
*/
|
|
1675
|
+
this.player = player;
|
|
1676
|
+
/**
|
|
1677
|
+
* @type {string}
|
|
1678
|
+
*/
|
|
1679
|
+
this.playerName = playerName;
|
|
1680
|
+
/**
|
|
1681
|
+
* @type {string}
|
|
1682
|
+
*/
|
|
1683
|
+
this.playerVersion = playerVersion;
|
|
1684
|
+
/**
|
|
1685
|
+
* @type {string}
|
|
1686
|
+
*/
|
|
1687
|
+
this.platform = platform;
|
|
1688
|
+
/**
|
|
1689
|
+
* @type {string}
|
|
1690
|
+
*/
|
|
1691
|
+
this.schemaVersion = schemaVersion;
|
|
1692
|
+
/**
|
|
1693
|
+
* @type {Number}
|
|
1694
|
+
*/
|
|
1695
|
+
this.heartbeatInterval = heartbeatInterval;
|
|
1696
|
+
/**
|
|
1697
|
+
* @type {string}
|
|
1698
|
+
*/
|
|
1699
|
+
this.beaconUrl = beaconUrl;
|
|
1700
|
+
/**
|
|
1701
|
+
* @type {string}
|
|
1702
|
+
*/
|
|
1703
|
+
this.currentSessionId = undefined;
|
|
1704
|
+
/**
|
|
1705
|
+
* @type {Number}
|
|
1706
|
+
*/
|
|
1707
|
+
this.lastPlaybackDuration = 0;
|
|
1708
|
+
/**
|
|
1709
|
+
* @type {Number}
|
|
1710
|
+
*/
|
|
1711
|
+
this.lastPlaybackStartTimestamp = 0;
|
|
1712
|
+
/**
|
|
1713
|
+
* @type {Number}
|
|
1714
|
+
*/
|
|
1715
|
+
this.lastStallCount = 0;
|
|
1716
|
+
/**
|
|
1717
|
+
* @type {Number}
|
|
1718
|
+
*/
|
|
1719
|
+
this.lastStallDuration = 0;
|
|
1720
|
+
/**
|
|
1721
|
+
* @type {Number}
|
|
1722
|
+
*/
|
|
1723
|
+
this.loadStartTimestamp = undefined;
|
|
1724
|
+
/**
|
|
1725
|
+
* @type {Number}
|
|
1726
|
+
*/
|
|
1727
|
+
this.metadataRequestTime = 0;
|
|
1728
|
+
/**
|
|
1729
|
+
* @type {string}
|
|
1730
|
+
*/
|
|
1731
|
+
this.mediaAssetUrl = undefined;
|
|
1732
|
+
/**
|
|
1733
|
+
* @type {string}
|
|
1734
|
+
*/
|
|
1735
|
+
this.mediaId = undefined;
|
|
1736
|
+
/**
|
|
1737
|
+
* @type {string}
|
|
1738
|
+
*/
|
|
1739
|
+
this.mediaMetadataUrl = undefined;
|
|
1740
|
+
/**
|
|
1741
|
+
* @type {string}
|
|
1742
|
+
*/
|
|
1743
|
+
this.mediaOrigin = undefined;
|
|
1744
|
+
/**
|
|
1745
|
+
* @type {Number}
|
|
1746
|
+
*/
|
|
1747
|
+
this.tokenRequestTime = 0;
|
|
1748
|
+
this.addListeners();
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
/**
|
|
1752
|
+
* Adds event listeners to the player and the window.
|
|
1753
|
+
*/
|
|
1754
|
+
addListeners() {
|
|
1755
|
+
this.bindCallBacks();
|
|
1756
|
+
this.player.on('loadstart', this.loadStart);
|
|
1757
|
+
this.player.on('loadeddata', this.loadedData);
|
|
1758
|
+
this.player.on('playing', this.playbackStart);
|
|
1759
|
+
this.player.on('pause', this.playbackStop);
|
|
1760
|
+
this.player.on('error', this.error);
|
|
1761
|
+
this.player.on(['playerreset', 'dispose', 'ended'], this.sessionStop);
|
|
1762
|
+
this.player.on(['waiting', 'stalled'], this.stalled);
|
|
1763
|
+
window.addEventListener('beforeunload', this.sessionStop);
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
/**
|
|
1767
|
+
* The current bandwidth of the last segment download.
|
|
1768
|
+
*
|
|
1769
|
+
* @returns {number|undefined} The current bandwidth in bits per second,
|
|
1770
|
+
* undefined otherwise.
|
|
1771
|
+
*/
|
|
1772
|
+
bandwidth() {
|
|
1773
|
+
const playerStats = this.player.tech(true).vhs ? this.player.tech(true).vhs.stats : undefined;
|
|
1774
|
+
return playerStats ? playerStats.bandwidth : undefined;
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
/**
|
|
1778
|
+
* Binds the callback functions to the current instance.
|
|
1779
|
+
*/
|
|
1780
|
+
bindCallBacks() {
|
|
1781
|
+
this.error = this.error.bind(this);
|
|
1782
|
+
this.loadedData = this.loadedData.bind(this);
|
|
1783
|
+
this.loadStart = this.loadStart.bind(this);
|
|
1784
|
+
this.playbackStart = this.playbackStart.bind(this);
|
|
1785
|
+
this.playbackStop = this.playbackStop.bind(this);
|
|
1786
|
+
this.stalled = this.stalled.bind(this);
|
|
1787
|
+
this.sessionStop = this.sessionStop.bind(this);
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
/**
|
|
1791
|
+
* Get the buffer duration in milliseconds.
|
|
1792
|
+
*
|
|
1793
|
+
* @returns {Number} The buffer duration
|
|
1794
|
+
*/
|
|
1795
|
+
bufferDuration() {
|
|
1796
|
+
const buffered = this.player.buffered();
|
|
1797
|
+
let bufferDuration = 0;
|
|
1798
|
+
for (let i = 0; i < buffered.length; i++) {
|
|
1799
|
+
const start = buffered.start(i);
|
|
1800
|
+
const end = buffered.end(i);
|
|
1801
|
+
bufferDuration += end - start;
|
|
1802
|
+
}
|
|
1803
|
+
return PillarboxMonitoring.secondsToMilliseconds(bufferDuration);
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
/**
|
|
1807
|
+
* Get the current representation when playing a Dash or Hls media.
|
|
1808
|
+
*
|
|
1809
|
+
* @typedef {Object} Representation
|
|
1810
|
+
* @property {number|undefined} bandwidth The bandwidth of the current
|
|
1811
|
+
* representation
|
|
1812
|
+
* @property {number|undefined} programDateTime The program date time of the
|
|
1813
|
+
* current representation
|
|
1814
|
+
* @property {string|undefined} uri The URL of the current representation
|
|
1815
|
+
*
|
|
1816
|
+
* @returns {Representation|undefined} The current representation object
|
|
1817
|
+
* undefined otherwise
|
|
1818
|
+
*/
|
|
1819
|
+
currentRepresentation() {
|
|
1820
|
+
const {
|
|
1821
|
+
activeCues: {
|
|
1822
|
+
cues_: [cue]
|
|
1823
|
+
} = {
|
|
1824
|
+
cues_: []
|
|
1825
|
+
}
|
|
1826
|
+
} = Array.from(this.player.textTracks()).find(({
|
|
1827
|
+
label,
|
|
1828
|
+
kind
|
|
1829
|
+
}) => kind === 'metadata' && label === 'segment-metadata') || {};
|
|
1830
|
+
return cue ? cue.value : undefined;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
/**
|
|
1834
|
+
* Get the current resource information including bitrate and URL when available.
|
|
1835
|
+
*
|
|
1836
|
+
* @typedef {Object} Resource
|
|
1837
|
+
* @property {number|undefined} bitrate The bitrate of the current resource
|
|
1838
|
+
* @property {string|undefined} url The URL of the current resource
|
|
1839
|
+
*
|
|
1840
|
+
* @returns {Resource} The current resource information.
|
|
1841
|
+
*/
|
|
1842
|
+
currentResource() {
|
|
1843
|
+
let {
|
|
1844
|
+
bandwidth: bitrate,
|
|
1845
|
+
uri: url
|
|
1846
|
+
} = this.currentRepresentation() || {};
|
|
1847
|
+
if (pillarbox.browser.IS_ANY_SAFARI) {
|
|
1848
|
+
const {
|
|
1849
|
+
configuration
|
|
1850
|
+
} = Array.from(this.player.videoTracks()).find(track => track.selected) || {};
|
|
1851
|
+
bitrate = configuration ? configuration.bitrate : undefined;
|
|
1852
|
+
url = this.player.currentSource().src;
|
|
1853
|
+
}
|
|
1854
|
+
return {
|
|
1855
|
+
bitrate,
|
|
1856
|
+
url
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
/**
|
|
1861
|
+
* The media data of the current source.
|
|
1862
|
+
*
|
|
1863
|
+
* @returns {Object} The media data of the current source, or an empty object
|
|
1864
|
+
* if no media data is available.
|
|
1865
|
+
*/
|
|
1866
|
+
currentSourceMediaData() {
|
|
1867
|
+
if (!this.player.currentSource().mediaData) return {};
|
|
1868
|
+
return this.player.currentSource().mediaData;
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
/**
|
|
1872
|
+
* Handles player errors by sending an `ERROR` event, then resets the session.
|
|
1873
|
+
*/
|
|
1874
|
+
error() {
|
|
1875
|
+
const error = this.player.error();
|
|
1876
|
+
const playbackPosition = this.playbackPosition();
|
|
1877
|
+
const representation = this.currentRepresentation();
|
|
1878
|
+
const url = representation ? representation.uri : this.player.currentSource().src;
|
|
1879
|
+
if (!this.player.hasStarted()) {
|
|
1880
|
+
this.sendEvent('START', this.startEventData());
|
|
1881
|
+
}
|
|
1882
|
+
this.sendEvent('ERROR', _objectSpread2(_objectSpread2({
|
|
1883
|
+
log: JSON.stringify(error.metadata || pillarbox.log.history().slice(-15)),
|
|
1884
|
+
message: error.message,
|
|
1885
|
+
name: error.code
|
|
1886
|
+
}, playbackPosition), {}, {
|
|
1887
|
+
severity: 'Fatal',
|
|
1888
|
+
url
|
|
1889
|
+
}));
|
|
1890
|
+
this.reset();
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
/**
|
|
1894
|
+
* Get the DRM license request duration from performance API.
|
|
1895
|
+
*
|
|
1896
|
+
* @returns {number|undefined} The request duration
|
|
1897
|
+
*/
|
|
1898
|
+
getDrmRequestDuration() {
|
|
1899
|
+
const keySystems = Object.values(this.player.currentSource().keySystems || {}).map(keySystem => keySystem.url);
|
|
1900
|
+
if (!keySystems.length) return;
|
|
1901
|
+
const resource = performance.getEntriesByType('resource').filter(({
|
|
1902
|
+
initiatorType,
|
|
1903
|
+
name
|
|
1904
|
+
}) => initiatorType === 'xmlhttprequest' && keySystems.includes(name)).pop();
|
|
1905
|
+
return resource && resource.duration;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
/**
|
|
1909
|
+
* Get metadata information from the performance API for a given id.
|
|
1910
|
+
*
|
|
1911
|
+
* @typedef {Object} MetadataInfo
|
|
1912
|
+
* @property {string} name The URL of the resource
|
|
1913
|
+
* @property {number} duration The duration of the resource fetch in milliseconds
|
|
1914
|
+
*
|
|
1915
|
+
* @param {string} id The id to search for in the resource entries
|
|
1916
|
+
*
|
|
1917
|
+
* @returns {MetadataInfo|undefined} An object containing metadata
|
|
1918
|
+
* information, or undefined otherwise
|
|
1919
|
+
*/
|
|
1920
|
+
getMetadataInfo(id) {
|
|
1921
|
+
const resource = performance.getEntriesByType('resource').filter(({
|
|
1922
|
+
initiatorType,
|
|
1923
|
+
name
|
|
1924
|
+
}) => initiatorType === 'fetch' && name.includes(id)).pop();
|
|
1925
|
+
if (!resource) return {};
|
|
1926
|
+
return {
|
|
1927
|
+
name: resource.name,
|
|
1928
|
+
duration: resource.duration
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
/**
|
|
1933
|
+
* Get the Akamai token request duration from performance API.
|
|
1934
|
+
*
|
|
1935
|
+
* @returns {number|undefined} The request duration
|
|
1936
|
+
*/
|
|
1937
|
+
getTokenRequestDuration(tokenType) {
|
|
1938
|
+
if (!tokenType) return;
|
|
1939
|
+
const resource = performance.getEntriesByType('resource').filter(({
|
|
1940
|
+
initiatorType,
|
|
1941
|
+
name
|
|
1942
|
+
}) => initiatorType === 'fetch' && name.includes('/akahd/token')).pop();
|
|
1943
|
+
return resource && resource.duration;
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
/**
|
|
1947
|
+
* Send an 'HEARTBEAT' event with the date of the current playback state at
|
|
1948
|
+
* regular intervals.
|
|
1949
|
+
*/
|
|
1950
|
+
heartbeat() {
|
|
1951
|
+
this.heartbeatIntervalId = setInterval(() => {
|
|
1952
|
+
this.sendEvent('HEARTBEAT', this.statusEventData());
|
|
1953
|
+
}, this.heartbeatInterval);
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
/**
|
|
1957
|
+
* Check if the tracker is disabled.
|
|
1958
|
+
*
|
|
1959
|
+
* @returns {Boolean} __true__ if disabled __false__ otherwise.
|
|
1960
|
+
*/
|
|
1961
|
+
isTrackerDisabled() {
|
|
1962
|
+
const currentSource = this.player.currentSource();
|
|
1963
|
+
if (!Array.isArray(currentSource.disableTrackers)) {
|
|
1964
|
+
return Boolean(currentSource.disableTrackers);
|
|
1965
|
+
}
|
|
1966
|
+
return Boolean(currentSource.disableTrackers.find(tracker => tracker.toLowerCase() === PillarboxMonitoring.name.toLowerCase()));
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
/**
|
|
1970
|
+
* Handles the session start by sending a `START` event immediately followed
|
|
1971
|
+
* by a `HEARTBEAT` when the `loadeddata` event is triggered.
|
|
1972
|
+
*/
|
|
1973
|
+
loadedData() {
|
|
1974
|
+
this.sendEvent('START', this.startEventData());
|
|
1975
|
+
this.sendEvent('HEARTBEAT', this.statusEventData());
|
|
1976
|
+
// starts the heartbeat interval
|
|
1977
|
+
this.heartbeat();
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
/**
|
|
1981
|
+
* Handles `loadstart` event and captures the current timestamp. Will be used
|
|
1982
|
+
* to calculate the media loading time.
|
|
1983
|
+
*/
|
|
1984
|
+
loadStart() {
|
|
1985
|
+
// if the content is a plain old URL
|
|
1986
|
+
if (!Object.keys(this.currentSourceMediaData()).length && this.currentSessionId) {
|
|
1987
|
+
this.sessionStop();
|
|
1988
|
+
// Reference timestamp used to calculate the different time metrics.
|
|
1989
|
+
this.sessionStartTimestamp = PillarboxMonitoring.timestamp();
|
|
1990
|
+
}
|
|
1991
|
+
this.loadStartTimestamp = PillarboxMonitoring.timestamp();
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
/**
|
|
1995
|
+
* The media information.
|
|
1996
|
+
*
|
|
1997
|
+
* @typedef {Object} MediaInfo
|
|
1998
|
+
* @property {string} asset_url The URL of the media
|
|
1999
|
+
* @property {string} id The ID of the media
|
|
2000
|
+
* @property {string} metadata_url The URL of the media metadata
|
|
2001
|
+
* @property {string} origin The origin of the media
|
|
2002
|
+
*
|
|
2003
|
+
* @returns {MediaInfo} An object container the media information
|
|
2004
|
+
*/
|
|
2005
|
+
mediaInfo() {
|
|
2006
|
+
return {
|
|
2007
|
+
asset_url: this.mediaAssetUrl,
|
|
2008
|
+
id: this.mediaId,
|
|
2009
|
+
metadata_url: this.mediaMetadataUrl,
|
|
2010
|
+
origin: this.mediaOrigin
|
|
2011
|
+
};
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
/**
|
|
2015
|
+
* The total playback duration for the current session.
|
|
2016
|
+
*
|
|
2017
|
+
* @returns {number} The total playback duration in milliseconds.
|
|
2018
|
+
*/
|
|
2019
|
+
playbackDuration() {
|
|
2020
|
+
if (!this.lastPlaybackStartTimestamp) {
|
|
2021
|
+
return this.lastPlaybackDuration;
|
|
2022
|
+
}
|
|
2023
|
+
return PillarboxMonitoring.timestamp() + this.lastPlaybackDuration - this.lastPlaybackStartTimestamp;
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
/**
|
|
2027
|
+
* The current playback position and position timestamp.
|
|
2028
|
+
*
|
|
2029
|
+
* @typedef {Object} PlaybackPosition
|
|
2030
|
+
* @property {number} position The current playback position in milliseconds
|
|
2031
|
+
* @property {number|undefined} position_timestamp The timestamp of the
|
|
2032
|
+
* current playback position, or undefined if not available
|
|
2033
|
+
*
|
|
2034
|
+
* @returns {PlaybackPosition} The playback position object.
|
|
2035
|
+
*/
|
|
2036
|
+
playbackPosition() {
|
|
2037
|
+
const currentRepresentation = this.currentRepresentation();
|
|
2038
|
+
const position = PillarboxMonitoring.secondsToMilliseconds(this.player.currentTime());
|
|
2039
|
+
let position_timestamp;
|
|
2040
|
+
|
|
2041
|
+
// Get the position timestamp from the program date time when VHS is used
|
|
2042
|
+
// or undefined if there is no value
|
|
2043
|
+
if (currentRepresentation) {
|
|
2044
|
+
position_timestamp = currentRepresentation.programDateTime;
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
// Calculate the position timestamp from the start date on Safari
|
|
2048
|
+
if (pillarbox.browser.IS_ANY_SAFARI) {
|
|
2049
|
+
const startDate = Date.parse(this.player.$('video').getStartDate());
|
|
2050
|
+
position_timestamp = !isNaN(startDate) ? startDate + position : undefined;
|
|
2051
|
+
}
|
|
2052
|
+
return {
|
|
2053
|
+
position,
|
|
2054
|
+
position_timestamp
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
/**
|
|
2059
|
+
* Assign the timestamp each time the playback starts.
|
|
2060
|
+
*/
|
|
2061
|
+
playbackStart() {
|
|
2062
|
+
this.lastPlaybackStartTimestamp = PillarboxMonitoring.timestamp();
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
/**
|
|
2066
|
+
* Calculates and accumulates the duration of the playback session each time
|
|
2067
|
+
* the playback stops for the current media.
|
|
2068
|
+
*/
|
|
2069
|
+
playbackStop() {
|
|
2070
|
+
this.lastPlaybackDuration += PillarboxMonitoring.timestamp() - this.lastPlaybackStartTimestamp;
|
|
2071
|
+
this.lastPlaybackStartTimestamp = 0;
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
/**
|
|
2075
|
+
* The current dimensions of the player.
|
|
2076
|
+
*
|
|
2077
|
+
* @typedef {Object} PlayerCurrentDimensions
|
|
2078
|
+
* @property {number} width The current width of the player
|
|
2079
|
+
* @property {number} height The current height of the player
|
|
2080
|
+
*
|
|
2081
|
+
* @returns {PlayerCurrentDimensions} The current dimensions of the player object.
|
|
2082
|
+
*/
|
|
2083
|
+
playerCurrentDimensions() {
|
|
2084
|
+
return this.player.currentDimensions();
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
/**
|
|
2088
|
+
* Information about the player.
|
|
2089
|
+
*
|
|
2090
|
+
* @typedef {Object} PlayerInfo
|
|
2091
|
+
* @property {string} name The name of the player
|
|
2092
|
+
* @property {string} version The version of the player
|
|
2093
|
+
* @property {string} platform The platform on which the player is running
|
|
2094
|
+
*
|
|
2095
|
+
* @returns {PlayerInfo} An object containing player information.
|
|
2096
|
+
*/
|
|
2097
|
+
playerInfo() {
|
|
2098
|
+
return {
|
|
2099
|
+
name: this.playerName,
|
|
2100
|
+
version: this.playerVersion,
|
|
2101
|
+
platform: this.platform
|
|
2102
|
+
};
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
/**
|
|
2106
|
+
* Generates the QoE timings object.
|
|
2107
|
+
*
|
|
2108
|
+
* @typedef {Object} QoeTimings
|
|
2109
|
+
* @property {number} metadata The time taken to load metadata
|
|
2110
|
+
* @property {number} asset The time taken to load the asset
|
|
2111
|
+
* @property {number} total The total time taken from session start to data load
|
|
2112
|
+
*
|
|
2113
|
+
* @param {number} timeToLoadedData The time taken to load the data
|
|
2114
|
+
* @param {number} timestamp The current timestamp
|
|
2115
|
+
*
|
|
2116
|
+
* @returns {QoeTimings} The QoE timings
|
|
2117
|
+
*/
|
|
2118
|
+
qoeTimings(timeToLoadedData, timestamp) {
|
|
2119
|
+
return {
|
|
2120
|
+
metadata: this.metadataRequestTime,
|
|
2121
|
+
asset: timeToLoadedData,
|
|
2122
|
+
total: timestamp - this.sessionStartTimestamp
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
/**
|
|
2127
|
+
* Generates the QoS timings object.
|
|
2128
|
+
*
|
|
2129
|
+
* @typedef {Object} QosTimings
|
|
2130
|
+
* @property {number} asset The time taken to load the asset
|
|
2131
|
+
* @property {number} drm The time taken for DRM processing
|
|
2132
|
+
* @property {number} metadata The time taken to load metadata
|
|
2133
|
+
* @property {number} token The time taken to request the token
|
|
2134
|
+
*
|
|
2135
|
+
* @param {number} timeToLoadedData The time taken to load the data
|
|
2136
|
+
*
|
|
2137
|
+
* @returns {QosTimings} The QoS timings
|
|
2138
|
+
*/
|
|
2139
|
+
qosTimings(timeToLoadedData) {
|
|
2140
|
+
return {
|
|
2141
|
+
asset: timeToLoadedData,
|
|
2142
|
+
drm: this.getDrmRequestDuration(),
|
|
2143
|
+
metadata: this.metadataRequestTime,
|
|
2144
|
+
token: this.tokenRequestTime
|
|
2145
|
+
};
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
/**
|
|
2149
|
+
* Removes all event listeners from the player and the window.
|
|
2150
|
+
*/
|
|
2151
|
+
removeListeners() {
|
|
2152
|
+
this.player.off('loadstart', this.loadStart);
|
|
2153
|
+
this.player.off('loadeddata', this.loadedData);
|
|
2154
|
+
this.player.off('playing', this.playbackStart);
|
|
2155
|
+
this.player.off('pause', this.playbackStop);
|
|
2156
|
+
this.player.off('error', this.error);
|
|
2157
|
+
this.player.off(['playerreset', 'dispose', 'ended'], this.sessionStop);
|
|
2158
|
+
this.player.off(['waiting', 'stalled'], this.stalled);
|
|
2159
|
+
window.removeEventListener('beforeunload', this.sessionStop);
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
/**
|
|
2163
|
+
* Remove the token from the asset URL.
|
|
2164
|
+
*
|
|
2165
|
+
* @param {string} assetUrl The URL of the asset
|
|
2166
|
+
*
|
|
2167
|
+
* @returns {string|undefined} The URL without the token, or undefined if the
|
|
2168
|
+
* input URL is invalid
|
|
2169
|
+
*/
|
|
2170
|
+
removeTokenFromAssetUrl(assetUrl) {
|
|
2171
|
+
if (!assetUrl) return;
|
|
2172
|
+
try {
|
|
2173
|
+
const url = new URL(assetUrl);
|
|
2174
|
+
url.searchParams.delete('hdnts');
|
|
2175
|
+
return url.href;
|
|
2176
|
+
} catch (e) {
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
/**
|
|
2182
|
+
* Resets the playback session and clears relevant properties.
|
|
2183
|
+
*
|
|
2184
|
+
* @param {Event} event The event that triggered the reset. If the event type
|
|
2185
|
+
* is not 'ended' or 'playerreset', listeners will be removed.
|
|
2186
|
+
*/
|
|
2187
|
+
reset(event) {
|
|
2188
|
+
this.currentSessionId = undefined;
|
|
2189
|
+
this.lastPlaybackDuration = 0;
|
|
2190
|
+
this.lastPlaybackStartTimestamp = 0;
|
|
2191
|
+
this.lastStallCount = 0;
|
|
2192
|
+
this.lastStallDuration = 0;
|
|
2193
|
+
this.loadStartTimestamp = 0;
|
|
2194
|
+
this.metadataRequestTime = 0;
|
|
2195
|
+
this.mediaAssetUrl = undefined;
|
|
2196
|
+
this.mediaId = undefined;
|
|
2197
|
+
this.mediaMetadataUrl = undefined;
|
|
2198
|
+
this.mediaOrigin = undefined;
|
|
2199
|
+
this.sessionStartTimestamp = undefined;
|
|
2200
|
+
this.tokenRequestTime = 0;
|
|
2201
|
+
clearInterval(this.heartbeatIntervalId);
|
|
2202
|
+
if (event && !['ended', 'playerreset'].includes(event.type)) {
|
|
2203
|
+
this.removeListeners();
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
/**
|
|
2208
|
+
* Sends an event to the server using the Beacon API.
|
|
2209
|
+
*
|
|
2210
|
+
* @param {string} eventName Either START, STOP, ERROR, HEARTBEAT
|
|
2211
|
+
* @param {Object} [data={}] The payload object to be sent. Defaults to an
|
|
2212
|
+
* empty object if not provided
|
|
2213
|
+
*/
|
|
2214
|
+
sendEvent(eventName, data = {}) {
|
|
2215
|
+
// If the tracker is disabled for the current session, and there has been no
|
|
2216
|
+
// previous session, no event is sent. However, if a session was already
|
|
2217
|
+
// active, we still want to send the STOP event so that it is properly
|
|
2218
|
+
// stopped.
|
|
2219
|
+
if (this.isTrackerDisabled() && !this.currentSessionId || !this.currentSessionId) return;
|
|
2220
|
+
const payload = JSON.stringify({
|
|
2221
|
+
event_name: eventName,
|
|
2222
|
+
session_id: this.currentSessionId,
|
|
2223
|
+
timestamp: PillarboxMonitoring.timestamp(),
|
|
2224
|
+
version: this.schemaVersion,
|
|
2225
|
+
data
|
|
2226
|
+
});
|
|
2227
|
+
navigator.sendBeacon(this.beaconUrl, payload);
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
/**
|
|
2231
|
+
* Starts a new session by first stopping the previous session, then resetting
|
|
2232
|
+
* the session start timestamp and media ID to their new values.
|
|
2233
|
+
*/
|
|
2234
|
+
sessionStart() {
|
|
2235
|
+
if (this.sessionStartTimestamp) {
|
|
2236
|
+
this.sessionStop();
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2239
|
+
// Reference timestamp used to calculate the different time metrics.
|
|
2240
|
+
this.sessionStartTimestamp = PillarboxMonitoring.timestamp();
|
|
2241
|
+
// At this stage currentSource().src is the media identifier
|
|
2242
|
+
// and not the playable source.
|
|
2243
|
+
this.mediaId = this.player.currentSource().src || undefined;
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
/**
|
|
2247
|
+
* Stops the current session by sending a `STOP` event and resetting the
|
|
2248
|
+
* session.
|
|
2249
|
+
*
|
|
2250
|
+
* @param {Event} [event] The event that triggered the stop. This is passed
|
|
2251
|
+
* to the reset function.
|
|
2252
|
+
*/
|
|
2253
|
+
sessionStop(event) {
|
|
2254
|
+
this.sendEvent('STOP', this.statusEventData());
|
|
2255
|
+
this.reset(event);
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
/**
|
|
2259
|
+
* Handles the stalled state of the player. Sets the stalled state and listens
|
|
2260
|
+
* for the event that indicates the player is no longer stalled.
|
|
2261
|
+
*/
|
|
2262
|
+
stalled() {
|
|
2263
|
+
if (!this.player.hasStarted() || this.player.seeking() || this.isStalled) return;
|
|
2264
|
+
this.isStalled = true;
|
|
2265
|
+
const stallStart = PillarboxMonitoring.timestamp();
|
|
2266
|
+
const unstalled = () => {
|
|
2267
|
+
const stallEnd = PillarboxMonitoring.timestamp();
|
|
2268
|
+
this.isStalled = false;
|
|
2269
|
+
this.lastStallCount += 1;
|
|
2270
|
+
this.lastStallDuration += stallEnd - stallStart;
|
|
2271
|
+
};
|
|
2272
|
+
|
|
2273
|
+
// As Safari is not consistent with its playing event, it is better to use
|
|
2274
|
+
// the timeupdate event.
|
|
2275
|
+
if (pillarbox.browser.IS_ANY_SAFARI) {
|
|
2276
|
+
this.player.one('timeupdate', unstalled);
|
|
2277
|
+
} else {
|
|
2278
|
+
// As Chromium-based browsers are not consistent with their timeupdate
|
|
2279
|
+
// event, it is better to use the playing event.
|
|
2280
|
+
//
|
|
2281
|
+
// Firefox is consistent with its playing event.
|
|
2282
|
+
this.player.one('playing', unstalled);
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
/**
|
|
2287
|
+
* Information about the player's stall events.
|
|
2288
|
+
*
|
|
2289
|
+
* @typedef {Object} StallInfo
|
|
2290
|
+
* @property {number} count The number of stall events
|
|
2291
|
+
* @property {number} duration The total duration of stall events in
|
|
2292
|
+
* milliseconds
|
|
2293
|
+
*
|
|
2294
|
+
* @returns {StallInfo} An object containing the stall information
|
|
2295
|
+
*/
|
|
2296
|
+
stallInfo() {
|
|
2297
|
+
return {
|
|
2298
|
+
count: this.lastStallCount,
|
|
2299
|
+
duration: this.lastStallDuration
|
|
2300
|
+
};
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
/**
|
|
2304
|
+
* Get data on the current playback state. Will be used when sending `HEARTBEAT` or `STOP` events.
|
|
2305
|
+
*
|
|
2306
|
+
* @typedef {Object} StatusEventData
|
|
2307
|
+
* @property {number} bandwidth The current bandwidth
|
|
2308
|
+
* @property {number|undefined} bitrate The bitrate of the current resource
|
|
2309
|
+
* @property {number} buffered_duration The duration of the buffered content
|
|
2310
|
+
* @property {number} frame_drops The number of dropped frames
|
|
2311
|
+
* @property {number} playback_duration The duration of the playback
|
|
2312
|
+
* @property {number} position The current playback position
|
|
2313
|
+
* @property {number} position_timestamp The timestamp of the current playback position
|
|
2314
|
+
* @property {Object} stall Information about any stalls
|
|
2315
|
+
* @property {string} stream_type The type of stream, either 'on-demand' or 'live'
|
|
2316
|
+
* @property {string|undefined} url The URL of the current resource
|
|
2317
|
+
*
|
|
2318
|
+
* @returns {StatusEventData} The current event data
|
|
2319
|
+
*/
|
|
2320
|
+
statusEventData() {
|
|
2321
|
+
const bandwidth = this.bandwidth();
|
|
2322
|
+
const buffered_duration = this.bufferDuration();
|
|
2323
|
+
const {
|
|
2324
|
+
bitrate,
|
|
2325
|
+
url
|
|
2326
|
+
} = this.currentResource();
|
|
2327
|
+
const {
|
|
2328
|
+
droppedVideoFrames: frame_drops
|
|
2329
|
+
} = this.player.getVideoPlaybackQuality();
|
|
2330
|
+
const playback_duration = this.playbackDuration();
|
|
2331
|
+
const {
|
|
2332
|
+
position,
|
|
2333
|
+
position_timestamp
|
|
2334
|
+
} = this.playbackPosition();
|
|
2335
|
+
const stream_type = isFinite(this.player.duration()) ? 'On-demand' : 'Live';
|
|
2336
|
+
const stall = this.stallInfo();
|
|
2337
|
+
const data = {
|
|
2338
|
+
bandwidth,
|
|
2339
|
+
bitrate,
|
|
2340
|
+
buffered_duration,
|
|
2341
|
+
frame_drops,
|
|
2342
|
+
playback_duration,
|
|
2343
|
+
position,
|
|
2344
|
+
position_timestamp,
|
|
2345
|
+
stall,
|
|
2346
|
+
stream_type,
|
|
2347
|
+
url
|
|
2348
|
+
};
|
|
2349
|
+
return data;
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
/**
|
|
2353
|
+
* Generates the data for the start event.
|
|
2354
|
+
*
|
|
2355
|
+
* @typedef {Object} Device
|
|
2356
|
+
* @property {string} id The device ID.
|
|
2357
|
+
*
|
|
2358
|
+
* @typedef {Object} StartEventData
|
|
2359
|
+
* @property {string} browser The user agent string of the browser.
|
|
2360
|
+
* @property {Device} device Information about the device.
|
|
2361
|
+
* @property {MediaInfo} media Information about the media.
|
|
2362
|
+
* @property {PlayerInfo} player Information about the player.
|
|
2363
|
+
* @property {QoeTimings} qoe_timings Quality of Experience timings.
|
|
2364
|
+
* @property {QosTimings} qos_timings Quality of Service timings.
|
|
2365
|
+
* @property {PlayerCurrentDimensions} screen The current dimensions of the
|
|
2366
|
+
* player.
|
|
2367
|
+
*
|
|
2368
|
+
* @returns {StartEventData} An object containing the start event data.
|
|
2369
|
+
*/
|
|
2370
|
+
startEventData() {
|
|
2371
|
+
const timestamp = PillarboxMonitoring.timestamp();
|
|
2372
|
+
// This avoids false subtraction results when loadStartTimestamp is not
|
|
2373
|
+
// initialized.
|
|
2374
|
+
// loadStartTimestamp will be 0 if loadstart is not triggered.
|
|
2375
|
+
// This is the case when a STARTDATE error occurs.
|
|
2376
|
+
const timeToLoadedData = this.loadStartTimestamp ? timestamp - this.loadStartTimestamp : 0;
|
|
2377
|
+
if (!this.isTrackerDisabled()) {
|
|
2378
|
+
this.currentSessionId = PillarboxMonitoring.sessionId();
|
|
2379
|
+
}
|
|
2380
|
+
this.mediaAssetUrl = this.removeTokenFromAssetUrl(this.player.currentSource().src);
|
|
2381
|
+
this.mediaMetadataUrl = this.getMetadataInfo(this.mediaId).name;
|
|
2382
|
+
this.metadataRequestTime = this.getMetadataInfo(this.mediaId).duration;
|
|
2383
|
+
this.mediaOrigin = window.location.href;
|
|
2384
|
+
this.tokenRequestTime = this.getTokenRequestDuration(this.currentSourceMediaData().tokenType);
|
|
2385
|
+
return {
|
|
2386
|
+
browser: PillarboxMonitoring.userAgent(),
|
|
2387
|
+
device: {
|
|
2388
|
+
id: PillarboxMonitoring.deviceId()
|
|
2389
|
+
},
|
|
2390
|
+
media: this.mediaInfo(),
|
|
2391
|
+
player: this.playerInfo(),
|
|
2392
|
+
qoe_timings: this.qoeTimings(timeToLoadedData, timestamp),
|
|
2393
|
+
qos_timings: this.qosTimings(timeToLoadedData),
|
|
2394
|
+
screen: this.playerCurrentDimensions()
|
|
2395
|
+
};
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
/**
|
|
2399
|
+
* Generates a new session ID.
|
|
2400
|
+
*
|
|
2401
|
+
* @returns {string} random UUID
|
|
2402
|
+
*/
|
|
2403
|
+
static sessionId() {
|
|
2404
|
+
return PillarboxMonitoring.randomUUID();
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
/**
|
|
2408
|
+
* Retrieve or generate a unique device ID and stores it in localStorage.
|
|
2409
|
+
*
|
|
2410
|
+
* @returns {string|undefined} The device ID if localStorage is available,
|
|
2411
|
+
* otherwise `undefined`
|
|
2412
|
+
*/
|
|
2413
|
+
static deviceId() {
|
|
2414
|
+
if (!localStorage) return;
|
|
2415
|
+
const deviceIdKey = 'pillarbox_device_id';
|
|
2416
|
+
let deviceId = localStorage.getItem(deviceIdKey);
|
|
2417
|
+
if (!deviceId) {
|
|
2418
|
+
deviceId = PillarboxMonitoring.randomUUID();
|
|
2419
|
+
localStorage.setItem(deviceIdKey, deviceId);
|
|
2420
|
+
}
|
|
2421
|
+
return deviceId;
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
/**
|
|
2425
|
+
* Generate a cryptographically secure random UUID.
|
|
2426
|
+
*
|
|
2427
|
+
* @returns {string}
|
|
2428
|
+
*/
|
|
2429
|
+
static randomUUID() {
|
|
2430
|
+
if (!crypto.randomUUID) {
|
|
2431
|
+
// Polyfill from the author of uuid js which is simple and
|
|
2432
|
+
// cryptographically secure.
|
|
2433
|
+
// https://stackoverflow.com/a/2117523
|
|
2434
|
+
return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, c =>
|
|
2435
|
+
// eslint-disable-next-line
|
|
2436
|
+
(+c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16));
|
|
2437
|
+
}
|
|
2438
|
+
return crypto.randomUUID();
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
/**
|
|
2442
|
+
* converts seconds into milliseconds.
|
|
2443
|
+
*
|
|
2444
|
+
* @param {number} seconds
|
|
2445
|
+
*
|
|
2446
|
+
* @returns {number} milliseconds as an integer value
|
|
2447
|
+
*/
|
|
2448
|
+
static secondsToMilliseconds(seconds) {
|
|
2449
|
+
return parseInt(seconds * 1000);
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
/**
|
|
2453
|
+
* The timestamp in milliseconds.
|
|
2454
|
+
*
|
|
2455
|
+
* @return {number} milliseconds as an integer value
|
|
2456
|
+
*/
|
|
2457
|
+
static timestamp() {
|
|
2458
|
+
return Date.now();
|
|
2459
|
+
}
|
|
2460
|
+
|
|
2461
|
+
/**
|
|
2462
|
+
* The browser's user agent.
|
|
2463
|
+
*
|
|
2464
|
+
* @returns {string}
|
|
2465
|
+
*/
|
|
2466
|
+
static userAgent() {
|
|
2467
|
+
return {
|
|
2468
|
+
user_agent: navigator.userAgent
|
|
2469
|
+
};
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
|
|
1646
2473
|
/**
|
|
1647
2474
|
* Represents the composition of media content.
|
|
1648
2475
|
*
|
|
@@ -3164,8 +3991,9 @@ class SrgSsr {
|
|
|
3164
3991
|
* @returns {Array.<import('./typedef').MainResourceWithKeySystems>}
|
|
3165
3992
|
*/
|
|
3166
3993
|
static composeKeySystemsResources(resources = []) {
|
|
3167
|
-
if (!Drm.hasDrm(resources)) ;
|
|
3168
|
-
|
|
3994
|
+
if (!Drm.hasDrm(resources)) return resources;
|
|
3995
|
+
const resourcesWithKeySystems = resources.map(resource => _objectSpread2(_objectSpread2({}, resource), Drm.buildKeySystems(resource.drmList)));
|
|
3996
|
+
return resourcesWithKeySystems;
|
|
3169
3997
|
}
|
|
3170
3998
|
|
|
3171
3999
|
/**
|
|
@@ -3406,6 +4234,9 @@ class SrgSsr {
|
|
|
3406
4234
|
*/
|
|
3407
4235
|
static getSrcMediaObj(player, srcObj) {
|
|
3408
4236
|
return _asyncToGenerator(function* () {
|
|
4237
|
+
if (SrgSsr.pillarboxMonitoring(player)) {
|
|
4238
|
+
SrgSsr.pillarboxMonitoring(player).sessionStart();
|
|
4239
|
+
}
|
|
3409
4240
|
const {
|
|
3410
4241
|
src: urn
|
|
3411
4242
|
} = srcObj,
|
|
@@ -3523,6 +4354,30 @@ class SrgSsr {
|
|
|
3523
4354
|
}
|
|
3524
4355
|
}
|
|
3525
4356
|
|
|
4357
|
+
/**
|
|
4358
|
+
* PillarboxMonitoring monitoring singleton.
|
|
4359
|
+
*
|
|
4360
|
+
* @param {import('video.js/dist/types/player').default} player
|
|
4361
|
+
*
|
|
4362
|
+
* @returns {PillarboxMonitoring} instance of PillarboxMonitoring
|
|
4363
|
+
*/
|
|
4364
|
+
static pillarboxMonitoring(player) {
|
|
4365
|
+
if (player.options().trackers.pillarboxMonitoring === false) return;
|
|
4366
|
+
if (!player.options().trackers.pillarboxMonitoring) {
|
|
4367
|
+
const pillarboxMonitoring = new PillarboxMonitoring(player, {
|
|
4368
|
+
debug: player.debug(),
|
|
4369
|
+
playerVersion: pillarbox.VERSION.pillarbox,
|
|
4370
|
+
playerName: 'Pillarbox'
|
|
4371
|
+
});
|
|
4372
|
+
player.options({
|
|
4373
|
+
trackers: {
|
|
4374
|
+
pillarboxMonitoring
|
|
4375
|
+
}
|
|
4376
|
+
});
|
|
4377
|
+
}
|
|
4378
|
+
return player.options().trackers.pillarboxMonitoring;
|
|
4379
|
+
}
|
|
4380
|
+
|
|
3526
4381
|
/**
|
|
3527
4382
|
* Update player's poster.
|
|
3528
4383
|
*
|
|
@@ -3558,6 +4413,7 @@ class SrgSsr {
|
|
|
3558
4413
|
* @returns {Object}
|
|
3559
4414
|
*/
|
|
3560
4415
|
static middleware(player) {
|
|
4416
|
+
SrgSsr.pillarboxMonitoring(player);
|
|
3561
4417
|
SrgSsr.cuechangeEventProxy(player);
|
|
3562
4418
|
return {
|
|
3563
4419
|
currentTime: currentTime => SrgSsr.handleCurrentTime(player, currentTime),
|