@webex/plugin-meetings 3.11.0-next.4 → 3.11.0-next.41
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 +3 -3
- 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 +5 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +26 -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 +709 -380
- 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 +233 -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/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 +125 -68
- 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 +209 -90
- 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 +128 -2
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +78 -36
- 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 +1 -1
- 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/reactions/reactions.type.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 +3 -0
- package/dist/types/constants.d.ts +21 -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 +99 -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 +35 -0
- package/dist/types/interceptors/index.d.ts +2 -1
- package/dist/types/locus-info/index.d.ts +9 -2
- package/dist/types/locus-info/types.d.ts +1 -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 +24 -2
- 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/reactions/reactions.type.d.ts +1 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +22 -22
- 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 +7 -4
- package/src/config.ts +3 -0
- package/src/constants.ts +26 -1
- package/src/hashTree/constants.ts +1 -0
- package/src/hashTree/hashTree.ts +17 -0
- package/src/hashTree/hashTreeParser.ts +627 -249
- 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 +142 -0
- package/src/interceptors/index.ts +2 -1
- package/src/interpretation/index.ts +2 -2
- package/src/locus-info/controlsUtils.ts +11 -0
- package/src/locus-info/index.ts +146 -58
- package/src/locus-info/selfUtils.ts +1 -0
- package/src/locus-info/types.ts +1 -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 +127 -17
- package/src/meeting/request.ts +42 -0
- package/src/meeting/request.type.ts +6 -0
- package/src/meeting/util.ts +156 -1
- package/src/meetings/index.ts +94 -9
- 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 +1 -1
- package/src/multistream/remoteMediaManager.ts +13 -0
- package/src/reactions/reactions.type.ts +1 -0
- package/test/unit/spec/aiEnableRequest/index.ts +981 -0
- package/test/unit/spec/aiEnableRequest/utils.ts +130 -0
- package/test/unit/spec/hashTree/hashTree.ts +66 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +1869 -189
- package/test/unit/spec/interceptors/dataChannelAuthToken.ts +141 -0
- package/test/unit/spec/locus-info/controlsUtils.js +29 -0
- package/test/unit/spec/locus-info/index.js +201 -45
- 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 +441 -75
- package/test/unit/spec/meeting/request.js +64 -0
- package/test/unit/spec/meeting/utils.js +433 -22
- package/test/unit/spec/meetings/index.js +550 -10
- package/test/unit/spec/member/index.js +28 -4
- package/test/unit/spec/member/util.js +65 -27
- package/test/unit/spec/multistream/remoteMediaManager.ts +30 -0
package/src/locus-info/index.ts
CHANGED
|
@@ -35,10 +35,11 @@ import HashTreeParser, {
|
|
|
35
35
|
DataSet,
|
|
36
36
|
HashTreeMessage,
|
|
37
37
|
LocusInfoUpdateType,
|
|
38
|
+
Metadata,
|
|
38
39
|
} from '../hashTree/hashTreeParser';
|
|
39
40
|
import {HashTreeObject, ObjectType, ObjectTypeToLocusKeyMap} from '../hashTree/types';
|
|
40
|
-
import {
|
|
41
|
-
import {Links, LocusDTO
|
|
41
|
+
import {isMetadata} from '../hashTree/utils';
|
|
42
|
+
import {Links, LocusDTO} from './types';
|
|
42
43
|
|
|
43
44
|
export type LocusLLMEvent = {
|
|
44
45
|
data: {
|
|
@@ -52,6 +53,7 @@ export type LocusLLMEvent = {
|
|
|
52
53
|
const LocusDtoTopLevelKeys = [
|
|
53
54
|
'controls',
|
|
54
55
|
'fullState',
|
|
56
|
+
'embeddedApps',
|
|
55
57
|
'host',
|
|
56
58
|
'info',
|
|
57
59
|
'links',
|
|
@@ -69,6 +71,7 @@ const LocusDtoTopLevelKeys = [
|
|
|
69
71
|
export type LocusApiResponseBody = {
|
|
70
72
|
dataSets?: DataSet[];
|
|
71
73
|
locus: LocusDTO; // this LocusDTO here might not be the full one (for example it won't have all the participants, but it should have self)
|
|
74
|
+
metadata?: Metadata;
|
|
72
75
|
};
|
|
73
76
|
|
|
74
77
|
const LocusObjectStateAfterUpdates = {
|
|
@@ -239,7 +242,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
239
242
|
'Locus-info:index#doLocusSync --> got full DTO when we asked for delta'
|
|
240
243
|
);
|
|
241
244
|
}
|
|
242
|
-
meeting.locusInfo.onFullLocus(res.body);
|
|
245
|
+
meeting.locusInfo.onFullLocus('classic Locus sync', res.body);
|
|
243
246
|
})
|
|
244
247
|
.catch((e) => {
|
|
245
248
|
LoggerProxy.logger.info(
|
|
@@ -362,17 +365,21 @@ export default class LocusInfo extends EventsScope {
|
|
|
362
365
|
*/
|
|
363
366
|
private createHashTreeParser({
|
|
364
367
|
initialLocus,
|
|
368
|
+
metadata,
|
|
365
369
|
}: {
|
|
366
370
|
initialLocus: {
|
|
367
371
|
dataSets: Array<DataSet>;
|
|
368
372
|
locus: any;
|
|
369
373
|
};
|
|
374
|
+
metadata: Metadata;
|
|
370
375
|
}) {
|
|
371
376
|
return new HashTreeParser({
|
|
372
377
|
initialLocus,
|
|
378
|
+
metadata,
|
|
373
379
|
webexRequest: this.webex.request.bind(this.webex),
|
|
374
380
|
locusInfoUpdateCallback: this.updateFromHashTree.bind(this),
|
|
375
381
|
debugId: `HT-${this.meetingId.substring(0, 4)}`,
|
|
382
|
+
excludedDataSets: this.webex.config.meetings.locus?.excludedDataSets,
|
|
376
383
|
});
|
|
377
384
|
}
|
|
378
385
|
|
|
@@ -387,6 +394,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
387
394
|
trigger: 'join-response';
|
|
388
395
|
locus: LocusDTO;
|
|
389
396
|
dataSets?: DataSet[];
|
|
397
|
+
metadata?: Metadata;
|
|
390
398
|
}
|
|
391
399
|
| {
|
|
392
400
|
trigger: 'locus-message';
|
|
@@ -401,28 +409,36 @@ export default class LocusInfo extends EventsScope {
|
|
|
401
409
|
switch (data.trigger) {
|
|
402
410
|
case 'locus-message':
|
|
403
411
|
if (data.hashTreeMessage) {
|
|
404
|
-
// we need the
|
|
412
|
+
// we need the Metadata object to be in the received message, because it contains visibleDataSets
|
|
405
413
|
// and these are needed to initialize all the hash trees
|
|
406
|
-
const
|
|
414
|
+
const metadataObject = data.hashTreeMessage.locusStateElements?.find((el) =>
|
|
415
|
+
isMetadata(el)
|
|
416
|
+
);
|
|
407
417
|
|
|
408
|
-
if (!
|
|
409
|
-
|
|
410
|
-
|
|
418
|
+
if (!metadataObject?.data?.visibleDataSets) {
|
|
419
|
+
// this is a common case (not an error)
|
|
420
|
+
// it happens for example after we leave the meeting and still get some heartbeats or delayed messages
|
|
421
|
+
LoggerProxy.logger.info(
|
|
422
|
+
`Locus-info:index#initialSetup --> cannot initialize HashTreeParser, Metadata object with visibleDataSets is missing in the message`
|
|
411
423
|
);
|
|
412
424
|
|
|
413
|
-
throw
|
|
425
|
+
// throw so that handleLocusEvent() catches it and destroys the partially created meeting object
|
|
426
|
+
throw new Error('Metadata object with visibleDataSets is missing in the message');
|
|
414
427
|
}
|
|
415
428
|
|
|
416
429
|
LoggerProxy.logger.info(
|
|
417
430
|
'Locus-info:index#initialSetup --> creating HashTreeParser from message'
|
|
418
431
|
);
|
|
419
432
|
// first create the HashTreeParser, but don't initialize it with any data yet
|
|
420
|
-
// pass just a fake locus that contains only the visibleDataSets
|
|
421
433
|
this.hashTreeParser = this.createHashTreeParser({
|
|
422
434
|
initialLocus: {
|
|
423
|
-
locus:
|
|
435
|
+
locus: null,
|
|
424
436
|
dataSets: [], // empty, because they will be populated in initializeFromMessage() call // dataSets: data.hashTreeMessage.dataSets,
|
|
425
437
|
},
|
|
438
|
+
metadata: {
|
|
439
|
+
htMeta: metadataObject.htMeta,
|
|
440
|
+
visibleDataSets: metadataObject.data.visibleDataSets,
|
|
441
|
+
},
|
|
426
442
|
});
|
|
427
443
|
|
|
428
444
|
// now handle the message - that should populate all the visible datasets
|
|
@@ -430,12 +446,12 @@ export default class LocusInfo extends EventsScope {
|
|
|
430
446
|
} else {
|
|
431
447
|
// "classic" Locus case, no hash trees involved
|
|
432
448
|
this.updateLocusCache(data.locus);
|
|
433
|
-
this.onFullLocus(data.locus, undefined);
|
|
449
|
+
this.onFullLocus('classic locus message', data.locus, undefined);
|
|
434
450
|
}
|
|
435
451
|
break;
|
|
436
452
|
case 'join-response':
|
|
437
453
|
this.updateLocusCache(data.locus);
|
|
438
|
-
this.onFullLocus(data.locus, undefined, data.dataSets);
|
|
454
|
+
this.onFullLocus('join response', data.locus, undefined, data.dataSets, data.metadata);
|
|
439
455
|
break;
|
|
440
456
|
case 'get-loci-response':
|
|
441
457
|
if (data.locus?.links?.resources?.visibleDataSets?.url) {
|
|
@@ -443,12 +459,12 @@ export default class LocusInfo extends EventsScope {
|
|
|
443
459
|
'Locus-info:index#initialSetup --> creating HashTreeParser from get-loci-response'
|
|
444
460
|
);
|
|
445
461
|
// first create the HashTreeParser, but don't initialize it with any data yet
|
|
446
|
-
// pass just a fake locus that contains only the visibleDataSets
|
|
447
462
|
this.hashTreeParser = this.createHashTreeParser({
|
|
448
463
|
initialLocus: {
|
|
449
|
-
locus:
|
|
464
|
+
locus: null,
|
|
450
465
|
dataSets: [], // empty, because we don't have them yet
|
|
451
466
|
},
|
|
467
|
+
metadata: null, // get-loci-response doesn't contain Metadata object
|
|
452
468
|
});
|
|
453
469
|
|
|
454
470
|
// now initialize all the data
|
|
@@ -456,7 +472,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
456
472
|
} else {
|
|
457
473
|
// "classic" Locus case, no hash trees involved
|
|
458
474
|
this.updateLocusCache(data.locus);
|
|
459
|
-
this.onFullLocus(data.locus, undefined);
|
|
475
|
+
this.onFullLocus('classic get-loci-response', data.locus, undefined);
|
|
460
476
|
}
|
|
461
477
|
}
|
|
462
478
|
// Change it to true after it receives it first locus object
|
|
@@ -571,6 +587,31 @@ export default class LocusInfo extends EventsScope {
|
|
|
571
587
|
);
|
|
572
588
|
}
|
|
573
589
|
break;
|
|
590
|
+
case ObjectType.embeddedApp:
|
|
591
|
+
if (object.data) {
|
|
592
|
+
LoggerProxy.logger.info(
|
|
593
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> embeddedApp id=${object.htMeta.elementId.id} url='${object.data.url}' updated version=${object.htMeta.elementId.version}:`,
|
|
594
|
+
object.data
|
|
595
|
+
);
|
|
596
|
+
const existingEmbeddedApp = locus.embeddedApps?.find(
|
|
597
|
+
(ms) => ms.htMeta.elementId.id === object.htMeta.elementId.id
|
|
598
|
+
);
|
|
599
|
+
|
|
600
|
+
if (existingEmbeddedApp) {
|
|
601
|
+
Object.assign(existingEmbeddedApp, object.data);
|
|
602
|
+
} else {
|
|
603
|
+
locus.embeddedApps = locus.embeddedApps || [];
|
|
604
|
+
locus.embeddedApps.push(object.data);
|
|
605
|
+
}
|
|
606
|
+
} else {
|
|
607
|
+
LoggerProxy.logger.info(
|
|
608
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> embeddedApp id=${object.htMeta.elementId.id} removed, version=${object.htMeta.elementId.version}`
|
|
609
|
+
);
|
|
610
|
+
locus.embeddedApps = locus.embeddedApps?.filter(
|
|
611
|
+
(ms) => ms.htMeta.elementId.id !== object.htMeta.elementId.id
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
break;
|
|
574
615
|
case ObjectType.participant:
|
|
575
616
|
LoggerProxy.logger.info(
|
|
576
617
|
`Locus-info:index#updateLocusFromHashTreeObject --> participant id=${
|
|
@@ -643,6 +684,12 @@ export default class LocusInfo extends EventsScope {
|
|
|
643
684
|
}
|
|
644
685
|
}
|
|
645
686
|
break;
|
|
687
|
+
case ObjectType.metadata:
|
|
688
|
+
LoggerProxy.logger.info(
|
|
689
|
+
`Locus-info:index#updateLocusFromHashTreeObject --> metadata object updated to version ${object.htMeta.elementId.version}`
|
|
690
|
+
);
|
|
691
|
+
// we don't use hash tree metadata right now for anything, it's mainly used internally by HashTreeParser
|
|
692
|
+
break;
|
|
646
693
|
default:
|
|
647
694
|
LoggerProxy.logger.warn(
|
|
648
695
|
`Locus-info:index#updateLocusFromHashTreeObject --> received unsupported object type ${type}`
|
|
@@ -683,7 +730,11 @@ export default class LocusInfo extends EventsScope {
|
|
|
683
730
|
* @param {HashTreeMessage} message incoming hash tree message
|
|
684
731
|
* @returns {void}
|
|
685
732
|
*/
|
|
686
|
-
private handleHashTreeMessage(
|
|
733
|
+
private async handleHashTreeMessage(
|
|
734
|
+
meeting: any,
|
|
735
|
+
eventType: LOCUSEVENT,
|
|
736
|
+
message: HashTreeMessage
|
|
737
|
+
) {
|
|
687
738
|
if (eventType !== LOCUSEVENT.HASH_TREE_DATA_UPDATED) {
|
|
688
739
|
this.sendClassicVsHashTreeMismatchMetric(
|
|
689
740
|
meeting,
|
|
@@ -815,8 +866,18 @@ export default class LocusInfo extends EventsScope {
|
|
|
815
866
|
data.stateElementsMessage as HashTreeMessage
|
|
816
867
|
);
|
|
817
868
|
} else {
|
|
818
|
-
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
819
869
|
const {eventType} = data;
|
|
870
|
+
|
|
871
|
+
if (eventType === LOCUSEVENT.HASH_TREE_DATA_UPDATED) {
|
|
872
|
+
// this can happen when we get an event before join http response
|
|
873
|
+
// it's OK to just ignore it
|
|
874
|
+
LoggerProxy.logger.info(
|
|
875
|
+
`Locus-info:index#parse --> received locus hash tree event before hashTreeParser is created`
|
|
876
|
+
);
|
|
877
|
+
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
|
|
820
881
|
const locus = this.getTheLocusToUpdate(data.locus);
|
|
821
882
|
LoggerProxy.logger.info(`Locus-info:index#parse --> received locus data: ${eventType}`);
|
|
822
883
|
|
|
@@ -837,17 +898,11 @@ export default class LocusInfo extends EventsScope {
|
|
|
837
898
|
case LOCUSEVENT.PARTICIPANT_DECLINED:
|
|
838
899
|
case LOCUSEVENT.FLOOR_GRANTED:
|
|
839
900
|
case LOCUSEVENT.FLOOR_RELEASED:
|
|
840
|
-
this.onFullLocus(locus, eventType);
|
|
901
|
+
this.onFullLocus(`classic locus event ${eventType}`, locus, eventType);
|
|
841
902
|
break;
|
|
842
903
|
case LOCUSEVENT.DIFFERENCE:
|
|
843
904
|
this.handleLocusDelta(locus, meeting);
|
|
844
905
|
break;
|
|
845
|
-
case LOCUSEVENT.HASH_TREE_DATA_UPDATED:
|
|
846
|
-
this.sendClassicVsHashTreeMismatchMetric(
|
|
847
|
-
meeting,
|
|
848
|
-
`got ${eventType}, expected classic events`
|
|
849
|
-
);
|
|
850
|
-
break;
|
|
851
906
|
|
|
852
907
|
default:
|
|
853
908
|
// Why will there be a event with no eventType ????
|
|
@@ -871,22 +926,35 @@ export default class LocusInfo extends EventsScope {
|
|
|
871
926
|
/**
|
|
872
927
|
* Function for handling full locus when it's using hash trees (so not the "classic" one).
|
|
873
928
|
*
|
|
929
|
+
* @param {string} debugText string explaining the trigger for this call, added to logs for debugging purposes
|
|
874
930
|
* @param {object} locus locus object
|
|
931
|
+
* @param {object} metadata locus hash trees metadata
|
|
875
932
|
* @param {string} eventType locus event
|
|
876
933
|
* @param {DataSet[]} dataSets
|
|
877
934
|
* @returns {void}
|
|
878
935
|
*/
|
|
879
|
-
private onFullLocusWithHashTrees(
|
|
936
|
+
private onFullLocusWithHashTrees(
|
|
937
|
+
debugText: string,
|
|
938
|
+
locus: any,
|
|
939
|
+
metadata: Metadata,
|
|
940
|
+
eventType?: string,
|
|
941
|
+
dataSets?: Array<DataSet>
|
|
942
|
+
) {
|
|
880
943
|
if (!this.hashTreeParser) {
|
|
881
|
-
LoggerProxy.logger.info(`Locus-info:index#onFullLocus --> creating hash tree parser`);
|
|
882
944
|
LoggerProxy.logger.info(
|
|
883
|
-
|
|
945
|
+
`Locus-info:index#onFullLocus (${debugText}) --> creating hash tree parser`
|
|
946
|
+
);
|
|
947
|
+
LoggerProxy.logger.info(
|
|
948
|
+
`Locus-info:index#onFullLocus (${debugText}) --> dataSets:`,
|
|
884
949
|
dataSets,
|
|
885
950
|
' and locus:',
|
|
886
|
-
locus
|
|
951
|
+
locus,
|
|
952
|
+
' and metadata:',
|
|
953
|
+
metadata
|
|
887
954
|
);
|
|
888
955
|
this.hashTreeParser = this.createHashTreeParser({
|
|
889
956
|
initialLocus: {locus, dataSets},
|
|
957
|
+
metadata,
|
|
890
958
|
});
|
|
891
959
|
this.onFullLocusCommon(locus, eventType);
|
|
892
960
|
} else {
|
|
@@ -894,23 +962,24 @@ export default class LocusInfo extends EventsScope {
|
|
|
894
962
|
// so treat it like if we just got it in any api response
|
|
895
963
|
|
|
896
964
|
LoggerProxy.logger.info(
|
|
897
|
-
|
|
965
|
+
`Locus-info:index#onFullLocus (${debugText}) --> hash tree parser already exists, handling it like a normal API response`
|
|
898
966
|
);
|
|
899
|
-
this.handleLocusAPIResponse(undefined, {dataSets, locus});
|
|
967
|
+
this.handleLocusAPIResponse(undefined, {dataSets, locus, metadata});
|
|
900
968
|
}
|
|
901
969
|
}
|
|
902
970
|
|
|
903
971
|
/**
|
|
904
972
|
* Function for handling full locus when it's the "classic" one (not hash trees)
|
|
905
973
|
*
|
|
974
|
+
* @param {string} debugText string explaining the trigger for this call, added to logs for debugging purposes
|
|
906
975
|
* @param {object} locus locus object
|
|
907
976
|
* @param {string} eventType locus event
|
|
908
977
|
* @returns {void}
|
|
909
978
|
*/
|
|
910
|
-
private onFullLocusClassic(locus: any, eventType?: string) {
|
|
979
|
+
private onFullLocusClassic(debugText: string, locus: any, eventType?: string) {
|
|
911
980
|
if (!this.locusParser.isNewFullLocus(locus)) {
|
|
912
981
|
LoggerProxy.logger.info(
|
|
913
|
-
`Locus-info:index#onFullLocus --> ignoring old full locus DTO, eventType=${eventType}`
|
|
982
|
+
`Locus-info:index#onFullLocus (${debugText}) --> ignoring old full locus DTO, eventType=${eventType}`
|
|
914
983
|
);
|
|
915
984
|
|
|
916
985
|
return;
|
|
@@ -920,24 +989,37 @@ export default class LocusInfo extends EventsScope {
|
|
|
920
989
|
|
|
921
990
|
/**
|
|
922
991
|
* updates the locus with full locus object
|
|
992
|
+
* @param {string} debugText string explaining the trigger for this call, added to logs for debugging purposes
|
|
923
993
|
* @param {object} locus locus object
|
|
924
994
|
* @param {string} eventType locus event
|
|
925
995
|
* @param {DataSet[]} dataSets
|
|
996
|
+
* @param {object} metadata locus hash trees metadata
|
|
926
997
|
* @returns {object} null
|
|
927
998
|
* @memberof LocusInfo
|
|
928
999
|
*/
|
|
929
|
-
onFullLocus(
|
|
1000
|
+
onFullLocus(
|
|
1001
|
+
debugText: string,
|
|
1002
|
+
locus: any,
|
|
1003
|
+
eventType?: string,
|
|
1004
|
+
dataSets?: Array<DataSet>,
|
|
1005
|
+
metadata?: Metadata
|
|
1006
|
+
) {
|
|
930
1007
|
if (!locus) {
|
|
931
1008
|
LoggerProxy.logger.error(
|
|
932
|
-
|
|
1009
|
+
`Locus-info:index#onFullLocus (${debugText}) --> object passed as argument was invalid, continuing.`
|
|
933
1010
|
);
|
|
934
1011
|
}
|
|
935
1012
|
|
|
936
1013
|
if (dataSets) {
|
|
1014
|
+
if (!metadata) {
|
|
1015
|
+
throw new Error(
|
|
1016
|
+
`Locus-info:index#onFullLocus (${debugText}) --> hash tree metadata is missing with full Locus`
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
937
1019
|
// this is the new hashmap Locus DTO format (only applicable to webinars for now)
|
|
938
|
-
this.onFullLocusWithHashTrees(locus, eventType, dataSets);
|
|
1020
|
+
this.onFullLocusWithHashTrees(debugText, locus, metadata, eventType, dataSets);
|
|
939
1021
|
} else {
|
|
940
|
-
this.onFullLocusClassic(locus, eventType);
|
|
1022
|
+
this.onFullLocusClassic(debugText, locus, eventType);
|
|
941
1023
|
}
|
|
942
1024
|
}
|
|
943
1025
|
|
|
@@ -1217,27 +1299,6 @@ export default class LocusInfo extends EventsScope {
|
|
|
1217
1299
|
shouldLeave: false,
|
|
1218
1300
|
}
|
|
1219
1301
|
);
|
|
1220
|
-
} else if (this.fullState && this.fullState.removed) {
|
|
1221
|
-
// user has been dropped from a meeting
|
|
1222
|
-
|
|
1223
|
-
// @ts-ignore
|
|
1224
|
-
this.webex.internal.newMetrics.submitClientEvent({
|
|
1225
|
-
name: 'client.call.remote-ended',
|
|
1226
|
-
options: {
|
|
1227
|
-
meetingId: this.meetingId,
|
|
1228
|
-
},
|
|
1229
|
-
});
|
|
1230
|
-
this.emitScoped(
|
|
1231
|
-
{
|
|
1232
|
-
file: 'locus-info',
|
|
1233
|
-
function: 'isMeetingActive',
|
|
1234
|
-
},
|
|
1235
|
-
EVENTS.DESTROY_MEETING,
|
|
1236
|
-
{
|
|
1237
|
-
reason: MEETING_REMOVED_REASON.FULLSTATE_REMOVED,
|
|
1238
|
-
shouldLeave: false,
|
|
1239
|
-
}
|
|
1240
|
-
);
|
|
1241
1302
|
}
|
|
1242
1303
|
// If you are guest and you are removed from the meeting
|
|
1243
1304
|
// You wont get any further events
|
|
@@ -1373,6 +1434,7 @@ export default class LocusInfo extends EventsScope {
|
|
|
1373
1434
|
hasMeetingContainerChanged,
|
|
1374
1435
|
hasTranscribeChanged,
|
|
1375
1436
|
hasHesiodLLMIdChanged,
|
|
1437
|
+
hasAiSummaryNotificationChanged,
|
|
1376
1438
|
hasTranscribeSpokenLanguageChanged,
|
|
1377
1439
|
hasManualCaptionChanged,
|
|
1378
1440
|
hasEntryExitToneChanged,
|
|
@@ -1529,6 +1591,19 @@ export default class LocusInfo extends EventsScope {
|
|
|
1529
1591
|
);
|
|
1530
1592
|
}
|
|
1531
1593
|
|
|
1594
|
+
if (hasAiSummaryNotificationChanged) {
|
|
1595
|
+
this.emitScoped(
|
|
1596
|
+
{
|
|
1597
|
+
file: 'locus-info',
|
|
1598
|
+
function: 'updateControls',
|
|
1599
|
+
},
|
|
1600
|
+
LOCUSINFO.EVENTS.CONTROLS_AI_SUMMARY_NOTIFICATION_UPDATED,
|
|
1601
|
+
{
|
|
1602
|
+
aiSummaryNotification: current.transcribe.aiSummaryNotification,
|
|
1603
|
+
}
|
|
1604
|
+
);
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1532
1607
|
if (hasTranscribeSpokenLanguageChanged) {
|
|
1533
1608
|
const {spokenLanguage} = current.transcribe;
|
|
1534
1609
|
|
|
@@ -2038,6 +2113,19 @@ export default class LocusInfo extends EventsScope {
|
|
|
2038
2113
|
);
|
|
2039
2114
|
}
|
|
2040
2115
|
|
|
2116
|
+
if (parsedSelves.updates.selfIdChanged) {
|
|
2117
|
+
this.emitScoped(
|
|
2118
|
+
{
|
|
2119
|
+
file: 'locus-info',
|
|
2120
|
+
function: 'updateSelf',
|
|
2121
|
+
},
|
|
2122
|
+
LOCUSINFO.EVENTS.SELF_ID_CHANGED,
|
|
2123
|
+
{
|
|
2124
|
+
selfId: parsedSelves.current.selfId,
|
|
2125
|
+
}
|
|
2126
|
+
);
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2041
2129
|
if (parsedSelves.updates.interpretationChanged) {
|
|
2042
2130
|
this.emitScoped(
|
|
2043
2131
|
{
|
|
@@ -138,6 +138,7 @@ const SelfUtils = {
|
|
|
138
138
|
updates.breakoutsChanged = SelfUtils.breakoutsChanged(previous, current);
|
|
139
139
|
updates.interpretationChanged = SelfUtils.interpretationChanged(previous, current);
|
|
140
140
|
updates.brbChanged = SelfUtils.brbChanged(previous, current);
|
|
141
|
+
updates.selfIdChanged = previous?.selfId !== current.selfId;
|
|
141
142
|
|
|
142
143
|
return {
|
|
143
144
|
previous,
|
package/src/locus-info/types.ts
CHANGED
|
@@ -2,9 +2,12 @@ import {Defer} from '@webex/common';
|
|
|
2
2
|
import {ConnectionState, MediaConnectionEventNames} from '@webex/internal-media-core';
|
|
3
3
|
import LoggerProxy from '../common/logs/logger-proxy';
|
|
4
4
|
import {ICE_AND_DTLS_CONNECTION_TIMEOUT} from '../constants';
|
|
5
|
+
import BEHAVIORAL_METRICS from '../metrics/constants';
|
|
6
|
+
import Metrics from '../metrics';
|
|
5
7
|
|
|
6
8
|
export interface MediaConnectionAwaiterProps {
|
|
7
9
|
webrtcMediaConnection: any;
|
|
10
|
+
correlationId: string;
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
/**
|
|
@@ -16,6 +19,7 @@ export default class MediaConnectionAwaiter {
|
|
|
16
19
|
private defer: Defer;
|
|
17
20
|
private retried: boolean;
|
|
18
21
|
private iceConnected: boolean;
|
|
22
|
+
private correlationId: string;
|
|
19
23
|
private onTimeoutCallback: () => void;
|
|
20
24
|
private peerConnectionStateCallback: () => void;
|
|
21
25
|
private iceConnectionStateCallback: () => void;
|
|
@@ -24,11 +28,12 @@ export default class MediaConnectionAwaiter {
|
|
|
24
28
|
/**
|
|
25
29
|
* @param {MediaConnectionAwaiterProps} mediaConnectionAwaiterProps
|
|
26
30
|
*/
|
|
27
|
-
constructor({webrtcMediaConnection}: MediaConnectionAwaiterProps) {
|
|
31
|
+
constructor({webrtcMediaConnection, correlationId}: MediaConnectionAwaiterProps) {
|
|
28
32
|
this.webrtcMediaConnection = webrtcMediaConnection;
|
|
29
33
|
this.defer = new Defer();
|
|
30
34
|
this.retried = false;
|
|
31
35
|
this.iceConnected = false;
|
|
36
|
+
this.correlationId = correlationId;
|
|
32
37
|
this.onTimeoutCallback = this.onTimeout.bind(this);
|
|
33
38
|
this.peerConnectionStateCallback = this.peerConnectionStateHandler.bind(this);
|
|
34
39
|
this.iceConnectionStateCallback = this.iceConnectionStateHandler.bind(this);
|
|
@@ -175,6 +180,32 @@ export default class MediaConnectionAwaiter {
|
|
|
175
180
|
this.timer = setTimeout(this.onTimeoutCallback, ICE_AND_DTLS_CONNECTION_TIMEOUT);
|
|
176
181
|
}
|
|
177
182
|
|
|
183
|
+
/**
|
|
184
|
+
* sends a metric with some additional info that might help debugging
|
|
185
|
+
* issues where browser doesn't update the RTCPeerConnection's iceConnectionState or connectionState
|
|
186
|
+
*
|
|
187
|
+
* @returns {void}
|
|
188
|
+
*/
|
|
189
|
+
async sendMetric() {
|
|
190
|
+
const stats = await this.webrtcMediaConnection.getStats();
|
|
191
|
+
|
|
192
|
+
// in theory we can end up with more than one transport report in the stats,
|
|
193
|
+
// but for the purpose of this metric it's fine to just use the first one
|
|
194
|
+
const transportReports = Array.from(
|
|
195
|
+
stats.values().filter((report) => report.type === 'transport')
|
|
196
|
+
) as Record<string, number | string>[];
|
|
197
|
+
|
|
198
|
+
Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEDIA_STILL_NOT_CONNECTED, {
|
|
199
|
+
correlation_id: this.correlationId,
|
|
200
|
+
numTransports: transportReports.length,
|
|
201
|
+
dtlsState: transportReports[0]?.dtlsState,
|
|
202
|
+
iceState: transportReports[0]?.iceState,
|
|
203
|
+
packetsSent: transportReports[0]?.packetsSent,
|
|
204
|
+
packetsReceived: transportReports[0]?.packetsReceived,
|
|
205
|
+
dataChannelState: this.webrtcMediaConnection.multistreamConnection?.dataChannel?.readyState,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
178
209
|
/**
|
|
179
210
|
* Function called when the timeout is reached.
|
|
180
211
|
*
|
|
@@ -189,6 +220,8 @@ export default class MediaConnectionAwaiter {
|
|
|
189
220
|
return;
|
|
190
221
|
}
|
|
191
222
|
|
|
223
|
+
this.sendMetric();
|
|
224
|
+
|
|
192
225
|
if (!this.isIceGatheringCompleted()) {
|
|
193
226
|
if (!this.retried) {
|
|
194
227
|
LoggerProxy.logger.warn(
|
|
@@ -226,8 +259,15 @@ export default class MediaConnectionAwaiter {
|
|
|
226
259
|
*/
|
|
227
260
|
waitForMediaConnectionConnected(): Promise<void> {
|
|
228
261
|
if (this.isConnected()) {
|
|
262
|
+
LoggerProxy.logger.log(
|
|
263
|
+
'Media:MediaConnectionAwaiter#waitForMediaConnectionConnected --> Already connected'
|
|
264
|
+
);
|
|
265
|
+
|
|
229
266
|
return Promise.resolve();
|
|
230
267
|
}
|
|
268
|
+
LoggerProxy.logger.log(
|
|
269
|
+
'Media:MediaConnectionAwaiter#waitForMediaConnectionConnected --> Waiting for media connection to be connected'
|
|
270
|
+
);
|
|
231
271
|
|
|
232
272
|
this.webrtcMediaConnection.on(
|
|
233
273
|
MediaConnectionEventNames.PEER_CONNECTION_STATE_CHANGED,
|
package/src/media/properties.ts
CHANGED
|
@@ -196,11 +196,13 @@ export default class MediaProperties {
|
|
|
196
196
|
/**
|
|
197
197
|
* Waits for the webrtc media connection to be connected.
|
|
198
198
|
*
|
|
199
|
+
* @param {string} correlationId
|
|
199
200
|
* @returns {Promise<void>}
|
|
200
201
|
*/
|
|
201
|
-
waitForMediaConnectionConnected(): Promise<void> {
|
|
202
|
+
waitForMediaConnectionConnected(correlationId: string): Promise<void> {
|
|
202
203
|
const mediaConnectionAwaiter = new MediaConnectionAwaiter({
|
|
203
204
|
webrtcMediaConnection: this.webrtcMediaConnection,
|
|
205
|
+
correlationId,
|
|
204
206
|
});
|
|
205
207
|
|
|
206
208
|
return mediaConnectionAwaiter.waitForMediaConnectionConnected();
|
|
@@ -32,6 +32,7 @@ interface IInMeetingActions {
|
|
|
32
32
|
canLowerAllHands?: boolean;
|
|
33
33
|
canLowerSomeoneElsesHand?: boolean;
|
|
34
34
|
bothLeaveAndEndMeetingAvailable?: boolean;
|
|
35
|
+
requireHostEndMeetingBeforeLeave?: boolean;
|
|
35
36
|
canEnableClosedCaption?: boolean;
|
|
36
37
|
canStartTranscribing?: boolean;
|
|
37
38
|
canStopTranscribing?: boolean;
|
|
@@ -117,6 +118,8 @@ interface IInMeetingActions {
|
|
|
117
118
|
canMoveToLobby?: boolean;
|
|
118
119
|
canEnablePollingQA?: boolean;
|
|
119
120
|
canDisablePollingQA?: boolean;
|
|
121
|
+
canAttendeeRequestAiAssistantEnabled?: boolean;
|
|
122
|
+
isAttendeeRequestAiAssistantDeclinedAll?: boolean;
|
|
120
123
|
}
|
|
121
124
|
|
|
122
125
|
/**
|
|
@@ -169,6 +172,8 @@ export default class InMeetingActions implements IInMeetingActions {
|
|
|
169
172
|
|
|
170
173
|
bothLeaveAndEndMeetingAvailable = null;
|
|
171
174
|
|
|
175
|
+
requireHostEndMeetingBeforeLeave = null;
|
|
176
|
+
|
|
172
177
|
canEnableClosedCaption = null;
|
|
173
178
|
|
|
174
179
|
canStartTranscribing = null;
|
|
@@ -337,6 +342,10 @@ export default class InMeetingActions implements IInMeetingActions {
|
|
|
337
342
|
|
|
338
343
|
canDisablePollingQA = null;
|
|
339
344
|
|
|
345
|
+
canAttendeeRequestAiAssistantEnabled = null;
|
|
346
|
+
|
|
347
|
+
isAttendeeRequestAiAssistantDeclinedAll = null;
|
|
348
|
+
|
|
340
349
|
/**
|
|
341
350
|
* Returns all meeting action options
|
|
342
351
|
* @returns {Object}
|
|
@@ -364,6 +373,7 @@ export default class InMeetingActions implements IInMeetingActions {
|
|
|
364
373
|
canLowerAllHands: this.canLowerAllHands,
|
|
365
374
|
canLowerSomeoneElsesHand: this.canLowerSomeoneElsesHand,
|
|
366
375
|
bothLeaveAndEndMeetingAvailable: this.bothLeaveAndEndMeetingAvailable,
|
|
376
|
+
requireHostEndMeetingBeforeLeave: this.requireHostEndMeetingBeforeLeave,
|
|
367
377
|
canEnableClosedCaption: this.canEnableClosedCaption,
|
|
368
378
|
canStartTranscribing: this.canStartTranscribing,
|
|
369
379
|
canStopTranscribing: this.canStopTranscribing,
|
|
@@ -448,6 +458,8 @@ export default class InMeetingActions implements IInMeetingActions {
|
|
|
448
458
|
canMoveToLobby: this.canMoveToLobby,
|
|
449
459
|
canEnablePollingQA: this.canEnablePollingQA,
|
|
450
460
|
canDisablePollingQA: this.canDisablePollingQA,
|
|
461
|
+
canAttendeeRequestAiAssistantEnabled: this.canAttendeeRequestAiAssistantEnabled,
|
|
462
|
+
isAttendeeRequestAiAssistantDeclinedAll: this.isAttendeeRequestAiAssistantDeclinedAll,
|
|
451
463
|
});
|
|
452
464
|
|
|
453
465
|
/**
|