@webex/plugin-meetings 3.7.0-next.6 → 3.7.0-next.60

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 (138) hide show
  1. package/dist/annotation/index.js +17 -0
  2. package/dist/annotation/index.js.map +1 -1
  3. package/dist/breakouts/breakout.js +1 -1
  4. package/dist/breakouts/index.js +1 -1
  5. package/dist/common/errors/join-forbidden-error.js +52 -0
  6. package/dist/common/errors/join-forbidden-error.js.map +1 -0
  7. package/dist/common/errors/{webinar-registration-error.js → join-webinar-error.js} +12 -12
  8. package/dist/common/errors/join-webinar-error.js.map +1 -0
  9. package/dist/common/errors/multistream-not-supported-error.js +53 -0
  10. package/dist/common/errors/multistream-not-supported-error.js.map +1 -0
  11. package/dist/config.js +1 -1
  12. package/dist/config.js.map +1 -1
  13. package/dist/constants.js +46 -5
  14. package/dist/constants.js.map +1 -1
  15. package/dist/index.js +16 -11
  16. package/dist/index.js.map +1 -1
  17. package/dist/interpretation/index.js +1 -1
  18. package/dist/interpretation/siLanguage.js +1 -1
  19. package/dist/locus-info/index.js +14 -3
  20. package/dist/locus-info/index.js.map +1 -1
  21. package/dist/locus-info/selfUtils.js +35 -17
  22. package/dist/locus-info/selfUtils.js.map +1 -1
  23. package/dist/meeting/brbState.js +167 -0
  24. package/dist/meeting/brbState.js.map +1 -0
  25. package/dist/meeting/in-meeting-actions.js +2 -0
  26. package/dist/meeting/in-meeting-actions.js.map +1 -1
  27. package/dist/meeting/index.js +774 -649
  28. package/dist/meeting/index.js.map +1 -1
  29. package/dist/meeting/locusMediaRequest.js +9 -0
  30. package/dist/meeting/locusMediaRequest.js.map +1 -1
  31. package/dist/meeting/muteState.js +1 -6
  32. package/dist/meeting/muteState.js.map +1 -1
  33. package/dist/meeting/request.js +30 -0
  34. package/dist/meeting/request.js.map +1 -1
  35. package/dist/meeting/request.type.js.map +1 -1
  36. package/dist/meeting/util.js +16 -16
  37. package/dist/meeting/util.js.map +1 -1
  38. package/dist/meeting-info/meeting-info-v2.js +96 -33
  39. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  40. package/dist/meeting-info/utilv2.js +1 -1
  41. package/dist/meeting-info/utilv2.js.map +1 -1
  42. package/dist/meetings/index.js +107 -55
  43. package/dist/meetings/index.js.map +1 -1
  44. package/dist/meetings/meetings.types.js +2 -0
  45. package/dist/meetings/meetings.types.js.map +1 -1
  46. package/dist/meetings/util.js +1 -1
  47. package/dist/meetings/util.js.map +1 -1
  48. package/dist/member/index.js +9 -0
  49. package/dist/member/index.js.map +1 -1
  50. package/dist/member/types.js.map +1 -1
  51. package/dist/member/util.js +39 -28
  52. package/dist/member/util.js.map +1 -1
  53. package/dist/metrics/constants.js +3 -2
  54. package/dist/metrics/constants.js.map +1 -1
  55. package/dist/multistream/remoteMedia.js +30 -15
  56. package/dist/multistream/remoteMedia.js.map +1 -1
  57. package/dist/multistream/sendSlotManager.js +24 -0
  58. package/dist/multistream/sendSlotManager.js.map +1 -1
  59. package/dist/reachability/index.js +31 -3
  60. package/dist/reachability/index.js.map +1 -1
  61. package/dist/roap/index.js +10 -8
  62. package/dist/roap/index.js.map +1 -1
  63. package/dist/types/annotation/index.d.ts +5 -0
  64. package/dist/types/common/errors/join-forbidden-error.d.ts +15 -0
  65. package/dist/types/common/errors/{webinar-registration-error.d.ts → join-webinar-error.d.ts} +2 -2
  66. package/dist/types/common/errors/multistream-not-supported-error.d.ts +17 -0
  67. package/dist/types/constants.d.ts +38 -1
  68. package/dist/types/index.d.ts +3 -3
  69. package/dist/types/locus-info/index.d.ts +2 -1
  70. package/dist/types/meeting/brbState.d.ts +54 -0
  71. package/dist/types/meeting/in-meeting-actions.d.ts +2 -0
  72. package/dist/types/meeting/index.d.ts +21 -12
  73. package/dist/types/meeting/locusMediaRequest.d.ts +4 -0
  74. package/dist/types/meeting/request.d.ts +12 -1
  75. package/dist/types/meeting/request.type.d.ts +6 -0
  76. package/dist/types/meeting/util.d.ts +1 -1
  77. package/dist/types/meeting-info/meeting-info-v2.d.ts +27 -4
  78. package/dist/types/meetings/index.d.ts +19 -1
  79. package/dist/types/meetings/meetings.types.d.ts +8 -0
  80. package/dist/types/member/index.d.ts +1 -0
  81. package/dist/types/member/types.d.ts +7 -0
  82. package/dist/types/metrics/constants.d.ts +2 -1
  83. package/dist/types/multistream/sendSlotManager.d.ts +8 -1
  84. package/dist/types/reachability/index.d.ts +9 -1
  85. package/dist/webinar/index.js +354 -3
  86. package/dist/webinar/index.js.map +1 -1
  87. package/package.json +23 -22
  88. package/src/annotation/index.ts +16 -0
  89. package/src/common/errors/join-forbidden-error.ts +26 -0
  90. package/src/common/errors/join-webinar-error.ts +24 -0
  91. package/src/common/errors/multistream-not-supported-error.ts +30 -0
  92. package/src/config.ts +1 -1
  93. package/src/constants.ts +43 -3
  94. package/src/index.ts +5 -3
  95. package/src/locus-info/index.ts +20 -3
  96. package/src/locus-info/selfUtils.ts +24 -6
  97. package/src/meeting/brbState.ts +169 -0
  98. package/src/meeting/in-meeting-actions.ts +4 -0
  99. package/src/meeting/index.ts +256 -82
  100. package/src/meeting/locusMediaRequest.ts +7 -0
  101. package/src/meeting/muteState.ts +1 -6
  102. package/src/meeting/request.ts +26 -1
  103. package/src/meeting/request.type.ts +7 -0
  104. package/src/meeting/util.ts +8 -10
  105. package/src/meeting-info/meeting-info-v2.ts +74 -11
  106. package/src/meeting-info/utilv2.ts +3 -1
  107. package/src/meetings/index.ts +79 -20
  108. package/src/meetings/meetings.types.ts +10 -0
  109. package/src/meetings/util.ts +2 -1
  110. package/src/member/index.ts +9 -0
  111. package/src/member/types.ts +8 -0
  112. package/src/member/util.ts +34 -24
  113. package/src/metrics/constants.ts +2 -1
  114. package/src/multistream/remoteMedia.ts +28 -15
  115. package/src/multistream/sendSlotManager.ts +31 -0
  116. package/src/reachability/index.ts +29 -1
  117. package/src/roap/index.ts +10 -8
  118. package/src/webinar/index.ts +197 -3
  119. package/test/unit/spec/annotation/index.ts +46 -1
  120. package/test/unit/spec/locus-info/index.js +292 -60
  121. package/test/unit/spec/locus-info/selfConstant.js +7 -0
  122. package/test/unit/spec/locus-info/selfUtils.js +101 -1
  123. package/test/unit/spec/meeting/brbState.ts +114 -0
  124. package/test/unit/spec/meeting/in-meeting-actions.ts +2 -0
  125. package/test/unit/spec/meeting/index.js +733 -106
  126. package/test/unit/spec/meeting/muteState.js +0 -24
  127. package/test/unit/spec/meeting/utils.js +22 -19
  128. package/test/unit/spec/meeting-info/meetinginfov2.js +46 -4
  129. package/test/unit/spec/meeting-info/utilv2.js +17 -0
  130. package/test/unit/spec/meetings/index.js +159 -18
  131. package/test/unit/spec/meetings/utils.js +10 -0
  132. package/test/unit/spec/member/util.js +52 -11
  133. package/test/unit/spec/multistream/remoteMedia.ts +11 -7
  134. package/test/unit/spec/reachability/index.ts +120 -10
  135. package/test/unit/spec/roap/index.ts +47 -0
  136. package/test/unit/spec/webinar/index.ts +457 -0
  137. package/dist/common/errors/webinar-registration-error.js.map +0 -1
  138. package/src/common/errors/webinar-registration-error.ts +0 -27
