@gcorevideo/player 2.19.14 → 2.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/assets/level-selector/list.ejs +2 -2
  2. package/dist/core.js +2 -2
  3. package/dist/index.css +780 -780
  4. package/dist/index.js +1720 -1473
  5. package/dist/player.d.ts +425 -94
  6. package/dist/plugins/index.css +1466 -1466
  7. package/dist/plugins/index.js +1501 -1252
  8. package/docs/api/player.bottomgear.getelement.md +56 -0
  9. package/docs/api/player.bottomgear.md +51 -0
  10. package/docs/api/player.bottomgear.setcontent.md +56 -0
  11. package/docs/api/player.clapprnerdstats.md +12 -259
  12. package/docs/api/player.dvrcontrols.md +5 -1
  13. package/docs/api/player.errorscreen.attributes.md +3 -0
  14. package/docs/api/player.errorscreen.bindevents.md +3 -0
  15. package/docs/api/player.errorscreen.container.md +3 -0
  16. package/docs/api/player.errorscreen.hide.md +3 -0
  17. package/docs/api/player.errorscreen.md +27 -0
  18. package/docs/api/player.errorscreen.name.md +3 -0
  19. package/docs/api/player.errorscreen.render.md +3 -0
  20. package/docs/api/player.errorscreen.show.md +3 -0
  21. package/docs/api/player.errorscreen.supportedversion.md +3 -0
  22. package/docs/api/player.errorscreen.template.md +3 -0
  23. package/docs/api/player.errorscreen.unbindevents.md +3 -0
  24. package/docs/api/player.gearevents.md +49 -0
  25. package/docs/api/{player.sourcecontroller.name.md → player.gearitemelement.md} +5 -3
  26. package/docs/api/{player.playbackrate.template.md → player.initeventdata.event.md} +3 -3
  27. package/docs/api/{player.playbackrate.updateplaybackrate.md → player.initeventdata.md} +15 -11
  28. package/docs/api/player.levelselector.md +9 -1
  29. package/docs/api/player.md +124 -4
  30. package/docs/api/{player.clapprnerdstats.render.md → player.mediacontrol.getrightpanel.md} +8 -4
  31. package/docs/api/player.mediacontrol.md +14 -0
  32. package/docs/api/player.mediacontrolelement.md +1 -1
  33. package/docs/api/player.pictureinpicture.md +9 -197
  34. package/docs/api/player.playbackrate.md +10 -314
  35. package/docs/api/player.sourcecontroller.md +0 -90
  36. package/docs/api/player.spinnerevents.md +49 -0
  37. package/docs/api/player.spinnerthreebounce._constructor_.md +3 -0
  38. package/docs/api/player.spinnerthreebounce.hide.md +5 -0
  39. package/docs/api/player.spinnerthreebounce.md +14 -95
  40. package/docs/api/player.spinnerthreebounce.show.md +6 -37
  41. package/docs/api/{player.playbackrate.onplay.md → player.stalleventdata.count.md} +5 -7
  42. package/docs/api/{player.playbackrate.name.md → player.stalleventdata.event.md} +3 -3
  43. package/docs/api/player.stalleventdata.md +112 -0
  44. package/docs/api/player.stalleventdata.time.md +13 -0
  45. package/docs/api/player.stalleventdata.total_ms.md +13 -0
  46. package/docs/api/{player.pluginsettings.md → player.starteventdata.event.md} +3 -5
  47. package/docs/api/{player.playbackrate.onrateselect.md → player.starteventdata.md} +15 -11
  48. package/docs/api/player.subtitles.hide.md +5 -0
  49. package/docs/api/player.subtitles.md +23 -275
  50. package/docs/api/player.subtitles.show.md +5 -0
  51. package/docs/api/{player.statistics._constructor_.md → player.telemetry._constructor_.md} +6 -3
  52. package/docs/api/player.telemetry.md +146 -0
  53. package/docs/api/{player.volumefade.name.md → player.telemetry.name.md} +4 -2
  54. package/docs/api/{player.sourcecontroller.supportedversion.md → player.telemetry.supportedversion.md} +4 -2
  55. package/docs/api/player.telemetryevent.md +89 -0
  56. package/docs/api/player.telemetryeventdata.md +15 -0
  57. package/docs/api/player.telemetrypluginsettings.md +57 -0
  58. package/docs/api/player.telemetrypluginsettings.send.md +13 -0
  59. package/docs/api/player.telemetryrecord.md +17 -0
  60. package/docs/api/player.volumefade.md +0 -93
  61. package/docs/api/{player.pictureinpicture.name.md → player.watcheventdata.event.md} +3 -3
  62. package/docs/api/{player.playbackrate.setselectedrate.md → player.watcheventdata.md} +15 -11
  63. package/lib/index.plugins.d.ts +2 -2
  64. package/lib/index.plugins.d.ts.map +1 -1
  65. package/lib/index.plugins.js +2 -2
  66. package/lib/playback/hls-playback/HlsPlayback.js +1 -1
  67. package/lib/plugins/bottom-gear/BottomGear.d.ts +20 -1
  68. package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
  69. package/lib/plugins/bottom-gear/BottomGear.js +28 -7
  70. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.d.ts +38 -5
  71. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.d.ts.map +1 -1
  72. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.js +67 -21
  73. package/lib/plugins/dvr-controls/DvrControls.d.ts +5 -2
  74. package/lib/plugins/dvr-controls/DvrControls.d.ts.map +1 -1
  75. package/lib/plugins/dvr-controls/DvrControls.js +5 -2
  76. package/lib/plugins/error-screen/ErrorScreen.d.ts +5 -0
  77. package/lib/plugins/error-screen/ErrorScreen.d.ts.map +1 -1
  78. package/lib/plugins/error-screen/ErrorScreen.js +5 -0
  79. package/lib/plugins/index.d.ts +2 -3
  80. package/lib/plugins/index.d.ts.map +1 -1
  81. package/lib/plugins/index.js +2 -3
  82. package/lib/plugins/level-selector/LevelSelector.d.ts +10 -3
  83. package/lib/plugins/level-selector/LevelSelector.d.ts.map +1 -1
  84. package/lib/plugins/level-selector/LevelSelector.js +20 -19
  85. package/lib/plugins/media-control/MediaControl.d.ts +6 -2
  86. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  87. package/lib/plugins/media-control/MediaControl.js +40 -39
  88. package/lib/plugins/picture-in-picture/PictureInPicture.d.ts +32 -4
  89. package/lib/plugins/picture-in-picture/PictureInPicture.d.ts.map +1 -1
  90. package/lib/plugins/picture-in-picture/PictureInPicture.js +30 -2
  91. package/lib/plugins/playback-rate/PlaybackRate.d.ts +47 -14
  92. package/lib/plugins/playback-rate/PlaybackRate.d.ts.map +1 -1
  93. package/lib/plugins/playback-rate/PlaybackRate.js +38 -9
  94. package/lib/plugins/source-controller/SourceController.d.ts +9 -0
  95. package/lib/plugins/source-controller/SourceController.d.ts.map +1 -1
  96. package/lib/plugins/source-controller/SourceController.js +11 -1
  97. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts +35 -1
  98. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts.map +1 -1
  99. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.js +46 -23
  100. package/lib/plugins/statistics/Statistics.d.ts +38 -3
  101. package/lib/plugins/statistics/Statistics.d.ts.map +1 -1
  102. package/lib/plugins/statistics/Statistics.js +51 -9
  103. package/lib/plugins/subtitles/Subtitles.d.ts +65 -16
  104. package/lib/plugins/subtitles/Subtitles.d.ts.map +1 -1
  105. package/lib/plugins/subtitles/Subtitles.js +131 -109
  106. package/lib/plugins/telemetry/Telemetry.d.ts +135 -0
  107. package/lib/plugins/telemetry/Telemetry.d.ts.map +1 -0
  108. package/lib/plugins/telemetry/Telemetry.js +180 -0
  109. package/lib/plugins/volume-fade/VolumeFade.d.ts +7 -1
  110. package/lib/plugins/volume-fade/VolumeFade.d.ts.map +1 -1
  111. package/lib/plugins/volume-fade/VolumeFade.js +8 -1
  112. package/package.json +1 -1
  113. package/src/index.plugins.ts +2 -2
  114. package/src/playback/hls-playback/HlsPlayback.ts +1 -1
  115. package/src/plugins/bottom-gear/BottomGear.ts +26 -4
  116. package/src/plugins/clappr-nerd-stats/ClapprNerdStats.ts +240 -173
  117. package/src/plugins/dvr-controls/DvrControls.ts +5 -2
  118. package/src/plugins/error-screen/ErrorScreen.ts +5 -0
  119. package/src/plugins/index.ts +2 -3
  120. package/src/plugins/level-selector/LevelSelector.ts +22 -19
  121. package/src/plugins/level-selector/__tests__/LevelSelector.test.ts +47 -26
  122. package/src/plugins/level-selector/__tests__/__snapshots__/LevelSelector.test.ts.snap +18 -18
  123. package/src/plugins/media-control/MediaControl.ts +43 -41
  124. package/src/plugins/picture-in-picture/PictureInPicture.ts +35 -7
  125. package/src/plugins/playback-rate/PlaybackRate.ts +53 -24
  126. package/src/plugins/source-controller/SourceController.ts +11 -1
  127. package/src/plugins/source-controller/__tests__/SourceController.test.ts +1 -1
  128. package/src/plugins/spinner-three-bounce/SpinnerThreeBounce.ts +46 -22
  129. package/src/plugins/subtitles/Subtitles.ts +146 -155
  130. package/src/plugins/telemetry/Telemetry.ts +299 -0
  131. package/src/plugins/volume-fade/VolumeFade.ts +9 -2
  132. package/temp/player.api.json +3384 -4872
  133. package/tsconfig.tsbuildinfo +1 -1
  134. package/docs/api/player.clapprnerdstats.attributes.md +0 -17
  135. package/docs/api/player.clapprnerdstats.bindevents.md +0 -18
  136. package/docs/api/player.clapprnerdstats.events.md +0 -18
  137. package/docs/api/player.clapprnerdstats.name.md +0 -14
  138. package/docs/api/player.clapprnerdstats.playerheight.md +0 -14
  139. package/docs/api/player.clapprnerdstats.playerwidth.md +0 -14
  140. package/docs/api/player.clapprnerdstats.statsboxelem.md +0 -14
  141. package/docs/api/player.clapprnerdstats.statsboxwidththreshold.md +0 -14
  142. package/docs/api/player.clapprnerdstats.supportedversion.md +0 -16
  143. package/docs/api/player.clapprnerdstats.template.md +0 -14
  144. package/docs/api/player.pictureinpicture.bindevents.md +0 -15
  145. package/docs/api/player.pictureinpicture.events.md +0 -13
  146. package/docs/api/player.pictureinpicture.exitpictureinpicture.md +0 -15
  147. package/docs/api/player.pictureinpicture.render.md +0 -15
  148. package/docs/api/player.pictureinpicture.requestpictureinpicture.md +0 -15
  149. package/docs/api/player.pictureinpicture.supportedversion.md +0 -13
  150. package/docs/api/player.pictureinpicture.togglepictureinpicture.md +0 -15
  151. package/docs/api/player.pictureinpicture.version.md +0 -11
  152. package/docs/api/player.pictureinpicture.videoelement.md +0 -11
  153. package/docs/api/player.playbackrate.attributes.md +0 -14
  154. package/docs/api/player.playbackrate.bindevents.md +0 -15
  155. package/docs/api/player.playbackrate.events.md +0 -15
  156. package/docs/api/player.playbackrate.gettitle.md +0 -15
  157. package/docs/api/player.playbackrate.goback.md +0 -15
  158. package/docs/api/player.playbackrate.highlightcurrentrate.md +0 -15
  159. package/docs/api/player.playbackrate.onfinishad.md +0 -15
  160. package/docs/api/player.playbackrate.onshowmenu.md +0 -15
  161. package/docs/api/player.playbackrate.onstartad.md +0 -15
  162. package/docs/api/player.playbackrate.onstop.md +0 -15
  163. package/docs/api/player.playbackrate.reload.md +0 -15
  164. package/docs/api/player.playbackrate.render.md +0 -15
  165. package/docs/api/player.playbackrate.supportedversion.md +0 -13
  166. package/docs/api/player.playbackrate.unbindevents.md +0 -15
  167. package/docs/api/player.sourcecontroller.version.md +0 -14
  168. package/docs/api/player.spinnerthreebounce.attributes.md +0 -14
  169. package/docs/api/player.spinnerthreebounce.name.md +0 -11
  170. package/docs/api/player.spinnerthreebounce.render.md +0 -15
  171. package/docs/api/player.spinnerthreebounce.supportedversion.md +0 -13
  172. package/docs/api/player.statistics.bindevents.md +0 -15
  173. package/docs/api/player.statistics.md +0 -141
  174. package/docs/api/player.statistics.name.md +0 -11
  175. package/docs/api/player.statistics.supportedversion.md +0 -13
  176. package/docs/api/player.subtitles.attributes.md +0 -14
  177. package/docs/api/player.subtitles.bindevents.md +0 -15
  178. package/docs/api/player.subtitles.buttonelement.md +0 -15
  179. package/docs/api/player.subtitles.events.md +0 -14
  180. package/docs/api/player.subtitles.levelelement.md +0 -51
  181. package/docs/api/player.subtitles.name.md +0 -11
  182. package/docs/api/player.subtitles.preselectedlanguage.md +0 -11
  183. package/docs/api/player.subtitles.reload.md +0 -15
  184. package/docs/api/player.subtitles.render.md +0 -15
  185. package/docs/api/player.subtitles.selectsubtitles.md +0 -15
  186. package/docs/api/player.subtitles.startlevelswitch.md +0 -15
  187. package/docs/api/player.subtitles.stoplevelswitch.md +0 -15
  188. package/docs/api/player.subtitles.supportedversion.md +0 -13
  189. package/docs/api/player.subtitles.template.md +0 -11
  190. package/docs/api/player.subtitles.templatestring.md +0 -11
  191. package/docs/api/player.subtitles.unbindevents.md +0 -15
  192. package/docs/api/player.subtitles.version.md +0 -11
  193. package/docs/api/player.volumefade.bindevents.md +0 -18
  194. package/docs/api/player.volumefade.unbindevents.md +0 -18
  195. package/src/plugins/statistics/Statistics.ts +0 -207
  196. /package/src/plugins/{statistics → telemetry}/Statistics copy.xts +0 -0
