@gcorevideo/player 2.22.16 → 2.22.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/clappr-nerd-stats/clappr-nerd-stats.ejs +76 -78
- package/assets/clappr-nerd-stats/clappr-nerd-stats.scss +10 -7
- package/dist/core.js +10 -14
- package/dist/index.css +1441 -1440
- package/dist/index.js +589 -522
- package/dist/player.d.ts +216 -159
- package/dist/plugins/index.css +1204 -1203
- package/dist/plugins/index.js +581 -506
- package/docs/api/player.clapprstats.exportmetrics.md +1 -1
- package/docs/api/player.clapprstats.md +5 -15
- package/docs/api/player.clapprstatssettings.md +13 -0
- package/docs/api/player.clips.destroy.md +18 -0
- package/docs/api/player.clips.disable.md +18 -0
- package/docs/api/player.clips.enable.md +18 -0
- package/docs/api/player.clips.md +170 -0
- package/docs/api/player.clips.render.md +18 -0
- package/docs/api/player.clips.supportedversion.md +16 -0
- package/docs/api/player.clips.version.md +14 -0
- package/docs/api/player.clipspluginsettings.md +2 -2
- package/docs/api/player.clipspluginsettings.text.md +1 -1
- package/docs/api/player.md +27 -18
- package/docs/api/player.mediacontrol.md +1 -1
- package/docs/api/{player.mediacontrol.getelement.md → player.mediacontrol.mount.md} +20 -7
- package/docs/api/player.mediacontrolleftelement.md +1 -1
- package/docs/api/{player.clapprnerdstats._constructor_.md → player.nerdstats._constructor_.md} +3 -3
- package/docs/api/{player.clapprnerdstats.md → player.nerdstats.md} +5 -5
- package/docs/api/player.qualitylevel.height.md +1 -1
- package/docs/api/player.qualitylevel.level.md +1 -1
- package/docs/api/player.qualitylevel.md +4 -4
- package/docs/api/player.qualitylevel.width.md +1 -1
- package/docs/api/player.timeposition.current.md +1 -1
- package/docs/api/player.timeposition.md +2 -2
- package/docs/api/player.timeposition.total.md +1 -1
- package/docs/api/player.timeprogress.md +6 -4
- package/docs/api/player.timevalue.md +1 -1
- package/lib/index.plugins.d.ts +2 -1
- package/lib/index.plugins.d.ts.map +1 -1
- package/lib/index.plugins.js +2 -1
- package/lib/playback/dash-playback/DashPlayback.d.ts +0 -1
- package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
- package/lib/playback/dash-playback/DashPlayback.js +9 -12
- package/lib/playback/hls-playback/HlsPlayback.d.ts +1 -1
- package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
- package/lib/playback/hls-playback/HlsPlayback.js +0 -1
- package/lib/playback.types.d.ts +24 -12
- package/lib/playback.types.d.ts.map +1 -1
- package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.d.ts +4 -0
- package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.d.ts.map +1 -1
- package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.js +20 -23
- package/lib/plugins/clappr-nerd-stats/NerdStats.d.ts +86 -0
- package/lib/plugins/clappr-nerd-stats/NerdStats.d.ts.map +1 -0
- package/lib/plugins/clappr-nerd-stats/NerdStats.js +390 -0
- package/lib/plugins/clappr-nerd-stats/formatter.d.ts +5 -0
- package/lib/plugins/clappr-nerd-stats/formatter.d.ts.map +1 -1
- package/lib/plugins/clappr-nerd-stats/formatter.js +56 -24
- package/lib/plugins/clappr-nerd-stats/speedtest/index.d.ts +2 -2
- package/lib/plugins/clappr-nerd-stats/speedtest/index.d.ts.map +1 -1
- package/lib/plugins/clappr-nerd-stats/speedtest/types.d.ts +1 -1
- package/lib/plugins/clappr-nerd-stats/speedtest/types.d.ts.map +1 -1
- package/lib/plugins/clappr-nerd-stats/types.d.ts +3 -0
- package/lib/plugins/clappr-nerd-stats/types.d.ts.map +1 -1
- package/lib/plugins/clappr-nerd-stats/utils.d.ts +7 -0
- package/lib/plugins/clappr-nerd-stats/utils.d.ts.map +1 -0
- package/lib/plugins/clappr-nerd-stats/utils.js +67 -0
- package/lib/plugins/clappr-stats/ClapprStats.d.ts +27 -32
- package/lib/plugins/clappr-stats/ClapprStats.d.ts.map +1 -1
- package/lib/plugins/clappr-stats/ClapprStats.js +94 -202
- package/lib/plugins/clappr-stats/types.d.ts +65 -25
- package/lib/plugins/clappr-stats/types.d.ts.map +1 -1
- package/lib/plugins/clappr-stats/types.js +37 -2
- package/lib/plugins/clappr-stats/utils.d.ts +1 -1
- package/lib/plugins/clappr-stats/utils.d.ts.map +1 -1
- package/lib/plugins/clappr-stats/utils.js +1 -3
- package/lib/plugins/seek-time/SeekTime.d.ts +1 -1
- package/lib/plugins/seek-time/SeekTime.d.ts.map +1 -1
- package/lib/plugins/seek-time/SeekTime.js +3 -4
- package/lib/testUtils.d.ts +2 -1
- package/lib/testUtils.d.ts.map +1 -1
- package/lib/testUtils.js +3 -2
- package/package.json +1 -1
- package/src/index.plugins.ts +2 -1
- package/src/playback/dash-playback/DashPlayback.ts +10 -15
- package/src/playback/hls-playback/HlsPlayback.ts +2 -4
- package/src/playback.types.ts +25 -11
- package/src/plugins/clappr-nerd-stats/NerdStats.ts +503 -0
- package/src/plugins/clappr-nerd-stats/formatter.ts +91 -47
- package/src/plugins/clappr-nerd-stats/speedtest/index.ts +2 -2
- package/src/plugins/clappr-nerd-stats/speedtest/types.ts +1 -1
- package/src/plugins/clappr-nerd-stats/types.ts +43 -3
- package/src/plugins/clappr-nerd-stats/utils.ts +75 -0
- package/src/plugins/clappr-stats/ClapprStats.ts +242 -306
- package/src/plugins/clappr-stats/__tests__/ClapprStats.test.ts +133 -0
- package/src/plugins/clappr-stats/types.ts +93 -47
- package/src/plugins/clappr-stats/utils.ts +4 -6
- package/src/plugins/error-screen/__tests__/ErrorScreen.test.ts +3 -4
- package/src/plugins/seek-time/SeekTime.ts +4 -5
- package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +1 -0
- package/src/testUtils.ts +3 -2
- package/temp/player.api.json +311 -159
- package/tsconfig.tsbuildinfo +1 -1
- package/docs/api/player.clapprstats.setupdatemetrics.md +0 -56
- package/docs/api/player.clipsplugin.gettext.md +0 -58
- package/docs/api/player.clipsplugin.md +0 -59
- package/src/plugins/clappr-nerd-stats/ClapprNerdStats.ts +0 -435
package/dist/index.js
CHANGED
|
@@ -12703,7 +12703,6 @@ class DashPlayback extends BasePlayback {
|
|
|
12703
12703
|
_playbackType = Playback.VOD;
|
|
12704
12704
|
// #EXT-X-PLAYLIST-TYPE
|
|
12705
12705
|
_playlistType = null;
|
|
12706
|
-
// #EXT-X-PROGRAM-DATE-TIME
|
|
12707
12706
|
_programDateTime = 0;
|
|
12708
12707
|
_dash = null;
|
|
12709
12708
|
_extrapolatedWindowDuration = 0;
|
|
@@ -12891,9 +12890,9 @@ class DashPlayback extends BasePlayback {
|
|
|
12891
12890
|
clearInterval(this._timeUpdateTimer);
|
|
12892
12891
|
}
|
|
12893
12892
|
}
|
|
12894
|
-
getProgramDateTime() {
|
|
12895
|
-
|
|
12896
|
-
}
|
|
12893
|
+
// getProgramDateTime() {
|
|
12894
|
+
// return this._programDateTime
|
|
12895
|
+
// }
|
|
12897
12896
|
// the duration on the video element itself should not be used
|
|
12898
12897
|
// as this does not necesarily represent the duration of the stream
|
|
12899
12898
|
// https://github.com/clappr/clappr/issues/668#issuecomment-157036678
|
|
@@ -13012,7 +13011,7 @@ class DashPlayback extends BasePlayback {
|
|
|
13012
13011
|
const update = {
|
|
13013
13012
|
current: this.getCurrentTime(),
|
|
13014
13013
|
total: this.getDuration(),
|
|
13015
|
-
firstFragDateTime: this.getProgramDateTime(),
|
|
13014
|
+
// firstFragDateTime: this.getProgramDateTime(), // TODO figure out if needed
|
|
13016
13015
|
};
|
|
13017
13016
|
const isSame = this._lastTimeUpdate &&
|
|
13018
13017
|
update.current === this._lastTimeUpdate.current &&
|
|
@@ -13045,13 +13044,11 @@ class DashPlayback extends BasePlayback {
|
|
|
13045
13044
|
this.getPlaybackType() === Playback.LIVE);
|
|
13046
13045
|
}
|
|
13047
13046
|
_onProgress() {
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
|
|
13051
|
-
|
|
13052
|
-
|
|
13053
|
-
buffer = this._dash.getDashMetrics().getCurrentBufferLevel('audio');
|
|
13054
|
-
}
|
|
13047
|
+
const buffer =
|
|
13048
|
+
// @ts-expect-error
|
|
13049
|
+
this._dash.getDashMetrics().getCurrentBufferLevel('video') ||
|
|
13050
|
+
// @ts-expect-error
|
|
13051
|
+
this._dash.getDashMetrics().getCurrentBufferLevel('audio');
|
|
13055
13052
|
const progress = {
|
|
13056
13053
|
start: this.getCurrentTime(),
|
|
13057
13054
|
current: this.getCurrentTime() + buffer,
|
|
@@ -42361,7 +42358,6 @@ class HlsPlayback extends BasePlayback {
|
|
|
42361
42358
|
const update = {
|
|
42362
42359
|
current: this.getCurrentTime(),
|
|
42363
42360
|
total: this.getDuration(),
|
|
42364
|
-
firstFragDateTime: this.getProgramDateTime(),
|
|
42365
42361
|
};
|
|
42366
42362
|
const isSame = this._lastTimeUpdate &&
|
|
42367
42363
|
update.current === this._lastTimeUpdate.current &&
|
|
@@ -43307,7 +43303,7 @@ class Player {
|
|
|
43307
43303
|
}
|
|
43308
43304
|
}
|
|
43309
43305
|
|
|
43310
|
-
var version$1 = "2.22.
|
|
43306
|
+
var version$1 = "2.22.18";
|
|
43311
43307
|
|
|
43312
43308
|
var packages = {
|
|
43313
43309
|
"node_modules/@clappr/core": {
|
|
@@ -43885,6 +43881,338 @@ class BottomGear extends UICorePlugin {
|
|
|
43885
43881
|
}
|
|
43886
43882
|
}
|
|
43887
43883
|
|
|
43884
|
+
/**
|
|
43885
|
+
* @beta
|
|
43886
|
+
*/
|
|
43887
|
+
var Chronograph;
|
|
43888
|
+
(function (Chronograph) {
|
|
43889
|
+
Chronograph["Startup"] = "startup";
|
|
43890
|
+
Chronograph["Watch"] = "watch";
|
|
43891
|
+
Chronograph["Pause"] = "pause";
|
|
43892
|
+
Chronograph["Buffering"] = "buffering";
|
|
43893
|
+
Chronograph["Session"] = "session";
|
|
43894
|
+
// Latency = 'latency',
|
|
43895
|
+
})(Chronograph || (Chronograph = {}));
|
|
43896
|
+
/**
|
|
43897
|
+
* @beta
|
|
43898
|
+
*/
|
|
43899
|
+
var Counter;
|
|
43900
|
+
(function (Counter) {
|
|
43901
|
+
Counter["Play"] = "play";
|
|
43902
|
+
Counter["Pause"] = "pause";
|
|
43903
|
+
Counter["Error"] = "error";
|
|
43904
|
+
Counter["Buffering"] = "buffering";
|
|
43905
|
+
Counter["DecodedFrames"] = "decodedFrames";
|
|
43906
|
+
Counter["DroppedFrames"] = "droppedFrames";
|
|
43907
|
+
Counter["Fps"] = "fps";
|
|
43908
|
+
Counter["ChangeLevel"] = "changeLevel";
|
|
43909
|
+
Counter["Seek"] = "seek";
|
|
43910
|
+
Counter["Fullscreen"] = "fullscreen";
|
|
43911
|
+
Counter["DvrUsage"] = "dvrUsage";
|
|
43912
|
+
})(Counter || (Counter = {}));
|
|
43913
|
+
/**
|
|
43914
|
+
* @beta
|
|
43915
|
+
*/
|
|
43916
|
+
var ClapprStatsEvents;
|
|
43917
|
+
(function (ClapprStatsEvents) {
|
|
43918
|
+
/**
|
|
43919
|
+
* Emitted periodically with current measurements.
|
|
43920
|
+
*/
|
|
43921
|
+
ClapprStatsEvents["REPORT"] = "clappr:stats:report";
|
|
43922
|
+
/**
|
|
43923
|
+
* Emitted when the playback reaches a certain percentage of the total duration.
|
|
43924
|
+
*/
|
|
43925
|
+
// PERCENTAGE = 'clappr:stats:percentage',
|
|
43926
|
+
})(ClapprStatsEvents || (ClapprStatsEvents = {}));
|
|
43927
|
+
|
|
43928
|
+
function newMetrics$1() {
|
|
43929
|
+
return {
|
|
43930
|
+
counters: {
|
|
43931
|
+
play: 0,
|
|
43932
|
+
pause: 0,
|
|
43933
|
+
error: 0,
|
|
43934
|
+
buffering: 0,
|
|
43935
|
+
decodedFrames: 0,
|
|
43936
|
+
droppedFrames: 0,
|
|
43937
|
+
fps: 0,
|
|
43938
|
+
changeLevel: 0,
|
|
43939
|
+
seek: 0,
|
|
43940
|
+
fullscreen: 0,
|
|
43941
|
+
dvrUsage: 0,
|
|
43942
|
+
},
|
|
43943
|
+
chrono: {
|
|
43944
|
+
startup: 0,
|
|
43945
|
+
watch: 0,
|
|
43946
|
+
pause: 0,
|
|
43947
|
+
buffering: 0,
|
|
43948
|
+
session: 0,
|
|
43949
|
+
},
|
|
43950
|
+
extra: {
|
|
43951
|
+
playbackName: '',
|
|
43952
|
+
playbackType: '',
|
|
43953
|
+
bitratesHistory: [],
|
|
43954
|
+
bitrateWeightedMean: 0,
|
|
43955
|
+
bitrateMostUsed: 0,
|
|
43956
|
+
buffersize: 0,
|
|
43957
|
+
watchHistory: [],
|
|
43958
|
+
watchedPercentage: 0,
|
|
43959
|
+
bufferingPercentage: 0,
|
|
43960
|
+
bandwidth: 0,
|
|
43961
|
+
duration: 0,
|
|
43962
|
+
currentTime: 0,
|
|
43963
|
+
},
|
|
43964
|
+
};
|
|
43965
|
+
}
|
|
43966
|
+
|
|
43967
|
+
/**
|
|
43968
|
+
* `PLUGIN` that measures data about playback, which can be useful for analyzing performance and UX.
|
|
43969
|
+
* @beta
|
|
43970
|
+
* @remarks
|
|
43971
|
+
* This plugin does not render anything and is supposed to be extended or used together with other plugins that actually render something.
|
|
43972
|
+
*
|
|
43973
|
+
* Configuration options - {@link ClapprStatsSettings}
|
|
43974
|
+
*
|
|
43975
|
+
* Events - {@link ClapprStatsEvents}
|
|
43976
|
+
*/
|
|
43977
|
+
class ClapprStats extends ContainerPlugin {
|
|
43978
|
+
timerId = null;
|
|
43979
|
+
lastDecodedFramesCount = 0;
|
|
43980
|
+
metrics = newMetrics$1();
|
|
43981
|
+
timers = {
|
|
43982
|
+
[Chronograph.Startup]: 0,
|
|
43983
|
+
[Chronograph.Watch]: 0,
|
|
43984
|
+
[Chronograph.Pause]: 0,
|
|
43985
|
+
[Chronograph.Buffering]: 0,
|
|
43986
|
+
[Chronograph.Session]: 0,
|
|
43987
|
+
};
|
|
43988
|
+
runEach;
|
|
43989
|
+
/**
|
|
43990
|
+
* @internal
|
|
43991
|
+
*/
|
|
43992
|
+
get name() {
|
|
43993
|
+
return 'clappr_stats';
|
|
43994
|
+
}
|
|
43995
|
+
/**
|
|
43996
|
+
* @internal
|
|
43997
|
+
*/
|
|
43998
|
+
get supportedVersion() {
|
|
43999
|
+
return { min: CLAPPR_VERSION$1 };
|
|
44000
|
+
}
|
|
44001
|
+
get playbackName() {
|
|
44002
|
+
return String(this.container.playback.name || '');
|
|
44003
|
+
}
|
|
44004
|
+
get playbackType() {
|
|
44005
|
+
return this.container.getPlaybackType();
|
|
44006
|
+
}
|
|
44007
|
+
now() {
|
|
44008
|
+
const hasPerformanceSupport = window.performance && typeof window.performance.now === 'function';
|
|
44009
|
+
return hasPerformanceSupport
|
|
44010
|
+
? window.performance.now()
|
|
44011
|
+
: new Date().getTime();
|
|
44012
|
+
}
|
|
44013
|
+
inc(counter) {
|
|
44014
|
+
this.metrics.counters[counter] += 1;
|
|
44015
|
+
}
|
|
44016
|
+
// _timerHasStarted(timer) {
|
|
44017
|
+
// return this[`_start${timer}`] !== undefined;
|
|
44018
|
+
// }
|
|
44019
|
+
start(timer) {
|
|
44020
|
+
// this[`_start${timer}`] = this._now();
|
|
44021
|
+
this.timers[timer] = this.now();
|
|
44022
|
+
}
|
|
44023
|
+
stop(timer) {
|
|
44024
|
+
// this._metrics.timers[timer] += this._now() - this[`_start${timer}`];
|
|
44025
|
+
this.metrics.chrono[timer] += this.now() - this.timers[timer];
|
|
44026
|
+
}
|
|
44027
|
+
constructor(container) {
|
|
44028
|
+
super(container);
|
|
44029
|
+
this.runEach = container.options.clapprStats?.runEach ?? 5000;
|
|
44030
|
+
}
|
|
44031
|
+
/**
|
|
44032
|
+
* @internal
|
|
44033
|
+
*/
|
|
44034
|
+
bindEvents() {
|
|
44035
|
+
this.listenTo(this.container, Events$1.CONTAINER_BITRATE, this.onBitrate);
|
|
44036
|
+
this.listenTo(this.container, Events$1.CONTAINER_STOP, this.stopReporting);
|
|
44037
|
+
this.listenTo(this.container, Events$1.CONTAINER_ENDED, this.stopReporting);
|
|
44038
|
+
this.listenToOnce(this.container.playback, Events$1.PLAYBACK_PLAY_INTENT, this.startTimers);
|
|
44039
|
+
this.listenToOnce(this.container, Events$1.CONTAINER_PLAY, this.onFirstPlaying);
|
|
44040
|
+
this.listenTo(this.container, Events$1.CONTAINER_PLAY, this.onPlay);
|
|
44041
|
+
this.listenTo(this.container, Events$1.CONTAINER_PAUSE, this.onPause);
|
|
44042
|
+
this.listenToOnce(this.container, Events$1.CONTAINER_STATE_BUFFERING, this.onBuffering);
|
|
44043
|
+
this.listenTo(this.container, Events$1.CONTAINER_SEEK, this.onSeek);
|
|
44044
|
+
this.listenTo(this.container, Events$1.CONTAINER_ERROR, () => this.inc(Counter.Error));
|
|
44045
|
+
this.listenTo(this.container, Events$1.CONTAINER_FULLSCREEN, () => this.inc(Counter.Fullscreen));
|
|
44046
|
+
this.listenTo(this.container, Events$1.CONTAINER_PLAYBACKDVRSTATECHANGED, (dvrInUse) => {
|
|
44047
|
+
dvrInUse && this.inc(Counter.DvrUsage);
|
|
44048
|
+
});
|
|
44049
|
+
this.listenTo(this.container.playback, Events$1.PLAYBACK_PROGRESS, this.onProgress);
|
|
44050
|
+
this.listenTo(this.container.playback, Events$1.PLAYBACK_TIMEUPDATE, this.onTimeUpdate);
|
|
44051
|
+
}
|
|
44052
|
+
/**
|
|
44053
|
+
* @internal
|
|
44054
|
+
*/
|
|
44055
|
+
destroy() {
|
|
44056
|
+
this.stopReporting();
|
|
44057
|
+
super.destroy();
|
|
44058
|
+
}
|
|
44059
|
+
/**
|
|
44060
|
+
* Returns the collected metrics.
|
|
44061
|
+
* @returns Measurements collected so far
|
|
44062
|
+
*/
|
|
44063
|
+
exportMetrics() {
|
|
44064
|
+
return structuredClone(this.metrics);
|
|
44065
|
+
}
|
|
44066
|
+
onBitrate(newBitrate) {
|
|
44067
|
+
const bitrate = newBitrate.bitrate;
|
|
44068
|
+
const now = this.now();
|
|
44069
|
+
if (this.metrics.extra.bitratesHistory.length > 0) {
|
|
44070
|
+
const last = this.metrics.extra.bitratesHistory[this.metrics.extra.bitratesHistory.length - 1];
|
|
44071
|
+
last.end = now;
|
|
44072
|
+
last.time = now - last.start;
|
|
44073
|
+
}
|
|
44074
|
+
this.metrics.extra.bitratesHistory.push({ start: this.now(), bitrate });
|
|
44075
|
+
this.inc(Counter.ChangeLevel);
|
|
44076
|
+
}
|
|
44077
|
+
stopReporting() {
|
|
44078
|
+
this.buildReport();
|
|
44079
|
+
if (this.timerId !== null) {
|
|
44080
|
+
clearInterval(this.timerId);
|
|
44081
|
+
this.timerId = null;
|
|
44082
|
+
}
|
|
44083
|
+
}
|
|
44084
|
+
startTimers() {
|
|
44085
|
+
this.timerId = setInterval(this.buildReport.bind(this), this.runEach);
|
|
44086
|
+
this.start(Chronograph.Session);
|
|
44087
|
+
this.start(Chronograph.Startup);
|
|
44088
|
+
}
|
|
44089
|
+
onFirstPlaying() {
|
|
44090
|
+
this.listenTo(this.container, Events$1.CONTAINER_TIMEUPDATE, this.onContainerUpdateWhilePlaying);
|
|
44091
|
+
this.start(Chronograph.Watch);
|
|
44092
|
+
this.stop(Chronograph.Startup);
|
|
44093
|
+
}
|
|
44094
|
+
playAfterPause() {
|
|
44095
|
+
this.listenTo(this.container, Events$1.CONTAINER_TIMEUPDATE, this.onContainerUpdateWhilePlaying);
|
|
44096
|
+
this.stop(Chronograph.Pause);
|
|
44097
|
+
this.start(Chronograph.Watch);
|
|
44098
|
+
}
|
|
44099
|
+
onPlay() {
|
|
44100
|
+
this.inc(Counter.Play);
|
|
44101
|
+
}
|
|
44102
|
+
onPause() {
|
|
44103
|
+
this.stop(Chronograph.Watch);
|
|
44104
|
+
this.start(Chronograph.Pause);
|
|
44105
|
+
this.inc(Counter.Pause);
|
|
44106
|
+
this.listenToOnce(this.container, Events$1.CONTAINER_PLAY, this.playAfterPause);
|
|
44107
|
+
this.stopListening(this.container, Events$1.CONTAINER_TIMEUPDATE, this.onContainerUpdateWhilePlaying);
|
|
44108
|
+
}
|
|
44109
|
+
onSeek(e) {
|
|
44110
|
+
this.inc(Counter.Seek);
|
|
44111
|
+
this.metrics.extra.watchHistory.push([e * 1000, e * 1000]);
|
|
44112
|
+
}
|
|
44113
|
+
onTimeUpdate(e) {
|
|
44114
|
+
const current = e.current * 1000, total = e.total * 1000, l = this.metrics.extra.watchHistory.length;
|
|
44115
|
+
this.metrics.extra.duration = total;
|
|
44116
|
+
this.metrics.extra.currentTime = current;
|
|
44117
|
+
// TODO what if it's a live stream?
|
|
44118
|
+
this.metrics.extra.watchedPercentage = (current / total) * 100;
|
|
44119
|
+
if (l === 0) {
|
|
44120
|
+
this.metrics.extra.watchHistory.push([current, current]);
|
|
44121
|
+
}
|
|
44122
|
+
else {
|
|
44123
|
+
this.metrics.extra.watchHistory[l - 1][1] = current;
|
|
44124
|
+
}
|
|
44125
|
+
if (this.metrics.extra.bitratesHistory.length > 0) {
|
|
44126
|
+
const lastBitrate = this.metrics.extra.bitratesHistory[this.metrics.extra.bitratesHistory.length - 1];
|
|
44127
|
+
if (!lastBitrate.end) {
|
|
44128
|
+
lastBitrate.time = this.now() - lastBitrate.start;
|
|
44129
|
+
}
|
|
44130
|
+
}
|
|
44131
|
+
this.onCompletion();
|
|
44132
|
+
}
|
|
44133
|
+
onContainerUpdateWhilePlaying() {
|
|
44134
|
+
if (this.container.playback.isPlaying()) {
|
|
44135
|
+
this.stop(Chronograph.Watch);
|
|
44136
|
+
this.start(Chronograph.Watch);
|
|
44137
|
+
}
|
|
44138
|
+
}
|
|
44139
|
+
onBuffering() {
|
|
44140
|
+
this.inc(Counter.Buffering);
|
|
44141
|
+
this.start(Chronograph.Buffering);
|
|
44142
|
+
this.listenToOnce(this.container, Events$1.CONTAINER_STATE_BUFFERFULL, this.onBufferfull);
|
|
44143
|
+
}
|
|
44144
|
+
onBufferfull() {
|
|
44145
|
+
this.stop(Chronograph.Buffering);
|
|
44146
|
+
this.listenToOnce(this.container, Events$1.CONTAINER_STATE_BUFFERING, this.onBuffering);
|
|
44147
|
+
}
|
|
44148
|
+
onProgress(progress) {
|
|
44149
|
+
this.metrics.extra.buffersize = progress.current * 1000;
|
|
44150
|
+
}
|
|
44151
|
+
onCompletion() {
|
|
44152
|
+
// Decide if this is needed
|
|
44153
|
+
// const currentPercentage = this.metrics.extra.watchedPercentage;
|
|
44154
|
+
// this.trigger(ClapprStatsEvents.PERCENTAGE, currentPercentage);
|
|
44155
|
+
}
|
|
44156
|
+
buildReport() {
|
|
44157
|
+
this.stop(Chronograph.Session);
|
|
44158
|
+
this.start(Chronograph.Session);
|
|
44159
|
+
this.metrics.extra.playbackName = this.playbackName;
|
|
44160
|
+
this.metrics.extra.playbackType = this.playbackType;
|
|
44161
|
+
this.calcBitrates();
|
|
44162
|
+
this.calcBufferingPercentage();
|
|
44163
|
+
// TODO calc FPS properly, e.g., on TIMEUPDATE event
|
|
44164
|
+
this.fetchFPS();
|
|
44165
|
+
this.trigger(ClapprStatsEvents.REPORT, structuredClone(this.metrics));
|
|
44166
|
+
}
|
|
44167
|
+
fetchFPS() {
|
|
44168
|
+
// TODO check if the playback and media sources support video, then use the common method
|
|
44169
|
+
// flashls ??? - hls.droppedFramesl hls.stream.bufferLength (seconds)
|
|
44170
|
+
// hls ??? (use the same?)
|
|
44171
|
+
const fetchFPS = {
|
|
44172
|
+
html5_video: this.html5FetchFPS,
|
|
44173
|
+
hls: this.html5FetchFPS,
|
|
44174
|
+
dash: this.html5FetchFPS,
|
|
44175
|
+
};
|
|
44176
|
+
if (this.playbackName in fetchFPS) {
|
|
44177
|
+
fetchFPS[this.playbackName].call(this);
|
|
44178
|
+
}
|
|
44179
|
+
}
|
|
44180
|
+
// TODO sort out
|
|
44181
|
+
calcBitrates() {
|
|
44182
|
+
const { bitratesHistory } = this.metrics.extra;
|
|
44183
|
+
if (bitratesHistory.length === 0) {
|
|
44184
|
+
return;
|
|
44185
|
+
}
|
|
44186
|
+
let totalTime = 0;
|
|
44187
|
+
let weightedTotal = 0;
|
|
44188
|
+
for (const { bitrate, time = 0 } of bitratesHistory) {
|
|
44189
|
+
totalTime += time;
|
|
44190
|
+
weightedTotal += bitrate * time;
|
|
44191
|
+
}
|
|
44192
|
+
this.metrics.extra.bitrateWeightedMean = weightedTotal / totalTime;
|
|
44193
|
+
this.metrics.extra.bitrateMostUsed = bitratesHistory.reduce((mostUsed, current) => (current.time || 0) > (mostUsed.time || 0) ? current : mostUsed, { time: 0, bitrate: 0, start: 0, end: 0 }).bitrate;
|
|
44194
|
+
}
|
|
44195
|
+
calcBufferingPercentage() {
|
|
44196
|
+
if (this.metrics.extra.duration > 0) {
|
|
44197
|
+
this.metrics.extra.bufferingPercentage =
|
|
44198
|
+
(this.metrics.chrono.buffering / this.metrics.extra.duration) * 100;
|
|
44199
|
+
}
|
|
44200
|
+
}
|
|
44201
|
+
html5FetchFPS() {
|
|
44202
|
+
const videoTag = this.container.playback.el;
|
|
44203
|
+
const getFirstValidValue = (...args) => args.find((val) => val !== undefined);
|
|
44204
|
+
const decodedFrames = getFirstValidValue(videoTag.webkitDecodedFrameCount, videoTag.mozDecodedFrames, 0);
|
|
44205
|
+
const droppedFrames = getFirstValidValue(videoTag.webkitDroppedFrameCount, videoTag.mozParsedFrames && videoTag.mozDecodedFrames
|
|
44206
|
+
? videoTag.mozParsedFrames - videoTag.mozDecodedFrames
|
|
44207
|
+
: 0, 0);
|
|
44208
|
+
const delta = decodedFrames - (this.lastDecodedFramesCount || 0);
|
|
44209
|
+
this.metrics.counters.decodedFrames = decodedFrames;
|
|
44210
|
+
this.metrics.counters.droppedFrames = droppedFrames;
|
|
44211
|
+
this.metrics.counters.fps = delta / (this.runEach / 1000); // TODO use time delta instead of runEach
|
|
44212
|
+
this.lastDecodedFramesCount = decodedFrames;
|
|
44213
|
+
}
|
|
44214
|
+
}
|
|
44215
|
+
|
|
43888
44216
|
var mousetrap = {exports: {}};
|
|
43889
44217
|
|
|
43890
44218
|
/*global define:false */
|
|
@@ -44952,56 +45280,6 @@ function requireMousetrap () {
|
|
|
44952
45280
|
var mousetrapExports = requireMousetrap();
|
|
44953
45281
|
const Mousetrap = /*@__PURE__*/getDefaultExportFromCjs$1(mousetrapExports);
|
|
44954
45282
|
|
|
44955
|
-
/**
|
|
44956
|
-
* @beta
|
|
44957
|
-
*/
|
|
44958
|
-
var ClapprStatsEvents;
|
|
44959
|
-
(function (ClapprStatsEvents) {
|
|
44960
|
-
ClapprStatsEvents["REPORT_EVENT"] = "clappr:stats:report";
|
|
44961
|
-
ClapprStatsEvents["PERCENTAGE_EVENT"] = "clappr:stats:percentage";
|
|
44962
|
-
})(ClapprStatsEvents || (ClapprStatsEvents = {}));
|
|
44963
|
-
|
|
44964
|
-
function newMetrics$1() {
|
|
44965
|
-
return {
|
|
44966
|
-
counters: {
|
|
44967
|
-
play: 0,
|
|
44968
|
-
pause: 0,
|
|
44969
|
-
error: 0,
|
|
44970
|
-
buffering: 0,
|
|
44971
|
-
decodedFrames: 0,
|
|
44972
|
-
droppedFrames: 0,
|
|
44973
|
-
fps: 0,
|
|
44974
|
-
changeLevel: 0,
|
|
44975
|
-
seek: 0,
|
|
44976
|
-
fullscreen: 0,
|
|
44977
|
-
dvrUsage: 0,
|
|
44978
|
-
},
|
|
44979
|
-
timers: {
|
|
44980
|
-
startup: 0,
|
|
44981
|
-
watch: 0,
|
|
44982
|
-
pause: 0,
|
|
44983
|
-
buffering: 0,
|
|
44984
|
-
session: 0,
|
|
44985
|
-
latency: 0,
|
|
44986
|
-
},
|
|
44987
|
-
extra: {
|
|
44988
|
-
playbackName: '',
|
|
44989
|
-
playbackType: '',
|
|
44990
|
-
bitratesHistory: [],
|
|
44991
|
-
bitrateWeightedMean: 0,
|
|
44992
|
-
bitrateMostUsed: 0,
|
|
44993
|
-
buffersize: 0,
|
|
44994
|
-
watchHistory: [],
|
|
44995
|
-
watchedPercentage: 0,
|
|
44996
|
-
bufferingPercentage: 0,
|
|
44997
|
-
bandwidth: 0,
|
|
44998
|
-
duration: 0,
|
|
44999
|
-
currentTime: 0,
|
|
45000
|
-
},
|
|
45001
|
-
custom: {},
|
|
45002
|
-
};
|
|
45003
|
-
}
|
|
45004
|
-
|
|
45005
45283
|
var humanFormat$2 = {exports: {}};
|
|
45006
45284
|
|
|
45007
45285
|
var humanFormat$1 = humanFormat$2.exports;
|
|
@@ -45376,63 +45654,72 @@ const timeScale = new humanFormat.Scale({
|
|
|
45376
45654
|
ms: 1,
|
|
45377
45655
|
sec: 1000,
|
|
45378
45656
|
min: 60000,
|
|
45379
|
-
hours: 3600000
|
|
45657
|
+
hours: 3600000,
|
|
45380
45658
|
});
|
|
45381
45659
|
const percentScale = new humanFormat.Scale({
|
|
45382
|
-
'%': 1
|
|
45660
|
+
'%': 1,
|
|
45383
45661
|
});
|
|
45662
|
+
const metricTemplates = {
|
|
45663
|
+
fps: {
|
|
45664
|
+
scale: 'SI',
|
|
45665
|
+
decimals: 0,
|
|
45666
|
+
},
|
|
45667
|
+
volume: {
|
|
45668
|
+
scale: percentScale,
|
|
45669
|
+
},
|
|
45670
|
+
};
|
|
45384
45671
|
const formattingTemplate = {
|
|
45385
45672
|
general: {
|
|
45386
45673
|
volume: {
|
|
45387
|
-
scale: percentScale
|
|
45388
|
-
}
|
|
45674
|
+
scale: percentScale,
|
|
45675
|
+
},
|
|
45389
45676
|
},
|
|
45390
45677
|
timers: {
|
|
45391
45678
|
startup: {
|
|
45392
|
-
scale: timeScale
|
|
45679
|
+
scale: timeScale,
|
|
45393
45680
|
},
|
|
45394
45681
|
watch: {
|
|
45395
|
-
scale: timeScale
|
|
45682
|
+
scale: timeScale,
|
|
45396
45683
|
},
|
|
45397
45684
|
pause: {
|
|
45398
|
-
scale: timeScale
|
|
45685
|
+
scale: timeScale,
|
|
45399
45686
|
},
|
|
45400
45687
|
buffering: {
|
|
45401
|
-
scale: timeScale
|
|
45688
|
+
scale: timeScale,
|
|
45402
45689
|
},
|
|
45403
45690
|
session: {
|
|
45404
|
-
scale: timeScale
|
|
45691
|
+
scale: timeScale,
|
|
45405
45692
|
},
|
|
45406
45693
|
latency: {
|
|
45407
|
-
scale: timeScale
|
|
45408
|
-
}
|
|
45694
|
+
scale: timeScale,
|
|
45695
|
+
},
|
|
45409
45696
|
},
|
|
45410
45697
|
extra: {
|
|
45411
45698
|
buffersize: {
|
|
45412
|
-
scale: timeScale
|
|
45699
|
+
scale: timeScale,
|
|
45413
45700
|
},
|
|
45414
45701
|
duration: {
|
|
45415
|
-
scale: timeScale
|
|
45702
|
+
scale: timeScale,
|
|
45416
45703
|
},
|
|
45417
45704
|
currentTime: {
|
|
45418
|
-
scale: timeScale
|
|
45705
|
+
scale: timeScale,
|
|
45419
45706
|
},
|
|
45420
45707
|
bitrateWeightedMean: {
|
|
45421
|
-
unit: 'bps'
|
|
45708
|
+
unit: 'bps',
|
|
45422
45709
|
},
|
|
45423
45710
|
bitrateMostUsed: {
|
|
45424
|
-
unit: 'bps'
|
|
45711
|
+
unit: 'bps',
|
|
45425
45712
|
},
|
|
45426
45713
|
bandwidth: {
|
|
45427
|
-
unit: 'bps'
|
|
45714
|
+
unit: 'bps',
|
|
45428
45715
|
},
|
|
45429
45716
|
watchedPercentage: {
|
|
45430
|
-
scale: percentScale
|
|
45717
|
+
scale: percentScale,
|
|
45431
45718
|
},
|
|
45432
45719
|
bufferingPercentage: {
|
|
45433
|
-
scale: percentScale
|
|
45434
|
-
}
|
|
45435
|
-
}
|
|
45720
|
+
scale: percentScale,
|
|
45721
|
+
},
|
|
45722
|
+
},
|
|
45436
45723
|
};
|
|
45437
45724
|
class Formatter {
|
|
45438
45725
|
static format(metrics) {
|
|
@@ -45442,8 +45729,10 @@ class Formatter {
|
|
|
45442
45729
|
formattedMetrics[type] = fmt;
|
|
45443
45730
|
const typeTemplate = formattingTemplate[type];
|
|
45444
45731
|
Object.entries(mm).forEach(([name, value]) => {
|
|
45445
|
-
|
|
45446
|
-
|
|
45732
|
+
if (typeTemplate &&
|
|
45733
|
+
typeTemplate[name] &&
|
|
45734
|
+
typeof value === 'number' &&
|
|
45735
|
+
!isNaN(value)) {
|
|
45447
45736
|
// @ts-ignore
|
|
45448
45737
|
const templateScale = typeTemplate[name].scale || 'SI';
|
|
45449
45738
|
// @ts-ignore
|
|
@@ -45451,7 +45740,7 @@ class Formatter {
|
|
|
45451
45740
|
fmt[name] = humanFormat(value, {
|
|
45452
45741
|
scale: templateScale,
|
|
45453
45742
|
unit: templateUnit,
|
|
45454
|
-
decimals: 2
|
|
45743
|
+
decimals: 2,
|
|
45455
45744
|
});
|
|
45456
45745
|
}
|
|
45457
45746
|
else {
|
|
@@ -45461,6 +45750,27 @@ class Formatter {
|
|
|
45461
45750
|
});
|
|
45462
45751
|
return formattedMetrics;
|
|
45463
45752
|
}
|
|
45753
|
+
static formatVolume(volume) {
|
|
45754
|
+
return humanFormat(volume, metricTemplates.volume);
|
|
45755
|
+
}
|
|
45756
|
+
static formatTime(time) {
|
|
45757
|
+
return humanFormat(time, {
|
|
45758
|
+
scale: timeScale,
|
|
45759
|
+
});
|
|
45760
|
+
}
|
|
45761
|
+
static formatFps(fps) {
|
|
45762
|
+
return humanFormat(fps, metricTemplates.fps);
|
|
45763
|
+
}
|
|
45764
|
+
static formatPercentage(percentage) {
|
|
45765
|
+
return humanFormat(percentage, {
|
|
45766
|
+
scale: percentScale,
|
|
45767
|
+
});
|
|
45768
|
+
}
|
|
45769
|
+
static formatBitrate(bitrate) {
|
|
45770
|
+
return humanFormat(bitrate, {
|
|
45771
|
+
unit: 'bps',
|
|
45772
|
+
});
|
|
45773
|
+
}
|
|
45464
45774
|
}
|
|
45465
45775
|
|
|
45466
45776
|
const SpeedtestWorkerModule = "// data reported to main thread\n\n// -1=not started, 0=starting, 1=download test, 2=ping+jitter test, 3=upload test, 4=finished, 5=abort\nlet testState = -1;\n// download speed in megabit/s with 2 decimal digits\nlet dlStatus = 0;\n// upload speed in megabit/s with 2 decimal digits\nlet ulStatus = '';\n// ping in milliseconds with 2 decimal digits\nlet pingStatus = '';\n// jitter in milliseconds with 2 decimal digits\nlet jitterStatus = '';\n// client's IP address as reported by getIP.php\nlet clientIp = '';\nlet serverHostName = '';\n//progress of download test 0-1\nlet dlProgress = 0;\n//progress of upload test 0-1\nlet ulProgress = 0;\n//progress of ping+jitter test 0-1\nlet pingProgress = 0;\n//test ID (sent back by telemetry if used, null otherwise)\nlet testId = null;\n\nlet log = ''; //telemetry log\n\nfunction tlog(s) {\n if (settings.telemetry_level >= 2) {\n log += Date.now() + ': ' + s + '\\n';\n }\n}\n\nfunction tverb(s) {\n if (settings.telemetry_level >= 3) {\n log += Date.now() + ': ' + s + '\\n';\n }\n}\n\nfunction twarn(s) {\n if (settings.telemetry_level >= 2) {\n log += Date.now() + ' WARN: ' + s + '\\n';\n }\n\n console.warn(s);\n}\n\n// test settings. can be overridden by sending specific values with the start command\nconst settings = {\n //set to true when in MPOT mode\n mpot: false,\n //order in which tests will be performed as a string. D=Download, U=Upload, P=Ping+Jitter, I=IP, _=1 second delay\n test_order: 'P_D',\n // max duration of upload test in seconds\n time_ul_max: 0,\n // max duration of download test in seconds\n time_dl_max: 15,\n // if set to true, tests will take less time on faster connections\n time_auto: true,\n //time to wait in seconds before actually measuring ul speed (wait for buffers to fill)\n time_ulGraceTime: 3,\n //time to wait in seconds before actually measuring dl speed (wait for TCP window to increase)\n time_dlGraceTime: 1.5,\n // number of pings to perform in ping test\n count_ping: 10,\n // path to a large file or garbage.php, used for download test. must be relative to this js file\n url_dl: 'backend/garbage.php',\n // path to an empty file, used for upload test. must be relative to this js file\n url_ul: 'backend/empty.php',\n // path to an empty file, used for ping test. must be relative to this js file\n url_ping: 'backend/empty.php',\n // path to getIP.php relative to this js file, or a similar thing that outputs the client's ip\n url_getIp: 'backend/getIP.php',\n // if set to true, the server will include ISP info with the IP address\n getIp_ispInfo: true,\n // km or mi=estimate distance from server in km/mi; set to false to disable distance estimation.\n // getIp_ispInfo must be enabled in order for this to work\n getIp_ispInfo_distance: false,\n // number of download streams to use (can be different if enable_quirks is active)\n xhr_dlMultistream: 6,\n // number of upload streams to use (can be different if enable_quirks is active)\n xhr_ulMultistream: 3,\n // how much concurrent requests should be delayed\n xhr_multistreamDelay: 300,\n // 0=fail on errors, 1=attempt to restart a stream if it fails, 2=ignore all errors\n xhr_ignoreErrors: 1,\n // if set to true, it reduces ram usage but uses the hard drive (useful with large garbagePhp_chunkSize\n // and/or high xhr_dlMultistream)\n xhr_dlUseBlob: false,\n // size in megabytes of the upload blobs sent in the upload test (forced to 4 on chrome mobile)\n xhr_ul_blob_megabytes: 20,\n // size of chunks sent by garbage.php (can be different if enable_quirks is active)\n garbagePhp_chunkSize: 100,\n // enable quirks for specific browsers. currently it overrides settings to optimize for specific browsers,\n // unless they are already being overridden with the start command\n enable_quirks: true,\n // if enabled, the ping test will attempt to calculate the ping more precisely using the Performance API.\n // Currently works perfectly in Chrome, badly in Edge, and not at all in Firefox.\n // If Performance API is not supported or the result is obviously wrong, a fallback is provided.\n ping_allowPerformanceApi: true,\n // can be changed to compensatie for transport overhead. (see doc.md for some other values)\n overheadCompensationFactor: 1.06,\n //if set to true, speed will be reported in mebibits/s instead of megabits/s\n useMebibits: false,\n // 0=disabled, 1=basic (results only), 2=full (results and timing) 3=debug (results+log)\n telemetry_level: 0,\n // path to the script that adds telemetry data to the database\n url_telemetry: 'results/telemetry.php',\n //extra data that can be passed to the telemetry through the settings\n telemetry_extra: ''\n};\n\nlet xhr = null; // array of currently active xhr requests\nlet interval = null; // timer used in tests\nlet test_pointer = 0; //pointer to the next test to run inside settings.test_order\n\n/*\n this function is used on URLs passed in the settings to determine whether we need a ? or an & as a separator\n*/\nfunction url_sep(url) {\n return url.match(/\\?/) ? '&' : '?';\n}\n\n/*\n listener for commands from main thread to this worker.\n commands:\n -status: returns the current status as a JSON string containing testState,\n dlStatus, ulStatus, pingStatus, clientIp, jitterStatus, dlProgress, ulProgress, pingProgress\n -abort: aborts the current test\n -start: starts the test. optionally, settings can be passed as JSON.\n example: start {\"time_ul_max\":\"10\", \"time_dl_max\":\"10\", \"count_ping\":\"50\"}\n*/\nself.addEventListener('message', function (e) {\n const params = e.data.split(' ');\n\n if (params[0] === 'status') {\n // return status\n postMessage(\n {\n testState: testState,\n dlStatus: dlStatus,\n ulStatus: ulStatus,\n pingStatus: pingStatus,\n clientIp: clientIp,\n serverHostName: serverHostName,\n jitterStatus: jitterStatus,\n dlProgress: dlProgress,\n ulProgress: ulProgress,\n pingProgress: pingProgress,\n testId: testId\n }\n );\n }\n if (params[0] === 'start' && testState === -1) {\n const ua = navigator.userAgent;\n\n // start new test\n testState = 0;\n try {\n // parse settings, if present\n let s = {};\n\n try {\n const ss = e.data.substring(5);\n\n if (ss) {\n s = JSON.parse(ss);\n }\n } catch (e) {\n twarn('Error parsing custom settings JSON. Please check your syntax');\n }\n //copy custom settings\n for (const key in s) {\n if (typeof settings[key] !== 'undefined') {\n settings[key] = s[key];\n } else {\n twarn('Unknown setting ignored: ' + key);\n }\n }\n // quirks for specific browsers. apply only if not overridden. more may be added in future releases\n if (settings.enable_quirks || (typeof s.enable_quirks !== 'undefined' && s.enable_quirks)) {\n if (/Firefox.(\\d+\\.\\d+)/i.test(ua)) {\n if (typeof s.ping_allowPerformanceApi === 'undefined') {\n // ff performance API sucks\n settings.ping_allowPerformanceApi = false;\n }\n }\n if (/Edge.(\\d+\\.\\d+)/i.test(ua)) {\n if (typeof s.xhr_dlMultistream === 'undefined') {\n // edge more precise with 3 download streams\n settings.xhr_dlMultistream = 3;\n }\n }\n if (/Chrome.(\\d+)/i.test(ua) && !!self.fetch) {\n if (typeof s.xhr_dlMultistream === 'undefined') {\n // chrome more precise with 5 streams\n settings.xhr_dlMultistream = 5;\n }\n }\n }\n if (/Edge.(\\d+\\.\\d+)/i.test(ua)) {\n //Edge 15 introduced a bug that causes onprogress events to not get fired,\n // we have to use the \"small chunks\" workaround that reduces accuracy\n settings.forceIE11Workaround = true;\n }\n if (/PlayStation 4.(\\d+\\.\\d+)/i.test(ua)) {\n //PS4 browser has the same bug as IE11/Edge\n settings.forceIE11Workaround = true;\n }\n if (/Chrome.(\\d+)/i.test(ua) && /Android|iPhone|iPad|iPod|Windows Phone/i.test(ua)) {\n // cheap af\n // Chrome mobile introduced a limitation somewhere around version 65,\n // we have to limit XHR upload size to 4 megabytes\n settings.xhr_ul_blob_megabytes = 4;\n }\n if (/^((?!chrome|android|crios|fxios).)*safari/i.test(ua)) {\n //Safari also needs the IE11 workaround but only for the MPOT version\n settings.forceIE11Workaround = true;\n }\n // telemetry_level has to be parsed and not just copied\n if (typeof s.telemetry_level !== 'undefined') {\n const telemetryLevels = {\n 'basic': 1,\n 'full': 2,\n 'debug': 3\n };\n\n settings.telemetry_level = telemetryLevels[s.telemetry_level] || 0;\n } // telemetry level\n // transform test_order to uppercase, just in case\n settings.test_order = settings.test_order.toUpperCase();\n } catch (e) {\n twarn('Possible error in custom test settings. Some settings might not have been applied. Exception: ' + e);\n }\n // run the tests\n tverb(JSON.stringify(settings));\n test_pointer = 0;\n let iRun = false,\n dRun = false,\n // uRun = false,\n pRun = false;\n // eslint-disable-next-line no-var\n var runNextTest = function () {\n if (testState === 5) {\n return;\n }\n if (test_pointer >= settings.test_order.length) {\n //test is finished\n if (settings.telemetry_level > 0) {\n sendTelemetry(function (id) {\n testState = 4;\n if (id !== null || id !== undefined) {\n testId = id;\n }\n });\n } else {\n testState = 4;\n }\n\n return;\n }\n switch (settings.test_order.charAt(test_pointer)) {\n case 'I': {\n test_pointer++;\n if (iRun) {\n runNextTest();\n\n return;\n } else {\n iRun = true;\n }\n getIp(runNextTest);\n }\n break;\n case 'D': {\n test_pointer++;\n if (dRun) {\n runNextTest();\n\n return;\n } else {\n dRun = true;\n }\n testState = 1;\n dlTest(runNextTest);\n }\n break;\n case 'U': {\n // test_pointer++;\n // if (uRun) {\n // runNextTest();\n // return;\n // } else uRun = true;\n // testState = 3;\n // ulTest(runNextTest);\n }\n break;\n case 'P': {\n test_pointer++;\n if (pRun) {\n runNextTest();\n\n return;\n } else {\n pRun = true;\n }\n testState = 2;\n pingTest(runNextTest);\n }\n break;\n case '_': {\n test_pointer++;\n setTimeout(runNextTest, 1000);\n }\n break;\n default:\n test_pointer++;\n }\n };\n\n runNextTest();\n }\n if (params[0] === 'abort') {\n // abort command\n if (testState >= 4) {\n return;\n }\n tlog('manually aborted');\n clearRequests(); // stop all xhr activity\n runNextTest = null;\n if (interval) {\n clearInterval(interval);\n } // clear timer if present\n if (settings.telemetry_level > 1) {\n sendTelemetry(function () {\n });\n }\n testState = 5; //set test as aborted\n dlStatus = 0;\n ulStatus = '';\n pingStatus = '';\n jitterStatus = '';\n clientIp = '';\n serverHostName = '';\n dlProgress = 0;\n ulProgress = 0;\n pingProgress = 0;\n }\n});\n\n// stops all XHR activity, aggressively\nfunction clearRequests() {\n tverb('stopping pending XHRs');\n if (xhr) {\n for (let i = 0; i < xhr.length; i++) {\n try {\n xhr[i].onprogress = null;\n xhr[i].onload = null;\n xhr[i].onerror = null;\n } catch (e) {\n console.warn(e);\n }\n try {\n xhr[i].upload.onprogress = null;\n xhr[i].upload.onload = null;\n xhr[i].upload.onerror = null;\n } catch (e) {\n console.warn(e);\n }\n try {\n xhr[i].abort();\n } catch (e) {\n console.warn(e);\n }\n try {\n delete xhr[i];\n } catch (e) {\n console.warn(e);\n }\n }\n xhr = null;\n }\n}\n\n// gets client's IP using url_getIp, then calls the done function\nlet ipCalled = false; // used to prevent multiple accidental calls to getIp\nlet ispInfo = ''; //used for telemetry\n\nfunction getIp(done) {\n tverb('getIp');\n if (ipCalled) {\n return;\n } else {\n ipCalled = true;\n } // getIp already called?\n const startT = new Date().getTime();\n\n xhr = new XMLHttpRequest();\n xhr.onload = function () {\n tlog('IP: ' + xhr.responseText + ', took ' + (new Date().getTime() - startT) + 'ms');\n try {\n const data = JSON.parse(xhr.responseText);\n\n clientIp = data.processedString;\n serverHostName = data.serverHostName;\n ispInfo = data.rawIspInfo;\n } catch (e) {\n clientIp = xhr.responseText;\n ispInfo = '';\n }\n done();\n };\n xhr.onerror = function () {\n tlog('getIp failed, took ' + (new Date().getTime() - startT) + 'ms');\n done();\n };\n const queryParams = [\n settings.mpot ? 'cors=true' : '',\n settings.getIp_ispInfo ?\n `isp=true${settings.getIp_ispInfo_distance ? '&distance=' + settings.getIp_ispInfo_distance : ''}` :\n '',\n 'r=' + Math.random()\n ].filter(Boolean).join('&');\n\n const url = `${settings.url_getIp}${url_sep(settings.url_getIp)}${queryParams}`;\n\n xhr.open(\n 'GET',\n url,\n true\n );\n xhr.send();\n}\n\n// download test, calls done function when it's over\nlet dlCalled = false; // used to prevent multiple accidental calls to dlTest\n\nfunction dlTest(done) {\n tverb('dlTest');\n if (dlCalled) {\n return;\n } else {\n dlCalled = true;\n } // dlTest already called?\n let totLoaded = 0.0, // total number of loaded bytes\n startT = new Date().getTime(), // timestamp when test was started\n bonusT = 0, //how many milliseconds the test has been shortened by (higher on faster connections)\n graceTimeDone = false, //set to true after the grace time is past\n failed = false; // set to true if a stream fails\n\n xhr = [];\n // function to create a download stream. streams are slightly delayed so that they will not end at the same time\n const testStream = function (i, delay) {\n setTimeout(\n function () {\n if (testState !== 1) {\n return;\n } // delayed stream ended up starting after the end of the download test\n tverb('dl test stream started ' + i + ' ' + delay);\n let prevLoaded = 0; // number of bytes loaded last time onprogress was called\n const x = new XMLHttpRequest();\n\n xhr[i] = x;\n xhr[i].onprogress = function (event) {\n tverb('dl stream progress event ' + i + ' ' + event.loaded);\n if (testState !== 1) {\n try {\n x.abort();\n } catch (e) {\n console.warn(e);\n }\n } // just in case this XHR is still running after the download test\n // progress event, add number of new loaded bytes to totLoaded\n const loadDiff = event.loaded <= 0 ? 0 : event.loaded - prevLoaded;\n\n if (isNaN(loadDiff) || !isFinite(loadDiff) || loadDiff < 0) {\n return;\n } // just in case\n totLoaded += loadDiff;\n prevLoaded = event.loaded;\n }.bind(this);\n xhr[i].onload = function () {\n // the large file has been loaded entirely, start again\n tverb('dl stream finished ' + i);\n try {\n xhr[i].abort();\n } catch (e) {\n console.warn(e);\n } // reset the stream data to empty ram\n testStream(i, 0);\n }.bind(this);\n xhr[i].onerror = function () {\n // error\n tverb('dl stream failed ' + i);\n if (settings.xhr_ignoreErrors === 0) {\n failed = true;\n } //abort\n try {\n xhr[i].abort();\n } catch (e) {\n console.warn(e);\n }\n delete xhr[i];\n if (settings.xhr_ignoreErrors === 1) {\n testStream(i, 0);\n } //restart stream\n }.bind(this);\n // send xhr\n try {\n if (settings.xhr_dlUseBlob) {\n xhr[i].responseType = 'blob';\n } else {\n xhr[i].responseType = 'arraybuffer';\n }\n } catch (e) {\n console.warn(e);\n }\n\n const queryParams = [\n settings.mpot ? 'cors=true' : '',\n 'r=' + Math.random(),\n 'ckSize=' + settings.garbagePhp_chunkSize\n ].join('&');\n\n const url = `${settings.url_dl}${url_sep(settings.url_dl)}${queryParams}`;\n\n // random string to prevent caching\n xhr[i].open('GET', url, true);\n xhr[i].send();\n }.bind(this),\n 1 + delay\n );\n }.bind(this);\n\n // open streams\n for (let i = 0; i < settings.xhr_dlMultistream; i++) {\n testStream(i, settings.xhr_multistreamDelay * i);\n }\n // every 200ms, update dlStatus\n interval = setInterval(\n function () {\n tverb('DL: ' + dlStatus + (graceTimeDone ? '' : ' (in grace time)'));\n const t = new Date().getTime() - startT;\n\n if (graceTimeDone) {\n dlProgress = (t + bonusT) / (settings.time_dl_max * 1000);\n }\n if (t < 200) {\n return;\n }\n if (!graceTimeDone) {\n if (t > 1000 * settings.time_dlGraceTime) {\n if (totLoaded > 0) {\n // if the connection is so slow that we didn't get a single chunk yet, do not reset\n startT = new Date().getTime();\n bonusT = 0;\n totLoaded = 0.0;\n }\n graceTimeDone = true;\n }\n } else {\n const speed = totLoaded / (t / 1000.0);\n\n if (settings.time_auto) {\n //decide how much to shorten the test. Every 200ms, the test is shortened by the bonusT calculated here\n const bonus = (6.4 * speed) / 100000;\n\n bonusT += bonus > 800 ? 800 : bonus;\n }\n // update status\n // speed is multiplied by 8 to go from bytes to bits, overhead compensation is applied,\n // then everything is divided by 1048576 or 1000000 to go to megabits/mebibits\n dlStatus = ((speed * 8 * settings.overheadCompensationFactor) / (settings.useMebibits ? 1048576 : 1000000));\n if ((t + bonusT) / 1000.0 > settings.time_dl_max || failed) {\n // test is over, stop streams and timer\n if (failed || isNaN(dlStatus)) {\n dlStatus = 'Fail';\n }\n clearRequests();\n clearInterval(interval);\n dlProgress = 1;\n tlog('dlTest: ' + dlStatus + ', took ' + (new Date().getTime() - startT) + 'ms');\n done();\n }\n }\n }.bind(this),\n 200\n );\n}\n\n// ping+jitter test, function done is called when it's over\nlet ptCalled = false; // used to prevent multiple accidental calls to pingTest\n\nfunction pingTest(done) {\n tverb('pingTest');\n if (ptCalled) {\n return;\n } else {\n ptCalled = true;\n } // pingTest already called?\n const startT = new Date().getTime(); //when the test was started\n let prevT = null; // last time a pong was received\n let ping = 0.0; // current ping value\n let jitter = 0.0; // current jitter value\n let i = 0; // counter of pongs received\n let prevInstspd = 0; // last ping time, used for jitter calculation\n\n xhr = [];\n // ping function\n const doPing = function () {\n tverb('ping');\n pingProgress = i / settings.count_ping;\n prevT = new Date().getTime();\n xhr[0] = new XMLHttpRequest();\n xhr[0].onload = function () {\n // pong\n tverb('pong');\n if (i === 0) {\n prevT = new Date().getTime(); // first pong\n } else {\n let instspd = new Date().getTime() - prevT;\n\n if (settings.ping_allowPerformanceApi) {\n try {\n //try to get accurate performance timing using performance api\n let p = performance.getEntries();\n\n p = p[p.length - 1];\n let d = p.responseStart - p.requestStart;\n\n if (d <= 0) {\n d = p.duration;\n }\n if (d > 0 && d < instspd) {\n instspd = d;\n }\n } catch (e) {\n //if not possible, keep the estimate\n tverb('Performance API not supported, using estimate');\n }\n }\n //noticed that some browsers randomly have 0ms ping\n if (instspd < 1) {\n instspd = prevInstspd;\n }\n if (instspd < 1) {\n instspd = 1;\n }\n const instjitter = Math.abs(instspd - prevInstspd);\n\n if (i === 1) {\n ping = instspd;\n }/* first ping, can't tell jitter yet*/ else {\n if (instspd < ping) {\n ping = instspd;\n } // update ping, if the instant ping is lower\n if (i === 2) {\n jitter = instjitter;\n } else {\n //discard the first jitter measurement because it might be much higher than it should be\n jitter = instjitter > jitter ? jitter * 0.3 + instjitter * 0.7 : jitter * 0.8 + instjitter * 0.2;\n } // update jitter, weighted average. spikes in ping values are given more weight.\n }\n prevInstspd = instspd;\n }\n pingStatus = ping.toFixed(2);\n jitterStatus = jitter.toFixed(2);\n i++;\n tverb('ping: ' + pingStatus + ' jitter: ' + jitterStatus);\n if (i < settings.count_ping) {\n doPing();\n } else {\n // more pings to do?\n pingProgress = 1;\n tlog('ping: ' + pingStatus + ' jitter: ' + jitterStatus + ', took ' + (new Date().getTime() - startT) + 'ms');\n done();\n }\n }.bind(this);\n xhr[0].onerror = function () {\n // a ping failed, cancel test\n tverb('ping failed');\n if (settings.xhr_ignoreErrors === 0) {\n //abort\n pingStatus = 'Fail';\n jitterStatus = 'Fail';\n clearRequests();\n tlog('ping test failed, took ' + (new Date().getTime() - startT) + 'ms');\n pingProgress = 1;\n done();\n }\n if (settings.xhr_ignoreErrors === 1) {\n doPing();\n } //retry ping\n if (settings.xhr_ignoreErrors === 2) {\n //ignore failed ping\n i++;\n if (i < settings.count_ping) {\n doPing();\n } else {\n // more pings to do?\n pingProgress = 1;\n tlog('ping: ' + pingStatus + ' jitter: ' + jitterStatus + ', took ' + (new Date().getTime() - startT) + 'ms');\n done();\n }\n }\n }.bind(this);\n // send xhr\n const queryString = [\n settings.mpot ? 'cors=true' : '',\n `r=${Math.random()}`\n ].filter(part => part !== '').join('&');\n\n const url = `${settings.url_ping}${url_sep(settings.url_ping)}${queryString}`;\n\n // random string to prevent caching\n xhr[0].open('GET', url, true);\n xhr[0].send();\n }.bind(this);\n\n doPing(); // start first ping\n}\n\n// telemetry\nfunction sendTelemetry(done) {\n if (settings.telemetry_level < 1) {\n return;\n }\n xhr = new XMLHttpRequest();\n xhr.onload = function () {\n try {\n const parts = xhr.responseText.split(' ');\n\n if (parts[0] === 'id') {\n try {\n const id = parts[1];\n\n done(id);\n } catch (e) {\n done(null);\n }\n } else {\n done(null);\n }\n } catch (e) {\n done(null);\n }\n };\n xhr.onerror = function () {\n console.warn('TELEMETRY ERROR ' + xhr.status);\n done(null);\n };\n xhr.open('POST', settings.url_telemetry + url_sep(settings.url_telemetry) + (settings.mpot ? 'cors=true&' : '') + 'r=' + Math.random(), true);\n const telemetryIspInfo = {\n processedString: clientIp,\n serverHostName: serverHostName,\n rawIspInfo: typeof ispInfo === 'object' ? ispInfo : ''\n };\n\n try {\n const fd = new FormData();\n\n fd.append('ispinfo', JSON.stringify(telemetryIspInfo));\n fd.append('dl', dlStatus);\n fd.append('ul', ulStatus);\n fd.append('ping', pingStatus);\n fd.append('jitter', jitterStatus);\n fd.append('log', settings.telemetry_level > 1 ? log : '');\n fd.append('extra', settings.telemetry_extra);\n xhr.send(fd);\n } catch (ex) {\n const postData = 'extra=' + encodeURIComponent(settings.telemetry_extra) + '&ispinfo=' + encodeURIComponent(JSON.stringify(telemetryIspInfo)) + '&dl=' + encodeURIComponent(dlStatus) + '&ul=' + encodeURIComponent(ulStatus) + '&ping=' + encodeURIComponent(pingStatus) + '&jitter=' + encodeURIComponent(jitterStatus) + '&log=' + encodeURIComponent(settings.telemetry_level > 1 ? log : '');\n\n xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');\n xhr.send(postData);\n }\n}\n";
|
|
@@ -45953,7 +46263,7 @@ function rankConnectionSpeed(dlSpeed) {
|
|
|
45953
46263
|
return 0;
|
|
45954
46264
|
}
|
|
45955
46265
|
|
|
45956
|
-
const pluginHtml$4 = "<% general = metrics.general %>\n<% counters = metrics.counters %>\n<% timers = metrics.timers %>\n<% extra = metrics.extra %>\n<% custom = metrics.custom %>\n\n<div class=\"stats-box\">\n <div class=\"stats-box-top\">\n <a class=\"close-button gplayer-lite-btn\" data-close-button>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <g clip-path=\"url(#clip0_184_1489)\">\n <path fill-rule=\"evenodd\" clip-rule=\"evenodd\"\n d=\"M7.41376 6.00013L13.7068 -0.292872C14.0978 -0.683872 14.0978 -1.31587 13.7068 -1.70687C13.3158 -2.09787 12.6838 -2.09787 12.2928 -1.70687L5.99976 4.58613L-0.293238 -1.70687C-0.684238 -2.09787 -1.31624 -2.09787 -1.70724 -1.70687C-2.09824 -1.31587 -2.09824 -0.683872 -1.70724 -0.292872L4.58576 6.00013L-1.70724 12.2931C-2.09824 12.6841 -2.09824 13.3161 -1.70724 13.7071C-1.51224 13.9021 -1.25624 14.0001 -1.00024 14.0001C-0.744238 14.0001 -0.488238 13.9021 -0.293238 13.7071L5.99976 7.41413L12.2928 13.7071C12.4878 13.9021 12.7438 14.0001 12.9998 14.0001C13.2558 14.0001 13.5118 13.9021 13.7068 13.7071C14.0978 13.3161 14.0978 12.6841 13.7068 12.2931L7.41376 6.00013Z\"\n fill=\"white\"/>\n </g>\n <defs>\n <clipPath id=\"clip0_184_1489\">\n <rect width=\"12\" height=\"12\" fill=\"white\"/>\n </clipPath>\n </defs>\n </svg>\n </a>\n </div>\n <div class=\"stats-box-main\">\n <ul>\n <li class=\"title\"><span>General</span></li>\n <li>\n Display resolution:\n <div><span><%= general.displayResolution %></span></div>\n </li>\n <li>\n Volume:\n <div><span><%= general.volume %></span></div>\n </li>\n <li>\n Connection speed:\n <div><span id=\"dlText\"><%= custom.connectionSpeed %></span> Mbps</div>\n </li>\n <li class=\"canvas-wrapper\">\n <canvas id=\"speedTestCanvas\" width=\"190\" height=\"20\"></canvas>\n </li>\n <li>\n Ping:\n <div><span id=\"pingText\"><%= custom.ping %></span> ms</div>\n </li>\n <li>\n Jitter:\n <div><span id=\"jitterText\"><%= custom.jitter %></span> ms</div>\n </li>\n </ul>\n\n <ul>\n <li class=\"title\"><span>Counters</span></li>\n <li>\n Plays:\n <div><span><%= counters.play %></span></div>\n </li>\n <li>\n Pauses:\n <div><span><%= counters.pause %></span></div>\n </li>\n <li>\n Errors:\n <div><span><%= counters.error %></span></div>\n </li>\n <li>\n Bufferings:\n <div><span><%= counters.buffering %></span></div>\n </li>\n <li>\n Decoded frames:\n <div><span><%= counters.decodedFrames %></span></div>\n </li>\n <li>\n Dropped frames:\n <div><span><%= counters.droppedFrames %></span></div>\n </li>\n <li>\n Frames per second:\n <div><span><%= counters.fps %></span></div>\n </li>\n <li>\n Bitrate changes:\n <div><span><%= counters.changeLevel %></span></div>\n </li>\n <li>\n Seeks:\n <div><span><%= counters.seek %></span></div>\n </li>\n <li>\n Fullscreen:\n <div><span><%= counters.fullscreen %></span></div>\n </li>\n <li>\n DVR seeks:\n <div><span><%= counters.dvrUsage %></span></div>\n </li>\n </ul>\n\n <ul>\n <li class=\"title\"><span>Timers</span></li>\n <li>\n Startup time:\n <div><span><%= timers.startup %></span></div>\n </li>\n <li>\n Watching time:\n <div><span><%= timers.watch %></span></div>\n </li>\n <li>\n Pause time:\n <div><span><%= timers.pause %></span></div>\n </li>\n <li>\n Buffering time:\n <div><span><%= timers.buffering %></span></div>\n </li>\n <li>\n Session time:\n <div><span><%= timers.session %></span></div>\n </li>\n <!-- <li>-->\n <!-- Latency:-->\n <!-- <div><span><%= timers.latency %></span></div>-->\n <!-- </li>-->\n </ul>\n\n <ul>\n <li class=\"title\"><span>Extra</span></li>\n <li>\n Playback:\n <div><span><%= extra.playbackName %></span></div>\n </li>\n <li>\n Playback type:\n <div><span><%= extra.playbackType %></span></div>\n </li>\n <li>\n Buffer size:\n <div><span><%= extra.buffersize %></span></div>\n </li>\n <li>\n Video duration:\n <div><span><%= extra.duration %></span></div>\n </li>\n <li>\n Current time:\n <div><span><%= extra.currentTime %></span></div>\n </li>\n <li>\n Bitrate weighted mean:\n <div><span><%= extra.bitrateWeightedMean %></span></div>\n </li>\n <li>\n Bitrate most used:\n <div><span><%= extra.bitrateMostUsed %></span></div>\n </li>\n <li>\n % Watched:\n <div><span><%= extra.watchedPercentage %></span></div>\n </li>\n <li>\n % Buffering:\n <div><span><%= extra.bufferingPercentage %></span></div>\n </li>\n </ul>\n </div>\n <div class=\"speedtest-summary\">\n <div class=\"speedtest-summary-header\">Your internet quality summary</div>\n <div class=\"speedtest-summary-block\">\n <div class=\"speedtest-summary-subblock\">\n <div class=\"speedtest-summary-subblock-content\">\n <div class=\"speedtest-quality\">\n <div class=\"speedtest-quality-header\">VOD: <%= custom.vodQuality %></div>\n <div class=\"speedtest-quality-content\" data-streaming-type=\"vod\">\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"speedtest-summary-subblock\">\n <div class=\"speedtest-summary-subblock-content\">\n <div class=\"speedtest-quality\">\n <div class=\"speedtest-quality-header\">Live: <%= custom.liveQuality %></div>\n <div class=\"speedtest-quality-content\" data-streaming-type=\"live\">\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"speedtest-footer\">\n <!-- <a class=\"speedtest-footer-about-link\" href=\"\" target=\"_blank\">I am not a nerd, what's this all about?</a>-->\n <button class=\"gplayer-lite-btn speedtest-btn speedtest-footer-refresh\" data-refresh-button type=\"button\">\n <svg width=\"12\" height=\"10\" viewBox=\"0 0 12 10\" fill=\"none\">\n <path\n d=\"M6.03968 0.124998C3.64268 0.124998 1.67268 1.9565 1.48068 4.2915H1.00018C0.925833 4.29146 0.853156 4.31353 0.791378 4.35489C0.729601 4.39625 0.681511 4.45503 0.653218 4.52378C0.624925 4.59253 0.617705 4.66814 0.632476 4.74101C0.647248 4.81387 0.683343 4.88069 0.736177 4.933L1.57618 5.766C1.64641 5.83561 1.74129 5.87467 1.84018 5.87467C1.93906 5.87467 2.03395 5.83561 2.10418 5.766L2.94418 4.933C2.99701 4.88069 3.03311 4.81387 3.04788 4.74101C3.06265 4.66814 3.05543 4.59253 3.02714 4.52378C2.99884 4.45503 2.95075 4.39625 2.88898 4.35489C2.8272 4.31353 2.75452 4.29146 2.68018 4.2915H2.23368C2.42368 2.376 4.05268 0.874998 6.03968 0.874998C6.6948 0.873639 7.33932 1.04039 7.91158 1.35931C8.48384 1.67822 8.9647 2.13863 9.30818 2.6965C9.33331 2.73978 9.36686 2.7776 9.40684 2.80771C9.44682 2.83783 9.49243 2.85963 9.54097 2.87184C9.58951 2.88405 9.64001 2.88643 9.68948 2.87881C9.73895 2.8712 9.7864 2.85377 9.82902 2.82753C9.87165 2.80129 9.90859 2.76679 9.93767 2.72605C9.96675 2.68531 9.98739 2.63916 9.99835 2.59032C10.0093 2.54148 10.0104 2.49095 10.0015 2.44168C9.99264 2.39242 9.974 2.34544 9.94668 2.3035C9.53615 1.63664 8.96146 1.08621 8.27752 0.704805C7.59359 0.323402 6.82277 0.123774 6.03968 0.124998ZM10.4207 4.2335C10.3505 4.16419 10.2558 4.12532 10.1572 4.12532C10.0585 4.12532 9.96386 4.16419 9.89368 4.2335L9.05018 5.0665C8.9972 5.11874 8.96096 5.18557 8.94608 5.25847C8.93119 5.33137 8.93833 5.40705 8.96658 5.47588C8.99483 5.54472 9.04292 5.60359 9.10473 5.64501C9.16654 5.68644 9.23927 5.70853 9.31368 5.7085H9.76318C9.57218 7.6235 7.93768 9.125 5.94118 9.125C5.28399 9.12683 4.63729 8.96035 4.06269 8.64141C3.48808 8.32247 3.00473 7.86169 2.65868 7.303C2.63281 7.26107 2.59893 7.22465 2.55899 7.19582C2.51904 7.16699 2.47381 7.14631 2.42587 7.13495C2.37793 7.1236 2.32823 7.1218 2.27959 7.12966C2.23096 7.13752 2.18435 7.15488 2.14243 7.18075C2.05776 7.233 1.99731 7.31674 1.97438 7.41355C1.95146 7.51037 1.96793 7.61233 2.02018 7.697C2.43345 8.36457 3.01076 8.91521 3.69713 9.29647C4.38349 9.67772 5.15604 9.87689 5.94118 9.875C8.34518 9.875 10.3237 8.045 10.5162 5.7085H11.0002C11.0746 5.70853 11.1473 5.68644 11.2091 5.64501C11.2709 5.60359 11.319 5.54472 11.3473 5.47588C11.3755 5.40705 11.3827 5.33137 11.3678 5.25847C11.3529 5.18557 11.3167 5.11874 11.2637 5.0665L10.4207 4.2335Z\"\n fill=\"white\"/>\n </svg>\n Refresh\n </button>\n </div>\n</div>\n";
|
|
46266
|
+
const pluginHtml$4 = "<% general = metrics.general %>\n<% counters = metrics.counters %>\n<% timers = metrics.chrono %>\n<% extra = metrics.extra %>\n<% custom = metrics.custom %>\n\n<div class=\"stats-box\" id=\"nerd-stats-box\">\n <div class=\"stats-box-top\">\n <a class=\"close-button gplayer-lite-btn\" id=\"nerd-stats-close\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <g clip-path=\"url(#clip0_184_1489)\">\n <path fill-rule=\"evenodd\" clip-rule=\"evenodd\"\n d=\"M7.41376 6.00013L13.7068 -0.292872C14.0978 -0.683872 14.0978 -1.31587 13.7068 -1.70687C13.3158 -2.09787 12.6838 -2.09787 12.2928 -1.70687L5.99976 4.58613L-0.293238 -1.70687C-0.684238 -2.09787 -1.31624 -2.09787 -1.70724 -1.70687C-2.09824 -1.31587 -2.09824 -0.683872 -1.70724 -0.292872L4.58576 6.00013L-1.70724 12.2931C-2.09824 12.6841 -2.09824 13.3161 -1.70724 13.7071C-1.51224 13.9021 -1.25624 14.0001 -1.00024 14.0001C-0.744238 14.0001 -0.488238 13.9021 -0.293238 13.7071L5.99976 7.41413L12.2928 13.7071C12.4878 13.9021 12.7438 14.0001 12.9998 14.0001C13.2558 14.0001 13.5118 13.9021 13.7068 13.7071C14.0978 13.3161 14.0978 12.6841 13.7068 12.2931L7.41376 6.00013Z\"\n fill=\"white\"/>\n </g>\n <defs>\n <clipPath id=\"clip0_184_1489\">\n <rect width=\"12\" height=\"12\" fill=\"white\"/>\n </clipPath>\n </defs>\n </svg>\n </a>\n </div>\n <div class=\"stats-box-main\">\n <ul>\n <li class=\"title\"><span><%= i18n.t('stats.general') %></span></li>\n <li>\n <%= i18n.t('stats.display_resolution') %>\n <div><span><span id=\"nerd-stats-resolution-width\"><%= general.resolution.width %></span>×<span id=\"nerd-stats-resolution-height\"><%= general.resolution.height %></span></span></div>\n </li>\n <li>\n <%= i18n.t('stats.volume') %>\n <div id=\"nerd-stats-volume\"><span><%= general.volume %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.connection_speed') %>\n <div><span id=\"dlText\"><%= custom.connectionSpeed %></span> <%= i18n.t('mbps') %></div>\n </li>\n <li class=\"canvas-wrapper\">\n <canvas id=\"speedTestCanvas\" width=\"190\" height=\"20\"></canvas>\n </li>\n <li>\n <%= i18n.t('stats.ping') %>\n <div><span id=\"pingText\"><%= custom.ping %></span> <%= i18n.t('ms') %></div>\n </li>\n <li>\n <%= i18n.t('stats.jitter') %>\n <div><span id=\"jitterText\"><%= custom.jitter %></span> <%= i18n.t('ms') %></div>\n </li>\n </ul>\n\n <ul>\n <li class=\"title\"><span><%= i18n.t('stats.counters') %></span></li>\n <li>\n <%= i18n.t('stats.plays') %>\n <div><span id=\"nerd-stats-plays\"><%= counters.play %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.pauses') %>\n <div><span id=\"nerd-stats-pauses\"><%= counters.pause %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.errors') %>\n <div><span id=\"nerd-stats-errors\"><%= counters.error %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.bufferings') %>\n <div><span id=\"nerd-stats-bufferings\"><%= counters.buffering %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.decoded_frames') %>\n <div><span id=\"nerd-stats-decoded-frames\"><%= counters.decodedFrames %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.dropped_frames') %>\n <div><span id=\"nerd-stats-dropped-frames\"><%= counters.droppedFrames %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.fps') %>\n <div><span id=\"nerd-stats-fps\"><%= counters.fps %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.bitrate_changes') %>\n <div><span id=\"nerd-stats-bitrate-changes\"><%= counters.changeLevel %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.seeks') %>\n <div><span id=\"nerd-stats-seeks\"><%= counters.seek %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.fullscreen') %>\n <div><span id=\"nerd-stats-fullscreen\"><%= counters.fullscreen %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.dvr_seeks') %>\n <div><span id=\"nerd-stats-dvr-usage\"><%= counters.dvrUsage %></span></div>\n </li>\n </ul>\n\n <ul>\n <li class=\"title\"><span><%= i18n.t('stats.duration') %></span></li>\n <li>\n <%= i18n.t('stats.startup') %>\n <div><span id=\"nerd-stats-startup-time\"><%= timers.startup %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.watching') %>\n <div><span id=\"nerd-stats-watch-time\"><%= timers.watch %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.paused') %>\n <div><span id=\"nerd-stats-pause-time\"><%= timers.pause %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.buffering') %>\n <div><span id=\"nerd-stats-buffering-time\"><%= timers.buffering %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.session') %>\n <div><span id=\"nerd-stats-session-time\"><%= timers.session %></span></div>\n </li>\n </ul>\n\n <ul>\n <li class=\"title\"><span><%= i18n.t('stats.extra') %></span></li>\n <li>\n <%= i18n.t('stats.playback') %>\n <div><span id=\"nerd-stats-playback-name\"><%= extra.playbackName %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.playback_type') %>\n <div><span id=\"nerd-stats-playback-type\"><%= extra.playbackType %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.buffer_size') %>\n <div><span id=\"nerd-stats-buffer-size\"><%= extra.buffersize %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.video_duration') %>\n <div><span id=\"nerd-stats-video-duration\"><%= extra.duration %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.current_time') %>\n <div><span id=\"nerd-stats-current-time\"><%= extra.currentTime %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.bitrate_weighted_mean') %>\n <div><span id=\"nerd-stats-bitrate-weighted-mean\"><%= extra.bitrateWeightedMean %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.bitrate_most_used') %>\n <div><span id=\"nerd-stats-bitrate-most-used\"><%= extra.bitrateMostUsed %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.watched_percentage') %>\n <div><span id=\"nerd-stats-watched-percentage\"><%= extra.watchedPercentage %></span></div>\n </li>\n <li>\n <%= i18n.t('stats.buffering_percentage') %>\n <div><span id=\"nerd-stats-buffering-percentage\"><%= extra.bufferingPercentage %></span></div>\n </li>\n </ul>\n </div>\n <div class=\"speedtest-summary\">\n <div class=\"speedtest-summary-header\"><%= i18n.t('stats.your_internet_quality_summary') %>:</div>\n <div class=\"speedtest-summary-block\">\n <div class=\"speedtest-summary-subblock\">\n <div class=\"speedtest-summary-subblock-content\">\n <div class=\"speedtest-quality\">\n <div class=\"speedtest-quality-header\"><%= i18n.t('vod') %>: \n <span id=\"nerd-stats-quality-vod-text\"><%= custom.vodQuality %></span></div>\n <div class=\"speedtest-quality-content\" data-streaming-type=\"vod\" id=\"nerd-stats-quality-vod\">\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"speedtest-summary-subblock\">\n <div class=\"speedtest-summary-subblock-content\">\n <div class=\"speedtest-quality\">\n <div class=\"speedtest-quality-header\"><%= i18n.t('live') %>: \n <span id=\"nerd-stats-quality-live-text\"><%= custom.liveQuality %></span></div>\n <div class=\"speedtest-quality-content\" data-streaming-type=\"live\" id=\"nerd-stats-quality-live\">\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n <div class=\"speedtest-quality-content-item\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"speedtest-footer\">\n <!-- <a class=\"speedtest-footer-about-link\" href=\"\" target=\"_blank\">I am not a nerd, what's this all about?</a>-->\n <button class=\"gplayer-lite-btn speedtest-btn speedtest-footer-refresh\" type=\"button\" id=\"nerd-stats-refresh\">\n <svg width=\"12\" height=\"10\" viewBox=\"0 0 12 10\" fill=\"none\">\n <path\n d=\"M6.03968 0.124998C3.64268 0.124998 1.67268 1.9565 1.48068 4.2915H1.00018C0.925833 4.29146 0.853156 4.31353 0.791378 4.35489C0.729601 4.39625 0.681511 4.45503 0.653218 4.52378C0.624925 4.59253 0.617705 4.66814 0.632476 4.74101C0.647248 4.81387 0.683343 4.88069 0.736177 4.933L1.57618 5.766C1.64641 5.83561 1.74129 5.87467 1.84018 5.87467C1.93906 5.87467 2.03395 5.83561 2.10418 5.766L2.94418 4.933C2.99701 4.88069 3.03311 4.81387 3.04788 4.74101C3.06265 4.66814 3.05543 4.59253 3.02714 4.52378C2.99884 4.45503 2.95075 4.39625 2.88898 4.35489C2.8272 4.31353 2.75452 4.29146 2.68018 4.2915H2.23368C2.42368 2.376 4.05268 0.874998 6.03968 0.874998C6.6948 0.873639 7.33932 1.04039 7.91158 1.35931C8.48384 1.67822 8.9647 2.13863 9.30818 2.6965C9.33331 2.73978 9.36686 2.7776 9.40684 2.80771C9.44682 2.83783 9.49243 2.85963 9.54097 2.87184C9.58951 2.88405 9.64001 2.88643 9.68948 2.87881C9.73895 2.8712 9.7864 2.85377 9.82902 2.82753C9.87165 2.80129 9.90859 2.76679 9.93767 2.72605C9.96675 2.68531 9.98739 2.63916 9.99835 2.59032C10.0093 2.54148 10.0104 2.49095 10.0015 2.44168C9.99264 2.39242 9.974 2.34544 9.94668 2.3035C9.53615 1.63664 8.96146 1.08621 8.27752 0.704805C7.59359 0.323402 6.82277 0.123774 6.03968 0.124998ZM10.4207 4.2335C10.3505 4.16419 10.2558 4.12532 10.1572 4.12532C10.0585 4.12532 9.96386 4.16419 9.89368 4.2335L9.05018 5.0665C8.9972 5.11874 8.96096 5.18557 8.94608 5.25847C8.93119 5.33137 8.93833 5.40705 8.96658 5.47588C8.99483 5.54472 9.04292 5.60359 9.10473 5.64501C9.16654 5.68644 9.23927 5.70853 9.31368 5.7085H9.76318C9.57218 7.6235 7.93768 9.125 5.94118 9.125C5.28399 9.12683 4.63729 8.96035 4.06269 8.64141C3.48808 8.32247 3.00473 7.86169 2.65868 7.303C2.63281 7.26107 2.59893 7.22465 2.55899 7.19582C2.51904 7.16699 2.47381 7.14631 2.42587 7.13495C2.37793 7.1236 2.32823 7.1218 2.27959 7.12966C2.23096 7.13752 2.18435 7.15488 2.14243 7.18075C2.05776 7.233 1.99731 7.31674 1.97438 7.41355C1.95146 7.51037 1.96793 7.61233 2.02018 7.697C2.43345 8.36457 3.01076 8.91521 3.69713 9.29647C4.38349 9.67772 5.15604 9.87689 5.94118 9.875C8.34518 9.875 10.3237 8.045 10.5162 5.7085H11.0002C11.0746 5.70853 11.1473 5.68644 11.2091 5.64501C11.2709 5.60359 11.319 5.54472 11.3473 5.47588C11.3755 5.40705 11.3827 5.33137 11.3678 5.25847C11.3529 5.18557 11.3167 5.11874 11.2637 5.0665L10.4207 4.2335Z\"\n fill=\"white\"/>\n </svg>\n <%= i18n.t('stats.refresh') %>\n </button>\n </div>\n</div>\n";
|
|
45957
46267
|
|
|
45958
46268
|
const buttonHtml$3 = "<button class='nerd-button gplayer-lite-btn gcore-skin-text-color gear-option' id=\"nerd-stats-button\">\n <span class=\"gear-option_icon\"><%= icon %></span>\n <span class=\"gear-option_label\"><%= i18n.t('statistics') %></span>\n</button>\n";
|
|
45959
46269
|
|
|
@@ -46026,7 +46336,13 @@ const drawSummary = (customMetrics, vodContainer, liveContainer) => {
|
|
|
46026
46336
|
vodContainer.html(vodHtml);
|
|
46027
46337
|
liveContainer.html(liveHtml);
|
|
46028
46338
|
};
|
|
46029
|
-
|
|
46339
|
+
|
|
46340
|
+
const PLAYBACK_NAMES = {
|
|
46341
|
+
dash: 'DASH.js',
|
|
46342
|
+
hls: 'HLS.js',
|
|
46343
|
+
html5_video: 'Native',
|
|
46344
|
+
};
|
|
46345
|
+
const T$f = 'plugins.nerd_stats';
|
|
46030
46346
|
/**
|
|
46031
46347
|
* `PLUGIN` that displays useful network-related statistics.
|
|
46032
46348
|
* @beta
|
|
@@ -46043,15 +46359,15 @@ const T$f = 'plugins.clappr_nerd_stats';
|
|
|
46043
46359
|
* When clicked, it shows an overlay window with the information about the network speed, latency, etc,
|
|
46044
46360
|
* and recommended quality level.
|
|
46045
46361
|
*/
|
|
46046
|
-
class
|
|
46362
|
+
class NerdStats extends UICorePlugin {
|
|
46047
46363
|
container = null;
|
|
46048
|
-
|
|
46364
|
+
speedtestMetrics = {
|
|
46049
46365
|
connectionSpeed: 0,
|
|
46050
46366
|
ping: 0,
|
|
46051
46367
|
jitter: 0,
|
|
46052
46368
|
};
|
|
46053
46369
|
metrics = newMetrics();
|
|
46054
|
-
|
|
46370
|
+
open = false;
|
|
46055
46371
|
shortcut;
|
|
46056
46372
|
iconPosition;
|
|
46057
46373
|
static buttonTemplate = tmpl(buttonHtml$3);
|
|
@@ -46073,7 +46389,6 @@ class ClapprNerdStats extends UICorePlugin {
|
|
|
46073
46389
|
*/
|
|
46074
46390
|
get attributes() {
|
|
46075
46391
|
return {
|
|
46076
|
-
'data-clappr-nerd-stats': '',
|
|
46077
46392
|
class: 'clappr-nerd-stats',
|
|
46078
46393
|
};
|
|
46079
46394
|
}
|
|
@@ -46082,13 +46397,17 @@ class ClapprNerdStats extends UICorePlugin {
|
|
|
46082
46397
|
*/
|
|
46083
46398
|
get events() {
|
|
46084
46399
|
return {
|
|
46085
|
-
|
|
46086
|
-
'click
|
|
46087
|
-
'click
|
|
46400
|
+
click: 'clicked',
|
|
46401
|
+
'click #nerd-stats-close': 'hide',
|
|
46402
|
+
'click #nerd-stats-refresh': 'refreshSpeedTest',
|
|
46088
46403
|
};
|
|
46089
46404
|
}
|
|
46405
|
+
clicked(e) {
|
|
46406
|
+
e.stopPropagation();
|
|
46407
|
+
e.preventDefault();
|
|
46408
|
+
}
|
|
46090
46409
|
get statsBoxElem() {
|
|
46091
|
-
return '
|
|
46410
|
+
return this.$el.find('#nerd-stats-box');
|
|
46092
46411
|
}
|
|
46093
46412
|
get statsBoxWidthThreshold() {
|
|
46094
46413
|
return 720;
|
|
@@ -46107,7 +46426,7 @@ class ClapprNerdStats extends UICorePlugin {
|
|
|
46107
46426
|
];
|
|
46108
46427
|
this.iconPosition =
|
|
46109
46428
|
core.options.clapprNerdStats?.iconPosition ?? 'bottom-right';
|
|
46110
|
-
this.
|
|
46429
|
+
this.speedtestMetrics = {
|
|
46111
46430
|
connectionSpeed: 0,
|
|
46112
46431
|
ping: 0,
|
|
46113
46432
|
jitter: 0,
|
|
@@ -46119,44 +46438,60 @@ class ClapprNerdStats extends UICorePlugin {
|
|
|
46119
46438
|
*/
|
|
46120
46439
|
bindEvents() {
|
|
46121
46440
|
this.listenToOnce(this.core, Events$1.CORE_READY, this.onCoreReady);
|
|
46441
|
+
this.listenTo(this.core, Events$1.CORE_RESIZE, this.onPlayerResize);
|
|
46442
|
+
this.listenTo(this.core, Events$1.CORE_ACTIVE_CONTAINER_CHANGED, this.onActiveContainerChanged);
|
|
46122
46443
|
}
|
|
46123
46444
|
onCoreReady() {
|
|
46124
46445
|
const bottomGear = this.core.getPlugin('bottom_gear');
|
|
46125
46446
|
assert(bottomGear, 'bottom_gear plugin is required');
|
|
46126
|
-
this.listenTo(bottomGear, GearEvents.RENDERED, this.
|
|
46447
|
+
this.listenTo(bottomGear, GearEvents.RENDERED, this.attach);
|
|
46448
|
+
Mousetrap.bind(this.shortcut, this.toggle);
|
|
46449
|
+
this.updateResolution();
|
|
46450
|
+
}
|
|
46451
|
+
onActiveContainerChanged() {
|
|
46127
46452
|
this.container = this.core.activeContainer;
|
|
46128
46453
|
const clapprStats = this.container?.getPlugin('clappr_stats');
|
|
46129
|
-
|
|
46130
|
-
|
|
46131
|
-
|
|
46132
|
-
|
|
46133
|
-
|
|
46134
|
-
|
|
46135
|
-
|
|
46136
|
-
|
|
46137
|
-
|
|
46138
|
-
|
|
46139
|
-
this
|
|
46140
|
-
|
|
46141
|
-
|
|
46142
|
-
|
|
46143
|
-
|
|
46144
|
-
|
|
46145
|
-
|
|
46454
|
+
assert(clapprStats, 'clappr-stats not available. Please, include it as a plugin of your Clappr instance.\n' +
|
|
46455
|
+
'For more info, visit: https://github.com/clappr/clappr-stats.');
|
|
46456
|
+
this.listenTo(clapprStats, ClapprStatsEvents.REPORT, this.updateMetrics);
|
|
46457
|
+
this.listenTo(this.core.activeContainer, Events$1.CONTAINER_VOLUME, () => {
|
|
46458
|
+
this.metrics.general.volume = this.container?.volume ?? 0;
|
|
46459
|
+
this.$el
|
|
46460
|
+
.find('#nerd-stats-volume')
|
|
46461
|
+
.text(Formatter.formatVolume(this.metrics.general.volume));
|
|
46462
|
+
});
|
|
46463
|
+
this.listenTo(this.core.activePlayback, Events$1.PLAYBACK_LOADEDMETADATA, () => {
|
|
46464
|
+
this.$el
|
|
46465
|
+
.find('#nerd-stats-playback-type')
|
|
46466
|
+
.text(this.formatPlaybackName(this.core.activePlayback.getPlaybackType()));
|
|
46467
|
+
});
|
|
46468
|
+
this.updateMetrics(clapprStats.exportMetrics());
|
|
46469
|
+
this.$el
|
|
46470
|
+
.find('#nerd-stats-playback-name')
|
|
46471
|
+
.text(PLAYBACK_NAMES[this.core.activePlayback.name] ?? '-');
|
|
46472
|
+
this.core.activeContainer.$el.append(this.$el);
|
|
46146
46473
|
}
|
|
46147
|
-
|
|
46148
|
-
|
|
46474
|
+
/**
|
|
46475
|
+
* @internal
|
|
46476
|
+
*/
|
|
46477
|
+
destroy() {
|
|
46478
|
+
Mousetrap.unbind(this.shortcut);
|
|
46479
|
+
return super.destroy();
|
|
46480
|
+
}
|
|
46481
|
+
toggle = () => {
|
|
46482
|
+
if (this.open) {
|
|
46149
46483
|
this.hide();
|
|
46150
46484
|
}
|
|
46151
46485
|
else {
|
|
46152
46486
|
this.show();
|
|
46153
46487
|
}
|
|
46154
|
-
}
|
|
46488
|
+
};
|
|
46155
46489
|
show() {
|
|
46156
|
-
this
|
|
46157
|
-
this.
|
|
46490
|
+
this.$el.show();
|
|
46491
|
+
this.statsBoxElem.scrollTop(this.statsBoxElem.scrollTop());
|
|
46492
|
+
this.open = true;
|
|
46158
46493
|
this.refreshSpeedTest();
|
|
46159
|
-
initSpeedTest(this.
|
|
46494
|
+
initSpeedTest(this.speedtestMetrics)
|
|
46160
46495
|
.then(() => {
|
|
46161
46496
|
startSpeedtest();
|
|
46162
46497
|
})
|
|
@@ -46166,21 +46501,28 @@ class ClapprNerdStats extends UICorePlugin {
|
|
|
46166
46501
|
});
|
|
46167
46502
|
}
|
|
46168
46503
|
hide() {
|
|
46169
|
-
this
|
|
46170
|
-
this.
|
|
46504
|
+
this.$el.hide();
|
|
46505
|
+
this.open = false;
|
|
46171
46506
|
stopSpeedtest();
|
|
46172
46507
|
}
|
|
46173
46508
|
onPlayerResize() {
|
|
46174
46509
|
this.setStatsBoxSize();
|
|
46510
|
+
this.updateResolution();
|
|
46175
46511
|
}
|
|
46176
|
-
|
|
46177
|
-
this.metrics.general = {
|
|
46178
|
-
|
|
46179
|
-
|
|
46512
|
+
updateResolution() {
|
|
46513
|
+
this.metrics.general.resolution = {
|
|
46514
|
+
width: this.playerWidth,
|
|
46515
|
+
height: this.playerHeight,
|
|
46180
46516
|
};
|
|
46517
|
+
this.$el
|
|
46518
|
+
.find('#nerd-stats-resolution-width')
|
|
46519
|
+
.text(this.metrics.general.resolution.width);
|
|
46520
|
+
this.$el
|
|
46521
|
+
.find('#nerd-stats-resolution-height')
|
|
46522
|
+
.text(this.metrics.general.resolution.height);
|
|
46181
46523
|
}
|
|
46182
|
-
|
|
46183
|
-
|
|
46524
|
+
estimateQuality() {
|
|
46525
|
+
trace(`${T$f} estimateQuality`);
|
|
46184
46526
|
const videoQualityNames = [
|
|
46185
46527
|
'SD (480p)',
|
|
46186
46528
|
'HD (720p)',
|
|
@@ -46188,9 +46530,9 @@ class ClapprNerdStats extends UICorePlugin {
|
|
|
46188
46530
|
'2K (1440p)',
|
|
46189
46531
|
'4K (2160p)',
|
|
46190
46532
|
];
|
|
46191
|
-
const { connectionSpeed, ping } = this.
|
|
46533
|
+
const { connectionSpeed, ping } = this.speedtestMetrics;
|
|
46192
46534
|
if (!connectionSpeed || !ping) {
|
|
46193
|
-
const calculatingText = '
|
|
46535
|
+
const calculatingText = this.core.i18n.t('stats.calculating');
|
|
46194
46536
|
this.metrics.custom.vodQuality = calculatingText;
|
|
46195
46537
|
this.metrics.custom.liveQuality = calculatingText;
|
|
46196
46538
|
return;
|
|
@@ -46205,48 +46547,113 @@ class ClapprNerdStats extends UICorePlugin {
|
|
|
46205
46547
|
prefix + videoQualityNames[liveQuality - 1];
|
|
46206
46548
|
}
|
|
46207
46549
|
updateMetrics(metrics) {
|
|
46550
|
+
trace(`${T$f} updateMetrics`, { custom: this.speedtestMetrics });
|
|
46208
46551
|
Object.assign(this.metrics, metrics);
|
|
46209
|
-
this.
|
|
46210
|
-
this
|
|
46211
|
-
|
|
46212
|
-
|
|
46213
|
-
|
|
46214
|
-
|
|
46215
|
-
|
|
46552
|
+
this.updateEstimatedQuality();
|
|
46553
|
+
this.$el
|
|
46554
|
+
.find('#nerd-stats-current-time')
|
|
46555
|
+
.text(Formatter.formatTime(this.metrics.extra.currentTime));
|
|
46556
|
+
this.$el
|
|
46557
|
+
.find('#nerd-stats-video-duration')
|
|
46558
|
+
.text(Formatter.formatTime(this.metrics.extra.duration));
|
|
46559
|
+
this.$el
|
|
46560
|
+
.find('#nerd-stats-buffer-size')
|
|
46561
|
+
.text(Formatter.formatTime(this.metrics.extra.buffersize));
|
|
46562
|
+
this.$el
|
|
46563
|
+
.find('#nerd-stats-bitrate-weighted-mean')
|
|
46564
|
+
.text(Formatter.formatBitrate(this.metrics.extra.bitrateWeightedMean));
|
|
46565
|
+
this.$el
|
|
46566
|
+
.find('#nerd-stats-bitrate-most-used')
|
|
46567
|
+
.text(Formatter.formatBitrate(this.metrics.extra.bitrateMostUsed));
|
|
46568
|
+
this.$el
|
|
46569
|
+
.find('#nerd-stats-watched-percentage')
|
|
46570
|
+
.text(Formatter.formatPercentage(this.metrics.extra.watchedPercentage));
|
|
46571
|
+
this.$el
|
|
46572
|
+
.find('#nerd-stats-buffering-percentage')
|
|
46573
|
+
.text(Formatter.formatPercentage(this.metrics.extra.bufferingPercentage));
|
|
46574
|
+
this.$el
|
|
46575
|
+
.find('#nerd-stats-startup-time')
|
|
46576
|
+
.text(Formatter.formatTime(this.metrics.chrono.startup));
|
|
46577
|
+
this.$el
|
|
46578
|
+
.find('#nerd-stats-watch-time')
|
|
46579
|
+
.text(Formatter.formatTime(this.metrics.chrono.watch));
|
|
46580
|
+
this.$el
|
|
46581
|
+
.find('#nerd-stats-pause-time')
|
|
46582
|
+
.text(Formatter.formatTime(this.metrics.chrono.pause));
|
|
46583
|
+
this.$el
|
|
46584
|
+
.find('#nerd-stats-buffering-time')
|
|
46585
|
+
.text(Formatter.formatTime(this.metrics.chrono.buffering));
|
|
46586
|
+
this.$el
|
|
46587
|
+
.find('#nerd-stats-session-time')
|
|
46588
|
+
.text(Formatter.formatTime(this.metrics.chrono.session));
|
|
46589
|
+
this.$el.find('#nerd-stats-plays').text(this.metrics.counters.play);
|
|
46590
|
+
this.$el.find('#nerd-stats-pauses').text(this.metrics.counters.pause);
|
|
46591
|
+
this.$el.find('#nerd-stats-errors').text(this.metrics.counters.error);
|
|
46592
|
+
this.$el
|
|
46593
|
+
.find('#nerd-stats-bufferings')
|
|
46594
|
+
.text(this.metrics.counters.buffering);
|
|
46595
|
+
this.$el
|
|
46596
|
+
.find('#nerd-stats-decoded-frames')
|
|
46597
|
+
.text(this.metrics.counters.decodedFrames);
|
|
46598
|
+
this.$el
|
|
46599
|
+
.find('#nerd-stats-dropped-frames')
|
|
46600
|
+
.text(this.metrics.counters.droppedFrames);
|
|
46601
|
+
this.$el
|
|
46602
|
+
.find('#nerd-stats-bitrate-changes')
|
|
46603
|
+
.text(this.metrics.counters.changeLevel);
|
|
46604
|
+
this.$el.find('#nerd-stats-seeks').text(this.metrics.counters.seek);
|
|
46605
|
+
this.$el
|
|
46606
|
+
.find('#nerd-stats-fullscreen')
|
|
46607
|
+
.text(this.metrics.counters.fullscreen);
|
|
46608
|
+
this.$el.find('#nerd-stats-dvr-usage').text(this.metrics.counters.dvrUsage);
|
|
46609
|
+
this.$el
|
|
46610
|
+
.find('#nerd-stats-fps')
|
|
46611
|
+
.text(Formatter.formatFps(this.metrics.counters.fps));
|
|
46216
46612
|
this.setStatsBoxSize();
|
|
46217
46613
|
drawSpeedTestResults();
|
|
46218
|
-
drawSummary(this.
|
|
46219
|
-
|
|
46220
|
-
if (!this.showing) {
|
|
46614
|
+
drawSummary(this.speedtestMetrics, this.$el.find('#nerd-stats-quality-vod'), this.$el.find('#nerd-stats-quality-live'));
|
|
46615
|
+
if (!this.open) {
|
|
46221
46616
|
this.hide();
|
|
46222
46617
|
}
|
|
46223
46618
|
}
|
|
46619
|
+
updateEstimatedQuality() {
|
|
46620
|
+
this.estimateQuality();
|
|
46621
|
+
this.$el
|
|
46622
|
+
.find('#nerd-stats-quality-vod-text')
|
|
46623
|
+
.html(this.metrics.custom.vodQuality);
|
|
46624
|
+
this.$el
|
|
46625
|
+
.find('#nerd-stats-quality-live-text')
|
|
46626
|
+
.html(this.metrics.custom.liveQuality);
|
|
46627
|
+
}
|
|
46224
46628
|
setStatsBoxSize() {
|
|
46225
46629
|
if (this.playerWidth >= this.statsBoxWidthThreshold) {
|
|
46226
|
-
this
|
|
46227
|
-
this
|
|
46630
|
+
this.statsBoxElem.addClass('wide');
|
|
46631
|
+
this.statsBoxElem.removeClass('narrow');
|
|
46228
46632
|
}
|
|
46229
46633
|
else {
|
|
46230
|
-
this
|
|
46231
|
-
this
|
|
46634
|
+
this.statsBoxElem.removeClass('wide');
|
|
46635
|
+
this.statsBoxElem.addClass('narrow');
|
|
46232
46636
|
}
|
|
46233
46637
|
}
|
|
46234
46638
|
/**
|
|
46235
46639
|
* @internal
|
|
46236
46640
|
*/
|
|
46237
46641
|
render() {
|
|
46238
|
-
|
|
46239
|
-
|
|
46240
|
-
|
|
46241
|
-
|
|
46642
|
+
this.$el
|
|
46643
|
+
.html(NerdStats.template({
|
|
46644
|
+
metrics: Formatter.format(this.metrics ?? newMetrics()),
|
|
46645
|
+
iconPosition: this.iconPosition,
|
|
46646
|
+
i18n: this.core.i18n,
|
|
46647
|
+
}))
|
|
46648
|
+
.hide();
|
|
46242
46649
|
return this;
|
|
46243
46650
|
}
|
|
46244
|
-
|
|
46245
|
-
trace(`${T$f}
|
|
46651
|
+
attach() {
|
|
46652
|
+
trace(`${T$f} attach`);
|
|
46246
46653
|
const gear = this.core.getPlugin('bottom_gear');
|
|
46247
46654
|
gear
|
|
46248
|
-
.addItem('
|
|
46249
|
-
.html(
|
|
46655
|
+
.addItem('nerd_stats')
|
|
46656
|
+
.html(NerdStats.buttonTemplate({
|
|
46250
46657
|
icon: statsIcon,
|
|
46251
46658
|
i18n: this.core.i18n,
|
|
46252
46659
|
}))
|
|
@@ -46255,11 +46662,11 @@ class ClapprNerdStats extends UICorePlugin {
|
|
|
46255
46662
|
this.toggle();
|
|
46256
46663
|
});
|
|
46257
46664
|
}
|
|
46258
|
-
|
|
46665
|
+
clearSpeedtestMetrics() {
|
|
46259
46666
|
const clapprStats = this.container?.getPlugin('clappr_stats');
|
|
46260
|
-
this.
|
|
46261
|
-
this.
|
|
46262
|
-
this.
|
|
46667
|
+
this.speedtestMetrics.connectionSpeed = 0;
|
|
46668
|
+
this.speedtestMetrics.ping = 0;
|
|
46669
|
+
this.speedtestMetrics.jitter = 0;
|
|
46263
46670
|
if (clapprStats) {
|
|
46264
46671
|
this.updateMetrics(clapprStats.exportMetrics());
|
|
46265
46672
|
}
|
|
@@ -46267,7 +46674,7 @@ class ClapprNerdStats extends UICorePlugin {
|
|
|
46267
46674
|
refreshSpeedTest() {
|
|
46268
46675
|
stopSpeedtest();
|
|
46269
46676
|
setTimeout(() => {
|
|
46270
|
-
this.
|
|
46677
|
+
this.clearSpeedtestMetrics();
|
|
46271
46678
|
clearSpeedTestResults();
|
|
46272
46679
|
drawSpeedTestResults();
|
|
46273
46680
|
}, 200);
|
|
@@ -46275,11 +46682,28 @@ class ClapprNerdStats extends UICorePlugin {
|
|
|
46275
46682
|
startSpeedtest();
|
|
46276
46683
|
}, 800);
|
|
46277
46684
|
}
|
|
46685
|
+
formatPlaybackName(playbackType) {
|
|
46686
|
+
switch (playbackType) {
|
|
46687
|
+
case Playback.VOD:
|
|
46688
|
+
return this.core.i18n.t('vod');
|
|
46689
|
+
case Playback.LIVE:
|
|
46690
|
+
return this.core.i18n.t('live');
|
|
46691
|
+
default:
|
|
46692
|
+
return '-';
|
|
46693
|
+
}
|
|
46694
|
+
}
|
|
46278
46695
|
}
|
|
46279
46696
|
function newMetrics() {
|
|
46280
46697
|
return {
|
|
46281
46698
|
...newMetrics$1(),
|
|
46282
|
-
general: {
|
|
46699
|
+
general: {
|
|
46700
|
+
displayResolution: '',
|
|
46701
|
+
resolution: {
|
|
46702
|
+
width: 0,
|
|
46703
|
+
height: 0,
|
|
46704
|
+
},
|
|
46705
|
+
volume: 0,
|
|
46706
|
+
},
|
|
46283
46707
|
custom: {
|
|
46284
46708
|
connectionSpeed: 0,
|
|
46285
46709
|
ping: 0,
|
|
@@ -46288,362 +46712,6 @@ function newMetrics() {
|
|
|
46288
46712
|
};
|
|
46289
46713
|
}
|
|
46290
46714
|
|
|
46291
|
-
// TODO: fix
|
|
46292
|
-
const updateMetrics = () => { };
|
|
46293
|
-
/**
|
|
46294
|
-
* `PLUGIN` that collects useful statistics about playback performance.
|
|
46295
|
-
* @beta
|
|
46296
|
-
* @remarks
|
|
46297
|
-
* This plugin does not render anything and is supposed to be extended or used together with other plugins that actually render something.
|
|
46298
|
-
*/
|
|
46299
|
-
class ClapprStats extends ContainerPlugin {
|
|
46300
|
-
bwMeasureCount = 0;
|
|
46301
|
-
intervalId = null;
|
|
46302
|
-
lastDecodedFramesCount = 0;
|
|
46303
|
-
metrics = newMetrics$1();
|
|
46304
|
-
completion;
|
|
46305
|
-
_onReport;
|
|
46306
|
-
runBandwidthTestEvery;
|
|
46307
|
-
runEach;
|
|
46308
|
-
timers = {
|
|
46309
|
-
startup: 0,
|
|
46310
|
-
watch: 0,
|
|
46311
|
-
pause: 0,
|
|
46312
|
-
buffering: 0,
|
|
46313
|
-
session: 0,
|
|
46314
|
-
latency: 0,
|
|
46315
|
-
};
|
|
46316
|
-
updateFn = updateMetrics;
|
|
46317
|
-
urisToMeasureBandwidth;
|
|
46318
|
-
uriToMeasureLatency;
|
|
46319
|
-
/**
|
|
46320
|
-
* @internal
|
|
46321
|
-
*/
|
|
46322
|
-
get name() {
|
|
46323
|
-
return 'clappr_stats';
|
|
46324
|
-
}
|
|
46325
|
-
/**
|
|
46326
|
-
* @internal
|
|
46327
|
-
*/
|
|
46328
|
-
get supportedVersion() {
|
|
46329
|
-
return { min: CLAPPR_VERSION$1 };
|
|
46330
|
-
}
|
|
46331
|
-
get _playbackName() {
|
|
46332
|
-
return String(this.container.playback.name || '');
|
|
46333
|
-
}
|
|
46334
|
-
get _playbackType() {
|
|
46335
|
-
return this.container.getPlaybackType();
|
|
46336
|
-
}
|
|
46337
|
-
_now() {
|
|
46338
|
-
const hasPerformanceSupport = window.performance && typeof (window.performance.now) === 'function';
|
|
46339
|
-
return (hasPerformanceSupport) ? window.performance.now() : new Date().getTime();
|
|
46340
|
-
}
|
|
46341
|
-
_inc(counter) {
|
|
46342
|
-
this.metrics.counters[counter] += 1;
|
|
46343
|
-
}
|
|
46344
|
-
// _timerHasStarted(timer) {
|
|
46345
|
-
// return this[`_start${timer}`] !== undefined;
|
|
46346
|
-
// }
|
|
46347
|
-
start(timer) {
|
|
46348
|
-
// this[`_start${timer}`] = this._now();
|
|
46349
|
-
this.timers[timer] = this._now();
|
|
46350
|
-
}
|
|
46351
|
-
_stop(timer) {
|
|
46352
|
-
// this._metrics.timers[timer] += this._now() - this[`_start${timer}`];
|
|
46353
|
-
this.metrics.timers[timer] += this._now() - this.timers[timer];
|
|
46354
|
-
}
|
|
46355
|
-
/**
|
|
46356
|
-
* Registers a callback to receive the metrics.
|
|
46357
|
-
* @param updateMetricsFn - The callback to receive the metrics
|
|
46358
|
-
*/
|
|
46359
|
-
setUpdateMetrics(updateMetricsFn) {
|
|
46360
|
-
// TODO use events instead
|
|
46361
|
-
this.updateFn = updateMetricsFn;
|
|
46362
|
-
}
|
|
46363
|
-
_defaultReport(metrics) {
|
|
46364
|
-
this.updateFn(metrics);
|
|
46365
|
-
}
|
|
46366
|
-
constructor(container) {
|
|
46367
|
-
super(container);
|
|
46368
|
-
this.runEach = container.options.clapprStats?.runEach ?? 5000;
|
|
46369
|
-
this._onReport = container.options.clapprStats?.onReport ?? this._defaultReport;
|
|
46370
|
-
this.uriToMeasureLatency = container.options.clapprStats?.uriToMeasureLatency;
|
|
46371
|
-
this.urisToMeasureBandwidth = container.options.clapprStats?.urisToMeasureBandwidth;
|
|
46372
|
-
this.runBandwidthTestEvery = container.options.clapprStats?.runBandwidthTestEvery ?? 10;
|
|
46373
|
-
this.completion = {
|
|
46374
|
-
watch: container.options.clapprStats?.onCompletion ?? [],
|
|
46375
|
-
calls: []
|
|
46376
|
-
};
|
|
46377
|
-
}
|
|
46378
|
-
/**
|
|
46379
|
-
* @internal
|
|
46380
|
-
*/
|
|
46381
|
-
bindEvents() {
|
|
46382
|
-
this.listenTo(this.container, Events$1.CONTAINER_BITRATE, this.onBitrate);
|
|
46383
|
-
this.listenTo(this.container, Events$1.CONTAINER_STOP, this.stopReporting);
|
|
46384
|
-
this.listenTo(this.container, Events$1.CONTAINER_ENDED, this.stopReporting);
|
|
46385
|
-
this.listenToOnce(this.container.playback, Events$1.PLAYBACK_PLAY_INTENT, this.startTimers);
|
|
46386
|
-
this.listenToOnce(this.container, Events$1.CONTAINER_PLAY, this.onFirstPlaying);
|
|
46387
|
-
this.listenTo(this.container, Events$1.CONTAINER_PLAY, this.onPlay);
|
|
46388
|
-
this.listenTo(this.container, Events$1.CONTAINER_PAUSE, this.onPause);
|
|
46389
|
-
this.listenToOnce(this.container, Events$1.CONTAINER_STATE_BUFFERING, this.onBuffering);
|
|
46390
|
-
this.listenTo(this.container, Events$1.CONTAINER_SEEK, this.onSeek);
|
|
46391
|
-
this.listenTo(this.container, Events$1.CONTAINER_ERROR, () => this._inc('error'));
|
|
46392
|
-
this.listenTo(this.container, Events$1.CONTAINER_FULLSCREEN, () => this._inc('fullscreen'));
|
|
46393
|
-
this.listenTo(this.container, Events$1.CONTAINER_PLAYBACKDVRSTATECHANGED, (dvrInUse) => {
|
|
46394
|
-
dvrInUse && this._inc('dvrUsage');
|
|
46395
|
-
});
|
|
46396
|
-
this.listenTo(this.container.playback, Events$1.PLAYBACK_PROGRESS, this.onProgress);
|
|
46397
|
-
this.listenTo(this.container.playback, Events$1.PLAYBACK_TIMEUPDATE, this.onTimeUpdate);
|
|
46398
|
-
}
|
|
46399
|
-
/**
|
|
46400
|
-
* @internal
|
|
46401
|
-
*/
|
|
46402
|
-
destroy() {
|
|
46403
|
-
this.stopReporting();
|
|
46404
|
-
super.destroy();
|
|
46405
|
-
}
|
|
46406
|
-
/**
|
|
46407
|
-
* Returns the collected metrics.
|
|
46408
|
-
* @returns Currently collected metrics
|
|
46409
|
-
*/
|
|
46410
|
-
exportMetrics() {
|
|
46411
|
-
return structuredClone(this.metrics);
|
|
46412
|
-
}
|
|
46413
|
-
onBitrate(newBitrate) {
|
|
46414
|
-
const bitrate = newBitrate.bitrate;
|
|
46415
|
-
const now = this._now();
|
|
46416
|
-
if (this.metrics.extra.bitratesHistory.length > 0) {
|
|
46417
|
-
const beforeLast = this.metrics.extra.bitratesHistory[this.metrics.extra.bitratesHistory.length - 1];
|
|
46418
|
-
beforeLast.end = now;
|
|
46419
|
-
beforeLast.time = now - beforeLast.start;
|
|
46420
|
-
}
|
|
46421
|
-
this.metrics.extra.bitratesHistory.push({ start: this._now(), bitrate: bitrate });
|
|
46422
|
-
this._inc('changeLevel');
|
|
46423
|
-
}
|
|
46424
|
-
stopReporting() {
|
|
46425
|
-
this._buildReport();
|
|
46426
|
-
if (this.intervalId !== null) {
|
|
46427
|
-
clearInterval(this.intervalId);
|
|
46428
|
-
this.intervalId = null;
|
|
46429
|
-
}
|
|
46430
|
-
this._newMetrics();
|
|
46431
|
-
// TODO
|
|
46432
|
-
// @ts-ignore
|
|
46433
|
-
this.stopListening();
|
|
46434
|
-
this.bindEvents();
|
|
46435
|
-
}
|
|
46436
|
-
startTimers() {
|
|
46437
|
-
this.intervalId = setInterval(this._buildReport.bind(this), this.runEach);
|
|
46438
|
-
this.start('session');
|
|
46439
|
-
this.start('startup');
|
|
46440
|
-
}
|
|
46441
|
-
onFirstPlaying() {
|
|
46442
|
-
this.listenTo(this.container, Events$1.CONTAINER_TIMEUPDATE, this.onContainerUpdateWhilePlaying);
|
|
46443
|
-
this.start('watch');
|
|
46444
|
-
this._stop('startup');
|
|
46445
|
-
}
|
|
46446
|
-
playAfterPause() {
|
|
46447
|
-
this.listenTo(this.container, Events$1.CONTAINER_TIMEUPDATE, this.onContainerUpdateWhilePlaying);
|
|
46448
|
-
this._stop('pause');
|
|
46449
|
-
this.start('watch');
|
|
46450
|
-
}
|
|
46451
|
-
onPlay() {
|
|
46452
|
-
this._inc('play');
|
|
46453
|
-
}
|
|
46454
|
-
onPause() {
|
|
46455
|
-
this._stop('watch');
|
|
46456
|
-
this.start('pause');
|
|
46457
|
-
this._inc('pause');
|
|
46458
|
-
this.listenToOnce(this.container, Events$1.CONTAINER_PLAY, this.playAfterPause);
|
|
46459
|
-
this.stopListening(this.container, Events$1.CONTAINER_TIMEUPDATE, this.onContainerUpdateWhilePlaying);
|
|
46460
|
-
}
|
|
46461
|
-
onSeek(e) {
|
|
46462
|
-
this._inc('seek');
|
|
46463
|
-
this.metrics.extra.watchHistory.push([e * 1000, e * 1000]);
|
|
46464
|
-
}
|
|
46465
|
-
onTimeUpdate(e) {
|
|
46466
|
-
const current = e.current * 1000, total = e.total * 1000, l = this.metrics.extra.watchHistory.length;
|
|
46467
|
-
this.metrics.extra.duration = total;
|
|
46468
|
-
this.metrics.extra.currentTime = current;
|
|
46469
|
-
this.metrics.extra.watchedPercentage = (current / total) * 100;
|
|
46470
|
-
if (l === 0) {
|
|
46471
|
-
this.metrics.extra.watchHistory.push([current, current]);
|
|
46472
|
-
}
|
|
46473
|
-
else {
|
|
46474
|
-
this.metrics.extra.watchHistory[l - 1][1] = current;
|
|
46475
|
-
}
|
|
46476
|
-
if (this.metrics.extra.bitratesHistory.length > 0) {
|
|
46477
|
-
const lastBitrate = this.metrics.extra.bitratesHistory[this.metrics.extra.bitratesHistory.length - 1];
|
|
46478
|
-
if (!lastBitrate.end) {
|
|
46479
|
-
lastBitrate.time = this._now() - lastBitrate.start;
|
|
46480
|
-
}
|
|
46481
|
-
}
|
|
46482
|
-
this._onCompletion();
|
|
46483
|
-
}
|
|
46484
|
-
onContainerUpdateWhilePlaying() {
|
|
46485
|
-
if (this.container.playback.isPlaying()) {
|
|
46486
|
-
this._stop('watch');
|
|
46487
|
-
this.start('watch');
|
|
46488
|
-
}
|
|
46489
|
-
}
|
|
46490
|
-
onBuffering() {
|
|
46491
|
-
this._inc('buffering');
|
|
46492
|
-
this.start('buffering');
|
|
46493
|
-
this.listenToOnce(this.container, Events$1.CONTAINER_STATE_BUFFERFULL, this.onBufferfull);
|
|
46494
|
-
}
|
|
46495
|
-
onBufferfull() {
|
|
46496
|
-
this._stop('buffering');
|
|
46497
|
-
this.listenToOnce(this.container, Events$1.CONTAINER_STATE_BUFFERING, this.onBuffering);
|
|
46498
|
-
}
|
|
46499
|
-
onProgress(progress) {
|
|
46500
|
-
this.metrics.extra.buffersize = progress.current * 1000;
|
|
46501
|
-
}
|
|
46502
|
-
_newMetrics() {
|
|
46503
|
-
this.metrics = newMetrics$1();
|
|
46504
|
-
}
|
|
46505
|
-
_onCompletion() {
|
|
46506
|
-
const currentPercentage = this.metrics.extra.watchedPercentage;
|
|
46507
|
-
const allPercentages = this.completion.watch;
|
|
46508
|
-
const isCalled = this.completion.calls.indexOf(currentPercentage) !== -1;
|
|
46509
|
-
if (allPercentages.indexOf(currentPercentage) !== -1 && !isCalled) {
|
|
46510
|
-
Log.info(this.name + ' PERCENTAGE_EVENT: ' + currentPercentage);
|
|
46511
|
-
this.completion.calls.push(currentPercentage);
|
|
46512
|
-
this.trigger(ClapprStatsEvents.PERCENTAGE_EVENT, currentPercentage);
|
|
46513
|
-
}
|
|
46514
|
-
}
|
|
46515
|
-
_buildReport() {
|
|
46516
|
-
this._stop('session');
|
|
46517
|
-
this.start('session');
|
|
46518
|
-
this.metrics.extra.playbackName = this._playbackName;
|
|
46519
|
-
this.metrics.extra.playbackType = this._playbackType;
|
|
46520
|
-
this._calculateBitrates();
|
|
46521
|
-
this._calculatePercentages();
|
|
46522
|
-
this._fetchFPS();
|
|
46523
|
-
this._measureLatency();
|
|
46524
|
-
this._measureBandwidth();
|
|
46525
|
-
this._onReport(this.metrics);
|
|
46526
|
-
this.trigger(ClapprStatsEvents.REPORT_EVENT, structuredClone(this.metrics));
|
|
46527
|
-
}
|
|
46528
|
-
_fetchFPS() {
|
|
46529
|
-
// flashls ??? - hls.droppedFramesl hls.stream.bufferLength (seconds)
|
|
46530
|
-
// hls ??? (use the same?)
|
|
46531
|
-
const fetchFPS = {
|
|
46532
|
-
'html5_video': this._html5FetchFPS,
|
|
46533
|
-
'hls': this._html5FetchFPS,
|
|
46534
|
-
'dash_shaka_playback': this._html5FetchFPS
|
|
46535
|
-
};
|
|
46536
|
-
if (this._playbackName in fetchFPS) {
|
|
46537
|
-
fetchFPS[this._playbackName].call(this);
|
|
46538
|
-
}
|
|
46539
|
-
}
|
|
46540
|
-
_calculateBitrates() {
|
|
46541
|
-
const { bitratesHistory } = this.metrics.extra;
|
|
46542
|
-
if (bitratesHistory.length === 0) {
|
|
46543
|
-
return;
|
|
46544
|
-
}
|
|
46545
|
-
let totalTime = 0;
|
|
46546
|
-
let weightedTotal = 0;
|
|
46547
|
-
for (const { bitrate, time = 0 } of bitratesHistory) {
|
|
46548
|
-
totalTime += time;
|
|
46549
|
-
weightedTotal += bitrate * time;
|
|
46550
|
-
}
|
|
46551
|
-
this.metrics.extra.bitrateWeightedMean = weightedTotal / totalTime;
|
|
46552
|
-
this.metrics.extra.bitrateMostUsed = bitratesHistory.reduce((mostUsed, current) => (current.time || 0) > (mostUsed.time || 0) ? current : mostUsed, { time: 0, bitrate: 0, start: 0, end: 0 }).bitrate;
|
|
46553
|
-
}
|
|
46554
|
-
_calculatePercentages() {
|
|
46555
|
-
if (this.metrics.extra.duration > 0) {
|
|
46556
|
-
this.metrics.extra.bufferingPercentage = (this.metrics.timers.buffering / this.metrics.extra.duration) * 100;
|
|
46557
|
-
}
|
|
46558
|
-
}
|
|
46559
|
-
_html5FetchFPS() {
|
|
46560
|
-
const videoTag = this.container.playback.el;
|
|
46561
|
-
const getFirstValidValue = (...args) => args.find(val => val !== undefined);
|
|
46562
|
-
const decodedFrames = getFirstValidValue(videoTag.webkitDecodedFrameCount, videoTag.mozDecodedFrames, 0);
|
|
46563
|
-
const droppedFrames = getFirstValidValue(videoTag.webkitDroppedFrameCount, videoTag.mozParsedFrames && videoTag.mozDecodedFrames ? videoTag.mozParsedFrames - videoTag.mozDecodedFrames : 0, 0);
|
|
46564
|
-
const decodedFramesLastTime = decodedFrames - (this.lastDecodedFramesCount || 0);
|
|
46565
|
-
this.metrics.counters.decodedFrames = decodedFrames;
|
|
46566
|
-
this.metrics.counters.droppedFrames = droppedFrames;
|
|
46567
|
-
this.metrics.counters.fps = decodedFramesLastTime / (this.runEach / 1000);
|
|
46568
|
-
this.lastDecodedFramesCount = decodedFrames;
|
|
46569
|
-
}
|
|
46570
|
-
// originally from https://www.smashingmagazine.com/2011/11/analyzing-network-characteristics-using-javascript-and-the-dom-part-1/
|
|
46571
|
-
_measureLatency() {
|
|
46572
|
-
if (this.uriToMeasureLatency) {
|
|
46573
|
-
const t = [];
|
|
46574
|
-
const n = 2;
|
|
46575
|
-
let rtt;
|
|
46576
|
-
const ld = () => {
|
|
46577
|
-
t.push(this._now());
|
|
46578
|
-
if (t.length > n) {
|
|
46579
|
-
done();
|
|
46580
|
-
}
|
|
46581
|
-
else {
|
|
46582
|
-
const img = new Image;
|
|
46583
|
-
img.onload = ld;
|
|
46584
|
-
img.src = this.uriToMeasureLatency + '?' + Math.random()
|
|
46585
|
-
+ '=' + this._now();
|
|
46586
|
-
}
|
|
46587
|
-
};
|
|
46588
|
-
const done = () => {
|
|
46589
|
-
rtt = t[2] - t[1];
|
|
46590
|
-
this.metrics.timers.latency = rtt;
|
|
46591
|
-
};
|
|
46592
|
-
ld();
|
|
46593
|
-
}
|
|
46594
|
-
}
|
|
46595
|
-
// originally from https://www.smashingmagazine.com/2011/11/analyzing-network-characteristics-using-javascript-and-the-dom-part-1/
|
|
46596
|
-
_measureBandwidth() {
|
|
46597
|
-
if (this.urisToMeasureBandwidth && (this.bwMeasureCount % this.runBandwidthTestEvery === 0)) {
|
|
46598
|
-
let i = 0;
|
|
46599
|
-
const ld = (e) => {
|
|
46600
|
-
if (i > 0) {
|
|
46601
|
-
const prev = this.urisToMeasureBandwidth[i - 1];
|
|
46602
|
-
prev.end = this._now();
|
|
46603
|
-
if (prev.timer !== null) {
|
|
46604
|
-
clearTimeout(prev.timer);
|
|
46605
|
-
}
|
|
46606
|
-
}
|
|
46607
|
-
if (i >= this.urisToMeasureBandwidth.length || (i > 0 && this.urisToMeasureBandwidth[i - 1].expired)) {
|
|
46608
|
-
assert(e, 'incorrect invocation in _measureBandwidth');
|
|
46609
|
-
done(e);
|
|
46610
|
-
}
|
|
46611
|
-
else {
|
|
46612
|
-
const xhr = new XMLHttpRequest();
|
|
46613
|
-
xhr.open('GET', this.urisToMeasureBandwidth[i].url, true);
|
|
46614
|
-
xhr.responseType = 'arraybuffer';
|
|
46615
|
-
xhr.onload = xhr.onabort = ld;
|
|
46616
|
-
this.urisToMeasureBandwidth[i].start = this._now();
|
|
46617
|
-
this.urisToMeasureBandwidth[i].timer = setTimeout((j) => {
|
|
46618
|
-
this.urisToMeasureBandwidth[j].expired = true;
|
|
46619
|
-
xhr.abort();
|
|
46620
|
-
}, this.urisToMeasureBandwidth[i].timeout, i);
|
|
46621
|
-
xhr.send();
|
|
46622
|
-
}
|
|
46623
|
-
i++;
|
|
46624
|
-
};
|
|
46625
|
-
const done = (e) => {
|
|
46626
|
-
const timeSpent = (this.urisToMeasureBandwidth[i - 1].end - this.urisToMeasureBandwidth[i - 1].start) / 1000;
|
|
46627
|
-
const bandwidthBps = (e.loaded * 8) / timeSpent;
|
|
46628
|
-
this.metrics.extra.bandwidth = bandwidthBps;
|
|
46629
|
-
this.urisToMeasureBandwidth.forEach((x) => {
|
|
46630
|
-
x.start = 0;
|
|
46631
|
-
x.end = 0;
|
|
46632
|
-
x.expired = false;
|
|
46633
|
-
if (x.timer !== null) {
|
|
46634
|
-
clearTimeout(x.timer);
|
|
46635
|
-
x.timer = null;
|
|
46636
|
-
}
|
|
46637
|
-
});
|
|
46638
|
-
};
|
|
46639
|
-
ld();
|
|
46640
|
-
}
|
|
46641
|
-
this.bwMeasureCount++;
|
|
46642
|
-
}
|
|
46643
|
-
}
|
|
46644
|
-
// ClapprStats.REPORT_EVENT = 'clappr:stats:report';
|
|
46645
|
-
// ClapprStats.PERCENTAGE_EVENT = 'clappr:stats:percentage';
|
|
46646
|
-
|
|
46647
46715
|
// This work is based on the original work of the following authors:
|
|
46648
46716
|
// Copyright 2014 Globo.com Player authors. All rights reserved.
|
|
46649
46717
|
// Use of this source code is governed by a BSD-style
|
|
@@ -50494,7 +50562,7 @@ class SeekTime extends UICorePlugin {
|
|
|
50494
50562
|
this.listenTo(this.mediaControl, Events$1.MEDIACONTROL_CONTAINERCHANGED, this.onContainerChanged);
|
|
50495
50563
|
if (this.mediaControlContainer) {
|
|
50496
50564
|
this.listenTo(this.mediaControlContainer, Events$1.CONTAINER_PLAYBACKDVRSTATECHANGED, this.update);
|
|
50497
|
-
this.listenTo(this.mediaControlContainer, Events$1.CONTAINER_TIMEUPDATE, this.
|
|
50565
|
+
this.listenTo(this.mediaControlContainer, Events$1.CONTAINER_TIMEUPDATE, this.onTimeUpdate);
|
|
50498
50566
|
}
|
|
50499
50567
|
}
|
|
50500
50568
|
onContainerChanged() {
|
|
@@ -50502,9 +50570,8 @@ class SeekTime extends UICorePlugin {
|
|
|
50502
50570
|
this.stopListening();
|
|
50503
50571
|
this.bindEvents();
|
|
50504
50572
|
}
|
|
50505
|
-
|
|
50506
|
-
this.duration =
|
|
50507
|
-
// this.firstFragDateTime = timeProgress.firstFragDateTime;
|
|
50573
|
+
onTimeUpdate({ total }) {
|
|
50574
|
+
this.duration = total;
|
|
50508
50575
|
this.update();
|
|
50509
50576
|
}
|
|
50510
50577
|
showTime(event) {
|
|
@@ -52404,4 +52471,4 @@ class VolumeFade extends UICorePlugin {
|
|
|
52404
52471
|
}
|
|
52405
52472
|
}
|
|
52406
52473
|
|
|
52407
|
-
export { AudioTracks as AudioSelector, AudioTracks, BigMuteButton, BottomGear, ClapprNerdStats, ClapprStats, ClickToPause, Clips, ClosedCaptions, ContextMenu, DvrControls, ErrorScreen, Favicon, GearEvents, GoogleAnalytics, QualityLevels as LevelSelector, LogTracer, Logger, Logo, MediaControl, MultiCamera, PictureInPicture, PlaybackErrorCode, PlaybackRate, Player, PlayerEvent, Poster, QualityLevels, SeekTime, SentryTracer, Share, SkipTime, SourceController, SpinnerThreeBounce as Spinner, SpinnerEvents, SpinnerThreeBounce, ClosedCaptions as Subtitles, Telemetry, TelemetryEvent, Thumbnails, VolumeFade, VolumeFadeEvents, reportError, setTracer, trace, version };
|
|
52474
|
+
export { AudioTracks as AudioSelector, AudioTracks, BigMuteButton, BottomGear, NerdStats as ClapprNerdStats, ClapprStats, ClickToPause, Clips, ClosedCaptions, ContextMenu, DvrControls, ErrorScreen, Favicon, GearEvents, GoogleAnalytics, QualityLevels as LevelSelector, LogTracer, Logger, Logo, MediaControl, MultiCamera, NerdStats, PictureInPicture, PlaybackErrorCode, PlaybackRate, Player, PlayerEvent, Poster, QualityLevels, SeekTime, SentryTracer, Share, SkipTime, SourceController, SpinnerThreeBounce as Spinner, SpinnerEvents, SpinnerThreeBounce, ClosedCaptions as Subtitles, Telemetry, TelemetryEvent, Thumbnails, VolumeFade, VolumeFadeEvents, reportError, setTracer, trace, version };
|