@@ -4,6 +4,7 @@ import {
4
4
  ServerRoles,
5
5
  ServerRoleShape,
6
6
  IMediaStatus,
7
+ ParticipantWithBrb,
7
8
  } from './types';
8
9
  import {
9
10
  _USER_,
@@ -29,7 +30,7 @@ import ParameterError from '../common/errors/parameter';
29
30
  const MemberUtil: any = {};
30
31
 
31
32
  /**
32
- * @param {Object} participant the locus participant
33
+ * @param {Object} participant - The locus participant object.
33
34
  * @returns {Boolean}
34
35
  */
35
36
  MemberUtil.canReclaimHost = (participant) => {
@@ -43,14 +44,23 @@ MemberUtil.canReclaimHost = (participant) => {
43
44
  };
44
45
 
45
46
  /**
46
- * @param {Object} participant the locus participant
47
+ * @param {Object} participant - The locus participant object.
47
48
  * @returns {[ServerRoleShape]}
48
49
  */
49
50
  MemberUtil.getControlsRoles = (participant: ParticipantWithRoles): Array<ServerRoleShape> =>
50
51
  participant?.controls?.role?.roles;
51
52
 
52
53
  /**
53
- * @param {Object} participant the locus participant
54
+ * Checks if the participant has the brb status enabled.
55
+ *
56
+ * @param {ParticipantWithBrb} participant - The locus participant object.
57
+ * @returns {boolean} - True if the participant has brb enabled, false otherwise.
58
+ */
59
+ MemberUtil.isBrb = (participant: ParticipantWithBrb): boolean =>
60
+ participant.controls?.brb?.enabled || false;
61
+
62
+ /**
63
+ * @param {Object} participant - The locus participant object.
54
64
  * @param {ServerRoles} controlRole the search role
55
65
  * @returns {Boolean}
56
66
  */
@@ -60,28 +70,28 @@ MemberUtil.hasRole = (participant: any, controlRole: ServerRoles): boolean =>
60
70
  );
61
71
 
62
72
  /**
63
- * @param {Object} participant the locus participant
73
+ * @param {Object} participant - The locus participant object.
64
74
  * @returns {Boolean}
65
75
  */
66
76
  MemberUtil.hasCohost = (participant: ParticipantWithRoles): boolean =>
67
77
  MemberUtil.hasRole(participant, ServerRoles.Cohost) || false;
68
78
 
69
79
  /**
70
- * @param {Object} participant the locus participant
80
+ * @param {Object} participant - The locus participant object.
71
81
  * @returns {Boolean}
72
82
  */
73
83
  MemberUtil.hasModerator = (participant: ParticipantWithRoles): boolean =>
74
84
  MemberUtil.hasRole(participant, ServerRoles.Moderator) || false;
75
85
 
76
86
  /**
77
- * @param {Object} participant the locus participant
87
+ * @param {Object} participant - The locus participant object.
78
88
  * @returns {Boolean}
79
89
  */
80
90
  MemberUtil.hasPresenter = (participant: ParticipantWithRoles): boolean =>
81
91
  MemberUtil.hasRole(participant, ServerRoles.Presenter) || false;
82
92
 
83
93
  /**
84
- * @param {Object} participant the locus participant
94
+ * @param {Object} participant - The locus participant object.
85
95
  * @returns {IExternalRoles}
86
96
  */
87
97
  MemberUtil.extractControlRoles = (participant: ParticipantWithRoles): IExternalRoles => {
@@ -95,7 +105,7 @@ MemberUtil.extractControlRoles = (participant: ParticipantWithRoles): IExternalR
95
105
  };
96
106
 
97
107
  /**
98
- * @param {Object} participant the locus participant
108
+ * @param {Object} participant - The locus participant object.
99
109
  * @returns {Boolean}
100
110
  */
101
111
  MemberUtil.isUser = (participant: any) => participant && participant.type === _USER_;
@@ -103,13 +113,13 @@ MemberUtil.isUser = (participant: any) => participant && participant.type === _U
103
113
  MemberUtil.isModerator = (participant) => participant && participant.moderator;
104
114
 
105
115
  /**
106
- * @param {Object} participant the locus participant
116
+ * @param {Object} participant - The locus participant object.
107
117
  * @returns {Boolean}
108
118
  */
109
119
  MemberUtil.isGuest = (participant: any) => participant && participant.guest;
110
120
 
111
121
  /**
112
- * @param {Object} participant the locus participant
122
+ * @param {Object} participant - The locus participant object.
113
123
  * @returns {Boolean}
114
124
  */
115
125
  MemberUtil.isDevice = (participant: any) => participant && participant.type === _RESOURCE_ROOM_;
@@ -120,7 +130,7 @@ MemberUtil.isModeratorAssignmentProhibited = (participant) =>
120
130
  /**
121
131
  * checks to see if the participant id is the same as the passed id
122
132
  * there are multiple ids that can be used
123
- * @param {Object} participant the locus participant
133
+ * @param {Object} participant - The locus participant object.
124
134
  * @param {String} id
125
135
  * @returns {Boolean}
126
136
  */
@@ -130,7 +140,7 @@ MemberUtil.isSame = (participant: any, id: string) =>
130
140
  /**
131
141
  * checks to see if the participant id is the same as the passed id for associated devices
132
142
  * there are multiple ids that can be used
133
- * @param {Object} participant the locus participant
143
+ * @param {Object} participant - The locus participant object.
134
144
  * @param {String} id
135
145
  * @returns {Boolean}
136
146
  */
@@ -142,7 +152,7 @@ MemberUtil.isAssociatedSame = (participant: any, id: string) =>
142
152
  );