@@ -1,5 +1,5 @@
1
1
  import { Events, UICorePlugin, Browser, template, $, } from '@clappr/core';
2
- import { reportError } from '@gcorevideo/utils';
2
+ import { reportError, trace } from '@gcorevideo/utils';
3
3
  import assert from 'assert';
4
4
  import { CLAPPR_VERSION } from '../../build.js';
5
5
  import '../../../assets/subtitles/style.scss';
@@ -8,88 +8,102 @@ import subtitlesOnIcon from '../../../assets/icons/new/subtitles-on.svg';
8
8
  import comboboxHTML from '../../../assets/subtitles/combobox.ejs';
9
9
  import stringHTML from '../../../assets/subtitles/string.ejs';
10
10
  import { isFullscreen } from '../utils.js';
11
- const VERSION = '0.0.1';
12
- const LOCAL_STORAGE_SUBTITLES_ID = 'subtitles_select';
13
- const T = 'plugins.media_control_subtitles';
11
+ const VERSION = '2.19.14';
12
+ const LOCAL_STORAGE_SUBTITLES_ID = 'gplayer.plugins.subtitles.selected';
13
+ const T = 'plugins.subtitles';
14
14
  const NO_TRACK = { language: 'off' };
15
+ /**
16
+ * A {@link MediaControl | media control} plugin that provides a UI to select the subtitles when available.
17
+ * @beta
18
+ *
19
+ * @remarks
20
+ * Depends on:
21
+ *
22
+ * - {@link MediaControl}
23
+ *
24
+ * Configuration options:
25
+ *
26
+ * - subtitles.language - The language of the subtitles to select by default.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { Subtitles } from '@gcorevideo/player'
31
+ *
32
+ * Player.registerPlugin(Subtitles)
33
+ *
34
+ * new Player({
35
+ * ...
36
+ * subtitles: {
37
+ * language: 'en',
38
+ * },
39
+ * })
40
+ * ```
41
+ */
15
42
  export class Subtitles extends UICorePlugin {
16
- currentContainer;
17
- currentLevel;
18
- currentPlayback;
43
+ currentLevel = null;
44
+ isPreselectedApplied = false;
19
45
  isShowing = false;
20
- tracks;
21
- $string;
46
+ track = { ...NO_TRACK };
47
+ tracks = null;
48
+ $string = null;
49
+ /**
50
+ * @internal
51
+ */
22
52
  get name() {
23
- return 'media_control_subtitles';
53
+ return 'subtitles';
24
54
  }
55
+ /**
56
+ * @internal
57
+ */
25
58
  get supportedVersion() {
26
59
  return { min: CLAPPR_VERSION };
27
60
  }
61
+ /**
62
+ * @internal
63
+ */
28
64
  static get version() {
29
65
  return VERSION;
30
66
  }
31
- get template() {
32
- return template(comboboxHTML);
33
- }
34
- get templateString() {
35
- return template(stringHTML);
36
- }
67
+ static template = template(comboboxHTML);
68
+ static templateString = template(stringHTML);
69
+ /**
70
+ * @internal
71
+ */
37
72
  get attributes() {
38
73
  return {
39
74
  class: this.name,
40
75
  'data-subtitles': '',
41
76
  };
42
77
  }
78
+ /**
79
+ * @internal
80
+ */
43
81
  get events() {
44
82
  return {
45
83
  'click [data-subtitles-select]': 'onLevelSelect',
46
84
  'click [data-subtitles-button]': 'onShowLevelSelectMenu',
47
85
  };
48
86
  }
49
- isPreselectedApplied = false;
50
- track = { ...NO_TRACK };
51
87
  get preselectedLanguage() {
52
88
  return this.core.options.subtitles?.language ?? 'off';
53
89
  }
90
+ /**
91
+ * @internal
92
+ */
54
93
  bindEvents() {
94
+ const mediaControl = this.core.getPlugin('media_control');
95
+ assert(mediaControl, 'media_control plugin is required');
55
96
  this.listenTo(this.core, Events.CORE_RESIZE, this.playerResize);
56
- this.listenToOnce(this.core, Events.CORE_READY, this.bindPlaybackEvents);
57
- this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_CONTAINERCHANGED, this.reload);
58
- this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_RENDERED, this.render);
59
- this.listenTo(this.core.mediaControl, Events.MEDIACONTROL_HIDE, this.hideSelectLevelMenu);
60
- }
61
- unBindEvents() {
62
- // @ts-ignore
63
- this.stopListening(this.core, Events.CORE_READY);
64
- // @ts-ignore
65
- this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_CONTAINERCHANGED);
66
- // @ts-ignore
67
- this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_RENDERED);
68
- // @ts-ignore
69
- this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_HIDE);
70
- // @ts-ignore
71
- this.stopListening(this.core.mediaControl, Events.MEDIACONTROL_SHOW);
72
- if (this.currentContainer) {
73
- // @ts-ignore
74
- this.stopListening(this.currentContainer, Events.CONTAINER_FULLSCREEN);
75
- // @ts-ignore
76
- this.stopListening(this.currentContainer, 'container:advertisement:start', this.onStartAd);
77
- // @ts-ignore
78
- this.stopListening(this.currentContainer, 'container:advertisement:finish', this.onFinishAd);
79
- }
97
+ this.listenTo(this.core, Events.CORE_ACTIVE_CONTAINER_CHANGED, this.bindPlaybackEvents);
98
+ this.listenTo(mediaControl, Events.MEDIACONTROL_RENDERED, this.render);
99
+ this.listenTo(mediaControl, Events.MEDIACONTROL_HIDE, this.hideSelectLevelMenu);
80
100
  }
