@webex/plugin-meetings 3.7.0-next.2 → 3.7.0-next.20
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/common/errors/{webinar-registration-error.js → join-webinar-error.js} +12 -12
- package/dist/common/errors/join-webinar-error.js.map +1 -0
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +27 -6
- package/dist/constants.js.map +1 -1
- package/dist/index.js +8 -15
- package/dist/index.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/meeting/in-meeting-actions.js +11 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +84 -131
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/util.js +0 -8
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +29 -17
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +6 -3
- package/dist/meetings/index.js.map +1 -1
- package/dist/members/util.js +4 -2
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +3 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/remoteMedia.js +30 -15
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/reachability/clusterReachability.js +12 -11
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/recording-controller/enums.js +8 -4
- package/dist/recording-controller/enums.js.map +1 -1
- package/dist/recording-controller/index.js +18 -9
- package/dist/recording-controller/index.js.map +1 -1
- package/dist/recording-controller/util.js +13 -9
- package/dist/recording-controller/util.js.map +1 -1
- package/dist/types/common/errors/{webinar-registration-error.d.ts → join-webinar-error.d.ts} +2 -2
- package/dist/types/constants.d.ts +19 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/meeting/in-meeting-actions.d.ts +10 -0
- package/dist/types/meeting/index.d.ts +1 -10
- package/dist/types/meeting/util.d.ts +0 -1
- package/dist/types/meeting-info/meeting-info-v2.d.ts +4 -4
- package/dist/types/meetings/index.d.ts +3 -0
- package/dist/types/members/util.d.ts +2 -0
- package/dist/types/metrics/constants.d.ts +3 -1
- package/dist/types/recording-controller/enums.d.ts +5 -2
- package/dist/types/recording-controller/index.d.ts +1 -0
- package/dist/types/recording-controller/util.d.ts +2 -1
- package/dist/webinar/index.js +357 -7
- package/dist/webinar/index.js.map +1 -1
- package/package.json +22 -22
- package/src/common/errors/join-webinar-error.ts +24 -0
- package/src/config.ts +1 -1
- package/src/constants.ts +24 -3
- package/src/index.ts +2 -3
- package/src/meeting/in-meeting-actions.ts +21 -0
- package/src/meeting/index.ts +54 -48
- package/src/meeting/util.ts +0 -9
- package/src/meeting-info/meeting-info-v2.ts +23 -11
- package/src/meetings/index.ts +8 -2
- package/src/members/util.ts +1 -0
- package/src/metrics/constants.ts +3 -1
- package/src/multistream/remoteMedia.ts +28 -15
- package/src/reachability/clusterReachability.ts +4 -1
- package/src/recording-controller/enums.ts +5 -2
- package/src/recording-controller/index.ts +17 -4
- package/src/recording-controller/util.ts +20 -5
- package/src/webinar/index.ts +201 -9
- package/test/unit/spec/meeting/in-meeting-actions.ts +13 -1
- package/test/unit/spec/meeting/index.js +106 -77
- package/test/unit/spec/meeting/utils.js +0 -15
- package/test/unit/spec/meeting-info/meetinginfov2.js +9 -4
- package/test/unit/spec/meetings/index.js +9 -5
- package/test/unit/spec/members/utils.js +95 -0
- package/test/unit/spec/multistream/remoteMedia.ts +11 -7
- package/test/unit/spec/reachability/clusterReachability.ts +7 -0
- package/test/unit/spec/recording-controller/index.js +61 -5
- package/test/unit/spec/recording-controller/util.js +39 -3
- package/test/unit/spec/webinar/index.ts +363 -0
- package/dist/common/errors/webinar-registration-error.js.map +0 -1
- package/src/common/errors/webinar-registration-error.ts +0 -27
|
@@ -26,6 +26,7 @@ interface IInMeetingActions {
|
|
|
26
26
|
canStartRecording?: boolean;
|
|
27
27
|
canPauseRecording?: boolean;
|
|
28
28
|
canResumeRecording?: boolean;
|
|
29
|
+
isPremiseRecordingEnabled?: boolean;
|
|
29
30
|
canStopRecording?: boolean;
|
|
30
31
|
canRaiseHand?: boolean;
|
|
31
32
|
canLowerAllHands?: boolean;
|
|
@@ -93,6 +94,10 @@ interface IInMeetingActions {
|
|
|
93
94
|
canShowStageView?: boolean;
|
|
94
95
|
canEnableStageView?: boolean;
|
|
95
96
|
canDisableStageView?: boolean;
|
|
97
|
+
isPracticeSessionOn?: boolean;
|
|
98
|
+
isPracticeSessionOff?: boolean;
|
|
99
|
+
canStartPracticeSession?: boolean;
|
|
100
|
+
canStopPracticeSession?: boolean;
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
/**
|
|
@@ -117,6 +122,8 @@ export default class InMeetingActions implements IInMeetingActions {
|
|
|
117
122
|
|
|
118
123
|
canResumeRecording = null;
|
|
119
124
|
|
|
125
|
+
isPremiseRecordingEnabled = null;
|
|
126
|
+
|
|
120
127
|
canStopRecording = null;
|
|
121
128
|
|
|
122
129
|
canSetMuteOnEntry = null;
|
|
@@ -266,6 +273,15 @@ export default class InMeetingActions implements IInMeetingActions {
|
|
|
266
273
|
canEnableStageView = null;
|
|
267
274
|
|
|
268
275
|
canDisableStageView = null;
|
|
276
|
+
|
|
277
|
+
isPracticeSessionOn = null;
|
|
278
|
+
|
|
279
|
+
isPracticeSessionOff = null;
|
|
280
|
+
|
|
281
|
+
canStartPracticeSession = null;
|
|
282
|
+
|
|
283
|
+
canStopPracticeSession = null;
|
|
284
|
+
|
|
269
285
|
/**
|
|
270
286
|
* Returns all meeting action options
|
|
271
287
|
* @returns {Object}
|
|
@@ -288,6 +304,7 @@ export default class InMeetingActions implements IInMeetingActions {
|
|
|
288
304
|
canPauseRecording: this.canPauseRecording,
|
|
289
305
|
canResumeRecording: this.canResumeRecording,
|
|
290
306
|
canStopRecording: this.canStopRecording,
|
|
307
|
+
isPremiseRecordingEnabled: this.isPremiseRecordingEnabled,
|
|
291
308
|
canRaiseHand: this.canRaiseHand,
|
|
292
309
|
canLowerAllHands: this.canLowerAllHands,
|
|
293
310
|
canLowerSomeoneElsesHand: this.canLowerSomeoneElsesHand,
|
|
@@ -354,6 +371,10 @@ export default class InMeetingActions implements IInMeetingActions {
|
|
|
354
371
|
canShowStageView: this.canShowStageView,
|
|
355
372
|
canEnableStageView: this.canEnableStageView,
|
|
356
373
|
canDisableStageView: this.canDisableStageView,
|
|
374
|
+
isPracticeSessionOn: this.isPracticeSessionOn,
|
|
375
|
+
isPracticeSessionOff: this.isPracticeSessionOff,
|
|
376
|
+
canStartPracticeSession: this.canStartPracticeSession,
|
|
377
|
+
canStopPracticeSession: this.canStopPracticeSession,
|
|
357
378
|
});
|
|
358
379
|
|
|
359
380
|
/**
|
package/src/meeting/index.ts
CHANGED
|
@@ -31,7 +31,6 @@ import {
|
|
|
31
31
|
} from '@webex/internal-media-core';
|
|
32
32
|
|
|
33
33
|
import {
|
|
34
|
-
getDevices,
|
|
35
34
|
LocalStream,
|
|
36
35
|
LocalCameraStream,
|
|
37
36
|
LocalDisplayStream,
|
|
@@ -122,6 +121,8 @@ import {
|
|
|
122
121
|
MEETING_PERMISSION_TOKEN_REFRESH_REASON,
|
|
123
122
|
ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT,
|
|
124
123
|
NAMED_MEDIA_GROUP_TYPE_AUDIO,
|
|
124
|
+
WEBINAR_ERROR_WEBCAST,
|
|
125
|
+
WEBINAR_ERROR_REGISTRATIONID,
|
|
125
126
|
} from '../constants';
|
|
126
127
|
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
127
128
|
import ParameterError from '../common/errors/parameter';
|
|
@@ -129,7 +130,7 @@ import {
|
|
|
129
130
|
MeetingInfoV2PasswordError,
|
|
130
131
|
MeetingInfoV2CaptchaError,
|
|
131
132
|
MeetingInfoV2PolicyError,
|
|
132
|
-
|
|
133
|
+
MeetingInfoV2JoinWebinarError,
|
|
133
134
|
} from '../meeting-info/meeting-info-v2';
|
|
134
135
|
import {CSI, ReceiveSlotManager} from '../multistream/receiveSlotManager';
|
|
135
136
|
import SendSlotManager from '../multistream/sendSlotManager';
|
|
@@ -158,7 +159,7 @@ import ControlsOptionsManager from '../controls-options-manager';
|
|
|
158
159
|
import PermissionError from '../common/errors/permission';
|
|
159
160
|
import {LocusMediaRequest} from './locusMediaRequest';
|
|
160
161
|
import {ConnectionStateHandler, ConnectionStateEvent} from './connectionStateHandler';
|
|
161
|
-
import
|
|
162
|
+
import JoinWebinarError from '../common/errors/join-webinar-error';
|
|
162
163
|
|
|
163
164
|
// default callback so we don't call an undefined function, but in practice it should never be used
|
|
164
165
|
const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
|
|
@@ -1767,15 +1768,20 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
1767
1768
|
this.meetingInfo = err.meetingInfo;
|
|
1768
1769
|
}
|
|
1769
1770
|
throw new PermissionError();
|
|
1770
|
-
} else if (err instanceof
|
|
1771
|
+
} else if (err instanceof MeetingInfoV2JoinWebinarError) {
|
|
1771
1772
|
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_REGISTRATION;
|
|
1773
|
+
if (WEBINAR_ERROR_WEBCAST.includes(err.wbxAppApiCode)) {
|
|
1774
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.NEED_JOIN_WITH_WEBCAST;
|
|
1775
|
+
} else if (WEBINAR_ERROR_REGISTRATIONID.includes(err.wbxAppApiCode)) {
|
|
1776
|
+
this.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WEBINAR_NEED_REGISTRATIONID;
|
|
1777
|
+
}
|
|
1772
1778
|
this.meetingInfoFailureCode = err.wbxAppApiCode;
|
|
1773
1779
|
|
|
1774
1780
|
if (err.meetingInfo) {
|
|
1775
1781
|
this.meetingInfo = err.meetingInfo;
|
|
1776
1782
|
}
|
|
1777
1783
|
|
|
1778
|
-
throw new
|
|
1784
|
+
throw new JoinWebinarError();
|
|
1779
1785
|
} else if (err instanceof MeetingInfoV2PasswordError) {
|
|
1780
1786
|
LoggerProxy.logger.info(
|
|
1781
1787
|
// @ts-ignore
|
|
@@ -2660,6 +2666,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
2660
2666
|
});
|
|
2661
2667
|
|
|
2662
2668
|
this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_PRACTICE_SESSION_STATUS_UPDATED, ({state}) => {
|
|
2669
|
+
this.webinar.updatePracticeSessionStatus(state);
|
|
2663
2670
|
Trigger.trigger(
|
|
2664
2671
|
this,
|
|
2665
2672
|
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
|
@@ -3254,6 +3261,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3254
3261
|
options: {meetingId: this.id},
|
|
3255
3262
|
});
|
|
3256
3263
|
}
|
|
3264
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.GUEST_ENTERED_LOBBY, {
|
|
3265
|
+
correlation_id: this.correlationId,
|
|
3266
|
+
});
|
|
3257
3267
|
this.updateLLMConnection();
|
|
3258
3268
|
});
|
|
3259
3269
|
this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ADMITTED_GUEST, async (payload) => {
|
|
@@ -3277,6 +3287,9 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3277
3287
|
name: 'client.lobby.exited',
|
|
3278
3288
|
options: {meetingId: this.id},
|
|
3279
3289
|
});
|
|
3290
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.GUEST_EXITED_LOBBY, {
|
|
3291
|
+
correlation_id: this.correlationId,
|
|
3292
|
+
});
|
|
3280
3293
|
}
|
|
3281
3294
|
this.rtcMetrics?.sendNextMetrics();
|
|
3282
3295
|
this.updateLLMConnection();
|
|
@@ -3510,6 +3523,7 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3510
3523
|
emailAddress: string;
|
|
3511
3524
|
email: string;
|
|
3512
3525
|
phoneNumber: string;
|
|
3526
|
+
roles: Array<string>;
|
|
3513
3527
|
},
|
|
3514
3528
|
alertIfActive = true
|
|
3515
3529
|
) {
|
|
@@ -3766,6 +3780,10 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3766
3780
|
this.userDisplayHints,
|
|
3767
3781
|
this.selfUserPolicies
|
|
3768
3782
|
),
|
|
3783
|
+
isPremiseRecordingEnabled: RecordingUtil.isPremiseRecordingEnabled(
|
|
3784
|
+
this.userDisplayHints,
|
|
3785
|
+
this.selfUserPolicies
|
|
3786
|
+
),
|
|
3769
3787
|
canRaiseHand: MeetingUtil.canUserRaiseHand(this.userDisplayHints),
|
|
3770
3788
|
canLowerAllHands: MeetingUtil.canUserLowerAllHands(this.userDisplayHints),
|
|
3771
3789
|
canLowerSomeoneElsesHand: MeetingUtil.canUserLowerSomeoneElsesHand(this.userDisplayHints),
|
|
@@ -3909,6 +3927,22 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
3909
3927
|
requiredHints: [DISPLAY_HINTS.DISABLE_STAGE_VIEW],
|
|
3910
3928
|
displayHints: this.userDisplayHints,
|
|
3911
3929
|
}),
|
|
3930
|
+
isPracticeSessionOn: ControlsOptionsUtil.hasHints({
|
|
3931
|
+
requiredHints: [DISPLAY_HINTS.PRACTICE_SESSION_ON],
|
|
3932
|
+
displayHints: this.userDisplayHints,
|
|
3933
|
+
}),
|
|
3934
|
+
isPracticeSessionOff: ControlsOptionsUtil.hasHints({
|
|
3935
|
+
requiredHints: [DISPLAY_HINTS.PRACTICE_SESSION_OFF],
|
|
3936
|
+
displayHints: this.userDisplayHints,
|
|
3937
|
+
}),
|
|
3938
|
+
canStartPracticeSession: ControlsOptionsUtil.hasHints({
|
|
3939
|
+
requiredHints: [DISPLAY_HINTS.SHOW_PRACTICE_SESSION_START],
|
|
3940
|
+
displayHints: this.userDisplayHints,
|
|
3941
|
+
}),
|
|
3942
|
+
canStopPracticeSession: ControlsOptionsUtil.hasHints({
|
|
3943
|
+
requiredHints: [DISPLAY_HINTS.SHOW_PRACTICE_SESSION_STOP],
|
|
3944
|
+
displayHints: this.userDisplayHints,
|
|
3945
|
+
}),
|
|
3912
3946
|
canShareFile:
|
|
3913
3947
|
(ControlsOptionsUtil.hasHints({
|
|
3914
3948
|
requiredHints: [DISPLAY_HINTS.SHARE_FILE],
|
|
@@ -4071,10 +4105,11 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
4071
4105
|
*/
|
|
4072
4106
|
private setLogUploadTimer() {
|
|
4073
4107
|
// start with short timeouts and increase them later on so in case users have very long multi-hour meetings we don't get too fragmented logs
|
|
4074
|
-
const LOG_UPLOAD_INTERVALS = [0.1,
|
|
4108
|
+
const LOG_UPLOAD_INTERVALS = [0.1, 15, 30, 60]; // in minutes
|
|
4075
4109
|
|
|
4076
4110
|
const delay =
|
|
4077
4111
|
1000 *
|
|
4112
|
+
60 *
|
|
4078
4113
|
// @ts-ignore - config coming from registerPlugin
|
|
4079
4114
|
this.config.logUploadIntervalMultiplicationFactor *
|
|
4080
4115
|
LOG_UPLOAD_INTERVALS[this.logUploadIntervalIndex];
|
|
@@ -5345,16 +5380,19 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
5345
5380
|
this.meetingFiniteStateMachine.reset();
|
|
5346
5381
|
}
|
|
5347
5382
|
|
|
5348
|
-
//
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5383
|
+
// send client.call.initiated unless told not to
|
|
5384
|
+
if (options.sendCallInitiated !== false) {
|
|
5385
|
+
// @ts-ignore
|
|
5386
|
+
this.webex.internal.newMetrics.submitClientEvent({
|
|
5387
|
+
name: 'client.call.initiated',
|
|
5388
|
+
payload: {
|
|
5389
|
+
trigger: this.callStateForMetrics.joinTrigger || 'user-interaction',
|
|
5390
|
+
isRoapCallEnabled: true,
|
|
5391
|
+
pstnAudioType: options?.pstnAudioType,
|
|
5392
|
+
},
|
|
5393
|
+
options: {meetingId: this.id},
|
|
5394
|
+
});
|
|
5395
|
+
}
|
|
5358
5396
|
|
|
5359
5397
|
LoggerProxy.logger.log('Meeting:index#join --> Joining a meeting');
|
|
5360
5398
|
|
|
@@ -6739,32 +6777,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
6739
6777
|
}
|
|
6740
6778
|
}
|
|
6741
6779
|
|
|
6742
|
-
/**
|
|
6743
|
-
* Handles device logging
|
|
6744
|
-
*
|
|
6745
|
-
* @private
|
|
6746
|
-
* @static
|
|
6747
|
-
* @param {boolean} isAudioEnabled
|
|
6748
|
-
* @param {boolean} isVideoEnabled
|
|
6749
|
-
* @returns {Promise<void>}
|
|
6750
|
-
*/
|
|
6751
|
-
|
|
6752
|
-
private static async handleDeviceLogging(isAudioEnabled, isVideoEnabled): Promise<void> {
|
|
6753
|
-
try {
|
|
6754
|
-
let devices = [];
|
|
6755
|
-
if (isVideoEnabled && isAudioEnabled) {
|
|
6756
|
-
devices = await getDevices();
|
|
6757
|
-
} else if (isVideoEnabled) {
|
|
6758
|
-
devices = await getDevices(Media.DeviceKind.VIDEO_INPUT);
|
|
6759
|
-
} else if (isAudioEnabled) {
|
|
6760
|
-
devices = await getDevices(Media.DeviceKind.AUDIO_INPUT);
|
|
6761
|
-
}
|
|
6762
|
-
MeetingUtil.handleDeviceLogging(devices);
|
|
6763
|
-
} catch {
|
|
6764
|
-
// getDevices may fail if we don't have browser permissions, that's ok, we still can have a media connection
|
|
6765
|
-
}
|
|
6766
|
-
}
|
|
6767
|
-
|
|
6768
6780
|
/**
|
|
6769
6781
|
* Returns a promise. This promise is created once the local sdp offer has been successfully created and is resolved
|
|
6770
6782
|
* once the remote sdp answer has been received.
|
|
@@ -7267,12 +7279,6 @@ export default class Meeting extends StatelessWebexPlugin {
|
|
|
7267
7279
|
turnServerInfo
|
|
7268
7280
|
);
|
|
7269
7281
|
|
|
7270
|
-
if (audioEnabled || videoEnabled) {
|
|
7271
|
-
await Meeting.handleDeviceLogging(audioEnabled, videoEnabled);
|
|
7272
|
-
} else {
|
|
7273
|
-
LoggerProxy.logger.info(`${LOG_HEADER} device logging not required`);
|
|
7274
|
-
}
|
|
7275
|
-
|
|
7276
7282
|
if (this.mediaProperties.hasLocalShareStream()) {
|
|
7277
7283
|
await this.enqueueScreenShareFloorRequest();
|
|
7278
7284
|
}
|
package/src/meeting/util.ts
CHANGED
|
@@ -496,15 +496,6 @@ const MeetingUtil = {
|
|
|
496
496
|
}
|
|
497
497
|
},
|
|
498
498
|
|
|
499
|
-
handleDeviceLogging: (devices = []) => {
|
|
500
|
-
const LOG_HEADER = 'MeetingUtil#handleDeviceLogging -->';
|
|
501
|
-
|
|
502
|
-
devices.forEach((device) => {
|
|
503
|
-
LoggerProxy.logger.log(LOG_HEADER, `deviceId = ${device.deviceId}`);
|
|
504
|
-
LoggerProxy.logger.log(LOG_HEADER, 'settings', JSON.stringify(device));
|
|
505
|
-
});
|
|
506
|
-
},
|
|
507
|
-
|
|
508
499
|
endMeetingForAll: (meeting) => {
|
|
509
500
|
if (meeting.meetingState === FULL_STATE.INACTIVE) {
|
|
510
501
|
return Promise.reject(new MeetingNotActiveError());
|
|
@@ -18,7 +18,19 @@ const ADHOC_MEETING_DEFAULT_ERROR =
|
|
|
18
18
|
'Failed starting the adhoc meeting, Please contact support team ';
|
|
19
19
|
const CAPTCHA_ERROR_REQUIRES_PASSWORD_CODES = [423005, 423006];
|
|
20
20
|
const POLICY_ERROR_CODES = [403049, 403104, 403103, 403048, 403102, 403101];
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* 403021 - Meeting registration is required
|
|
23
|
+
* 403022 - Meeting registration is still pending
|
|
24
|
+
* 403024 - Meeting registration have been rejected
|
|
25
|
+
* 403137 - Registration ID verified failure
|
|
26
|
+
* 423007 - Registration ID input too many time,please input captcha code
|
|
27
|
+
* 403026 - Need to join meeting via webcast
|
|
28
|
+
* 403037 - Meeting join required registration ID
|
|
29
|
+
* 403137 - Registration ID verified failure
|
|
30
|
+
*
|
|
31
|
+
*/
|
|
32
|
+
const JOIN_WEBINAR_ERROR_CODES = [403021, 403022, 403024, 403137, 423007, 403026, 403037, 403137];
|
|
33
|
+
|
|
22
34
|
/**
|
|
23
35
|
* Error to indicate that wbxappapi requires a password
|
|
24
36
|
*/
|
|
@@ -126,9 +138,9 @@ export class MeetingInfoV2CaptchaError extends Error {
|
|
|
126
138
|
}
|
|
127
139
|
|
|
128
140
|
/**
|
|
129
|
-
* Error preventing join because of a webinar
|
|
141
|
+
* Error preventing join because of a webinar have some error
|
|
130
142
|
*/
|
|
131
|
-
export class
|
|
143
|
+
export class MeetingInfoV2JoinWebinarError extends Error {
|
|
132
144
|
meetingInfo: any;
|
|
133
145
|
sdkMessage: any;
|
|
134
146
|
wbxAppApiCode: any;
|
|
@@ -142,7 +154,7 @@ export class MeetingInfoV2WebinarRegistrationError extends Error {
|
|
|
142
154
|
*/
|
|
143
155
|
constructor(wbxAppApiErrorCode?: number, meetingInfo?: object, message?: string) {
|
|
144
156
|
super(`${message}, code=${wbxAppApiErrorCode}`);
|
|
145
|
-
this.name = '
|
|
157
|
+
this.name = 'MeetingInfoV2JoinWebinarError';
|
|
146
158
|
this.sdkMessage = message;
|
|
147
159
|
this.stack = new Error().stack;
|
|
148
160
|
this.wbxAppApiCode = wbxAppApiErrorCode;
|
|
@@ -204,21 +216,21 @@ export default class MeetingInfoV2 {
|
|
|
204
216
|
};
|
|
205
217
|
|
|
206
218
|
/**
|
|
207
|
-
* Raises a
|
|
219
|
+
* Raises a handleJoinWebinarError for join webinar error codes
|
|
208
220
|
* @param {any} err the error from the request
|
|
209
221
|
* @returns {void}
|
|
210
222
|
*/
|
|
211
|
-
|
|
223
|
+
handleJoinWebinarError = (err) => {
|
|
212
224
|
if (!err.body) {
|
|
213
225
|
return;
|
|
214
226
|
}
|
|
215
227
|
|
|
216
|
-
if (
|
|
217
|
-
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.
|
|
228
|
+
if (JOIN_WEBINAR_ERROR_CODES.includes(err.body?.code)) {
|
|
229
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.JOIN_WEBINAR_ERROR, {
|
|
218
230
|
code: err.body?.code,
|
|
219
231
|
});
|
|
220
232
|
|
|
221
|
-
throw new
|
|
233
|
+
throw new MeetingInfoV2JoinWebinarError(
|
|
222
234
|
err.body?.code,
|
|
223
235
|
err.body?.data?.meetingInfo,
|
|
224
236
|
err.body?.message
|
|
@@ -286,7 +298,7 @@ export default class MeetingInfoV2 {
|
|
|
286
298
|
})
|
|
287
299
|
.catch((err) => {
|
|
288
300
|
this.handlePolicyError(err);
|
|
289
|
-
this.
|
|
301
|
+
this.handleJoinWebinarError(err);
|
|
290
302
|
|
|
291
303
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADHOC_MEETING_FAILURE, {
|
|
292
304
|
reason: err.message,
|
|
@@ -441,7 +453,7 @@ export default class MeetingInfoV2 {
|
|
|
441
453
|
|
|
442
454
|
if (err?.statusCode === 403) {
|
|
443
455
|
this.handlePolicyError(err);
|
|
444
|
-
this.
|
|
456
|
+
this.handleJoinWebinarError(err);
|
|
445
457
|
|
|
446
458
|
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.VERIFY_PASSWORD_ERROR, {
|
|
447
459
|
reason: err.message,
|
package/src/meetings/index.ts
CHANGED
|
@@ -56,7 +56,7 @@ import MeetingCollection from './collection';
|
|
|
56
56
|
import {MEETING_KEY, INoiseReductionEffect, IVirtualBackgroundEffect} from './meetings.types';
|
|
57
57
|
import MeetingsUtil from './util';
|
|
58
58
|
import PermissionError from '../common/errors/permission';
|
|
59
|
-
import
|
|
59
|
+
import JoinWebinarError from '../common/errors/join-webinar-error';
|
|
60
60
|
import {SpaceIDDeprecatedError} from '../common/errors/webex-errors';
|
|
61
61
|
import NoMeetingInfoError from '../common/errors/no-meeting-info';
|
|
62
62
|
|
|
@@ -155,6 +155,9 @@ export type BasicMeetingInformation = {
|
|
|
155
155
|
};
|
|
156
156
|
meetingInfo: any;
|
|
157
157
|
sessionCorrelationId: string;
|
|
158
|
+
roles: string[];
|
|
159
|
+
getCurUserType: () => string | null;
|
|
160
|
+
callStateForMetrics: CallStateForMetrics;
|
|
158
161
|
};
|
|
159
162
|
|
|
160
163
|
/**
|
|
@@ -1143,6 +1146,9 @@ export default class Meetings extends WebexPlugin {
|
|
|
1143
1146
|
sessionId: meeting.locusInfo?.fullState?.sessionId,
|
|
1144
1147
|
},
|
|
1145
1148
|
},
|
|
1149
|
+
roles: meeting.roles,
|
|
1150
|
+
callStateForMetrics: meeting.callStateForMetrics,
|
|
1151
|
+
getCurUserType: meeting.getCurUserType,
|
|
1146
1152
|
});
|
|
1147
1153
|
this.meetingCollection.delete(meeting.id);
|
|
1148
1154
|
Trigger.trigger(
|
|
@@ -1406,7 +1412,7 @@ export default class Meetings extends WebexPlugin {
|
|
|
1406
1412
|
!(err instanceof CaptchaError) &&
|
|
1407
1413
|
!(err instanceof PasswordError) &&
|
|
1408
1414
|
!(err instanceof PermissionError) &&
|
|
1409
|
-
!(err instanceof
|
|
1415
|
+
!(err instanceof JoinWebinarError)
|
|
1410
1416
|
) {
|
|
1411
1417
|
LoggerProxy.logger.info(
|
|
1412
1418
|
`Meetings:index#createMeeting --> Info Unable to fetch meeting info for ${destination}.`
|
package/src/members/util.ts
CHANGED
package/src/metrics/constants.ts
CHANGED
|
@@ -70,7 +70,9 @@ 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
|
-
|
|
73
|
+
JOIN_WEBINAR_ERROR: 'js_sdk_join_webinar_error',
|
|
74
|
+
GUEST_ENTERED_LOBBY: 'js_sdk_guest_entered_lobby',
|
|
75
|
+
GUEST_EXITED_LOBBY: 'js_sdk_guest_exited_lobby',
|
|
74
76
|
};
|
|
75
77
|
|
|
76
78
|
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 =
|
|
40
|
+
maxFs = MAX_FS_VALUES['90p'];
|
|
33
41
|
break;
|
|
34
42
|
case 'very small':
|
|
35
|
-
maxFs =
|
|
43
|
+
maxFs = MAX_FS_VALUES['180p'];
|
|
36
44
|
break;
|
|
37
45
|
case 'small':
|
|
38
|
-
maxFs =
|
|
46
|
+
maxFs = MAX_FS_VALUES['360p'];
|
|
39
47
|
break;
|
|
40
48
|
case 'medium':
|
|
41
|
-
maxFs =
|
|
49
|
+
maxFs = MAX_FS_VALUES['720p'];
|
|
42
50
|
break;
|
|
43
51
|
case 'large':
|
|
44
|
-
maxFs =
|
|
52
|
+
maxFs = MAX_FS_VALUES['1080p'];
|
|
45
53
|
break;
|
|
46
54
|
case 'best':
|
|
47
|
-
maxFs =
|
|
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 =
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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 =
|
|
140
|
+
fs = MAX_FS_VALUES['720p'];
|
|
128
141
|
} else {
|
|
129
|
-
fs =
|
|
142
|
+
fs = MAX_FS_VALUES['1080p'];
|
|
130
143
|
}
|
|
131
144
|
|
|
132
145
|
this.receiveSlot?.setMaxFs(fs);
|
|
@@ -357,11 +357,14 @@ export class ClusterReachability extends EventsScope {
|
|
|
357
357
|
|
|
358
358
|
this.startTimestamp = performance.now();
|
|
359
359
|
|
|
360
|
+
// Set up the state change listeners before triggering the ICE gathering
|
|
361
|
+
const gatherIceCandidatePromise = this.gatherIceCandidates();
|
|
362
|
+
|
|
360
363
|
// not awaiting the next call on purpose, because we're not sending the offer anywhere and there won't be any answer
|
|
361
364
|
// we just need to make this call to trigger the ICE gathering process
|
|
362
365
|
this.pc.setLocalDescription(offer);
|
|
363
366
|
|
|
364
|
-
await
|
|
367
|
+
await gatherIceCandidatePromise;
|
|
365
368
|
} catch (error) {
|
|
366
369
|
LoggerProxy.logger.warn(`Reachability:ClusterReachability#start --> Error: `, error);
|
|
367
370
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import PermissionError from '../common/errors/permission';
|
|
2
|
+
import LoggerProxy from '../common/logs/logger-proxy';
|
|
2
3
|
import {CONTROLS, HTTP_VERBS, SELF_POLICY} from '../constants';
|
|
3
4
|
import MeetingRequest from '../meeting/request';
|
|
4
|
-
import RecordingAction from './enums';
|
|
5
|
+
import {RecordingAction, RecordingType} from './enums';
|
|
5
6
|
import Util from './util';
|
|
6
|
-
import LoggerProxy from '../common/logs/logger-proxy';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @description Recording manages the recording functionality of the meeting object, there should only be one instantation of recording per meeting
|
|
@@ -228,11 +228,12 @@ export default class RecordingController {
|
|
|
228
228
|
|
|
229
229
|
/**
|
|
230
230
|
* @param {RecordingAction} action
|
|
231
|
+
* @param {RecordingType} recordingType
|
|
231
232
|
* @private
|
|
232
233
|
* @memberof RecordingController
|
|
233
234
|
* @returns {Promise}
|
|
234
235
|
*/
|
|
235
|
-
private recordingService(action: RecordingAction): Promise<any> {
|
|
236
|
+
private recordingService(action: RecordingAction, recordingType: RecordingType): Promise<any> {
|
|
236
237
|
// @ts-ignore
|
|
237
238
|
return this.request.request({
|
|
238
239
|
body: {
|
|
@@ -242,6 +243,7 @@ export default class RecordingController {
|
|
|
242
243
|
recording: {
|
|
243
244
|
action: action.toLowerCase(),
|
|
244
245
|
},
|
|
246
|
+
recordingType,
|
|
245
247
|
},
|
|
246
248
|
uri: `${this.serviceUrl}/loci/${this.locusId}/recording`,
|
|
247
249
|
method: HTTP_VERBS.PUT,
|
|
@@ -276,14 +278,25 @@ export default class RecordingController {
|
|
|
276
278
|
* @returns {Promise}
|
|
277
279
|
*/
|
|
278
280
|
private recordingFacade(action: RecordingAction): Promise<any> {
|
|
281
|
+
const isPremiseRecordingEnabled = Util.isPremiseRecordingEnabled(
|
|
282
|
+
this.displayHints,
|
|
283
|
+
this.selfUserPolicies
|
|
284
|
+
);
|
|
279
285
|
LoggerProxy.logger.log(
|
|
280
286
|
`RecordingController:index#recordingFacade --> recording action [${action}]`
|
|
281
287
|
);
|
|
282
288
|
|
|
289
|
+
let recordingType: RecordingType;
|
|
290
|
+
if (isPremiseRecordingEnabled) {
|
|
291
|
+
recordingType = RecordingType.Premise;
|
|
292
|
+
} else {
|
|
293
|
+
recordingType = RecordingType.Cloud;
|
|
294
|
+
}
|
|
295
|
+
|
|
283
296
|
// assumes action is proper cased (i.e., Example)
|
|
284
297
|
if (Util?.[`canUser${action}`](this.displayHints, this.selfUserPolicies)) {
|
|
285
298
|
if (this.serviceUrl) {
|
|
286
|
-
return this.recordingService(action);
|
|
299
|
+
return this.recordingService(action, recordingType);
|
|
287
300
|
}
|
|
288
301
|
|
|
289
302
|
return this.recordingControls(action);
|
|
@@ -1,33 +1,47 @@
|
|
|
1
1
|
import {DISPLAY_HINTS, SELF_POLICY} from '../constants';
|
|
2
|
-
import RecordingAction from './enums';
|
|
2
|
+
import {RecordingAction} from './enums';
|
|
3
3
|
import MeetingUtil from '../meeting/util';
|
|
4
4
|
|
|
5
5
|
const canUserStart = (
|
|
6
6
|
displayHints: Array<string>,
|
|
7
7
|
userPolicies: Record<SELF_POLICY, boolean>
|
|
8
8
|
): boolean =>
|
|
9
|
-
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_START)
|
|
9
|
+
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_START) ||
|
|
10
|
+
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_START)) &&
|
|
10
11
|
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies);
|
|
11
12
|
|
|
12
13
|
const canUserPause = (
|
|
13
14
|
displayHints: Array<string>,
|
|
14
15
|
userPolicies: Record<SELF_POLICY, boolean>
|
|
15
16
|
): boolean =>
|
|
16
|
-
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_PAUSE)
|
|
17
|
+
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_PAUSE) ||
|
|
18
|
+
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_PAUSE)) &&
|
|
17
19
|
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies);
|
|
18
20
|
|
|
19
21
|
const canUserResume = (
|
|
20
22
|
displayHints: Array<string>,
|
|
21
23
|
userPolicies: Record<SELF_POLICY, boolean>
|
|
22
24
|
): boolean =>
|
|
23
|
-
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_RESUME)
|
|
25
|
+
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_RESUME) ||
|
|
26
|
+
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_RESUME)) &&
|
|
24
27
|
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies);
|
|
25
28
|
|
|
26
29
|
const canUserStop = (
|
|
27
30
|
displayHints: Array<string>,
|
|
28
31
|
userPolicies: Record<SELF_POLICY, boolean>
|
|
29
32
|
): boolean =>
|
|
30
|
-
displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_STOP)
|
|
33
|
+
(displayHints.includes(DISPLAY_HINTS.RECORDING_CONTROL_STOP) ||
|
|
34
|
+
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_STOP)) &&
|
|
35
|
+
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies);
|
|
36
|
+
|
|
37
|
+
const isPremiseRecordingEnabled = (
|
|
38
|
+
displayHints: Array<string>,
|
|
39
|
+
userPolicies: Record<SELF_POLICY, boolean>
|
|
40
|
+
): boolean =>
|
|
41
|
+
(displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_START) ||
|
|
42
|
+
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_PAUSE) ||
|
|
43
|
+
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_STOP) ||
|
|
44
|
+
displayHints.includes(DISPLAY_HINTS.PREMISE_RECORDING_CONTROL_RESUME)) &&
|
|
31
45
|
MeetingUtil.selfSupportsFeature(SELF_POLICY.SUPPORT_NETWORK_BASED_RECORD, userPolicies);
|
|
32
46
|
|
|
33
47
|
const extractLocusId = (url: string) => {
|
|
@@ -70,6 +84,7 @@ export default {
|
|
|
70
84
|
canUserPause,
|
|
71
85
|
canUserResume,
|
|
72
86
|
canUserStop,
|
|
87
|
+
isPremiseRecordingEnabled,
|
|
73
88
|
deriveRecordingStates,
|
|
74
89
|
extractLocusId,
|
|
75
90
|
};
|