143
153
 
144
154
  /**
145
- * @param {Object} participant the locus participant
155
+ * @param {Object} participant - The locus participant object.
146
156
  * @param {Boolean} isGuest
147
157
  * @param {String} status
148
158
  * @returns {Boolean}
@@ -161,7 +171,7 @@ MemberUtil.isNotAdmitted = (participant: any, isGuest: boolean, status: string):
161
171
  !status === _IN_MEETING_);
162
172
 
163
173
  /**
164
- * @param {Object} participant the locus participant
174
+ * @param {Object} participant - The locus participant object.
165
175
  * @returns {Boolean}
166
176
  */
167
177
  MemberUtil.isAudioMuted = (participant: any) => {
@@ -173,7 +183,7 @@ MemberUtil.isAudioMuted = (participant: any) => {
173
183
  };
174
184
 
175
185
  /**
176
- * @param {Object} participant the locus participant
186
+ * @param {Object} participant - The locus participant object.
177
187
  * @returns {Boolean}
178
188
  */
179
189
  MemberUtil.isVideoMuted = (participant: any): boolean => {
@@ -185,7 +195,7 @@ MemberUtil.isVideoMuted = (participant: any): boolean => {
185
195
  };
186
196
 
187
197
  /**
188
- * @param {Object} participant the locus participant
198
+ * @param {Object} participant - The locus participant object.
189
199
  * @returns {Boolean}
190
200
  */
191
201
  MemberUtil.isHandRaised = (participant: any) => {
@@ -197,7 +207,7 @@ MemberUtil.isHandRaised = (participant: any) => {
197
207
  };
198
208
 
199
209
  /**
200
- * @param {Object} participant the locus participant
210
+ * @param {Object} participant - The locus participant object.
201
211
  * @returns {Boolean}
202
212
  */
203
213
  MemberUtil.isBreakoutsSupported = (participant) => {
@@ -209,7 +219,7 @@ MemberUtil.isBreakoutsSupported = (participant) => {
209
219
  };
210
220
 
211
221
  /**
212
- * @param {Object} participant the locus participant
222
+ * @param {Object} participant - The locus participant object.
213
223
  * @returns {Boolean}
214
224
  */
215
225
  MemberUtil.isInterpretationSupported = (participant) => {
@@ -223,7 +233,7 @@ MemberUtil.isInterpretationSupported = (participant) => {
223
233
  };
224
234
 
225
235
  /**
226
- * @param {Object} participant the locus participant
236
+ * @param {Object} participant - The locus participant object.
227
237
  * @returns {Boolean}
228
238
  */
229
239
  MemberUtil.isLiveAnnotationSupported = (participant) => {
@@ -279,7 +289,7 @@ MemberUtil.getRecordingMember = (controls: any) => {
279
289
  };
280
290
 
281
291
  /**
282
- * @param {Object} participant the locus participant
292
+ * @param {Object} participant - The locus participant object.
283
293
  * @returns {Boolean}
284
294
  */
285
295
  MemberUtil.isRecording = (participant: any) => {
@@ -325,7 +335,7 @@ MemberUtil.isMutable = (isSelf, isDevice, isInMeeting, isMuted, type) => {
325
335
  };
326
336
 
327
337
  /**
328
- * @param {Object} participant the locus participant
338
+ * @param {Object} participant - The locus participant object.
329
339
  * @returns {String}
330
340
  */
331
341
  MemberUtil.extractStatus = (participant: any) => {
@@ -355,7 +365,7 @@ MemberUtil.extractStatus = (participant: any) => {
355
365
  };
356
366
 
357
367
  /**
358
- * @param {Object} participant the locus participant
368
+ * @param {Object} participant - The locus participant object.
359
369
  * @returns {String}
360
370
  */
361
371
  MemberUtil.extractId = (participant: any) => {
@@ -368,7 +378,7 @@ MemberUtil.extractId = (participant: any) => {
368
378
 
369
379
  /**
370
380
  * extracts the media status from nested participant object
371
- * @param {Object} participant the locus participant
381
+ * @param {Object} participant - The locus participant object.
372
382
  * @returns {Object}
373
383
  */
374
384
  MemberUtil.extractMediaStatus = (participant: any): IMediaStatus => {
@@ -383,7 +393,7 @@ MemberUtil.extractMediaStatus = (participant: any): IMediaStatus => {
383
393
  };
384
394
 
385
395
  /**
386
- * @param {Object} participant the locus participant
396
+ * @param {Object} participant - The locus participant object.
387
397
  * @returns {String}
388
398
  */
389
399
  MemberUtil.extractName = (participant: any) => {
@@ -70,9 +70,10 @@ const BEHAVIORAL_METRICS = {
70
70
  ROAP_HTTP_RESPONSE_MISSING: 'js_sdk_roap_http_response_missing',
71
71
  TURN_DISCOVERY_REQUIRES_OK: 'js_sdk_turn_discovery_requires_ok',
72
72
  REACHABILITY_COMPLETED: 'js_sdk_reachability_completed',
73
- WEBINAR_REGISTRATION_ERROR: 'js_sdk_webinar_registration_error',
73
+ JOIN_WEBINAR_ERROR: 'js_sdk_join_webinar_error',
74
74
  GUEST_ENTERED_LOBBY: 'js_sdk_guest_entered_lobby',
75
75
  GUEST_EXITED_LOBBY: 'js_sdk_guest_exited_lobby',
76
+ JOIN_FORBIDDEN_ERROR: 'js_sdk_join_forbidden_error',
76
77
  };
77
78
 
78
79
  export {BEHAVIORAL_METRICS as default};
@@ -19,6 +19,14 @@ export type RemoteVideoResolution =
19
19
  | 'large' // 1080p or less
20
20
  | 'best'; // highest possible resolution
21
21
 
22
+ const MAX_FS_VALUES = {
23
+ '90p': 60,
24
+ '180p': 240,
25
+ '360p': 920,
26
+ '720p': 3600,
27
+ '1080p': 8192,
28
+ };
29
+
22
30
  /**
23
31
  * Converts pane size into h264 maxFs
24
32
  * @param {PaneSize} paneSize
@@ -29,28 +37,28 @@ export function getMaxFs(paneSize: RemoteVideoResolution): number {
29
37
 
30
38
  switch (paneSize) {
31
39
  case 'thumbnail':
32
- maxFs = 60;
40
+ maxFs = MAX_FS_VALUES['90p'];
33
41
  break;
34
42
  case 'very small':
35
- maxFs = 240;
43
+ maxFs = MAX_FS_VALUES['180p'];
36
44
  break;
37
45
  case 'small':
38
- maxFs = 920;
46
+ maxFs = MAX_FS_VALUES['360p'];
39
47
  break;
40
48
  case 'medium':
41
- maxFs = 3600;
49
+ maxFs = MAX_FS_VALUES['720p'];
42
50
  break;
43
51
  case 'large':
44
- maxFs = 8192;
52
+ maxFs = MAX_FS_VALUES['1080p'];
45
53
  break;
46
54
  case 'best':
47
- maxFs = 8192; // for now 'best' is 1080p, so same as 'large'
55
+ maxFs = MAX_FS_VALUES['1080p']; // for now 'best' is 1080p, so same as 'large'
48
56
  break;
49
57
  default:
50
58
  LoggerProxy.logger.warn(
51
59
  `RemoteMedia#getMaxFs --> unsupported paneSize: ${paneSize}, using "medium" instead`
52
60
  );
53
- maxFs = 3600;
61
+ maxFs = MAX_FS_VALUES['720p'];
54
62
  }
55
63
 
56
64
  return maxFs;
@@ -117,16 +125,21 @@ export class RemoteMedia extends EventsScope {
117
125
  return;
118
126
  }
119
127
 
120
- if (height < 135) {
121
- fs = 60;
122
- } else if (height < 270) {
123
- fs = 240;
124
- } else if (height < 540) {
125
- fs = 920;
128
+ // we switch to the next resolution level when the height is 10% more than the current resolution height
129
+ // except for 1080p - we switch to it immediately when the height is more than 720p
130
+ const threshold = 1.1;
131
+ const getThresholdHeight = (h: number) => Math.round(h * threshold);
132
+
133
+ if (height < getThresholdHeight(90)) {
134
+ fs = MAX_FS_VALUES['90p'];
135
+ } else if (height < getThresholdHeight(180)) {
136
+ fs = MAX_FS_VALUES['180p'];
137
+ } else if (height < getThresholdHeight(360)) {
138
+ fs = MAX_FS_VALUES['360p'];
126
139
  } else if (height <= 720) {
127
- fs = 3600;
140
+ fs = MAX_FS_VALUES['720p'];
128
141
  } else {
129
- fs = 8192;
142
+ fs = MAX_FS_VALUES['1080p'];
130
143
  }
131
144
 
132
145
  this.receiveSlot?.setMaxFs(fs);
@@ -4,6 +4,7 @@ import {
4
4
  LocalStream,
5
5
  MultistreamRoapMediaConnection,
6
6
  NamedMediaGroup,
7
+ StreamState,
7
8
  } from '@webex/internal-media-core';
8
9
 
9
10
  export default class SendSlotManager {
@@ -83,6 +84,36 @@ export default class SendSlotManager {
83
84
  );
84
85
  }
85
86
 
87
+ /**
88
+ * Sets the source state override for the given media type.
89
+ * @param {MediaType} mediaType - The type of media (must be MediaType.VideoMain to apply source state changes).
90
+ * @param {StreamState | null} state - The state to set or null to clear the override value.
91
+ * @returns {void}
92
+ */
93
+ public setSourceStateOverride(mediaType: MediaType, state: StreamState | null) {
94
+ if (mediaType !== MediaType.VideoMain) {
95
+ throw new Error(
96
+ `sendSlotManager cannot set source state override which media type is ${mediaType}`
97
+ );
98
+ }
99
+
100
+ const slot = this.slots.get(mediaType);
101
+
102
+ if (!slot) {
103
+ throw new Error(`Slot for ${mediaType} does not exist`);
104
+ }
105
+
106
+ if (state) {
107
+ slot.setSourceStateOverride(state);
108
+ } else {
109
+ slot.clearSourceStateOverride();
110
+ }
111
+
112
+ this.LoggerProxy.logger.info(
113
+ `SendSlotsManager->setSourceStateOverride#set source state override for ${mediaType} to ${state}`
114
+ );
115
+ }
116
+
86
117
  /**
87
118
  * This method publishes the given stream to the sendSlot for the given mediaType
88
119
  * @param {MediaType} mediaType MediaType of the sendSlot to which a stream needs to be published (AUDIO_MAIN/VIDEO_MAIN/AUDIO_SLIDES/VIDEO_SLIDES)
@@ -259,6 +259,32 @@ export default class Reachability extends EventsScope {
259
259
  }
260
260
  }
261
261
 
262
+ /**
263
+ * Stops all reachability checks that are in progress
264
+ * @public
265
+ * @memberof Reachability
266
+ * @returns {void}
267
+ */
268
+ public stopReachability() {
269
+ // overallTimer is always there only if there is reachability in progress
270
+ if (this.overallTimer) {
271
+ LoggerProxy.logger.log(
272
+ 'Reachability:index#stopReachability --> stopping reachability checks'
273
+ );
274
+ this.abortCurrentChecks();
275
+ this.emit(
276
+ {
277
+ file: 'reachability',
278
+ function: 'stopReachability',
279
+ },
280
+ 'reachability:stopped',
281
+ {}
282
+ );
283
+ this.sendMetric(true);
284
+ this.resolveReachabilityPromise();
285
+ }
286
+ }
287
+
262
288
  /**
263
289
  * Returns statistics about last reachability results. The returned value is an object
264
290
  * with a flat list of properties so that it can be easily sent with metrics
@@ -637,9 +663,10 @@ export default class Reachability extends EventsScope {
637
663
  /**
638
664
  * Sends a metric with all the statistics about how long reachability took
639
665
  *
666
+ * @param {boolean} aborted true if the reachability checks were aborted
640
667
  * @returns {void}
641
668
  */
642
- protected async sendMetric() {
669
+ protected async sendMetric(aborted = false) {
643
670
  const results = [];
644
671
 
645
672
  Object.values(this.clusterReachability).forEach((clusterReachability) => {
@@ -650,6 +677,7 @@ export default class Reachability extends EventsScope {
650
677
  });
651
678
 
652
679
  const stats = {
680
+ aborted,
653
681
  vmn: {
654
682
  udp: this.getStatistics(results, 'udp', true),
655
683
  },
package/src/roap/index.ts CHANGED
@@ -231,14 +231,16 @@ export default class Roap extends StatelessWebexPlugin {
231
231
  headers,
232
232
  } = remoteSdp.roapMessage;
233
233
 
234
- roapAnswer = {
235
- seq: answerSeq,
236
- messageType,
237
- sdp: sdps[0],
238
- errorType,
239
- errorCause,
240
- headers,
241
- };
234
+ if (messageType === ROAP.ROAP_TYPES.ANSWER) {
235
+ roapAnswer = {
236
+ seq: answerSeq,
237
+ messageType,
238
+ sdp: sdps[0],
239
+ errorType,
240
+ errorCause,
241
+ headers,
242
+ };
243
+ }
242
244
  }
243
245
  }
244
246
 
@@ -1,9 +1,10 @@
1
1
  /*!
2
2
  * Copyright (c) 2015-2023 Cisco Systems, Inc. See LICENSE file.
3
3
  */
4
- import {WebexPlugin} from '@webex/webex-core';
4
+ import {WebexPlugin, config} from '@webex/webex-core';
5
+ import uuid from 'uuid';
5
6
  import {get} from 'lodash';
6
- import {HTTP_VERBS, MEETINGS, SELF_ROLES} from '../constants';
7
+ import {_ID_, HEADERS, HTTP_VERBS, MEETINGS, SELF_ROLES, SHARE_STATUS} from '../constants';
7
8
 
8
9
  import WebinarCollection from './collection';
9
10
  import LoggerProxy from '../common/logs/logger-proxy';
@@ -24,6 +25,7 @@ const Webinar = WebexPlugin.extend({
24
25
  selfIsPanelist: 'boolean', // self is panelist
25
26
  selfIsAttendee: 'boolean', // self is attendee
26
27
  practiceSessionEnabled: 'boolean', // practice session enabled
28
+ meetingId: 'string',
27
29
  },
28
30
 
29
31
  /**
@@ -67,14 +69,47 @@ const Webinar = WebexPlugin.extend({
67
69
  const isPromoted =
68
70
  oldRoles.includes(SELF_ROLES.ATTENDEE) && newRoles.includes(SELF_ROLES.PANELIST);
69
71
  const isDemoted =
70
- oldRoles.includes(SELF_ROLES.PANELIST) && newRoles.includes(SELF_ROLES.ATTENDEE);
72
+ (oldRoles.includes(SELF_ROLES.PANELIST) && newRoles.includes(SELF_ROLES.ATTENDEE)) ||
73
+ (!oldRoles.includes(SELF_ROLES.ATTENDEE) && newRoles.includes(SELF_ROLES.ATTENDEE)); // for attendee just join meeting case
71
74
  this.set('selfIsPanelist', newRoles.includes(SELF_ROLES.PANELIST));
72
75
  this.set('selfIsAttendee', newRoles.includes(SELF_ROLES.ATTENDEE));
73
76
  this.updateCanManageWebcast(newRoles.includes(SELF_ROLES.MODERATOR));
77
+ this.updateStatusByRole({isPromoted, isDemoted});
74
78
 
75
79
  return {isPromoted, isDemoted};
76
80
  },
77
81
 
82
+ /**
83
+ * should join practice session data channel or not
84
+ * @param {Object} {isPromoted: boolean, isDemoted: boolean}} Role transition states
85
+ * @returns {void}
86
+ */
87
+ updateStatusByRole({isPromoted, isDemoted}) {
88
+ const meeting = this.webex.meetings.getMeetingByType(_ID_, this.meetingId);
89
+
90
+ if (
91
+ (isDemoted && meeting?.shareStatus === SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE) ||
92
+ isPromoted
93
+ ) {
94
+ // attendees in webinar should subscribe streaming for whiteboard sharing
95
+ // while panelist still need subscribe native mode so trigger force update here
96
+ meeting?.locusInfo?.updateMediaShares(meeting?.locusInfo?.mediaShares, true);
97
+ }
98
+
99
+ if (this.practiceSessionEnabled) {
100
+ // may need change data channel in practice session
101
+ meeting?.updateLLMConnection();
102
+ }
103
+ },
104
+
105
+ /**
106
+ * should join practice session data channel or not
107
+ * @returns {boolean}
108
+ */
109
+ isJoinPracticeSessionDataChannel() {
110
+ return this.selfIsPanelist && this.practiceSessionEnabled;
111
+ },
112
+
78
113
  /**
79
114
  * start or stop practice session for webinar
80
115
  * @param {boolean} enabled
@@ -103,6 +138,165 @@ const Webinar = WebexPlugin.extend({
103
138
  updatePracticeSessionStatus(payload) {
104
139
  this.set('practiceSessionEnabled', payload.enabled);
105
140
  },
141
+
142
+ /**
143
+ * start webcast mode for webinar
144
+ * @param {object} meeting
145
+ * @param {object} layout
146
+ * @returns {Promise}
147
+ */
148
+ async startWebcast(meeting, layout) {
149
+ if (!meeting) {
150
+ LoggerProxy.logger.error(
151
+ `Meeting:webinar#startWebcast failed --> meeting parameter : ${meeting}`
152
+ );
153
+ throw new Error('Meeting parameter does not meet expectations');
154
+ }
155
+
156
+ return this.request({
157
+ method: HTTP_VERBS.PUT,
158
+ uri: `${this.webcastInstanceUrl}/streaming`,
159
+ headers: {
160
+ authorization: await this.webex.credentials.getUserToken(),
161
+ trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
162
+ [HEADERS.CONTENT_TYPE]: HEADERS.CONTENT_TYPE_VALUE.APPLICATION_JSON,
163
+ },
164
+ body: {
165
+ action: 'start',
166
+ meetingInfo: {
167
+ locusId: meeting.locusId,
168
+ correlationId: meeting.correlationId,
169
+ },
170
+ layout,
171
+ },
172
+ }).catch((error) => {
173
+ LoggerProxy.logger.error('Meeting:webinar#startWebcast failed', error);
174
+ throw error;
175
+ });
176
+ },
177
+
178
+ /**
179
+ * stop webcast mode for webinar
180
+ * @returns {Promise}
181
+ */
182
+ async stopWebcast() {
183
+ return this.request({
184
+ method: HTTP_VERBS.PUT,
185
+ uri: `${this.webcastInstanceUrl}/streaming`,
186
+ headers: {
187
+ authorization: await this.webex.credentials.getUserToken(),
188
+ trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
189
+ [HEADERS.CONTENT_TYPE]: HEADERS.CONTENT_TYPE_VALUE.APPLICATION_JSON,
190
+ },
191
+ body: {
192
+ action: 'stop',
193
+ },
194
+ }).catch((error) => {
195
+ LoggerProxy.logger.error('Meeting:webinar#stopWebcast failed', error);
196
+ throw error;
197
+ });
198
+ },
199
+
200
+ /**
201
+ * query webcast layout for webinar
202
+ * @returns {Promise}
203
+ */
204
+ async queryWebcastLayout() {
205
+ return this.request({
206
+ method: HTTP_VERBS.GET,
207
+ uri: `${this.webcastInstanceUrl}/layout`,
208
+ headers: {
209
+ authorization: await this.webex.credentials.getUserToken(),
210
+ trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
211
+ },
212
+ }).catch((error) => {
213
+ LoggerProxy.logger.error('Meeting:webinar#queryWebcastLayout failed', error);
214
+ throw error;
215
+ });
216
+ },
217
+
218
+ /**
219
+ * update webcast layout for webinar
220
+ * @param {object} layout
221
+ * @returns {Promise}
222
+ */
223
+ async updateWebcastLayout(layout) {
224
+ return this.request({
225
+ method: HTTP_VERBS.PUT,
226
+ uri: `${this.webcastInstanceUrl}/layout`,
227
+ headers: {
228
+ authorization: await this.webex.credentials.getUserToken(),
229
+ trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
230
+ [HEADERS.CONTENT_TYPE]: HEADERS.CONTENT_TYPE_VALUE.APPLICATION_JSON,
231
+ },
232
+ body: {
233
+ videoLayout: layout.videoLayout,
234
+ contentLayout: layout.contentLayout,
235
+ syncStageLayout: layout.syncStageLayout,
236
+ syncStageInMeeting: layout.syncStageInMeeting,
237
+ },
238
+ }).catch((error) => {
239
+ LoggerProxy.logger.error('Meeting:webinar#updateWebcastLayout failed', error);
240
+ throw error;
241
+ });
242
+ },
243
+
244
+ /**
245
+ * view all webcast attendees
246
+ * @param {string} queryString
247
+ * @returns {Promise}
248
+ */
249
+ async viewAllWebcastAttendees() {
250
+ return this.request({
251
+ method: HTTP_VERBS.GET,
252
+ uri: `${this.webcastInstanceUrl}/attendees`,
253
+ headers: {
254
+ authorization: await this.webex.credentials.getUserToken(),
255
+ trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
256
+ },
257
+ }).catch((error) => {
258
+ LoggerProxy.logger.error('Meeting:webinar#viewAllWebcastAttendees failed', error);
259
+ throw error;
260
+ });
261
+ },
262
+
263
+ /**
264
+ * search webcast attendees by query string
265
+ * @param {string} queryString
266
+ * @returns {Promise}
267
+ */
268
+ async searchWebcastAttendees(queryString = '') {
269
+ return this.request({
270
+ method: HTTP_VERBS.GET,
271
+ uri: `${this.webcastInstanceUrl}/attendees?keyword=${encodeURIComponent(queryString)}`,
272
+ headers: {
273
+ authorization: await this.webex.credentials.getUserToken(),
274
+ trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
275
+ },
276
+ }).catch((error) => {
277
+ LoggerProxy.logger.error('Meeting:webinar#searchWebcastAttendees failed', error);
278
+ throw error;
279
+ });
280
+ },
281
+
282
+ /**
283
+ * expel webcast attendee by participantId
284
+ * @param {string} participantId
285
+ * @returns {Promise}
286
+ */
287
+ async expelWebcastAttendee(participantId) {
288
+ return this.request({
289
+ method: HTTP_VERBS.DELETE,
290
+ uri: `${this.webcastInstanceUrl}/attendees/${participantId}`,
291
+ headers: {
292
+ authorization: await this.webex.credentials.getUserToken(),
293
+ trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
294
+ },
295
+ }).catch((error) => {
296
+ LoggerProxy.logger.error('Meeting:webinar#expelWebcastAttendee failed', error);
297
+ throw error;
298
+ });
299
+ },
106
300
  });
107
301
 
108
302
  export default Webinar;