81
101
  bindPlaybackEvents() {
82
- if (this.currentPlayback &&
83
- this.currentPlayback === this.core.activePlayback) {
84
- return;
85
- }
86
- this.currentPlayback = this.core.activePlayback;
87
- this.currentContainer = this.core.activeContainer;
88
- this.listenTo(this.currentContainer, Events.CONTAINER_FULLSCREEN, this.playerResize);
89
- this.listenToOnce(this.currentPlayback, Events.PLAYBACK_PLAY, this.getTracks);
90
- this.listenTo(this.currentContainer, 'container:advertisement:start', this.onStartAd);
102
+ this.listenTo(this.core.activeContainer, Events.CONTAINER_FULLSCREEN, this.playerResize);
103
+ this.listenToOnce(this.core.activePlayback, Events.PLAYBACK_PLAY, this.getTracks);
104
+ this.listenTo(this.core.activeContainer, 'container:advertisement:start', this.onStartAd);
91
105
  // fix for iOS
92
- const video = this.currentPlayback?.el;
106
+ const video = this.core.activePlayback.el;
93
107
  assert(video, 'video element is required');
94
108
  video.addEventListener('webkitbeginfullscreen', () => {
95
109
  if (Browser.isiOS) {
@@ -103,10 +117,13 @@ export class Subtitles extends UICorePlugin {
103
117
  });
104
118
  }
105
119
  getTracks() {
106
- if (this.currentPlayback) {
120
+ if (this.core.activePlayback) {
107
121
  try {
108
- const tracks = this.currentPlayback.el.textTracks;
109
- tracks.length > 0 && this.fillLevels(tracks);
122
+ const tracks = this.core.activePlayback.el
123
+ .textTracks;
124
+ if (tracks.length > 0) {
125
+ this.setTracks(tracks);
126
+ }
110
127
  }
111
128
  catch (error) {
112
129
  reportError(error);
@@ -114,23 +131,18 @@ export class Subtitles extends UICorePlugin {
114
131
  }
115
132
  }
116
133
  onStartAd() {
117
- if (this.isShowing && this.currentContainer) {
134
+ if (this.isShowing && this.core.activeContainer) {
118
135
  this.hide();
119
- this.listenTo(this.currentContainer, 'container:advertisement:finish', this.onFinishAd);
136
+ this.listenTo(this.core.activeContainer, 'container:advertisement:finish', this.onFinishAd);
120
137
  }
121
138
  }
122
139
  onFinishAd() {
123
140
  this.show();
124
- this.stopListening(this.currentContainer, 'container:advertisement:finish', this.onFinishAd);
125
- }
126
- reload() {
127
- this.unBindEvents();
128
- this.bindEvents();
129
- this.bindPlaybackEvents();
141
+ this.stopListening(this.core.activeContainer, 'container:advertisement:finish', this.onFinishAd);
130
142
  }
131
143
  playerResize() {
132
- const shouldShow = this.currentContainer &&
133
- isFullscreen(this.currentContainer.el) &&
144
+ const shouldShow = this.core.activeContainer &&
145
+ isFullscreen(this.core.activeContainer.el) &&
134
146
  this.currentLevel &&
135
147
  this.currentLevel.mode &&
136
148
  Browser.isiOS &&
@@ -145,6 +157,9 @@ export class Subtitles extends UICorePlugin {
145
157
  reportError(error);
146
158
  }
147
159
  }
160
+ /**
161
+ * Hides the subtitles menu and the subtitles.
162
+ */
148
163
  hide() {
149
164
  this.isShowing = false;
150
165
  this.renderIcon();
@@ -155,11 +170,14 @@ export class Subtitles extends UICorePlugin {
155
170
  }
156
171
  }
157
172
  }
173
+ /**
174
+ * Shows the subtitles menu and the subtitles.
175
+ */
158
176
  show() {
159
177
  this.isShowing = true;
160
178
  this.renderIcon();
161
- if (this.currentContainer &&
162
- isFullscreen(this.currentContainer.el) &&
179
+ if (this.core.activeContainer &&
180
+ isFullscreen(this.core.activeContainer.el) &&
163
181
  this.currentLevel &&
164
182
  this.currentLevel.mode &&
165
183
  Browser.isiOS) {
@@ -171,59 +189,61 @@ export class Subtitles extends UICorePlugin {
171
189
  }
172
190
  }
173
191
  shouldRender() {
174
- if (!this.currentContainer) {
175
- return false;
176
- }
177
- if (!this.currentPlayback) {
178
- return false;
179
- }
180
- // Only care if we have at least 2 to choose from
181
- const hasLevels = !!(this.tracks && this.tracks.length > 0);
182
- return hasLevels;
192
+ return !!(this.tracks && this.tracks.length > 0);
183
193
  }
184
194
  resizeFont() {
185
- if (!this.currentContainer) {
195
+ if (!this.core.activeContainer) {
186
196
  return;
187
197
  }
188
198
  if (!this.$string) {
189
199
  return;
190
200
  }
191
- const skinWidth = this.currentContainer.$el.width();
201
+ const skinWidth = this.core.activeContainer.$el.width();
192
202
  this.$string.find('p').css('font-size', skinWidth * 0.03);
193
203
  }
204
+ /**
205
+ * @internal
206
+ */
194
207
  render() {
195
- if (this.shouldRender()) {
196
- this.$el.html(this.template({ tracks: this.tracks }));
197
- this.currentContainer?.$el.find('.subtitle-string').remove();
198
- this.$string = $(this.templateString());
199
- this.resizeFont();
200
- this.currentContainer?.$el.append(this.$string[0]);
201
- if (this.core.mediaControl.$subtitlesSelector &&
202
- this.core.mediaControl.$subtitlesSelector.length > 0) {
203
- this.core.mediaControl.$subtitlesSelector.append(this.el);
204
- }
205
- else {
206
- this.core.mediaControl.$('.media-control-right-panel').append(this.el);
207
- }
208
- this.updateCurrentLevel(this.track);
209
- this.highlightCurrentSubtitles();
210
- this.applyPreselectedSubtitles();
208
+ if (!this.core.activeContainer) {
209
+ return this;
210
+ }
211
+ if (!this.shouldRender()) {
212
+ return this;
213
+ }
214
+ trace(`${T} render`, {
215
+ tracks: this.tracks?.length,
216
+ track: this.track?.language,
217
+ });
218
+ const mediaControl = this.core.getPlugin('media_control');
219
+ assert(mediaControl, 'media_control plugin is required');
220
+ this.$el.html(Subtitles.template({ tracks: this.tracks }));
221
+ this.core.activeContainer.$el.find('.subtitle-string').remove();
222
+ this.$string = $(Subtitles.templateString());
223
+ this.resizeFont();
224
+ this.core.activeContainer.$el.append(this.$string[0]);
225
+ const ss = mediaControl.getElement('subtitlesSelector');
226
+ if (ss && ss.length > 0) {
227
+ ss.append(this.el);
211
228
  }
212
- if (this.core.mediaControl.$subtitlesSelector?.find('span.subtitle-text')
213
- .length > 0) {
214
- this.renderIcon();
229
+ else {
230
+ mediaControl.getRightPanel().append(this.el);
215
231
  }
232
+ this.updateCurrentLevel(this.track);
233
+ this.highlightCurrentSubtitles();
234
+ this.applyPreselectedSubtitles();
235
+ this.renderIcon();
216
236
  return this;
217
237
  }
218
- fillLevels(tracks) {
238
+ setTracks(tracks) {
219
239
  this.tracks = tracks;
220
240
  this.render();
221
241
  }
222
242
  findLevelBy(id) {
223
243
  if (this.tracks) {
224
- for (let i = 0; i < this.tracks.length; i++) {
225
- if (this.tracks[i].language === id) {
226
- return this.tracks[i]; // TODO TrackInfo?
244
+ for (const track of this.tracks) {
245
+ if (track.language === id) {
246
+ return track; // TODO TrackInfo?
227
247
  }
228
248
  }
229
249
  }
@@ -254,21 +274,21 @@ export class Subtitles extends UICorePlugin {
254
274
  }
255
275
  }
256
276
  onShowLevelSelectMenu() {
277
+ trace(`${T} onShowLevelSelectMenu`);
257
278
  this.toggleContextMenu();
258
279
  }
259
280
  hideSelectLevelMenu() {
260
281
  ;
261
- this.$('.subtitles ul').hide();
282
+ this.$('[data-subtitles] ul').hide();
262
283
  }
263
284
  toggleContextMenu() {
264
- ;
265
- this.$('.subtitles ul').toggle();
285
+ this.$('[data-subtitles] ul').toggle();
266
286
  }
267
287
  buttonElement() {
268
- return this.$('.subtitles button');
288
+ return this.$('[data-subtitles] button');
269
289
  }
270
290
  levelElement(id) {
271
- return this.$('.subtitles ul a' + (id ? '[data-subtitles-select="' + id + '"]' : '')).parent();
291
+ return this.$('[data-subtitles] ul a' + (id ? '[data-subtitles-select="' + id + '"]' : '')).parent();
272
292
  }
273
293
  startLevelSwitch() {
274
294
  this.buttonElement().addClass('changing');
@@ -285,7 +305,7 @@ export class Subtitles extends UICorePlugin {
285
305
  const track = this.tracks[i];
286
306
  if (track.language === this.currentLevel.language) {
287
307
  track.mode = 'showing';
288
- const currentTime = this.currentPlayback?.getCurrentTime() ?? 0;
308
+ const currentTime = this.core.activePlayback?.getCurrentTime() ?? 0;
289
309
  const cues = track.cues;
290
310
  let subtitleText = '';
291
311
  if (cues && cues.length) {
@@ -347,8 +367,10 @@ export class Subtitles extends UICorePlugin {
347
367
  }
348
368
  renderIcon() {
349
369
  const icon = this.isShowing ? subtitlesOnIcon : subtitlesOffIcon;
350
- this.core.mediaControl.$subtitlesSelector
351
- .find('span.subtitle-text')
370
+ this.core
371
+ .getPlugin('media_control')
372
+ .getElement('subtitlesSelector')
373
+ ?.find('span.subtitle-text')
352
374
  .html(icon);
353
375
  }
354
376
  }
@@ -0,0 +1,135 @@
1
+ import { Container, ContainerPlugin } from '@clappr/core';
2
+ import type { PlaybackType } from '../../types.js';
3
+ /**
4
+ * Telemetry event data
5
+ */
6
+ export type TelemetryEventData = StallEventData | InitEventData | StartEventData | WatchEventData;
7
+ /**
8
+ * Playback stall event data
9
+ */
10
+ export interface StallEventData {
11
+ event: TelemetryEvent.Stall;
12
+ /**
13
+ * Accumulated buffering duration over the measurement interval, ms
14
+ */
15
+ total_ms: number;
16
+ /**
17
+ * Number of stalls
18
+ */
19
+ count: number;
20
+ /**
21
+ * Playback time when the stall is reported at the end of a stall measurement interval, s
22
+ */
23
+ time: number;
24
+ }
25
+ export interface InitEventData {
26
+ event: TelemetryEvent.Init;
27
+ }
28
+ export interface StartEventData {
29
+ event: TelemetryEvent.Start;
30
+ }
31
+ export interface WatchEventData {
32
+ event: TelemetryEvent.Watch;
33
+ }
34
+ /**
35
+ * Telemetry record
36
+ */
37
+ export type TelemetryRecord = {
38
+ type: PlaybackType;
39
+ } & TelemetryEventData;
40
+ /**
41
+ * Callback to send the telemetry record to the storage.
42
+ * @param data - The telemetry record to send.
43
+ */
44
+ type TelemetrySendFn = (data: TelemetryRecord) => void;
45
+ /**
46
+ * Plugin settings
47
+ */
48
+ export interface TelemetryPluginSettings {
49
+ /**
50
+ * Sends the statistics record to the storage.
51
+ * The actual delivery is presumably async and batched.
52
+ */
53
+ send: TelemetrySendFn;
54
+ }
55
+ /**
56
+ * Telemetry event type
57
+ */
58
+ export declare enum TelemetryEvent {
59
+ Init = 1,
60
+ Start = 2,
61
+ Watch = 3,
62
+ Stall = 4
63
+ }
64
+ /**
65
+ * Collects and reports the performance statistics.
66
+ * @beta
67
+ * @remarks
68
+ * This plugin is experimental and its API is likely to change.
69
+ *
70
+ * Configuration options {@link TelemetryPluginSettings}
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * import { Statistics } from '@gcorevideo/player'
75
+ *
76
+ * Player.registerPlugin(Statistics)
77
+ *
78
+ * const player = new Player({
79
+ * statistics: {
80
+ * send: (data) => {
81
+ * fetch('/stats', {
82
+ * method: 'POST',
83
+ * body: JSON.stringify(data),
84
+ * headers: { 'content-type': 'application/json' },
85
+ * })
86
+ * },
87
+ * },
88
+ * ...
89
+ * })
90
+ * ```
91
+ */
92
+ export declare class Telemetry extends ContainerPlugin {
93
+ /**
94
+ * The name of the plugin.
95
+ */
96
+ get name(): string;
97
+ /**
98
+ * The supported version of the plugin.
99
+ */
100
+ get supportedVersion(): {
101
+ min: string;
102
+ };
103
+ private started;
104
+ private timeStart;
105
+ private stallSent;
106
+ private stallLastTime;
107
+ private watchSent;
108
+ private bufTracking;
109
+ private numStalls;
110
+ /**
111
+ * The time when buffering last started.
112
+ */
113
+ private bufLastStarted;
114
+ /**
115
+ * The accumulated buffering duration.
116
+ */
117
+ private stallAcc;
118
+ constructor(container: Container);
119
+ /**
120
+ * @internal
121
+ */
122
+ bindEvents(): void;
123
+ private startLevelSwitch;
124
+ private endLevelSwitch;
125
+ private onBuffering;
126
+ private onBufferFull;
127
+ private onReady;
128
+ private sendInit;
129
+ private send;
130
+ private sendStall;
131
+ private onTimeUpdate;
132
+ private onStart;
133
+ }
134
+ export {};
135
+ //# sourceMappingURL=Telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Telemetry.d.ts","sourceRoot":"","sources":["../../../src/plugins/telemetry/Telemetry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAU,MAAM,cAAc,CAAA;AAKjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAUlD;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,cAAc,GAAG,aAAa,GAAG,cAAc,GAAG,cAAc,CAAA;AAEjG;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,cAAc,CAAC,KAAK,CAAA;IAC3B;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,cAAc,CAAC,IAAI,CAAA;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,cAAc,CAAC,KAAK,CAAA;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,cAAc,CAAC,KAAK,CAAA;CAC5B;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,YAAY,CAAA;CACnB,GAAG,kBAAkB,CAAC;AAEvB;;;GAGG;AACH,KAAK,eAAe,GAAG,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAA;AAEtD;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,IAAI,EAAE,eAAe,CAAA;CACtB;AAED;;GAEG;AACH,oBAAY,cAAc;IACxB,IAAI,IAAI;IACR,KAAK,IAAA;IACL,KAAK,IAAA;IACL,KAAK,IAAA;CACN;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,SAAU,SAAQ,eAAe;IAC5C;;OAEG;IACH,IAAI,IAAI,WAEP;IAED;;OAEG;IACH,IAAI,gBAAgB;;MAEnB;IAED,OAAO,CAAC,OAAO,CAAQ;IAEvB,OAAO,CAAC,SAAS,CAAI;IAErB,OAAO,CAAC,SAAS,CAAQ;IAEzB,OAAO,CAAC,aAAa,CAAI;IAEzB,OAAO,CAAC,SAAS,CAAQ;IAEzB,OAAO,CAAC,WAAW,CAAQ;IAE3B,OAAO,CAAC,SAAS,CAAI;IAErB;;OAEG;IACH,OAAO,CAAC,cAAc,CAAI;IAE1B;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAI;gBAER,SAAS,EAAE,SAAS;IAUhC;;OAEG;IACM,UAAU;IAoCnB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,OAAO;IAgBf,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,IAAI;IAOZ,OAAO,CAAC,SAAS;IAejB,OAAO,CAAC,YAAY;IAuBpB,OAAO,CAAC,OAAO;CAShB"}
@@ -0,0 +1,180 @@
1
+ // An example implementation of client side performancestatistics
2
+ import { ContainerPlugin, Events } from '@clappr/core';
3
+ import { reportError, trace } from '@gcorevideo/utils';
4
+ import assert from 'assert';
5
+ import { CLAPPR_VERSION } from '../../build.js';
6
+ const WATCH_CUTOFF = 5;
7
+ const STALL_MEASURE_PERIOD = 10;
8
+ const T = 'plugins.telemetry';
9
+ /**
10
+ * Telemetry event type
11
+ */
12
+ export var TelemetryEvent;
13
+ (function (TelemetryEvent) {
14
+ TelemetryEvent[TelemetryEvent["Init"] = 1] = "Init";
15
+ TelemetryEvent[TelemetryEvent["Start"] = 2] = "Start";
16
+ TelemetryEvent[TelemetryEvent["Watch"] = 3] = "Watch";
17
+ TelemetryEvent[TelemetryEvent["Stall"] = 4] = "Stall";
18
+ })(TelemetryEvent || (TelemetryEvent = {}));
19
+ /**
20
+ * Collects and reports the performance statistics.
21
+ * @beta
22
+ * @remarks
23
+ * This plugin is experimental and its API is likely to change.
24
+ *
25
+ * Configuration options {@link TelemetryPluginSettings}
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * import { Statistics } from '@gcorevideo/player'
30
+ *
31
+ * Player.registerPlugin(Statistics)
32
+ *
33
+ * const player = new Player({
34
+ * statistics: {
35
+ * send: (data) => {
36
+ * fetch('/stats', {
37
+ * method: 'POST',
38
+ * body: JSON.stringify(data),
39
+ * headers: { 'content-type': 'application/json' },
40
+ * })
41
+ * },
42
+ * },
43
+ * ...
44
+ * })
45
+ * ```
46
+ */
47
+ export class Telemetry extends ContainerPlugin {
48
+ /**
49
+ * The name of the plugin.
50
+ */
51
+ get name() {
52
+ return 'telemetry';
53
+ }
54
+ /**
55
+ * The supported version of the plugin.
56
+ */
57
+ get supportedVersion() {
58
+ return { min: CLAPPR_VERSION };
59
+ }
60
+ started = false;
61
+ timeStart = 0;
62
+ stallSent = false;
63
+ stallLastTime = 0;
64
+ watchSent = false;
65
+ bufTracking = false;
66
+ numStalls = 0;
67
+ /**
68
+ * The time when buffering last started.
69
+ */
70
+ bufLastStarted = 0;
71
+ /**
72
+ * The accumulated buffering duration.
73
+ */
74
+ stallAcc = 0;
75
+ constructor(container) {
76
+ super(container);
77
+ assert(this.options.telemetry &&
78
+ typeof this.options.telemetry.send === 'function', 'Telemetry plugin configuration is invalid: `send` option is required');
79
+ }
80
+ /**
81
+ * @internal
82
+ */
83
+ bindEvents() {
84
+ // TODO remove this
85
+ // this.listenToOnce(
86
+ // this.container,
87
+ // CUSTOM_EVENTS_CONTAINER_START,
88
+ // this.onStart,
89
+ // )
90
+ this.listenToOnce(this.container, Events.CONTAINER_READY, this.onReady);
91
+ this.listenTo(this.container, Events.CONTAINER_STATE_BUFFERING, this.onBuffering);
92
+ this.listenTo(this.container, Events.CONTAINER_STATE_BUFFERFULL, this.onBufferFull);
93
+ this.listenTo(this.container.playback, Events.PLAYBACK_TIMEUPDATE, this.onTimeUpdate);
94
+ this.listenTo(this.container.playback, Events.PLAYBACK_LEVEL_SWITCH_START, this.startLevelSwitch);
95
+ this.listenTo(this.container.playback, Events.PLAYBACK_LEVEL_SWITCH_END, this.endLevelSwitch);
96
+ }
97
+ startLevelSwitch() {
98
+ this.bufTracking = false;
99
+ }
100
+ endLevelSwitch() {
101
+ this.bufTracking = true;
102
+ }
103
+ onBuffering() {
104
+ if (this.bufTracking) {
105
+ this.bufLastStarted = performance.now();
106
+ }
107
+ }
108
+ onBufferFull() {
109
+ if (this.bufTracking && this.bufLastStarted) {
110
+ this.stallAcc += performance.now() - this.bufLastStarted;
111
+ this.numStalls++;
112
+ }
113
+ this.bufTracking = true;
114
+ }
115
+ onReady() {
116
+ this.sendInit();
117
+ trace(`${T} onReady`, {
118
+ autoPlay: this.options.autoPlay,
119
+ });
120
+ if (this.options.autoPlay) {
121
+ this.onStart();
122
+ }
123
+ else {
124
+ this.listenToOnce(this.container.playback, Events.PLAYBACK_PLAY_INTENT, this.onStart);
125
+ }
126
+ }
127
+ sendInit() {
128
+ this.send({ event: TelemetryEvent.Init });
129
+ }
130
+ send(event) {
131
+ this.options.telemetry.send({
132
+ type: this.container.getPlaybackType(),
133
+ ...event,
134
+ });
135
+ }
136
+ sendStall(time) {
137
+ // TODO don't send if no stalls?
138
+ const res = {
139
+ event: TelemetryEvent.Stall,
140
+ count: this.numStalls,
141
+ time,
142
+ total_ms: Math.round(this.stallAcc * 1000),
143
+ };
144
+ this.stallAcc = 0;
145
+ this.numStalls = 0;
146
+ this.send(res);
147
+ this.stallSent = true;
148
+ this.stallLastTime = time;
149
+ }
150
+ onTimeUpdate({ current }) {
151
+ if (!this.timeStart) {
152
+ this.timeStart = current;
153
+ }
154
+ try {
155
+ const elapsed = current - this.timeStart;
156
+ const stallElapsed = current - this.stallLastTime;
157
+ if (!this.stallSent || stallElapsed >= STALL_MEASURE_PERIOD) {
158
+ this.sendStall(current);
159
+ }
160
+ if (!this.watchSent && elapsed >= WATCH_CUTOFF) {
161
+ this.watchSent = true;
162
+ this.send({
163
+ event: TelemetryEvent.Watch,
164
+ });
165
+ }
166
+ }
167
+ catch (error) {
168
+ reportError(error);
169
+ }
170
+ }
171
+ onStart() {
172
+ if (this.started) {
173
+ return;
174
+ }
175
+ this.started = true;
176
+ this.send({
177
+ event: TelemetryEvent.Start,
178
+ });
179
+ }
180
+ }