@webex/plugin-meetings 3.11.0 → 3.12.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/aiEnableRequest/index.js +184 -0
- package/dist/aiEnableRequest/index.js.map +1 -0
- package/dist/aiEnableRequest/utils.js +36 -0
- package/dist/aiEnableRequest/utils.js.map +1 -0
- package/dist/annotation/index.js +14 -5
- package/dist/annotation/index.js.map +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/config.js +7 -2
- package/dist/config.js.map +1 -1
- package/dist/constants.js +28 -6
- package/dist/constants.js.map +1 -1
- package/dist/hashTree/constants.js +3 -1
- package/dist/hashTree/constants.js.map +1 -1
- package/dist/hashTree/hashTree.js +18 -0
- package/dist/hashTree/hashTree.js.map +1 -1
- package/dist/hashTree/hashTreeParser.js +850 -410
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/hashTree/types.js +4 -2
- package/dist/hashTree/types.js.map +1 -1
- package/dist/hashTree/utils.js +10 -0
- package/dist/hashTree/utils.js.map +1 -1
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/dist/interceptors/constant.js +12 -0
- package/dist/interceptors/constant.js.map +1 -0
- package/dist/interceptors/dataChannelAuthToken.js +290 -0
- package/dist/interceptors/dataChannelAuthToken.js.map +1 -0
- package/dist/interceptors/index.js +7 -0
- package/dist/interceptors/index.js.map +1 -1
- package/dist/interceptors/utils.js +27 -0
- package/dist/interceptors/utils.js.map +1 -0
- package/dist/interpretation/index.js +2 -2
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +5 -3
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +522 -131
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +1 -0
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/locus-info/types.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +57 -1
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/properties.js +4 -2
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +7 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +1173 -877
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +50 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +133 -3
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +117 -48
- package/dist/meetings/index.js.map +1 -1
- package/dist/member/index.js +10 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +10 -0
- package/dist/member/util.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +9 -60
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +11 -0
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/reachability/index.js +18 -10
- package/dist/reachability/index.js.map +1 -1
- package/dist/reactions/reactions.type.js.map +1 -1
- package/dist/reconnection-manager/index.js +0 -1
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/types/aiEnableRequest/index.d.ts +5 -0
- package/dist/types/aiEnableRequest/utils.d.ts +2 -0
- package/dist/types/config.d.ts +4 -0
- package/dist/types/constants.d.ts +23 -1
- package/dist/types/hashTree/constants.d.ts +1 -0
- package/dist/types/hashTree/hashTree.d.ts +7 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +122 -14
- package/dist/types/hashTree/types.d.ts +3 -0
- package/dist/types/hashTree/utils.d.ts +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/interceptors/constant.d.ts +5 -0
- package/dist/types/interceptors/dataChannelAuthToken.d.ts +43 -0
- package/dist/types/interceptors/index.d.ts +2 -1
- package/dist/types/interceptors/utils.d.ts +1 -0
- package/dist/types/locus-info/index.d.ts +60 -8
- package/dist/types/locus-info/types.d.ts +7 -0
- package/dist/types/media/MediaConnectionAwaiter.d.ts +10 -1
- package/dist/types/media/properties.d.ts +2 -1
- package/dist/types/meeting/in-meeting-actions.d.ts +6 -0
- package/dist/types/meeting/index.d.ts +61 -7
- package/dist/types/meeting/request.d.ts +16 -1
- package/dist/types/meeting/request.type.d.ts +5 -0
- package/dist/types/meeting/util.d.ts +31 -0
- package/dist/types/meetings/index.d.ts +4 -2
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/util.d.ts +5 -0
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/types/multistream/mediaRequestManager.d.ts +0 -23
- package/dist/types/reactions/reactions.type.d.ts +1 -0
- package/dist/types/webinar/utils.d.ts +6 -0
- package/dist/webinar/index.js +291 -91
- package/dist/webinar/index.js.map +1 -1
- package/dist/webinar/utils.js +25 -0
- package/dist/webinar/utils.js.map +1 -0
- package/package.json +24 -23
- package/src/aiEnableRequest/README.md +84 -0
- package/src/aiEnableRequest/index.ts +170 -0
- package/src/aiEnableRequest/utils.ts +25 -0
- package/src/annotation/index.ts +27 -7
- package/src/config.ts +4 -0
- package/src/constants.ts +29 -1
- package/src/hashTree/constants.ts +1 -0
- package/src/hashTree/hashTree.ts +17 -0
- package/src/hashTree/hashTreeParser.ts +745 -252
- package/src/hashTree/types.ts +4 -0
- package/src/hashTree/utils.ts +9 -0
- package/src/index.ts +8 -1
- package/src/interceptors/constant.ts +6 -0
- package/src/interceptors/dataChannelAuthToken.ts +170 -0
- package/src/interceptors/index.ts +2 -1
- package/src/interceptors/utils.ts +16 -0
- package/src/interpretation/index.ts +2 -2
- package/src/locus-info/controlsUtils.ts +11 -0
- package/src/locus-info/index.ts +579 -113
- package/src/locus-info/selfUtils.ts +1 -0
- package/src/locus-info/types.ts +8 -0
- package/src/media/MediaConnectionAwaiter.ts +41 -1
- package/src/media/properties.ts +3 -1
- package/src/meeting/in-meeting-actions.ts +12 -0
- package/src/meeting/index.ts +291 -76
- package/src/meeting/request.ts +42 -0
- package/src/meeting/request.type.ts +6 -0
- package/src/meeting/util.ts +160 -2
- package/src/meetings/index.ts +157 -44
- package/src/member/index.ts +10 -0
- package/src/member/util.ts +12 -0
- package/src/metrics/constants.ts +1 -0
- package/src/multistream/mediaRequestManager.ts +4 -54
- package/src/multistream/remoteMediaManager.ts +13 -0
- package/src/reachability/index.ts +9 -0
- package/src/reactions/reactions.type.ts +1 -0
- package/src/reconnection-manager/index.ts +0 -1
- package/src/webinar/index.ts +191 -6
- package/src/webinar/utils.ts +16 -0
- package/test/unit/spec/aiEnableRequest/index.ts +981 -0
- package/test/unit/spec/aiEnableRequest/utils.ts +130 -0
- package/test/unit/spec/annotation/index.ts +69 -7
- package/test/unit/spec/hashTree/hashTree.ts +66 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +2225 -189
- package/test/unit/spec/interceptors/dataChannelAuthToken.ts +210 -0
- package/test/unit/spec/interceptors/utils.ts +75 -0
- package/test/unit/spec/locus-info/controlsUtils.js +29 -0
- package/test/unit/spec/locus-info/index.js +1134 -55
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +41 -1
- package/test/unit/spec/media/properties.ts +12 -3
- package/test/unit/spec/meeting/in-meeting-actions.ts +8 -2
- package/test/unit/spec/meeting/index.js +829 -115
- package/test/unit/spec/meeting/request.js +70 -0
- package/test/unit/spec/meeting/utils.js +438 -26
- package/test/unit/spec/meetings/index.js +653 -32
- package/test/unit/spec/member/index.js +28 -4
- package/test/unit/spec/member/util.js +65 -27
- package/test/unit/spec/multistream/mediaRequestManager.ts +2 -85
- package/test/unit/spec/multistream/remoteMediaManager.ts +30 -0
- package/test/unit/spec/reachability/index.ts +23 -0
- package/test/unit/spec/reconnection-manager/index.js +4 -8
- package/test/unit/spec/webinar/index.ts +474 -37
- package/test/unit/spec/webinar/utils.ts +39 -0
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
RecommendedOpusBitrates,
|
|
11
11
|
NamedMediaGroup,
|
|
12
12
|
} from '@webex/internal-media-core';
|
|
13
|
-
import {cloneDeepWith, debounce
|
|
13
|
+
import {cloneDeepWith, debounce} from 'lodash';
|
|
14
14
|
|
|
15
15
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
16
16
|
|
|
@@ -94,8 +94,6 @@ export class MediaRequestManager {
|
|
|
94
94
|
|
|
95
95
|
private debouncedSourceUpdateListener: () => void;
|
|
96
96
|
|
|
97
|
-
private previousStreamRequests: Array<StreamRequest> = [];
|
|
98
|
-
|
|
99
97
|
private trimRequestsToNumOfSources: boolean;
|
|
100
98
|
private numTotalSources: number;
|
|
101
99
|
private numLiveSources: number;
|
|
@@ -161,36 +159,6 @@ export class MediaRequestManager {
|
|
|
161
159
|
}
|
|
162
160
|
}
|
|
163
161
|
|
|
164
|
-
/**
|
|
165
|
-
* Returns true if two stream requests are the same, false otherwise.
|
|
166
|
-
*
|
|
167
|
-
* @param {StreamRequest} streamRequestA - Stream request A for comparison.
|
|
168
|
-
* @param {StreamRequest} streamRequestB - Stream request B for comparison.
|
|
169
|
-
* @returns {boolean} - Whether they are equal.
|
|
170
|
-
*/
|
|
171
|
-
// eslint-disable-next-line class-methods-use-this
|
|
172
|
-
public isEqual(streamRequestA: StreamRequest, streamRequestB: StreamRequest) {
|
|
173
|
-
return (
|
|
174
|
-
JSON.stringify(streamRequestA._toJmpStreamRequest()) ===
|
|
175
|
-
JSON.stringify(streamRequestB._toJmpStreamRequest())
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Compares new stream requests to previous ones and determines
|
|
181
|
-
* if they are the same.
|
|
182
|
-
*
|
|
183
|
-
* @param {StreamRequest[]} newRequests - Array with new requests.
|
|
184
|
-
* @returns {boolean} - True if they are equal, false otherwise.
|
|
185
|
-
*/
|
|
186
|
-
private checkIsNewRequestsEqualToPrev(newRequests: StreamRequest[]) {
|
|
187
|
-
return (
|
|
188
|
-
!isEmpty(this.previousStreamRequests) &&
|
|
189
|
-
this.previousStreamRequests.length === newRequests.length &&
|
|
190
|
-
this.previousStreamRequests.every((req, idx) => this.isEqual(req, newRequests[idx]))
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
162
|
/**
|
|
195
163
|
* Returns the maxPayloadBitsPerSecond per Stream
|
|
196
164
|
*
|
|
@@ -230,15 +198,6 @@ export class MediaRequestManager {
|
|
|
230
198
|
return (mediaRequest.codecInfo.maxFs * maxFps) / 100;
|
|
231
199
|
}
|
|
232
200
|
|
|
233
|
-
/**
|
|
234
|
-
* Clears the previous stream requests.
|
|
235
|
-
*
|
|
236
|
-
* @returns {void}
|
|
237
|
-
*/
|
|
238
|
-
public clearPreviousRequests(): void {
|
|
239
|
-
this.previousStreamRequests = [];
|
|
240
|
-
}
|
|
241
|
-
|
|
242
201
|
/** Modifies the passed in clientRequests and makes sure that in total they don't ask
|
|
243
202
|
* for more streams than there are available.
|
|
244
203
|
*
|
|
@@ -356,7 +315,7 @@ export class MediaRequestManager {
|
|
|
356
315
|
mr.receiveSlots.map((receiveSlot) => receiveSlot.wcmeReceiveSlot),
|
|
357
316
|
this.getMaxPayloadBitsPerSecond(mr),
|
|
358
317
|
mr.codecInfo && [
|
|
359
|
-
|
|
318
|
+
WcmeCodecInfo.fromH264(
|
|
360
319
|
0x80,
|
|
361
320
|
new H264Codec(
|
|
362
321
|
mr.codecInfo.maxFs,
|
|
@@ -372,17 +331,8 @@ export class MediaRequestManager {
|
|
|
372
331
|
}
|
|
373
332
|
});
|
|
374
333
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
if (!this.checkIsNewRequestsEqualToPrev(streamRequests)) {
|
|
378
|
-
this.sendMediaRequestsCallback(streamRequests);
|
|
379
|
-
this.previousStreamRequests = streamRequests;
|
|
380
|
-
LoggerProxy.logger.info(`multistream:sendRequests --> media requests sent. `);
|
|
381
|
-
} else {
|
|
382
|
-
LoggerProxy.logger.info(
|
|
383
|
-
`multistream:sendRequests --> detected duplicate WCME requests, skipping them... `
|
|
384
|
-
);
|
|
385
|
-
}
|
|
334
|
+
this.sendMediaRequestsCallback(streamRequests);
|
|
335
|
+
LoggerProxy.logger.info(`multistream:sendRequests --> media requests sent. `);
|
|
386
336
|
}
|
|
387
337
|
|
|
388
338
|
public addRequest(mediaRequest: MediaRequest, commit = true): MediaRequestId {
|
|
@@ -67,6 +67,18 @@ const AllEqualLayout: VideoLayout = {
|
|
|
67
67
|
],
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
+
// An "all equal" grid, with size up to 5 x 5 = 25:
|
|
71
|
+
const AllEqual25Layout: VideoLayout = {
|
|
72
|
+
activeSpeakerVideoPaneGroups: [
|
|
73
|
+
{
|
|
74
|
+
id: 'main',
|
|
75
|
+
numPanes: 25,
|
|
76
|
+
size: 'best',
|
|
77
|
+
priority: 255,
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
};
|
|
81
|
+
|
|
70
82
|
// A layout with just a single remote active speaker video pane:
|
|
71
83
|
const SingleLayout: VideoLayout = {
|
|
72
84
|
activeSpeakerVideoPaneGroups: [
|
|
@@ -164,6 +176,7 @@ export const DefaultConfiguration: Configuration = {
|
|
|
164
176
|
|
|
165
177
|
layouts: {
|
|
166
178
|
AllEqual: AllEqualLayout,
|
|
179
|
+
AllEqual25: AllEqual25Layout,
|
|
167
180
|
OnePlusFive: OnePlusFiveLayout,
|
|
168
181
|
Single: SingleLayout,
|
|
169
182
|
Stage: Stage2x2With6ThumbnailsLayout,
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import {isEqual, mapValues, mean} from 'lodash';
|
|
7
7
|
|
|
8
8
|
import {Defer} from '@webex/common';
|
|
9
|
+
import {CapabilityState, WebCapabilities} from '@webex/web-capabilities';
|
|
9
10
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
10
11
|
import MeetingUtil from '../meeting/util';
|
|
11
12
|
|
|
@@ -196,6 +197,14 @@ export default class Reachability extends EventsScope {
|
|
|
196
197
|
if (!this.webex.config.meetings.enableReachabilityChecks) {
|
|
197
198
|
throw new Error('enableReachabilityChecks is disabled in config');
|
|
198
199
|
}
|
|
200
|
+
|
|
201
|
+
if (WebCapabilities.supportsRTCPeerConnection() !== CapabilityState.CAPABLE) {
|
|
202
|
+
LoggerProxy.logger.warn(
|
|
203
|
+
'Reachability:index#gatherReachability --> WebRTC API is not available, skipping reachability checks'
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
return {};
|
|
207
|
+
}
|
|
199
208
|
// Fetch clusters and measure latency
|
|
200
209
|
try {
|
|
201
210
|
this.lastTrigger = trigger;
|
|
@@ -609,7 +609,6 @@ export default class ReconnectionManager {
|
|
|
609
609
|
if (this.meeting.isMultistream) {
|
|
610
610
|
Object.values(this.meeting.mediaRequestManagers).forEach(
|
|
611
611
|
(mediaRequestManager: MediaRequestManager) => {
|
|
612
|
-
mediaRequestManager.clearPreviousRequests();
|
|
613
612
|
mediaRequestManager.commit();
|
|
614
613
|
}
|
|
615
614
|
);
|
package/src/webinar/index.ts
CHANGED
|
@@ -4,10 +4,21 @@
|
|
|
4
4
|
import {WebexPlugin, config} from '@webex/webex-core';
|
|
5
5
|
import uuid from 'uuid';
|
|
6
6
|
import {get} from 'lodash';
|
|
7
|
-
import {
|
|
7
|
+
import {DataChannelTokenType} from '@webex/internal-plugin-llm';
|
|
8
|
+
import {
|
|
9
|
+
_ID_,
|
|
10
|
+
HEADERS,
|
|
11
|
+
HTTP_VERBS,
|
|
12
|
+
MEETINGS,
|
|
13
|
+
SELF_ROLES,
|
|
14
|
+
SHARE_STATUS,
|
|
15
|
+
DEFAULT_LARGE_SCALE_WEBINAR_ATTENDEE_SEARCH_LIMIT,
|
|
16
|
+
LLM_PRACTICE_SESSION,
|
|
17
|
+
} from '../constants';
|
|
8
18
|
|
|
9
19
|
import WebinarCollection from './collection';
|
|
10
20
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
21
|
+
import {sanitizeParams} from './utils';
|
|
11
22
|
|
|
12
23
|
/**
|
|
13
24
|
* @class Webinar
|
|
@@ -28,6 +39,14 @@ const Webinar = WebexPlugin.extend({
|
|
|
28
39
|
meetingId: 'string',
|
|
29
40
|
},
|
|
30
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Calls this to clean up listeners
|
|
44
|
+
* @returns {void}
|
|
45
|
+
*/
|
|
46
|
+
cleanUp() {
|
|
47
|
+
this.cleanupPSDataChannel();
|
|
48
|
+
},
|
|
49
|
+
|
|
31
50
|
/**
|
|
32
51
|
* Update the current locus url of the webinar
|
|
33
52
|
* @param {string} locusUrl
|
|
@@ -96,10 +115,7 @@ const Webinar = WebexPlugin.extend({
|
|
|
96
115
|
meeting?.locusInfo?.updateMediaShares(meeting?.locusInfo?.mediaShares, true);
|
|
97
116
|
}
|
|
98
117
|
|
|
99
|
-
|
|
100
|
-
// may need change data channel in practice session
|
|
101
|
-
meeting?.updateLLMConnection();
|
|
102
|
-
}
|
|
118
|
+
this.updatePSDataChannel();
|
|
103
119
|
},
|
|
104
120
|
|
|
105
121
|
/**
|
|
@@ -110,6 +126,141 @@ const Webinar = WebexPlugin.extend({
|
|
|
110
126
|
return this.selfIsPanelist && this.practiceSessionEnabled;
|
|
111
127
|
},
|
|
112
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Disconnects the practice session data channel and removes its relay listener.
|
|
131
|
+
* @returns {Promise<void>}
|
|
132
|
+
*/
|
|
133
|
+
async cleanupPSDataChannel() {
|
|
134
|
+
if (this._pendingOnlineListener) {
|
|
135
|
+
// @ts-ignore - Fix type
|
|
136
|
+
this.webex.internal.llm.off('online', this._pendingOnlineListener);
|
|
137
|
+
this._pendingOnlineListener = null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const meeting = this.webex.meetings.getMeetingByType(_ID_, this.meetingId);
|
|
141
|
+
|
|
142
|
+
// @ts-ignore - Fix type
|
|
143
|
+
await this.webex.internal.llm.disconnectLLM(
|
|
144
|
+
{
|
|
145
|
+
code: 3050,
|
|
146
|
+
reason: 'done (permanent)',
|
|
147
|
+
},
|
|
148
|
+
LLM_PRACTICE_SESSION
|
|
149
|
+
);
|
|
150
|
+
// @ts-ignore - Fix type
|
|
151
|
+
this.webex.internal.llm.off(
|
|
152
|
+
`event:relay.event:${LLM_PRACTICE_SESSION}`,
|
|
153
|
+
meeting?.processRelayEvent
|
|
154
|
+
);
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Connects to low latency mercury and reconnects if the address has changed
|
|
159
|
+
* It will also disconnect if called when the meeting has ended
|
|
160
|
+
* @returns {Promise}
|
|
161
|
+
*/
|
|
162
|
+
async updatePSDataChannel() {
|
|
163
|
+
const meeting = this.webex.meetings.getMeetingByType(_ID_, this.meetingId);
|
|
164
|
+
const isPracticeSession = meeting?.isJoined() && this.isJoinPracticeSessionDataChannel();
|
|
165
|
+
|
|
166
|
+
if (!isPracticeSession) {
|
|
167
|
+
await this.cleanupPSDataChannel();
|
|
168
|
+
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// @ts-ignore - Fix type
|
|
173
|
+
const {url = undefined, info: {practiceSessionDatachannelUrl = undefined} = {}} =
|
|
174
|
+
meeting?.locusInfo || {};
|
|
175
|
+
|
|
176
|
+
// @ts-ignore
|
|
177
|
+
const practiceSessionDatachannelToken = this.webex.internal.llm.getDatachannelToken(
|
|
178
|
+
DataChannelTokenType.PracticeSession
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const isCaptionBoxOn = this.webex.internal.voicea.getIsCaptionBoxOn();
|
|
182
|
+
|
|
183
|
+
if (!practiceSessionDatachannelUrl) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
// @ts-ignore - Fix type
|
|
187
|
+
if (this.webex.internal.llm.isConnected(LLM_PRACTICE_SESSION)) {
|
|
188
|
+
if (
|
|
189
|
+
// @ts-ignore - Fix type
|
|
190
|
+
url === this.webex.internal.llm.getLocusUrl(LLM_PRACTICE_SESSION) &&
|
|
191
|
+
// @ts-ignore - Fix type
|
|
192
|
+
practiceSessionDatachannelUrl ===
|
|
193
|
+
this.webex.internal.llm.getDatachannelUrl(LLM_PRACTICE_SESSION)
|
|
194
|
+
) {
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
await this.cleanupPSDataChannel();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Ensure the default session data channel is connected before connecting the practice session.
|
|
202
|
+
// Subscribe before checking isConnected() to avoid a race where the 'online' event fires
|
|
203
|
+
// between the check and the subscription — Mercury does not replay missed events.
|
|
204
|
+
if (!this._pendingOnlineListener) {
|
|
205
|
+
const onDefaultSessionConnected = () => {
|
|
206
|
+
this._pendingOnlineListener = null;
|
|
207
|
+
// @ts-ignore - Fix type
|
|
208
|
+
this.webex.internal.llm.off('online', onDefaultSessionConnected);
|
|
209
|
+
this.updatePSDataChannel();
|
|
210
|
+
};
|
|
211
|
+
this._pendingOnlineListener = onDefaultSessionConnected;
|
|
212
|
+
// @ts-ignore - Fix type
|
|
213
|
+
this.webex.internal.llm.on('online', onDefaultSessionConnected);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// @ts-ignore - Fix type
|
|
217
|
+
if (!this.webex.internal.llm.isConnected()) {
|
|
218
|
+
LoggerProxy.logger.info(
|
|
219
|
+
'Webinar:index#updatePSDataChannel --> default session not yet connected, deferring practice session connect.'
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Default session is already connected — cancel the pending listener and proceed
|
|
226
|
+
if (this._pendingOnlineListener) {
|
|
227
|
+
// @ts-ignore - Fix type
|
|
228
|
+
this.webex.internal.llm.off('online', this._pendingOnlineListener);
|
|
229
|
+
this._pendingOnlineListener = null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// @ts-ignore - Fix type
|
|
233
|
+
return this.webex.internal.llm
|
|
234
|
+
.registerAndConnect(
|
|
235
|
+
url,
|
|
236
|
+
practiceSessionDatachannelUrl,
|
|
237
|
+
practiceSessionDatachannelToken,
|
|
238
|
+
LLM_PRACTICE_SESSION
|
|
239
|
+
)
|
|
240
|
+
.then((registerAndConnectResult) => {
|
|
241
|
+
// @ts-ignore - Fix type
|
|
242
|
+
this.webex.internal.llm.off(
|
|
243
|
+
`event:relay.event:${LLM_PRACTICE_SESSION}`,
|
|
244
|
+
meeting?.processRelayEvent
|
|
245
|
+
);
|
|
246
|
+
// @ts-ignore - Fix type
|
|
247
|
+
this.webex.internal.llm.on(
|
|
248
|
+
`event:relay.event:${LLM_PRACTICE_SESSION}`,
|
|
249
|
+
meeting?.processRelayEvent
|
|
250
|
+
);
|
|
251
|
+
// @ts-ignore - Fix type
|
|
252
|
+
this.webex.internal.voicea?.announce?.();
|
|
253
|
+
if (isCaptionBoxOn) {
|
|
254
|
+
this.webex.internal.voicea.updateSubchannelSubscriptions({subscribe: ['transcription']});
|
|
255
|
+
}
|
|
256
|
+
LoggerProxy.logger.info(
|
|
257
|
+
`Webinar:index#updatePSDataChannel --> enabled to receive relay events for default session for ${LLM_PRACTICE_SESSION}!`
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
return Promise.resolve(registerAndConnectResult);
|
|
261
|
+
});
|
|
262
|
+
},
|
|
263
|
+
|
|
113
264
|
/**
|
|
114
265
|
* start or stop practice session for webinar
|
|
115
266
|
* @param {boolean} enabled
|
|
@@ -137,6 +288,7 @@ const Webinar = WebexPlugin.extend({
|
|
|
137
288
|
*/
|
|
138
289
|
updatePracticeSessionStatus(payload) {
|
|
139
290
|
this.set('practiceSessionEnabled', !!payload?.enabled);
|
|
291
|
+
this.updatePSDataChannel().then(() => {});
|
|
140
292
|
},
|
|
141
293
|
|
|
142
294
|
/**
|
|
@@ -243,7 +395,6 @@ const Webinar = WebexPlugin.extend({
|
|
|
243
395
|
|
|
244
396
|
/**
|
|
245
397
|
* view all webcast attendees
|
|
246
|
-
* @param {string} queryString
|
|
247
398
|
* @returns {Promise}
|
|
248
399
|
*/
|
|
249
400
|
async viewAllWebcastAttendees() {
|
|
@@ -297,6 +448,40 @@ const Webinar = WebexPlugin.extend({
|
|
|
297
448
|
throw error;
|
|
298
449
|
});
|
|
299
450
|
},
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* search large scale webinar attendees
|
|
454
|
+
* @param {object} payload
|
|
455
|
+
* @param {string} payload.queryString
|
|
456
|
+
* @param {number} payload.limit
|
|
457
|
+
* @param {string} payload.next
|
|
458
|
+
* @returns {Promise}
|
|
459
|
+
*/
|
|
460
|
+
async searchLargeScaleWebinarAttendees(payload) {
|
|
461
|
+
const meeting = this.webex.meetings.getMeetingByType(_ID_, this.meetingId);
|
|
462
|
+
const rawParams = {
|
|
463
|
+
search_text: payload?.queryString,
|
|
464
|
+
limit: payload?.limit ?? DEFAULT_LARGE_SCALE_WEBINAR_ATTENDEE_SEARCH_LIMIT,
|
|
465
|
+
next: payload?.next,
|
|
466
|
+
};
|
|
467
|
+
const attendeeSearchUrl = meeting?.locusInfo?.links?.resources?.attendeeSearch?.url;
|
|
468
|
+
if (!attendeeSearchUrl) {
|
|
469
|
+
LoggerProxy.logger.error(`Meeting:webinar5k#searchLargeScaleWebinarAttendees failed`);
|
|
470
|
+
throw new Error('Meeting:webinar5k#Attendee search url is not available');
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return this.request({
|
|
474
|
+
method: HTTP_VERBS.GET,
|
|
475
|
+
uri: `${attendeeSearchUrl}?${new URLSearchParams(sanitizeParams(rawParams)).toString()}`,
|
|
476
|
+
headers: {
|
|
477
|
+
authorization: await this.webex.credentials.getUserToken(),
|
|
478
|
+
trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
|
|
479
|
+
},
|
|
480
|
+
}).catch((error) => {
|
|
481
|
+
LoggerProxy.logger.error('Meeting:webinar5k#searchLargeScaleWebinarAttendees failed', error);
|
|
482
|
+
throw error;
|
|
483
|
+
});
|
|
484
|
+
},
|
|
300
485
|
});
|
|
301
486
|
|
|
302
487
|
export default Webinar;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove null/undefined/empty string values from an object
|
|
3
|
+
* @param {object} params
|
|
4
|
+
* @returns {object}
|
|
5
|
+
*/
|
|
6
|
+
export const sanitizeParams = (params: Record<string, any>) => {
|
|
7
|
+
const result: Record<string, any> = {};
|
|
8
|
+
Object.keys(params).forEach((key) => {
|
|
9
|
+
const value = params[key];
|
|
10
|
+
if (value !== null && value !== undefined && value !== '') {
|
|
11
|
+
result[key] = value;
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
return result;
|
|
16
|
+
};
|