@webex/plugin-meetings 3.9.0 → 3.10.0-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +9 -0
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/index.js +22 -5
- package/dist/controls-options-manager/index.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/interceptors/index.js +7 -0
- package/dist/interceptors/index.js.map +1 -1
- package/dist/interceptors/locusRouteToken.js +116 -0
- package/dist/interceptors/locusRouteToken.js.map +1 -0
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +11 -2
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +56 -14
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/parser.js +4 -1
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/media/index.js +5 -0
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +53 -5
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +8 -0
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +339 -185
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +2 -5
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +177 -14
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +39 -11
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +29 -21
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +31 -25
- package/dist/meetings/index.js.map +1 -1
- package/dist/member/index.js +9 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js.map +1 -1
- package/dist/member/util.js +10 -0
- package/dist/member/util.js.map +1 -1
- package/dist/members/collection.js +13 -0
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +42 -20
- package/dist/members/index.js.map +1 -1
- package/dist/members/util.js +7 -2
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reachability/index.js +3 -3
- package/dist/reachability/index.js.map +1 -1
- package/dist/types/constants.d.ts +7 -0
- package/dist/types/controls-options-manager/index.d.ts +9 -1
- package/dist/types/interceptors/index.d.ts +2 -1
- package/dist/types/interceptors/locusRouteToken.d.ts +38 -0
- package/dist/types/locus-info/index.d.ts +56 -2
- package/dist/types/media/properties.d.ts +21 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +8 -0
- package/dist/types/meeting/index.d.ts +41 -1
- package/dist/types/meeting/request.d.ts +42 -0
- package/dist/types/meeting/util.d.ts +13 -3
- package/dist/types/meeting-info/meeting-info-v2.d.ts +6 -3
- package/dist/types/meetings/index.d.ts +3 -1
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/types.d.ts +1 -0
- package/dist/types/member/util.d.ts +5 -0
- package/dist/types/members/collection.d.ts +6 -0
- package/dist/types/members/index.d.ts +12 -2
- package/dist/types/members/util.d.ts +6 -3
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +24 -24
- package/src/constants.ts +10 -0
- package/src/controls-options-manager/index.ts +26 -5
- package/src/index.ts +2 -1
- package/src/interceptors/index.ts +2 -1
- package/src/interceptors/locusRouteToken.ts +80 -0
- package/src/locus-info/controlsUtils.ts +18 -0
- package/src/locus-info/index.ts +99 -17
- package/src/locus-info/parser.ts +5 -1
- package/src/media/index.ts +6 -0
- package/src/media/properties.ts +43 -0
- package/src/meeting/in-meeting-actions.ts +16 -0
- package/src/meeting/index.ts +205 -24
- package/src/meeting/muteState.ts +2 -6
- package/src/meeting/request.ts +141 -0
- package/src/meeting/util.ts +50 -20
- package/src/meeting-info/meeting-info-v2.ts +24 -5
- package/src/meetings/index.ts +9 -3
- package/src/member/index.ts +10 -0
- package/src/member/types.ts +1 -0
- package/src/member/util.ts +14 -0
- package/src/members/collection.ts +11 -0
- package/src/members/index.ts +38 -5
- package/src/members/util.ts +18 -2
- package/src/metrics/constants.ts +1 -0
- package/src/reachability/index.ts +3 -3
- package/test/unit/spec/common/browser-detection.js +0 -24
- package/test/unit/spec/controls-options-manager/index.js +47 -0
- package/test/unit/spec/fixture/locus.js +1 -0
- package/test/unit/spec/interceptors/locusRouteToken.ts +87 -0
- package/test/unit/spec/locus-info/index.js +91 -15
- package/test/unit/spec/locus-info/parser.js +3 -2
- package/test/unit/spec/media/index.ts +140 -9
- package/test/unit/spec/media/properties.ts +137 -0
- package/test/unit/spec/meeting/in-meeting-actions.ts +8 -0
- package/test/unit/spec/meeting/index.js +398 -30
- package/test/unit/spec/meeting/muteState.js +32 -6
- package/test/unit/spec/meeting/request.js +21 -0
- package/test/unit/spec/meeting/utils.js +48 -16
- package/test/unit/spec/meeting-info/meetinginfov2.js +8 -3
- package/test/unit/spec/meetings/index.js +10 -7
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/members/collection.js +120 -0
- package/test/unit/spec/members/index.js +72 -3
- package/test/unit/spec/members/request.js +55 -0
- package/test/unit/spec/members/utils.js +116 -14
- package/test/unit/spec/reachability/index.ts +158 -3
- package/test/unit/spec/roap/turnDiscovery.ts +3 -3
package/src/meeting/index.ts
CHANGED
|
@@ -28,6 +28,9 @@ import {
|
|
|
28
28
|
StatsAnalyzerEventNames,
|
|
29
29
|
NetworkQualityEventNames,
|
|
30
30
|
NetworkQualityMonitor,
|
|
31
|
+
StatsMonitor,
|
|
32
|
+
StatsMonitorEventNames,
|
|
33
|
+
InboundAudioIssueSubTypes,
|
|
31
34
|
} from '@webex/internal-media-core';
|
|
32
35
|
|
|
33
36
|
import {
|
|
@@ -269,6 +272,7 @@ export enum ScreenShareFloorStatus {
|
|
|
269
272
|
type FetchMeetingInfoParams = {
|
|
270
273
|
password?: string;
|
|
271
274
|
registrationId?: string;
|
|
275
|
+
classificationId?: string;
|
|
272
276
|
captchaCode?: string;
|
|
273
277
|
extraParams?: Record<string, any>;
|
|
274
278
|
sendCAevents?: boolean;
|
|
@@ -633,6 +637,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
633
637
|
shareStatus: string;
|
|
634
638
|
screenShareFloorState: ScreenShareFloorStatus;
|
|
635
639
|
statsAnalyzer: StatsAnalyzer;
|
|
640
|
+
statsMonitor: StatsMonitor;
|
|
636
641
|
transcription: Transcription;
|
|
637
642
|
updateMediaConnections: (mediaConnections: any[]) => void;
|
|
638
643
|
userDisplayHints: any;
|
|
@@ -1286,6 +1291,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1286
1291
|
* @memberof Meeting
|
|
1287
1292
|
*/
|
|
1288
1293
|
this.networkQualityMonitor = null;
|
|
1294
|
+
/**
|
|
1295
|
+
* @instance
|
|
1296
|
+
* @type {StatsMonitor}
|
|
1297
|
+
* @private
|
|
1298
|
+
* @memberof Meeting
|
|
1299
|
+
*/
|
|
1300
|
+
this.statsMonitor = null;
|
|
1289
1301
|
/**
|
|
1290
1302
|
* Indicates network status of the webrtc media connection
|
|
1291
1303
|
* @instance
|
|
@@ -1902,6 +1914,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1902
1914
|
extraParams = {},
|
|
1903
1915
|
sendCAevents = false,
|
|
1904
1916
|
registrationId = null,
|
|
1917
|
+
classificationId = null,
|
|
1905
1918
|
}): Promise<void> {
|
|
1906
1919
|
try {
|
|
1907
1920
|
const captchaInfo = captchaCode
|
|
@@ -1918,7 +1931,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1918
1931
|
this.locusId,
|
|
1919
1932
|
extraParams,
|
|
1920
1933
|
{meetingId: this.id, sendCAevents},
|
|
1921
|
-
registrationId
|
|
1934
|
+
registrationId,
|
|
1935
|
+
null,
|
|
1936
|
+
classificationId
|
|
1922
1937
|
);
|
|
1923
1938
|
|
|
1924
1939
|
this.parseMeetingInfo(info?.body, this.destination, info?.errors);
|
|
@@ -2962,6 +2977,18 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2962
2977
|
);
|
|
2963
2978
|
});
|
|
2964
2979
|
|
|
2980
|
+
this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_AUTO_END_MEETING_WARNING_CHANGED, ({state}) => {
|
|
2981
|
+
Trigger.trigger(
|
|
2982
|
+
this,
|
|
2983
|
+
{
|
|
2984
|
+
file: 'meeting/index',
|
|
2985
|
+
function: 'setupLocusControlsListener',
|
|
2986
|
+
},
|
|
2987
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_AUTO_END_MEETING_WARNING_UPDATED,
|
|
2988
|
+
{state}
|
|
2989
|
+
);
|
|
2990
|
+
});
|
|
2991
|
+
|
|
2965
2992
|
this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_ANNOTATION_CHANGED, ({state}) => {
|
|
2966
2993
|
Trigger.trigger(
|
|
2967
2994
|
this,
|
|
@@ -3149,6 +3176,23 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3149
3176
|
},
|
|
3150
3177
|
EVENT_TRIGGERS.MEETING_STOPPED_SHARING_WHITEBOARD
|
|
3151
3178
|
);
|
|
3179
|
+
// @ts-ignore
|
|
3180
|
+
this.webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp({
|
|
3181
|
+
key: 'internal.client.share.stopped',
|
|
3182
|
+
});
|
|
3183
|
+
// @ts-ignore
|
|
3184
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
3185
|
+
name: 'client.share.stopped',
|
|
3186
|
+
payload: {
|
|
3187
|
+
mediaType: 'whiteboard',
|
|
3188
|
+
shareDuration:
|
|
3189
|
+
// @ts-ignore
|
|
3190
|
+
this.webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration(),
|
|
3191
|
+
},
|
|
3192
|
+
options: {
|
|
3193
|
+
meetingId: this.id,
|
|
3194
|
+
},
|
|
3195
|
+
});
|
|
3152
3196
|
break;
|
|
3153
3197
|
|
|
3154
3198
|
case SHARE_STATUS.NO_SHARE:
|
|
@@ -3167,6 +3211,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3167
3211
|
this.shareCAEventSentStatus.receiveStart = false;
|
|
3168
3212
|
this.shareCAEventSentStatus.receiveStop = false;
|
|
3169
3213
|
|
|
3214
|
+
let finalBeneficiaryId = contentShare.beneficiaryId;
|
|
3215
|
+
// In case of attendee in webinar, the whiteboard is shared by other participants
|
|
3216
|
+
if (this.locusInfo?.info?.isWebinar && this.webinar?.selfIsAttendee) {
|
|
3217
|
+
if (!finalBeneficiaryId && whiteboardShare.beneficiaryId) {
|
|
3218
|
+
finalBeneficiaryId = whiteboardShare.beneficiaryId;
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3170
3222
|
Trigger.trigger(
|
|
3171
3223
|
this,
|
|
3172
3224
|
{
|
|
@@ -3175,7 +3227,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3175
3227
|
},
|
|
3176
3228
|
EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
|
3177
3229
|
{
|
|
3178
|
-
memberId:
|
|
3230
|
+
memberId: finalBeneficiaryId,
|
|
3179
3231
|
url: contentShare.url,
|
|
3180
3232
|
shareInstanceId: this.remoteShareInstanceId,
|
|
3181
3233
|
annotationInfo: contentShare.annotation,
|
|
@@ -3317,27 +3369,31 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3317
3369
|
* @memberof Meeting
|
|
3318
3370
|
*/
|
|
3319
3371
|
private setUpLocusUrlListener() {
|
|
3320
|
-
this.locusInfo.on(
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3372
|
+
this.locusInfo.on(
|
|
3373
|
+
EVENTS.LOCUS_INFO_UPDATE_URL,
|
|
3374
|
+
(payload: {url: string; isMainLocus?: boolean}) => {
|
|
3375
|
+
const {url, isMainLocus} = payload;
|
|
3376
|
+
this.members.locusUrlUpdate(url);
|
|
3377
|
+
this.breakouts.locusUrlUpdate(url);
|
|
3378
|
+
this.simultaneousInterpretation.locusUrlUpdate(url);
|
|
3379
|
+
this.annotation.locusUrlUpdate(url);
|
|
3380
|
+
this.locusUrl = url;
|
|
3381
|
+
this.locusId = this.locusUrl?.split('/').pop();
|
|
3382
|
+
this.recordingController.setLocusUrl(this.locusUrl);
|
|
3383
|
+
this.controlsOptionsManager.setLocusUrl(this.locusUrl, !!isMainLocus);
|
|
3384
|
+
this.webinar.locusUrlUpdate(url);
|
|
3330
3385
|
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3386
|
+
Trigger.trigger(
|
|
3387
|
+
this,
|
|
3388
|
+
{
|
|
3389
|
+
file: 'meeting/index',
|
|
3390
|
+
function: 'setUpLocusSelfListener',
|
|
3391
|
+
},
|
|
3392
|
+
EVENT_TRIGGERS.MEETING_LOCUS_URL_UPDATE,
|
|
3393
|
+
{locusUrl: url}
|
|
3394
|
+
);
|
|
3395
|
+
}
|
|
3396
|
+
);
|
|
3341
3397
|
}
|
|
3342
3398
|
|
|
3343
3399
|
/**
|
|
@@ -4180,6 +4236,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4180
4236
|
this.userDisplayHints,
|
|
4181
4237
|
this.selfUserPolicies
|
|
4182
4238
|
),
|
|
4239
|
+
showAutoEndMeetingWarning: MeetingUtil.showAutoEndMeetingWarning(this.userDisplayHints),
|
|
4183
4240
|
canRaiseHand: MeetingUtil.canUserRaiseHand(this.userDisplayHints),
|
|
4184
4241
|
canLowerAllHands: MeetingUtil.canUserLowerAllHands(this.userDisplayHints),
|
|
4185
4242
|
canLowerSomeoneElsesHand: MeetingUtil.canUserLowerSomeoneElsesHand(this.userDisplayHints),
|
|
@@ -4195,8 +4252,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4195
4252
|
isLocalRecordingStarted: MeetingUtil.isLocalRecordingStarted(this.userDisplayHints),
|
|
4196
4253
|
isLocalRecordingStopped: MeetingUtil.isLocalRecordingStopped(this.userDisplayHints),
|
|
4197
4254
|
isLocalRecordingPaused: MeetingUtil.isLocalRecordingPaused(this.userDisplayHints),
|
|
4255
|
+
isLocalStreamingStarted: MeetingUtil.isLocalStreamingStarted(this.userDisplayHints),
|
|
4256
|
+
isLocalStreamingStopped: MeetingUtil.isLocalStreamingStopped(this.userDisplayHints),
|
|
4198
4257
|
isManualCaptionActive: MeetingUtil.isManualCaptionActive(this.userDisplayHints),
|
|
4199
4258
|
isSaveTranscriptsEnabled: MeetingUtil.isSaveTranscriptsEnabled(this.userDisplayHints),
|
|
4259
|
+
isSpokenLanguageAutoDetectionEnabled: MeetingUtil.isSpokenLanguageAutoDetectionEnabled(
|
|
4260
|
+
this.userDisplayHints
|
|
4261
|
+
),
|
|
4200
4262
|
isWebexAssistantActive: MeetingUtil.isWebexAssistantActive(this.userDisplayHints),
|
|
4201
4263
|
canViewCaptionPanel: MeetingUtil.canViewCaptionPanel(this.userDisplayHints),
|
|
4202
4264
|
isRealTimeTranslationEnabled: MeetingUtil.isRealTimeTranslationEnabled(
|
|
@@ -6779,6 +6841,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6779
6841
|
// @ts-ignore
|
|
6780
6842
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
6781
6843
|
name: 'client.ice.start',
|
|
6844
|
+
payload: {
|
|
6845
|
+
// @ts-ignore
|
|
6846
|
+
labels: MeetingUtil.getCaEventLabelsForIpVersion(this.webex),
|
|
6847
|
+
},
|
|
6782
6848
|
options: {
|
|
6783
6849
|
meetingId: this.id,
|
|
6784
6850
|
},
|
|
@@ -6948,10 +7014,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6948
7014
|
}
|
|
6949
7015
|
}
|
|
6950
7016
|
|
|
6951
|
-
// Count members that are in the meeting.
|
|
7017
|
+
// Count members that are in the meeting or in the lobby.
|
|
6952
7018
|
const {members} = this.getMembers().membersCollection;
|
|
6953
7019
|
event.data.intervalMetadata.meetingUserCount = Object.values(members).filter(
|
|
6954
|
-
(member: Member) => member.isInMeeting
|
|
7020
|
+
(member: Member) => member.isInMeeting || member.isInLobby
|
|
6955
7021
|
).length;
|
|
6956
7022
|
|
|
6957
7023
|
// @ts-ignore
|
|
@@ -7310,10 +7376,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7310
7376
|
if (this.config.stats.enableStatsAnalyzer) {
|
|
7311
7377
|
// @ts-ignore - config coming from registerPlugin
|
|
7312
7378
|
this.networkQualityMonitor = new NetworkQualityMonitor(this.config.stats);
|
|
7379
|
+
this.statsMonitor = new StatsMonitor();
|
|
7313
7380
|
this.statsAnalyzer = new StatsAnalyzer({
|
|
7314
7381
|
// @ts-ignore - config coming from registerPlugin
|
|
7315
7382
|
config: this.config.stats,
|
|
7316
7383
|
networkQualityMonitor: this.networkQualityMonitor,
|
|
7384
|
+
statsMonitor: this.statsMonitor,
|
|
7317
7385
|
isMultistream: this.isMultistream,
|
|
7318
7386
|
});
|
|
7319
7387
|
this.shareCAEventSentStatus = {
|
|
@@ -7327,6 +7395,33 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7327
7395
|
NetworkQualityEventNames.NETWORK_QUALITY,
|
|
7328
7396
|
this.sendNetworkQualityEvent.bind(this)
|
|
7329
7397
|
);
|
|
7398
|
+
|
|
7399
|
+
this.statsMonitor.on(StatsMonitorEventNames.INBOUND_AUDIO_ISSUE, (data) => {
|
|
7400
|
+
// Before forwarding any inbound audio issues to the app, make sure that we have at least one other
|
|
7401
|
+
// participant in the meeting with unmuted audio.
|
|
7402
|
+
// We don't check this.mediaProperties.mediaDirection here, because that's already handled in statsAnalyzer,
|
|
7403
|
+
// so we won't get this event if we are not setup to receive any audio
|
|
7404
|
+
const atLeastOneUnmutedOtherMember = Object.values(
|
|
7405
|
+
this.members.membersCollection.getAll()
|
|
7406
|
+
).find((member) => {
|
|
7407
|
+
return !member.isSelf && !member.isPairedWithSelf && !member.isAudioMuted;
|
|
7408
|
+
});
|
|
7409
|
+
|
|
7410
|
+
if (atLeastOneUnmutedOtherMember) {
|
|
7411
|
+
this.mediaProperties.sendMediaIssueMetric(
|
|
7412
|
+
'inbound_audio',
|
|
7413
|
+
data.issueSubType,
|
|
7414
|
+
this.correlationId
|
|
7415
|
+
);
|
|
7416
|
+
|
|
7417
|
+
Trigger.trigger(
|
|
7418
|
+
this,
|
|
7419
|
+
{file: 'meeting/index', function: 'createStatsAnalyzer'},
|
|
7420
|
+
EVENT_TRIGGERS.MEDIA_INBOUND_AUDIO_ISSUE_DETECTED,
|
|
7421
|
+
data
|
|
7422
|
+
);
|
|
7423
|
+
}
|
|
7424
|
+
});
|
|
7330
7425
|
}
|
|
7331
7426
|
}
|
|
7332
7427
|
|
|
@@ -7625,6 +7720,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7625
7720
|
}
|
|
7626
7721
|
|
|
7627
7722
|
this.statsAnalyzer = null;
|
|
7723
|
+
this.networkQualityMonitor?.removeAllListeners();
|
|
7724
|
+
this.networkQualityMonitor = null;
|
|
7725
|
+
this.statsMonitor?.removeAllListeners();
|
|
7726
|
+
this.statsMonitor = null;
|
|
7628
7727
|
|
|
7629
7728
|
// when media fails, we want to upload a webrtc dump to see whats going on
|
|
7630
7729
|
// this function is async, but returns once the stats have been gathered
|
|
@@ -7648,6 +7747,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7648
7747
|
await this.statsAnalyzer.stopAnalyzer();
|
|
7649
7748
|
}
|
|
7650
7749
|
this.statsAnalyzer = null;
|
|
7750
|
+
this.networkQualityMonitor?.removeAllListeners();
|
|
7751
|
+
this.networkQualityMonitor = null;
|
|
7752
|
+
this.statsMonitor?.removeAllListeners();
|
|
7753
|
+
this.statsMonitor = null;
|
|
7651
7754
|
|
|
7652
7755
|
this.isMultistream = false;
|
|
7653
7756
|
|
|
@@ -7819,6 +7922,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7819
7922
|
|
|
7820
7923
|
this.allowMediaInLobby = options?.allowMediaInLobby;
|
|
7821
7924
|
|
|
7925
|
+
// @ts-ignore
|
|
7926
|
+
const ipver = MeetingUtil.getIpVersion(this.webex); // used just for metrics
|
|
7927
|
+
|
|
7822
7928
|
// If the user is unjoined or guest waiting in lobby dont allow the user to addMedia
|
|
7823
7929
|
// @ts-ignore - isUserUnadmitted coming from SelfUtil
|
|
7824
7930
|
if (this.isUserUnadmitted && !this.wirelessShare && !this.allowMediaInLobby) {
|
|
@@ -7917,6 +8023,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7917
8023
|
locus_id: this.locusUrl.split('/').pop(),
|
|
7918
8024
|
connectionType,
|
|
7919
8025
|
ipVersion,
|
|
8026
|
+
ipver,
|
|
7920
8027
|
selectedCandidatePairChanges,
|
|
7921
8028
|
numTransports,
|
|
7922
8029
|
isMultistream: this.isMultistream,
|
|
@@ -7985,6 +8092,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7985
8092
|
...reachabilityMetrics,
|
|
7986
8093
|
...iceCandidateErrors,
|
|
7987
8094
|
iceCandidatesCount: this.iceCandidatesCount,
|
|
8095
|
+
ipver,
|
|
7988
8096
|
});
|
|
7989
8097
|
|
|
7990
8098
|
await this.cleanUpOnAddMediaFailure();
|
|
@@ -9361,6 +9469,36 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
9361
9469
|
return Promise.reject(new Error('Error sending reaction, service url not found.'));
|
|
9362
9470
|
}
|
|
9363
9471
|
|
|
9472
|
+
/**
|
|
9473
|
+
* Extend the current meeting duration.
|
|
9474
|
+
*
|
|
9475
|
+
* @param {number} extensionMinutes - how many minutes to extend
|
|
9476
|
+
* @returns {Promise}
|
|
9477
|
+
* @public
|
|
9478
|
+
* @memberof Meeting
|
|
9479
|
+
*/
|
|
9480
|
+
public extendMeeting({
|
|
9481
|
+
meetingPolicyUrl,
|
|
9482
|
+
meetingInstanceId,
|
|
9483
|
+
participantId,
|
|
9484
|
+
extensionMinutes = 30,
|
|
9485
|
+
}) {
|
|
9486
|
+
if (!meetingInstanceId || !participantId) {
|
|
9487
|
+
return Promise.reject(new Error('Missing meetingInstanceId or participantId'));
|
|
9488
|
+
}
|
|
9489
|
+
|
|
9490
|
+
if (!meetingPolicyUrl) {
|
|
9491
|
+
return Promise.reject(new Error('Missing meetingPolicyUrl'));
|
|
9492
|
+
}
|
|
9493
|
+
|
|
9494
|
+
return this.meetingRequest.extendMeeting({
|
|
9495
|
+
meetingInstanceId,
|
|
9496
|
+
participantId,
|
|
9497
|
+
extensionMinutes,
|
|
9498
|
+
meetingPolicyUrl,
|
|
9499
|
+
});
|
|
9500
|
+
}
|
|
9501
|
+
|
|
9364
9502
|
/**
|
|
9365
9503
|
* Method to enable or disable reactions inside the meeting.
|
|
9366
9504
|
*
|
|
@@ -9890,4 +10028,47 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
9890
10028
|
|
|
9891
10029
|
return this.meetingRequest.synchronizeStage(this.locusUrl, videoLayout);
|
|
9892
10030
|
}
|
|
10031
|
+
|
|
10032
|
+
/**
|
|
10033
|
+
* Notifies the host with the given meeting UUID and display names.
|
|
10034
|
+
*
|
|
10035
|
+
* @param {string} meetingUuid - The UUID of the meeting.
|
|
10036
|
+
* @param {string[]} displayName - An array of display names to notify the host with.
|
|
10037
|
+
* @returns {Promise<any>} The result of the notifyHost request.
|
|
10038
|
+
*/
|
|
10039
|
+
notifyHost(meetingUuid: string, displayName: string[]) {
|
|
10040
|
+
return this.meetingRequest.notifyHost(
|
|
10041
|
+
this.meetingInfo.siteFullUrl,
|
|
10042
|
+
this.locusId,
|
|
10043
|
+
meetingUuid,
|
|
10044
|
+
displayName
|
|
10045
|
+
);
|
|
10046
|
+
}
|
|
10047
|
+
|
|
10048
|
+
/**
|
|
10049
|
+
* Call out a SIP participant to a meeting
|
|
10050
|
+
* @param {string} address - The SIP address or phone number
|
|
10051
|
+
* @param {string} displayName - The display name for the participant
|
|
10052
|
+
* @param {string} [correlationId] - Optional correlation ID
|
|
10053
|
+
* @returns {Promise} Promise that resolves when the call-out is initiated
|
|
10054
|
+
*/
|
|
10055
|
+
sipCallOut(address: string, displayName: string) {
|
|
10056
|
+
return this.meetingRequest.sipCallOut(
|
|
10057
|
+
this.meetingInfo.meetingId,
|
|
10058
|
+
this.meetingInfo.meetingId,
|
|
10059
|
+
address,
|
|
10060
|
+
displayName
|
|
10061
|
+
);
|
|
10062
|
+
}
|
|
10063
|
+
|
|
10064
|
+
/**
|
|
10065
|
+
* Cancel an ongoing SIP call-out
|
|
10066
|
+
* @param {string} participantId - The participant ID to cancel
|
|
10067
|
+
* @returns {Promise} Promise that resolves when the call-out is cancelled
|
|
10068
|
+
* @public
|
|
10069
|
+
* @memberof Meetings
|
|
10070
|
+
*/
|
|
10071
|
+
cancelSipCallOut(participantId: string) {
|
|
10072
|
+
return this.meetingRequest.cancelSipCallOut(participantId);
|
|
10073
|
+
}
|
|
9893
10074
|
}
|
package/src/meeting/muteState.ts
CHANGED
|
@@ -291,18 +291,14 @@ export class MuteState {
|
|
|
291
291
|
);
|
|
292
292
|
|
|
293
293
|
return MeetingUtil.remoteUpdateAudioVideo(meeting, audioMuted, videoMuted)
|
|
294
|
-
.then((
|
|
294
|
+
.then((response) => {
|
|
295
295
|
LoggerProxy.logger.info(
|
|
296
296
|
`Meeting:muteState#sendLocalMuteRequestToServer --> ${this.type}: local mute (audio=${audioMuted}, video=${videoMuted}) applied to server`
|
|
297
297
|
);
|
|
298
298
|
|
|
299
299
|
this.state.server.localMute = this.type === AUDIO ? audioMuted : videoMuted;
|
|
300
300
|
|
|
301
|
-
|
|
302
|
-
meeting.locusInfo.handleLocusDelta(locus, meeting);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
return locus;
|
|
301
|
+
return MeetingUtil.updateLocusFromApiResponse(meeting, response);
|
|
306
302
|
})
|
|
307
303
|
.catch((remoteUpdateError) => {
|
|
308
304
|
LoggerProxy.logger.warn(
|
package/src/meeting/request.ts
CHANGED
|
@@ -886,6 +886,44 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
886
886
|
});
|
|
887
887
|
}
|
|
888
888
|
|
|
889
|
+
/**
|
|
890
|
+
* Extend the current meeting duration.
|
|
891
|
+
*
|
|
892
|
+
* @param {Object} params - Parameters for extending the meeting.
|
|
893
|
+
* @param {string} params.meetingInstanceId - The unique ID of the meeting instance.
|
|
894
|
+
* @param {string} params.participantId - The ID of the participant requesting the extension.
|
|
895
|
+
* @param {number} params.extensionMinutes - The number of minutes to extend the meeting by.
|
|
896
|
+
* @param {string} params.meetingPolicyUrl - The base URL for meeting policy service (dynamic, from locus links)
|
|
897
|
+
* @returns {Promise<any>} A promise that resolves with the server response.
|
|
898
|
+
*/
|
|
899
|
+
extendMeeting({
|
|
900
|
+
meetingInstanceId,
|
|
901
|
+
participantId,
|
|
902
|
+
extensionMinutes,
|
|
903
|
+
meetingPolicyUrl,
|
|
904
|
+
}: {
|
|
905
|
+
meetingInstanceId: string;
|
|
906
|
+
participantId: string;
|
|
907
|
+
extensionMinutes: number;
|
|
908
|
+
meetingPolicyUrl: string;
|
|
909
|
+
}) {
|
|
910
|
+
if (!meetingPolicyUrl) {
|
|
911
|
+
return Promise.reject(new Error('meetingPolicyUrl is required'));
|
|
912
|
+
}
|
|
913
|
+
const uri = `${meetingPolicyUrl}/continueMeeting`;
|
|
914
|
+
|
|
915
|
+
// @ts-ignore
|
|
916
|
+
return this.request({
|
|
917
|
+
method: HTTP_VERBS.POST,
|
|
918
|
+
uri,
|
|
919
|
+
body: {
|
|
920
|
+
meetingInstanceId,
|
|
921
|
+
requestParticipantId: participantId,
|
|
922
|
+
extensionMinutes,
|
|
923
|
+
},
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
|
|
889
927
|
/**
|
|
890
928
|
* Make a network request to enable or disable reactions.
|
|
891
929
|
* @param {boolean} options.enable - determines if we need to enable or disable.
|
|
@@ -985,4 +1023,107 @@ export default class MeetingRequest extends StatelessWebexPlugin {
|
|
|
985
1023
|
body: {videoLayout},
|
|
986
1024
|
});
|
|
987
1025
|
}
|
|
1026
|
+
|
|
1027
|
+
/**
|
|
1028
|
+
* Sends a request to notify the host of a meeting.
|
|
1029
|
+
* @param {string} siteFullUrl - The site URL.
|
|
1030
|
+
* @param {string} locusId - The locus ID.
|
|
1031
|
+
* @param {string} meetingUuid - The meeting UUID.
|
|
1032
|
+
* @param {Array<string>} displayName - The display names to notify the host about.
|
|
1033
|
+
* @returns {Promise}
|
|
1034
|
+
*/
|
|
1035
|
+
notifyHost(siteFullUrl: string, locusId: string, meetingUuid: string, displayName: string[]) {
|
|
1036
|
+
// @ts-ignore
|
|
1037
|
+
return this.request({
|
|
1038
|
+
method: HTTP_VERBS.POST,
|
|
1039
|
+
uri: `https://${siteFullUrl}/wbxappapi/v1/meetings/${meetingUuid}/notifyhost`,
|
|
1040
|
+
body: {
|
|
1041
|
+
displayName,
|
|
1042
|
+
size: displayName?.length,
|
|
1043
|
+
},
|
|
1044
|
+
headers: {
|
|
1045
|
+
locusId,
|
|
1046
|
+
},
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/**
|
|
1051
|
+
* Call out to a SIP participant
|
|
1052
|
+
*
|
|
1053
|
+
* @param {any} meetingId - The meeting ID.
|
|
1054
|
+
* @param {any} meetingNumber - The meeting number.
|
|
1055
|
+
* @param {string} address - The SIP address to call out.
|
|
1056
|
+
* @param {string} displayName - The display name for the participant.
|
|
1057
|
+
* @returns {Promise} The API response
|
|
1058
|
+
*/
|
|
1059
|
+
public async sipCallOut(meetingId, meetingNumber, address, displayName) {
|
|
1060
|
+
const body: any = {
|
|
1061
|
+
meetingId,
|
|
1062
|
+
meetingNumber,
|
|
1063
|
+
address,
|
|
1064
|
+
displayName,
|
|
1065
|
+
};
|
|
1066
|
+
try {
|
|
1067
|
+
// @ts-ignore
|
|
1068
|
+
const response = await this.request({
|
|
1069
|
+
method: HTTP_VERBS.POST,
|
|
1070
|
+
service: 'hydra',
|
|
1071
|
+
resource: 'meetingParticipants/callout',
|
|
1072
|
+
body,
|
|
1073
|
+
headers: {
|
|
1074
|
+
Accept: 'application/json',
|
|
1075
|
+
},
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
LoggerProxy.logger.info('Meetings:request#sipCallOut --> SIP call-out successful', response);
|
|
1079
|
+
|
|
1080
|
+
return response.body;
|
|
1081
|
+
} catch (err) {
|
|
1082
|
+
LoggerProxy.logger.error(
|
|
1083
|
+
`Meetings:request#sipCallOut --> Error calling out SIP participant, error ${JSON.stringify(
|
|
1084
|
+
err
|
|
1085
|
+
)}`
|
|
1086
|
+
);
|
|
1087
|
+
throw err;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/**
|
|
1092
|
+
* Cancel an ongoing SIP call-out
|
|
1093
|
+
*
|
|
1094
|
+
* @param {string} participantId - The ID of the participant whose SIP call-out should be cancelled.
|
|
1095
|
+
* @returns {Promise} The API response
|
|
1096
|
+
*/
|
|
1097
|
+
public async cancelSipCallOut(participantId) {
|
|
1098
|
+
const body = {
|
|
1099
|
+
participantId,
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
try {
|
|
1103
|
+
// @ts-ignore
|
|
1104
|
+
const response = await this.request({
|
|
1105
|
+
method: HTTP_VERBS.POST,
|
|
1106
|
+
service: 'hydra',
|
|
1107
|
+
resource: 'meetingParticipants/cancelCallout',
|
|
1108
|
+
body,
|
|
1109
|
+
headers: {
|
|
1110
|
+
Accept: 'application/json',
|
|
1111
|
+
},
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
LoggerProxy.logger.info(
|
|
1115
|
+
'Meetings:request#cancelSipCallOut --> SIP call-out cancelled successfully',
|
|
1116
|
+
response
|
|
1117
|
+
);
|
|
1118
|
+
|
|
1119
|
+
return response.body;
|
|
1120
|
+
} catch (err) {
|
|
1121
|
+
LoggerProxy.logger.error(
|
|
1122
|
+
`Meetings:request#cancelSipCallOut --> Error cancelling SIP participant call-out, error ${JSON.stringify(
|
|
1123
|
+
err
|
|
1124
|
+
)}`
|
|
1125
|
+
);
|
|
1126
|
+
throw err;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
988
1129
|
}
|
package/src/meeting/util.ts
CHANGED
|
@@ -59,18 +59,16 @@ const MeetingUtil = {
|
|
|
59
59
|
);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
return meeting.locusMediaRequest
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
})
|
|
73
|
-
.then((response) => response?.body?.locus);
|
|
62
|
+
return meeting.locusMediaRequest.send({
|
|
63
|
+
type: 'LocalMute',
|
|
64
|
+
selfUrl: meeting.selfUrl,
|
|
65
|
+
mediaId: meeting.mediaId,
|
|
66
|
+
sequence: meeting.locusInfo.sequence,
|
|
67
|
+
muteOptions: {
|
|
68
|
+
audioMuted,
|
|
69
|
+
videoMuted,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
74
72
|
},
|
|
75
73
|
|
|
76
74
|
hasOwner: (info) => info && info.owner,
|
|
@@ -115,6 +113,28 @@ const MeetingUtil = {
|
|
|
115
113
|
return IP_VERSION.unknown;
|
|
116
114
|
},
|
|
117
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Returns CA event labels related to Orpheus ipver parameter that can be sent to CA with any CA event
|
|
118
|
+
* @param {any} webex instance
|
|
119
|
+
* @returns {Array<string>|undefined} array of CA event labels or undefined if no labels should be sent
|
|
120
|
+
*/
|
|
121
|
+
getCaEventLabelsForIpVersion(webex: any): Array<string> | undefined {
|
|
122
|
+
const ipver = MeetingUtil.getIpVersion(webex);
|
|
123
|
+
|
|
124
|
+
switch (ipver) {
|
|
125
|
+
case IP_VERSION.unknown:
|
|
126
|
+
return undefined;
|
|
127
|
+
case IP_VERSION.only_ipv4:
|
|
128
|
+
return ['hasIpv4_true'];
|
|
129
|
+
case IP_VERSION.only_ipv6:
|
|
130
|
+
return ['hasIpv6_true'];
|
|
131
|
+
case IP_VERSION.ipv4_and_ipv6:
|
|
132
|
+
return ['hasIpv4_true', 'hasIpv6_true'];
|
|
133
|
+
default:
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
|
|
118
138
|
joinMeeting: async (meeting, options) => {
|
|
119
139
|
if (!meeting) {
|
|
120
140
|
return Promise.reject(new ParameterError('You need a meeting object.'));
|
|
@@ -613,11 +633,20 @@ const MeetingUtil = {
|
|
|
613
633
|
isLocalRecordingPaused: (displayHints) =>
|
|
614
634
|
displayHints.includes(DISPLAY_HINTS.LOCAL_RECORDING_STATUS_PAUSED),
|
|
615
635
|
|
|
636
|
+
isLocalStreamingStarted: (displayHints) =>
|
|
637
|
+
displayHints.includes(DISPLAY_HINTS.STREAMING_STATUS_STARTED),
|
|
638
|
+
|
|
639
|
+
isLocalStreamingStopped: (displayHints) =>
|
|
640
|
+
displayHints.includes(DISPLAY_HINTS.STREAMING_STATUS_STOPPED),
|
|
641
|
+
|
|
616
642
|
canStopManualCaption: (displayHints) => displayHints.includes(DISPLAY_HINTS.MANUAL_CAPTION_STOP),
|
|
617
643
|
|
|
618
644
|
isManualCaptionActive: (displayHints) =>
|
|
619
645
|
displayHints.includes(DISPLAY_HINTS.MANUAL_CAPTION_STATUS_ACTIVE),
|
|
620
646
|
|
|
647
|
+
isSpokenLanguageAutoDetectionEnabled: (displayHints) =>
|
|
648
|
+
displayHints.includes(DISPLAY_HINTS.SPOKEN_LANGUAGE_AUTO_DETECTION_ENABLED),
|
|
649
|
+
|
|
621
650
|
isWebexAssistantActive: (displayHints) =>
|
|
622
651
|
displayHints.includes(DISPLAY_HINTS.WEBEX_ASSISTANT_STATUS_ACTIVE),
|
|
623
652
|
|
|
@@ -631,6 +660,9 @@ const MeetingUtil = {
|
|
|
631
660
|
|
|
632
661
|
waitingForOthersToJoin: (displayHints) => displayHints.includes(DISPLAY_HINTS.WAITING_FOR_OTHERS),
|
|
633
662
|
|
|
663
|
+
showAutoEndMeetingWarning: (displayHints) =>
|
|
664
|
+
displayHints.includes(DISPLAY_HINTS.SHOW_AUTO_END_MEETING_WARNING),
|
|
665
|
+
|
|
634
666
|
canSendReactions: (originalValue, displayHints) => {
|
|
635
667
|
if (displayHints.includes(DISPLAY_HINTS.REACTIONS_ACTIVE)) {
|
|
636
668
|
return true;
|
|
@@ -673,22 +705,20 @@ const MeetingUtil = {
|
|
|
673
705
|
},
|
|
674
706
|
|
|
675
707
|
/**
|
|
676
|
-
* Updates the locus info for the meeting with the
|
|
677
|
-
* returned from requests
|
|
708
|
+
* Updates the locus info for the meeting with the locus
|
|
709
|
+
* information returned from API requests made to Locus
|
|
678
710
|
* Returns the original response object
|
|
679
711
|
* @param {Object} meeting The meeting object
|
|
680
712
|
* @param {Object} response The response of the http request
|
|
681
713
|
* @returns {Object}
|
|
682
714
|
*/
|
|
683
|
-
|
|
715
|
+
updateLocusFromApiResponse: (meeting, response) => {
|
|
684
716
|
if (!meeting) {
|
|
685
717
|
return response;
|
|
686
718
|
}
|
|
687
719
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
if (locus) {
|
|
691
|
-
meeting.locusInfo.handleLocusDelta(locus, meeting);
|
|
720
|
+
if (response?.body?.locus) {
|
|
721
|
+
meeting.locusInfo.handleLocusAPIResponse(meeting, response.body);
|
|
692
722
|
}
|
|
693
723
|
|
|
694
724
|
return response;
|
|
@@ -735,7 +765,7 @@ const MeetingUtil = {
|
|
|
735
765
|
|
|
736
766
|
return meeting
|
|
737
767
|
.request(options)
|
|
738
|
-
.then((response) => MeetingUtil.
|
|
768
|
+
.then((response) => MeetingUtil.updateLocusFromApiResponse(meeting, response));
|
|
739
769
|
};
|
|
740
770
|
|
|
741
771
|
return locusDeltaRequest;
|