@webex/plugin-meetings 3.9.0 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +8 -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/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 +23 -23
- 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/properties.ts +43 -0
- package/src/meeting/in-meeting-actions.ts +16 -0
- package/src/meeting/index.ts +204 -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/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 +49 -17
- package/test/unit/spec/meeting-info/meetinginfov2.js +8 -3
- package/test/unit/spec/meetings/index.js +10 -5
- 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,8 @@ import {
|
|
|
28
28
|
StatsAnalyzerEventNames,
|
|
29
29
|
NetworkQualityEventNames,
|
|
30
30
|
NetworkQualityMonitor,
|
|
31
|
+
StatsMonitor,
|
|
32
|
+
StatsMonitorEventNames,
|
|
31
33
|
} from '@webex/internal-media-core';
|
|
32
34
|
|
|
33
35
|
import {
|
|
@@ -269,6 +271,7 @@ export enum ScreenShareFloorStatus {
|
|
|
269
271
|
type FetchMeetingInfoParams = {
|
|
270
272
|
password?: string;
|
|
271
273
|
registrationId?: string;
|
|
274
|
+
classificationId?: string;
|
|
272
275
|
captchaCode?: string;
|
|
273
276
|
extraParams?: Record<string, any>;
|
|
274
277
|
sendCAevents?: boolean;
|
|
@@ -633,6 +636,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
633
636
|
shareStatus: string;
|
|
634
637
|
screenShareFloorState: ScreenShareFloorStatus;
|
|
635
638
|
statsAnalyzer: StatsAnalyzer;
|
|
639
|
+
statsMonitor: StatsMonitor;
|
|
636
640
|
transcription: Transcription;
|
|
637
641
|
updateMediaConnections: (mediaConnections: any[]) => void;
|
|
638
642
|
userDisplayHints: any;
|
|
@@ -1286,6 +1290,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1286
1290
|
* @memberof Meeting
|
|
1287
1291
|
*/
|
|
1288
1292
|
this.networkQualityMonitor = null;
|
|
1293
|
+
/**
|
|
1294
|
+
* @instance
|
|
1295
|
+
* @type {StatsMonitor}
|
|
1296
|
+
* @private
|
|
1297
|
+
* @memberof Meeting
|
|
1298
|
+
*/
|
|
1299
|
+
this.statsMonitor = null;
|
|
1289
1300
|
/**
|
|
1290
1301
|
* Indicates network status of the webrtc media connection
|
|
1291
1302
|
* @instance
|
|
@@ -1902,6 +1913,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1902
1913
|
extraParams = {},
|
|
1903
1914
|
sendCAevents = false,
|
|
1904
1915
|
registrationId = null,
|
|
1916
|
+
classificationId = null,
|
|
1905
1917
|
}): Promise<void> {
|
|
1906
1918
|
try {
|
|
1907
1919
|
const captchaInfo = captchaCode
|
|
@@ -1918,7 +1930,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1918
1930
|
this.locusId,
|
|
1919
1931
|
extraParams,
|
|
1920
1932
|
{meetingId: this.id, sendCAevents},
|
|
1921
|
-
registrationId
|
|
1933
|
+
registrationId,
|
|
1934
|
+
null,
|
|
1935
|
+
classificationId
|
|
1922
1936
|
);
|
|
1923
1937
|
|
|
1924
1938
|
this.parseMeetingInfo(info?.body, this.destination, info?.errors);
|
|
@@ -2962,6 +2976,18 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2962
2976
|
);
|
|
2963
2977
|
});
|
|
2964
2978
|
|
|
2979
|
+
this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_AUTO_END_MEETING_WARNING_CHANGED, ({state}) => {
|
|
2980
|
+
Trigger.trigger(
|
|
2981
|
+
this,
|
|
2982
|
+
{
|
|
2983
|
+
file: 'meeting/index',
|
|
2984
|
+
function: 'setupLocusControlsListener',
|
|
2985
|
+
},
|
|
2986
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_AUTO_END_MEETING_WARNING_UPDATED,
|
|
2987
|
+
{state}
|
|
2988
|
+
);
|
|
2989
|
+
});
|
|
2990
|
+
|
|
2965
2991
|
this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_ANNOTATION_CHANGED, ({state}) => {
|
|
2966
2992
|
Trigger.trigger(
|
|
2967
2993
|
this,
|
|
@@ -3149,6 +3175,23 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3149
3175
|
},
|
|
3150
3176
|
EVENT_TRIGGERS.MEETING_STOPPED_SHARING_WHITEBOARD
|
|
3151
3177
|
);
|
|
3178
|
+
// @ts-ignore
|
|
3179
|
+
this.webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp({
|
|
3180
|
+
key: 'internal.client.share.stopped',
|
|
3181
|
+
});
|
|
3182
|
+
// @ts-ignore
|
|
3183
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
3184
|
+
name: 'client.share.stopped',
|
|
3185
|
+
payload: {
|
|
3186
|
+
mediaType: 'whiteboard',
|
|
3187
|
+
shareDuration:
|
|
3188
|
+
// @ts-ignore
|
|
3189
|
+
this.webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration(),
|
|
3190
|
+
},
|
|
3191
|
+
options: {
|
|
3192
|
+
meetingId: this.id,
|
|
3193
|
+
},
|
|
3194
|
+
});
|
|
3152
3195
|
break;
|
|
3153
3196
|
|
|
3154
3197
|
case SHARE_STATUS.NO_SHARE:
|
|
@@ -3167,6 +3210,14 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3167
3210
|
this.shareCAEventSentStatus.receiveStart = false;
|
|
3168
3211
|
this.shareCAEventSentStatus.receiveStop = false;
|
|
3169
3212
|
|
|
3213
|
+
let finalBeneficiaryId = contentShare.beneficiaryId;
|
|
3214
|
+
// In case of attendee in webinar, the whiteboard is shared by other participants
|
|
3215
|
+
if (this.locusInfo?.info?.isWebinar && this.webinar?.selfIsAttendee) {
|
|
3216
|
+
if (!finalBeneficiaryId && whiteboardShare.beneficiaryId) {
|
|
3217
|
+
finalBeneficiaryId = whiteboardShare.beneficiaryId;
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
|
|
3170
3221
|
Trigger.trigger(
|
|
3171
3222
|
this,
|
|
3172
3223
|
{
|
|
@@ -3175,7 +3226,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3175
3226
|
},
|
|
3176
3227
|
EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
|
3177
3228
|
{
|
|
3178
|
-
memberId:
|
|
3229
|
+
memberId: finalBeneficiaryId,
|
|
3179
3230
|
url: contentShare.url,
|
|
3180
3231
|
shareInstanceId: this.remoteShareInstanceId,
|
|
3181
3232
|
annotationInfo: contentShare.annotation,
|
|
@@ -3317,27 +3368,31 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3317
3368
|
* @memberof Meeting
|
|
3318
3369
|
*/
|
|
3319
3370
|
private setUpLocusUrlListener() {
|
|
3320
|
-
this.locusInfo.on(
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3371
|
+
this.locusInfo.on(
|
|
3372
|
+
EVENTS.LOCUS_INFO_UPDATE_URL,
|
|
3373
|
+
(payload: {url: string; isMainLocus?: boolean}) => {
|
|
3374
|
+
const {url, isMainLocus} = payload;
|
|
3375
|
+
this.members.locusUrlUpdate(url);
|
|
3376
|
+
this.breakouts.locusUrlUpdate(url);
|
|
3377
|
+
this.simultaneousInterpretation.locusUrlUpdate(url);
|
|
3378
|
+
this.annotation.locusUrlUpdate(url);
|
|
3379
|
+
this.locusUrl = url;
|
|
3380
|
+
this.locusId = this.locusUrl?.split('/').pop();
|
|
3381
|
+
this.recordingController.setLocusUrl(this.locusUrl);
|
|
3382
|
+
this.controlsOptionsManager.setLocusUrl(this.locusUrl, !!isMainLocus);
|
|
3383
|
+
this.webinar.locusUrlUpdate(url);
|
|
3330
3384
|
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3385
|
+
Trigger.trigger(
|
|
3386
|
+
this,
|
|
3387
|
+
{
|
|
3388
|
+
file: 'meeting/index',
|
|
3389
|
+
function: 'setUpLocusSelfListener',
|
|
3390
|
+
},
|
|
3391
|
+
EVENT_TRIGGERS.MEETING_LOCUS_URL_UPDATE,
|
|
3392
|
+
{locusUrl: url}
|
|
3393
|
+
);
|
|
3394
|
+
}
|
|
3395
|
+
);
|
|
3341
3396
|
}
|
|
3342
3397
|
|
|
3343
3398
|
/**
|
|
@@ -4180,6 +4235,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4180
4235
|
this.userDisplayHints,
|
|
4181
4236
|
this.selfUserPolicies
|
|
4182
4237
|
),
|
|
4238
|
+
showAutoEndMeetingWarning: MeetingUtil.showAutoEndMeetingWarning(this.userDisplayHints),
|
|
4183
4239
|
canRaiseHand: MeetingUtil.canUserRaiseHand(this.userDisplayHints),
|
|
4184
4240
|
canLowerAllHands: MeetingUtil.canUserLowerAllHands(this.userDisplayHints),
|
|
4185
4241
|
canLowerSomeoneElsesHand: MeetingUtil.canUserLowerSomeoneElsesHand(this.userDisplayHints),
|
|
@@ -4195,8 +4251,13 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4195
4251
|
isLocalRecordingStarted: MeetingUtil.isLocalRecordingStarted(this.userDisplayHints),
|
|
4196
4252
|
isLocalRecordingStopped: MeetingUtil.isLocalRecordingStopped(this.userDisplayHints),
|
|
4197
4253
|
isLocalRecordingPaused: MeetingUtil.isLocalRecordingPaused(this.userDisplayHints),
|
|
4254
|
+
isLocalStreamingStarted: MeetingUtil.isLocalStreamingStarted(this.userDisplayHints),
|
|
4255
|
+
isLocalStreamingStopped: MeetingUtil.isLocalStreamingStopped(this.userDisplayHints),
|
|
4198
4256
|
isManualCaptionActive: MeetingUtil.isManualCaptionActive(this.userDisplayHints),
|
|
4199
4257
|
isSaveTranscriptsEnabled: MeetingUtil.isSaveTranscriptsEnabled(this.userDisplayHints),
|
|
4258
|
+
isSpokenLanguageAutoDetectionEnabled: MeetingUtil.isSpokenLanguageAutoDetectionEnabled(
|
|
4259
|
+
this.userDisplayHints
|
|
4260
|
+
),
|
|
4200
4261
|
isWebexAssistantActive: MeetingUtil.isWebexAssistantActive(this.userDisplayHints),
|
|
4201
4262
|
canViewCaptionPanel: MeetingUtil.canViewCaptionPanel(this.userDisplayHints),
|
|
4202
4263
|
isRealTimeTranslationEnabled: MeetingUtil.isRealTimeTranslationEnabled(
|
|
@@ -6779,6 +6840,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6779
6840
|
// @ts-ignore
|
|
6780
6841
|
this.webex.internal.newMetrics.submitClientEvent({
|
|
6781
6842
|
name: 'client.ice.start',
|
|
6843
|
+
payload: {
|
|
6844
|
+
// @ts-ignore
|
|
6845
|
+
labels: MeetingUtil.getCaEventLabelsForIpVersion(this.webex),
|
|
6846
|
+
},
|
|
6782
6847
|
options: {
|
|
6783
6848
|
meetingId: this.id,
|
|
6784
6849
|
},
|
|
@@ -6948,10 +7013,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6948
7013
|
}
|
|
6949
7014
|
}
|
|
6950
7015
|
|
|
6951
|
-
// Count members that are in the meeting.
|
|
7016
|
+
// Count members that are in the meeting or in the lobby.
|
|
6952
7017
|
const {members} = this.getMembers().membersCollection;
|
|
6953
7018
|
event.data.intervalMetadata.meetingUserCount = Object.values(members).filter(
|
|
6954
|
-
(member: Member) => member.isInMeeting
|
|
7019
|
+
(member: Member) => member.isInMeeting || member.isInLobby
|
|
6955
7020
|
).length;
|
|
6956
7021
|
|
|
6957
7022
|
// @ts-ignore
|
|
@@ -7310,10 +7375,12 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7310
7375
|
if (this.config.stats.enableStatsAnalyzer) {
|
|
7311
7376
|
// @ts-ignore - config coming from registerPlugin
|
|
7312
7377
|
this.networkQualityMonitor = new NetworkQualityMonitor(this.config.stats);
|
|
7378
|
+
this.statsMonitor = new StatsMonitor();
|
|
7313
7379
|
this.statsAnalyzer = new StatsAnalyzer({
|
|
7314
7380
|
// @ts-ignore - config coming from registerPlugin
|
|
7315
7381
|
config: this.config.stats,
|
|
7316
7382
|
networkQualityMonitor: this.networkQualityMonitor,
|
|
7383
|
+
statsMonitor: this.statsMonitor,
|
|
7317
7384
|
isMultistream: this.isMultistream,
|
|
7318
7385
|
});
|
|
7319
7386
|
this.shareCAEventSentStatus = {
|
|
@@ -7327,6 +7394,33 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7327
7394
|
NetworkQualityEventNames.NETWORK_QUALITY,
|
|
7328
7395
|
this.sendNetworkQualityEvent.bind(this)
|
|
7329
7396
|
);
|
|
7397
|
+
|
|
7398
|
+
this.statsMonitor.on(StatsMonitorEventNames.INBOUND_AUDIO_ISSUE, (data) => {
|
|
7399
|
+
// Before forwarding any inbound audio issues to the app, make sure that we have at least one other
|
|
7400
|
+
// participant in the meeting with unmuted audio.
|
|
7401
|
+
// We don't check this.mediaProperties.mediaDirection here, because that's already handled in statsAnalyzer,
|
|
7402
|
+
// so we won't get this event if we are not setup to receive any audio
|
|
7403
|
+
const atLeastOneUnmutedOtherMember = Object.values(
|
|
7404
|
+
this.members.membersCollection.getAll()
|
|
7405
|
+
).find((member) => {
|
|
7406
|
+
return !member.isSelf && !member.isPairedWithSelf && !member.isAudioMuted;
|
|
7407
|
+
});
|
|
7408
|
+
|
|
7409
|
+
if (atLeastOneUnmutedOtherMember) {
|
|
7410
|
+
this.mediaProperties.sendMediaIssueMetric(
|
|
7411
|
+
'inbound_audio',
|
|
7412
|
+
data.issueSubType,
|
|
7413
|
+
this.correlationId
|
|
7414
|
+
);
|
|
7415
|
+
|
|
7416
|
+
Trigger.trigger(
|
|
7417
|
+
this,
|
|
7418
|
+
{file: 'meeting/index', function: 'createStatsAnalyzer'},
|
|
7419
|
+
EVENT_TRIGGERS.MEDIA_INBOUND_AUDIO_ISSUE_DETECTED,
|
|
7420
|
+
data
|
|
7421
|
+
);
|
|
7422
|
+
}
|
|
7423
|
+
});
|
|
7330
7424
|
}
|
|
7331
7425
|
}
|
|
7332
7426
|
|
|
@@ -7625,6 +7719,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7625
7719
|
}
|
|
7626
7720
|
|
|
7627
7721
|
this.statsAnalyzer = null;
|
|
7722
|
+
this.networkQualityMonitor?.removeAllListeners();
|
|
7723
|
+
this.networkQualityMonitor = null;
|
|
7724
|
+
this.statsMonitor?.removeAllListeners();
|
|
7725
|
+
this.statsMonitor = null;
|
|
7628
7726
|
|
|
7629
7727
|
// when media fails, we want to upload a webrtc dump to see whats going on
|
|
7630
7728
|
// this function is async, but returns once the stats have been gathered
|
|
@@ -7648,6 +7746,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7648
7746
|
await this.statsAnalyzer.stopAnalyzer();
|
|
7649
7747
|
}
|
|
7650
7748
|
this.statsAnalyzer = null;
|
|
7749
|
+
this.networkQualityMonitor?.removeAllListeners();
|
|
7750
|
+
this.networkQualityMonitor = null;
|
|
7751
|
+
this.statsMonitor?.removeAllListeners();
|
|
7752
|
+
this.statsMonitor = null;
|
|
7651
7753
|
|
|
7652
7754
|
this.isMultistream = false;
|
|
7653
7755
|
|
|
@@ -7819,6 +7921,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7819
7921
|
|
|
7820
7922
|
this.allowMediaInLobby = options?.allowMediaInLobby;
|
|
7821
7923
|
|
|
7924
|
+
// @ts-ignore
|
|
7925
|
+
const ipver = MeetingUtil.getIpVersion(this.webex); // used just for metrics
|
|
7926
|
+
|
|
7822
7927
|
// If the user is unjoined or guest waiting in lobby dont allow the user to addMedia
|
|
7823
7928
|
// @ts-ignore - isUserUnadmitted coming from SelfUtil
|
|
7824
7929
|
if (this.isUserUnadmitted && !this.wirelessShare && !this.allowMediaInLobby) {
|
|
@@ -7917,6 +8022,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7917
8022
|
locus_id: this.locusUrl.split('/').pop(),
|
|
7918
8023
|
connectionType,
|
|
7919
8024
|
ipVersion,
|
|
8025
|
+
ipver,
|
|
7920
8026
|
selectedCandidatePairChanges,
|
|
7921
8027
|
numTransports,
|
|
7922
8028
|
isMultistream: this.isMultistream,
|
|
@@ -7985,6 +8091,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7985
8091
|
...reachabilityMetrics,
|
|
7986
8092
|
...iceCandidateErrors,
|
|
7987
8093
|
iceCandidatesCount: this.iceCandidatesCount,
|
|
8094
|
+
ipver,
|
|
7988
8095
|
});
|
|
7989
8096
|
|
|
7990
8097
|
await this.cleanUpOnAddMediaFailure();
|
|
@@ -9361,6 +9468,36 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
9361
9468
|
return Promise.reject(new Error('Error sending reaction, service url not found.'));
|
|
9362
9469
|
}
|
|
9363
9470
|
|
|
9471
|
+
/**
|
|
9472
|
+
* Extend the current meeting duration.
|
|
9473
|
+
*
|
|
9474
|
+
* @param {number} extensionMinutes - how many minutes to extend
|
|
9475
|
+
* @returns {Promise}
|
|
9476
|
+
* @public
|
|
9477
|
+
* @memberof Meeting
|
|
9478
|
+
*/
|
|
9479
|
+
public extendMeeting({
|
|
9480
|
+
meetingPolicyUrl,
|
|
9481
|
+
meetingInstanceId,
|
|
9482
|
+
participantId,
|
|
9483
|
+
extensionMinutes = 30,
|
|
9484
|
+
}) {
|
|
9485
|
+
if (!meetingInstanceId || !participantId) {
|
|
9486
|
+
return Promise.reject(new Error('Missing meetingInstanceId or participantId'));
|
|
9487
|
+
}
|
|
9488
|
+
|
|
9489
|
+
if (!meetingPolicyUrl) {
|
|
9490
|
+
return Promise.reject(new Error('Missing meetingPolicyUrl'));
|
|
9491
|
+
}
|
|
9492
|
+
|
|
9493
|
+
return this.meetingRequest.extendMeeting({
|
|
9494
|
+
meetingInstanceId,
|
|
9495
|
+
participantId,
|
|
9496
|
+
extensionMinutes,
|
|
9497
|
+
meetingPolicyUrl,
|
|
9498
|
+
});
|
|
9499
|
+
}
|
|
9500
|
+
|
|
9364
9501
|
/**
|
|
9365
9502
|
* Method to enable or disable reactions inside the meeting.
|
|
9366
9503
|
*
|
|
@@ -9890,4 +10027,47 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
9890
10027
|
|
|
9891
10028
|
return this.meetingRequest.synchronizeStage(this.locusUrl, videoLayout);
|
|
9892
10029
|
}
|
|
10030
|
+
|
|
10031
|
+
/**
|
|
10032
|
+
* Notifies the host with the given meeting UUID and display names.
|
|
10033
|
+
*
|
|
10034
|
+
* @param {string} meetingUuid - The UUID of the meeting.
|
|
10035
|
+
* @param {string[]} displayName - An array of display names to notify the host with.
|
|
10036
|
+
* @returns {Promise<any>} The result of the notifyHost request.
|
|
10037
|
+
*/
|
|
10038
|
+
notifyHost(meetingUuid: string, displayName: string[]) {
|
|
10039
|
+
return this.meetingRequest.notifyHost(
|
|
10040
|
+
this.meetingInfo.siteFullUrl,
|
|
10041
|
+
this.locusId,
|
|
10042
|
+
meetingUuid,
|
|
10043
|
+
displayName
|
|
10044
|
+
);
|
|
10045
|
+
}
|
|
10046
|
+
|
|
10047
|
+
/**
|
|
10048
|
+
* Call out a SIP participant to a meeting
|
|
10049
|
+
* @param {string} address - The SIP address or phone number
|
|
10050
|
+
* @param {string} displayName - The display name for the participant
|
|
10051
|
+
* @param {string} [correlationId] - Optional correlation ID
|
|
10052
|
+
* @returns {Promise} Promise that resolves when the call-out is initiated
|
|
10053
|
+
*/
|
|
10054
|
+
sipCallOut(address: string, displayName: string) {
|
|
10055
|
+
return this.meetingRequest.sipCallOut(
|
|
10056
|
+
this.meetingInfo.meetingId,
|
|
10057
|
+
this.meetingInfo.meetingId,
|
|
10058
|
+
address,
|
|
10059
|
+
displayName
|
|
10060
|
+
);
|
|
10061
|
+
}
|
|
10062
|
+
|
|
10063
|
+
/**
|
|
10064
|
+
* Cancel an ongoing SIP call-out
|
|
10065
|
+
* @param {string} participantId - The participant ID to cancel
|
|
10066
|
+
* @returns {Promise} Promise that resolves when the call-out is cancelled
|
|
10067
|
+
* @public
|
|
10068
|
+
* @memberof Meetings
|
|
10069
|
+
*/
|
|
10070
|
+
cancelSipCallOut(participantId: string) {
|
|
10071
|
+
return this.meetingRequest.cancelSipCallOut(participantId);
|
|
10072
|
+
}
|
|
9893
10073
|
}
|
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;
|