@webex/plugin-meetings 3.0.0-beta.391 → 3.0.0-beta.392

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.
Files changed (51) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/config.js +1 -0
  4. package/dist/config.js.map +1 -1
  5. package/dist/constants.js +16 -7
  6. package/dist/constants.js.map +1 -1
  7. package/dist/interpretation/index.js +1 -1
  8. package/dist/interpretation/siLanguage.js +1 -1
  9. package/dist/meeting/index.js +738 -616
  10. package/dist/meeting/index.js.map +1 -1
  11. package/dist/meeting/util.js +4 -1
  12. package/dist/meeting/util.js.map +1 -1
  13. package/dist/meeting/voicea-meeting.js +172 -0
  14. package/dist/meeting/voicea-meeting.js.map +1 -0
  15. package/dist/meeting-info/meeting-info-v2.js +9 -6
  16. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  17. package/dist/meeting-info/util.js +7 -6
  18. package/dist/meeting-info/util.js.map +1 -1
  19. package/dist/meeting-info/utilv2.js +8 -4
  20. package/dist/meeting-info/utilv2.js.map +1 -1
  21. package/dist/member/index.js +0 -1
  22. package/dist/member/index.js.map +1 -1
  23. package/dist/types/constants.d.ts +8 -2
  24. package/dist/types/meeting/index.d.ts +56 -12
  25. package/dist/types/meeting/voicea-meeting.d.ts +16 -0
  26. package/dist/webinar/index.js +1 -1
  27. package/package.json +20 -20
  28. package/src/config.ts +2 -4
  29. package/src/constants.ts +11 -2
  30. package/src/meeting/index.ts +320 -157
  31. package/src/meeting/util.ts +5 -1
  32. package/src/meeting/voicea-meeting.ts +122 -0
  33. package/src/meeting-info/meeting-info-v2.ts +5 -11
  34. package/src/meeting-info/util.ts +12 -9
  35. package/src/meeting-info/utilv2.ts +24 -14
  36. package/src/member/index.ts +0 -1
  37. package/test/integration/spec/journey.js +2 -2
  38. package/test/unit/spec/breakouts/breakout.ts +2 -1
  39. package/test/unit/spec/breakouts/index.ts +7 -4
  40. package/test/unit/spec/locus-info/index.js +27 -18
  41. package/test/unit/spec/locus-info/selfUtils.js +6 -11
  42. package/test/unit/spec/media/index.ts +5 -0
  43. package/test/unit/spec/meeting/index.js +315 -87
  44. package/test/unit/spec/meeting/utils.js +52 -10
  45. package/test/unit/spec/meeting/voicea-meeting.ts +266 -0
  46. package/test/unit/spec/meeting-info/meetinginfov2.js +20 -15
  47. package/test/unit/spec/meetings/index.js +78 -10
  48. package/test/unit/spec/metrics/index.js +1 -2
  49. package/test/unit/spec/multistream/mediaRequestManager.ts +1 -0
  50. package/test/unit/spec/recording-controller/index.js +0 -1
  51. package/test/unit/spec/roap/turnDiscovery.ts +1 -1
@@ -1,6 +1,6 @@
1
1
  import uuid from 'uuid';
2
2
  import {cloneDeep, isEqual, isEmpty} from 'lodash';
3
- import jwt from 'jsonwebtoken';
3
+ import jwtDecode from 'jwt-decode';
4
4
  // @ts-ignore - Fix this
5
5
  import {StatelessWebexPlugin} from '@webex/webex-core';
6
6
  // @ts-ignore - Types not available for @webex/common
@@ -33,6 +33,12 @@ import {
33
33
  RemoteStream,
34
34
  } from '@webex/media-helpers';
35
35
 
36
+ import {
37
+ EVENT_TRIGGERS as VOICEAEVENTS,
38
+ TURN_ON_CAPTION_STATUS,
39
+ } from '@webex/internal-plugin-voicea';
40
+ import {processNewCaptions} from './voicea-meeting';
41
+
36
42
  import {
37
43
  MeetingNotActiveError,
38
44
  UserInLobbyError,
@@ -40,6 +46,7 @@ import {
40
46
  UserNotJoinedError,
41
47
  AddMediaFailed,
42
48
  } from '../common/errors/webex-errors';
49
+
43
50
  import {StatsAnalyzer, EVENTS as StatsAnalyzerEvents} from '../statsAnalyzer';
44
51
  import NetworkQualityMonitor from '../networkQualityMonitor';
45
52
  import LoggerProxy from '../common/logs/logger-proxy';
@@ -59,7 +66,6 @@ import MeetingsUtil from '../meetings/util';
59
66
  import RecordingUtil from '../recording-controller/util';
60
67
  import ControlsOptionsUtil from '../controls-options-manager/util';
61
68
  import MediaUtil from '../media/util';
62
- import Transcription from '../transcription';
63
69
  import {Reactions, SkinTones} from '../reactions/reactions';
64
70
  import PasswordError from '../common/errors/password-error';
65
71
  import CaptchaError from '../common/errors/captcha-error';
@@ -97,7 +103,6 @@ import {
97
103
  SHARE_STATUS,
98
104
  SHARE_STOPPED_REASON,
99
105
  VIDEO,
100
- HTTP_VERBS,
101
106
  SELF_ROLES,
102
107
  INTERPRETATION,
103
108
  SELF_POLICY,
@@ -114,7 +119,6 @@ import {
114
119
  MeetingInfoV2CaptchaError,
115
120
  MeetingInfoV2PolicyError,
116
121
  } from '../meeting-info/meeting-info-v2';
117
- import BrowserDetection from '../common/browser-detection';
118
122
  import {CSI, ReceiveSlotManager} from '../multistream/receiveSlotManager';
119
123
  import SendSlotManager from '../multistream/sendSlotManager';
120
124
  import {MediaRequestManager} from '../multistream/mediaRequestManager';
@@ -142,8 +146,6 @@ import ControlsOptionsManager from '../controls-options-manager';
142
146
  import PermissionError from '../common/errors/permission';
143
147
  import {LocusMediaRequest} from './locusMediaRequest';
144
148
 
145
- const {isBrowser} = BrowserDetection();
146
-
147
149
  const logRequest = (request: any, {logText = ''}) => {
148
150
  LoggerProxy.logger.info(`${logText} - sending request`);
149
151
 
@@ -159,6 +161,36 @@ const logRequest = (request: any, {logText = ''}) => {
159
161
  });
160
162
  };
161
163
 
164
+ export type CaptionData = {
165
+ id: string;
166
+ isFinal: boolean;
167
+ translations: Array<string>;
168
+ text: string;
169
+ currentCaptionLanguage: string;
170
+ timestamp: string;
171
+ speaker: string;
172
+ };
173
+
174
+ export type Transcription = {
175
+ languageOptions: {
176
+ captionLanguages?: string; // list of supported caption languages from backend
177
+ maxLanguages?: number;
178
+ spokenLanguages?: Array<string>; // list of supported spoken languages from backend
179
+ currentCaptionLanguage?: string; // current caption language - default is english
180
+ requestedCaptionLanguage?: string; // requested caption language
181
+ currentSpokenLanguage?: string; // current spoken language - default is english
182
+ };
183
+ status: string;
184
+ isListening: boolean;
185
+ commandText: string;
186
+ captions: Array<CaptionData>;
187
+ showCaptionBox: boolean;
188
+ transcribingRequestStatus: string;
189
+ isCaptioning: boolean;
190
+ speakerProxy: Map<string, any>;
191
+ interimCaptions: Map<string, CaptionData>;
192
+ };
193
+
162
194
  export type LocalStreams = {
163
195
  microphone?: LocalMicrophoneStream;
164
196
  camera?: LocalCameraStream;
@@ -590,6 +622,46 @@ export default class Meeting extends StatelessWebexPlugin {
590
622
  remoteShareInstanceId: string;
591
623
  turnDiscoverySkippedReason: string;
592
624
  turnServerUsed: boolean;
625
+ areVoiceaEventsSetup = false;
626
+ voiceaListenerCallbacks: object = {
627
+ [VOICEAEVENTS.VOICEA_ANNOUNCEMENT]: (payload: Transcription['languageOptions']) => {
628
+ this.transcription.languageOptions = payload;
629
+ Trigger.trigger(
630
+ this,
631
+ {
632
+ file: 'meeting/index',
633
+ function: 'setUpVoiceaListeners',
634
+ },
635
+ EVENT_TRIGGERS.MEETING_STARTED_RECEIVING_TRANSCRIPTION,
636
+ payload
637
+ );
638
+ },
639
+ [VOICEAEVENTS.CAPTIONS_TURNED_ON]: () => {
640
+ this.transcription.status = TURN_ON_CAPTION_STATUS.ENABLED;
641
+ },
642
+ [VOICEAEVENTS.EVA_COMMAND]: (payload) => {
643
+ const {data} = payload;
644
+
645
+ this.transcription.isListening = !!data.isListening;
646
+ this.transcription.commandText = data.text ?? '';
647
+ },
648
+ [VOICEAEVENTS.NEW_CAPTION]: (data) => {
649
+ processNewCaptions({data, meeting: this});
650
+ Trigger.trigger(
651
+ this,
652
+ {
653
+ file: 'meeting/index',
654
+ function: 'setUpVoiceaListeners',
655
+ },
656
+ EVENT_TRIGGERS.MEETING_CAPTION_RECEIVED,
657
+ {
658
+ captions: this.transcription.captions,
659
+ interimCaptions: this.transcription.interimCaptions,
660
+ }
661
+ );
662
+ },
663
+ };
664
+
593
665
  private retriedWithTurnServer: boolean;
594
666
  private sendSlotManager: SendSlotManager = new SendSlotManager(LoggerProxy);
595
667
  private deferSDPAnswer?: Defer; // used for waiting for a response
@@ -1188,7 +1260,17 @@ export default class Meeting extends StatelessWebexPlugin {
1188
1260
  * @private
1189
1261
  * @memberof Meeting
1190
1262
  */
1191
- this.transcription = undefined;
1263
+ this.transcription = {
1264
+ captions: [],
1265
+ isListening: false,
1266
+ commandText: '',
1267
+ languageOptions: {},
1268
+ showCaptionBox: false,
1269
+ transcribingRequestStatus: 'INACTIVE',
1270
+ isCaptioning: false,
1271
+ interimCaptions: {} as Map<string, CaptionData>,
1272
+ speakerProxy: {} as Map<string, any>,
1273
+ } as Transcription;
1192
1274
 
1193
1275
  /**
1194
1276
  * Password status. If it's PASSWORD_STATUS.REQUIRED then verifyPassword() needs to be called
@@ -1904,6 +1986,7 @@ export default class Meeting extends StatelessWebexPlugin {
1904
1986
  * @memberof Meeting
1905
1987
  */
1906
1988
  private setUpInterpretationListener() {
1989
+ // TODO: check if its getting used or not
1907
1990
  this.simultaneousInterpretation.on(INTERPRETATION.EVENTS.SUPPORT_LANGUAGES_UPDATE, () => {
1908
1991
  Trigger.trigger(
1909
1992
  this,
@@ -1914,7 +1997,7 @@ export default class Meeting extends StatelessWebexPlugin {
1914
1997
  EVENT_TRIGGERS.MEETING_INTERPRETATION_SUPPORT_LANGUAGES_UPDATE
1915
1998
  );
1916
1999
  });
1917
-
2000
+ // TODO: check if its getting used or not
1918
2001
  this.simultaneousInterpretation.on(
1919
2002
  INTERPRETATION.EVENTS.HANDOFF_REQUESTS_ARRIVED,
1920
2003
  (payload) => {
@@ -1931,6 +2014,43 @@ export default class Meeting extends StatelessWebexPlugin {
1931
2014
  );
1932
2015
  }
1933
2016
 
2017
+ /**
2018
+ * Set up the listeners for captions
2019
+ * @returns {undefined}
2020
+ * @private
2021
+ * @memberof Meeting
2022
+ */
2023
+ private setUpVoiceaListeners() {
2024
+ // @ts-ignore
2025
+ this.webex.internal.voicea.listenToEvents();
2026
+
2027
+ // @ts-ignore
2028
+ this.webex.internal.voicea.on(
2029
+ VOICEAEVENTS.VOICEA_ANNOUNCEMENT,
2030
+ this.voiceaListenerCallbacks[VOICEAEVENTS.VOICEA_ANNOUNCEMENT]
2031
+ );
2032
+
2033
+ // @ts-ignore
2034
+ this.webex.internal.voicea.on(
2035
+ VOICEAEVENTS.CAPTIONS_TURNED_ON,
2036
+ this.voiceaListenerCallbacks[VOICEAEVENTS.CAPTIONS_TURNED_ON]
2037
+ );
2038
+
2039
+ // @ts-ignore
2040
+ this.webex.internal.voicea.on(
2041
+ VOICEAEVENTS.EVA_COMMAND,
2042
+ this.voiceaListenerCallbacks[VOICEAEVENTS.EVA_COMMAND]
2043
+ );
2044
+
2045
+ // @ts-ignore
2046
+ this.webex.internal.voicea.on(
2047
+ VOICEAEVENTS.NEW_CAPTION,
2048
+ this.voiceaListenerCallbacks[VOICEAEVENTS.NEW_CAPTION]
2049
+ );
2050
+
2051
+ this.areVoiceaEventsSetup = true;
2052
+ }
2053
+
1934
2054
  /**
1935
2055
  * Set up the locus info listener for meetings disconnected due to inactivity
1936
2056
  * @returns {undefined}
@@ -2220,7 +2340,6 @@ export default class Meeting extends StatelessWebexPlugin {
2220
2340
  modifiedBy,
2221
2341
  lastModified,
2222
2342
  };
2223
-
2224
2343
  Trigger.trigger(
2225
2344
  this,
2226
2345
  {
@@ -2251,19 +2370,22 @@ export default class Meeting extends StatelessWebexPlugin {
2251
2370
  this.locusInfo.on(
2252
2371
  LOCUSINFO.EVENTS.CONTROLS_MEETING_TRANSCRIBE_UPDATED,
2253
2372
  ({caption, transcribing}) => {
2254
- // @ts-ignore - config coming from registerPlugin
2255
- if (transcribing && this.transcription && this.config.receiveTranscription) {
2256
- this.receiveTranscription();
2257
- } else if (!transcribing && this.transcription) {
2258
- Trigger.trigger(
2259
- this,
2260
- {
2261
- file: 'meeting/index',
2262
- function: 'setupLocusControlsListener',
2263
- },
2264
- EVENT_TRIGGERS.MEETING_STOPPED_RECEIVING_TRANSCRIPTION,
2265
- {caption, transcribing}
2266
- );
2373
+ // user need to be joined to start the llm and receive transcription
2374
+ if (this.isJoined()) {
2375
+ // @ts-ignore - config coming from registerPlugin
2376
+ if (transcribing && !this.transcription) {
2377
+ this.startTranscription();
2378
+ } else if (!transcribing && this.transcription) {
2379
+ Trigger.trigger(
2380
+ this,
2381
+ {
2382
+ file: 'meeting/index',
2383
+ function: 'setupLocusControlsListener',
2384
+ },
2385
+ EVENT_TRIGGERS.MEETING_STOPPED_RECEIVING_TRANSCRIPTION,
2386
+ {caption, transcribing}
2387
+ );
2388
+ }
2267
2389
  }
2268
2390
  }
2269
2391
  );
@@ -2925,7 +3047,7 @@ export default class Meeting extends StatelessWebexPlugin {
2925
3047
  });
2926
3048
  }
2927
3049
  });
2928
- this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ADMITTED_GUEST, (payload) => {
3050
+ this.locusInfo.on(LOCUSINFO.EVENTS.SELF_ADMITTED_GUEST, async (payload) => {
2929
3051
  this.stopKeepAlive();
2930
3052
 
2931
3053
  if (payload) {
@@ -3619,7 +3741,7 @@ export default class Meeting extends StatelessWebexPlugin {
3619
3741
  * @returns {void}
3620
3742
  */
3621
3743
  public setPermissionTokenPayload(permissionToken: string) {
3622
- this.permissionTokenPayload = jwt.decode(permissionToken);
3744
+ this.permissionTokenPayload = jwtDecode(permissionToken);
3623
3745
  this.permissionTokenReceivedLocalTime = new Date().getTime();
3624
3746
  }
3625
3747
 
@@ -4489,7 +4611,7 @@ export default class Meeting extends StatelessWebexPlugin {
4489
4611
  }
4490
4612
 
4491
4613
  LoggerProxy.logger.error(
4492
- 'Meeting:index#isTranscriptionSupported --> Webex Assistant is not supported'
4614
+ 'Meeting:index#isTranscriptionSupported --> Webex Assistant is not enabled/supported'
4493
4615
  );
4494
4616
 
4495
4617
  return false;
@@ -4510,109 +4632,139 @@ export default class Meeting extends StatelessWebexPlugin {
4510
4632
  }
4511
4633
 
4512
4634
  /**
4513
- * Monitor the Low-Latency Mercury (LLM) web socket connection on `onError` and `onClose` states
4514
- * @private
4515
- * @returns {void}
4635
+ * sets Caption language for the meeting
4636
+ * @param {string} language
4637
+ * @returns {Promise}
4516
4638
  */
4517
- private monitorTranscriptionSocketConnection() {
4518
- this.transcription.onCloseSocket((event) => {
4519
- LoggerProxy.logger.info(
4520
- `Meeting:index#onCloseSocket -->
4521
- unable to continue receiving transcription;
4522
- low-latency mercury web socket connection is closed now.
4523
- ${event}`
4524
- );
4639
+ public setCaptionLanguage(language: string) {
4640
+ return new Promise((resolve, reject) => {
4641
+ if (!this.isTranscriptionSupported()) {
4642
+ LoggerProxy.logger.error(
4643
+ 'Meeting:index#setCaptionLanguage --> Webex Assistant is not enabled/supported'
4644
+ );
4525
4645
 
4526
- this.triggerStopReceivingTranscriptionEvent();
4527
- });
4646
+ reject(new Error('Webex Assistant is not enabled/supported'));
4647
+ }
4528
4648
 
4529
- this.transcription.onErrorSocket((event) => {
4530
- LoggerProxy.logger.error(
4531
- `Meeting:index#onErrorSocket -->
4532
- unable to continue receiving transcription;
4533
- low-latency mercury web socket connection error had occured.
4534
- ${event}`
4535
- );
4649
+ try {
4650
+ const voiceaListenerCaptionUpdate = (payload) => {
4651
+ // @ts-ignore
4652
+ this.webex.internal.voicea.off(
4653
+ VOICEAEVENTS.CAPTION_LANGUAGE_UPDATE,
4654
+ voiceaListenerCaptionUpdate
4655
+ );
4656
+ const {statusCode} = payload;
4536
4657
 
4537
- this.triggerStopReceivingTranscriptionEvent();
4658
+ if (statusCode === 200) {
4659
+ this.transcription.languageOptions = {
4660
+ ...this.transcription.languageOptions,
4661
+ currentCaptionLanguage: language,
4662
+ };
4663
+ resolve(language);
4664
+ } else {
4665
+ reject(payload);
4666
+ }
4667
+ };
4668
+ // @ts-ignore
4669
+ this.webex.internal.voicea.on(
4670
+ VOICEAEVENTS.CAPTION_LANGUAGE_UPDATE,
4671
+ voiceaListenerCaptionUpdate
4672
+ );
4673
+ // @ts-ignore
4674
+ this.webex.internal.voicea.requestLanguage(language);
4675
+ } catch (error) {
4676
+ LoggerProxy.logger.error(`Meeting:index#setCaptionLanguage --> ${error}`);
4538
4677
 
4539
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.RECEIVE_TRANSCRIPTION_FAILURE, {
4540
- correlation_id: this.correlationId,
4541
- reason: 'unexpected error: transcription LLM web socket connection error had occured.',
4542
- event,
4543
- });
4678
+ reject(error);
4679
+ }
4544
4680
  });
4545
4681
  }
4546
4682
 
4547
4683
  /**
4548
- * Request for a WebSocket Url, open and monitor the WebSocket connection
4549
- * @private
4550
- * @returns {Promise<void>} a promise to open the WebSocket connection
4684
+ * sets Spoken language for the meeting
4685
+ * @param {string} language
4686
+ * @returns {Promise}
4551
4687
  */
4552
- private async receiveTranscription() {
4553
- LoggerProxy.logger.info(
4554
- `Meeting:index#receiveTranscription -->
4555
- Attempting to generate a web socket url.`
4556
- );
4688
+ public setSpokenLanguage(language: string) {
4689
+ return new Promise((resolve, reject) => {
4690
+ if (!this.isTranscriptionSupported()) {
4691
+ LoggerProxy.logger.error(
4692
+ 'Meeting:index#setCaptionLanguage --> Webex Assistant is not enabled/supported'
4693
+ );
4694
+
4695
+ reject(new Error('Webex Assistant is not enabled/supported'));
4696
+ }
4697
+
4698
+ try {
4699
+ const voiceaListenerLanguageUpdate = (payload) => {
4700
+ // @ts-ignore
4701
+ this.webex.internal.voicea.off(
4702
+ VOICEAEVENTS.SPOKEN_LANGUAGE_UPDATE,
4703
+ voiceaListenerLanguageUpdate
4704
+ );
4705
+ const {languageCode} = payload;
4706
+
4707
+ if (languageCode) {
4708
+ this.transcription.languageOptions = {
4709
+ ...this.transcription.languageOptions,
4710
+ currentSpokenLanguage: languageCode,
4711
+ };
4712
+ resolve(languageCode);
4713
+ } else {
4714
+ reject(payload);
4715
+ }
4716
+ };
4557
4717
 
4558
- try {
4559
- const {datachannelUrl} = this.locusInfo.info;
4560
- // @ts-ignore - fix type
4561
- const {
4562
- body: {webSocketUrl},
4563
4718
  // @ts-ignore
4564
- } = await this.request({
4565
- method: HTTP_VERBS.POST,
4566
- uri: datachannelUrl,
4567
- body: {deviceUrl: this.deviceUrl},
4568
- });
4719
+ this.webex.internal.voicea.on(
4720
+ VOICEAEVENTS.SPOKEN_LANGUAGE_UPDATE,
4721
+ voiceaListenerLanguageUpdate
4722
+ );
4569
4723
 
4570
- LoggerProxy.logger.info(
4571
- `Meeting:index#receiveTranscription -->
4572
- Generated web socket url succesfully.`
4573
- );
4724
+ // @ts-ignore
4725
+ this.webex.internal.voicea.setSpokenLanguage(language);
4726
+ } catch (error) {
4727
+ LoggerProxy.logger.error(`Meeting:index#setSpokenLanguage --> ${error}`);
4574
4728
 
4575
- this.transcription = new Transcription(
4576
- webSocketUrl,
4577
- // @ts-ignore - fix type
4578
- this.webex.sessionId,
4579
- this.members
4580
- );
4729
+ reject(error);
4730
+ }
4731
+ });
4732
+ }
4581
4733
 
4734
+ /**
4735
+ * This method will enable the transcription for the current meeting if the meeting has enabled/supports Webex Assistant
4736
+ * @param {Object} options object with spokenlanguage setting
4737
+ * @public
4738
+ * @returns {Promise<void>} a promise to open the WebSocket connection
4739
+ */
4740
+ public async startTranscription(options?: {spokenLanguage?: string}) {
4741
+ if (this.isJoined()) {
4582
4742
  LoggerProxy.logger.info(
4583
- `Meeting:index#receiveTranscription -->
4584
- opened LLM web socket connection successfully.`
4743
+ 'Meeting:index#startTranscription --> Attempting to enable transcription!'
4585
4744
  );
4586
4745
 
4587
- if (!this.inMeetingActions.isClosedCaptionActive) {
4588
- LoggerProxy.logger.error(
4589
- `Meeting:index#receiveTranscription --> Transcription cannot be started until a licensed user enables it`
4590
- );
4591
- }
4592
-
4593
- // retrieve and pass the payload
4594
- this.transcription.subscribe((payload) => {
4595
- Trigger.trigger(
4596
- this,
4597
- {
4598
- file: 'meeting/index',
4599
- function: 'join',
4600
- },
4601
- EVENT_TRIGGERS.MEETING_STARTED_RECEIVING_TRANSCRIPTION,
4602
- payload
4603
- );
4604
- });
4746
+ try {
4747
+ if (!this.areVoiceaEventsSetup) {
4748
+ this.setUpVoiceaListeners();
4749
+ }
4605
4750
 
4606
- this.monitorTranscriptionSocketConnection();
4607
- // @ts-ignore - fix type
4608
- this.transcription.connect(this.webex.credentials.supertoken.access_token);
4609
- } catch (error) {
4610
- LoggerProxy.logger.error(`Meeting:index#receiveTranscription --> ${error}`);
4611
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.RECEIVE_TRANSCRIPTION_FAILURE, {
4612
- correlation_id: this.correlationId,
4613
- reason: error.message,
4614
- stack: error.stack,
4615
- });
4751
+ if (this.getCurUserType() === 'host') {
4752
+ // @ts-ignore
4753
+ await this.webex.internal.voicea.toggleTranscribing(true, options?.spokenLanguage);
4754
+ }
4755
+ } catch (error) {
4756
+ LoggerProxy.logger.error(`Meeting:index#startTranscription --> ${error}`);
4757
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.RECEIVE_TRANSCRIPTION_FAILURE, {
4758
+ correlation_id: this.correlationId,
4759
+ reason: error.message,
4760
+ stack: error.stack,
4761
+ });
4762
+ }
4763
+ } else {
4764
+ LoggerProxy.logger.error(
4765
+ `Meeting:index#startTranscription --> meeting joined : ${this.isJoined()}`
4766
+ );
4767
+ throw new Error('Meeting is not joined');
4616
4768
  }
4617
4769
  }
4618
4770
 
@@ -4655,13 +4807,37 @@ export default class Meeting extends StatelessWebexPlugin {
4655
4807
  };
4656
4808
 
4657
4809
  /**
4658
- * stop recieving Transcription by closing
4659
- * the web socket connection properly
4810
+ * This method stops receiving transcription for the current meeting
4660
4811
  * @returns {void}
4661
4812
  */
4662
- stopReceivingTranscription() {
4813
+ stopTranscription() {
4663
4814
  if (this.transcription) {
4664
- this.transcription.closeSocket();
4815
+ // @ts-ignore
4816
+ this.webex.internal.voicea.off(
4817
+ VOICEAEVENTS.VOICEA_ANNOUNCEMENT,
4818
+ this.voiceaListenerCallbacks[VOICEAEVENTS.VOICEA_ANNOUNCEMENT]
4819
+ );
4820
+
4821
+ // @ts-ignore
4822
+ this.webex.internal.voicea.off(
4823
+ VOICEAEVENTS.CAPTIONS_TURNED_ON,
4824
+ this.voiceaListenerCallbacks[VOICEAEVENTS.CAPTIONS_TURNED_ON]
4825
+ );
4826
+
4827
+ // @ts-ignore
4828
+ this.webex.internal.voicea.off(
4829
+ VOICEAEVENTS.EVA_COMMAND,
4830
+ this.voiceaListenerCallbacks[VOICEAEVENTS.EVA_COMMAND]
4831
+ );
4832
+
4833
+ // @ts-ignore
4834
+ this.webex.internal.voicea.off(
4835
+ VOICEAEVENTS.NEW_CAPTION,
4836
+ this.voiceaListenerCallbacks[VOICEAEVENTS.NEW_CAPTION]
4837
+ );
4838
+
4839
+ this.areVoiceaEventsSetup = false;
4840
+ this.triggerStopReceivingTranscriptionEvent();
4665
4841
  }
4666
4842
  }
4667
4843
 
@@ -4674,12 +4850,12 @@ export default class Meeting extends StatelessWebexPlugin {
4674
4850
  private triggerStopReceivingTranscriptionEvent() {
4675
4851
  LoggerProxy.logger.info(`
4676
4852
  Meeting:index#stopReceivingTranscription -->
4677
- closed transcription LLM web socket connection successfully.`);
4853
+ closed voicea event listeners successfully.`);
4678
4854
 
4679
4855
  Trigger.trigger(
4680
4856
  this,
4681
4857
  {
4682
- file: 'meeting',
4858
+ file: 'meeting/index',
4683
4859
  function: 'triggerStopReceivingTranscriptionEvent',
4684
4860
  },
4685
4861
  EVENT_TRIGGERS.MEETING_STOPPED_RECEIVING_TRANSCRIPTION
@@ -4905,48 +5081,33 @@ export default class Meeting extends StatelessWebexPlugin {
4905
5081
  .then((join) => {
4906
5082
  // @ts-ignore - config coming from registerPlugin
4907
5083
  if (this.config.enableAutomaticLLM) {
4908
- this.updateLLMConnection().catch((error) => {
4909
- LoggerProxy.logger.error('Meeting:index#join --> Update LLM Connection Failed', error);
4910
-
4911
- Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.LLM_CONNECTION_AFTER_JOIN_FAILURE, {
4912
- correlation_id: this.correlationId,
4913
- reason: error?.message,
4914
- stack: error.stack,
4915
- });
4916
- });
4917
- }
5084
+ this.updateLLMConnection()
5085
+ .catch((error) => {
5086
+ LoggerProxy.logger.error(
5087
+ 'Meeting:index#join --> Transcription Socket Connection Failed',
5088
+ error
5089
+ );
4918
5090
 
4919
- return join;
4920
- })
4921
- .then((join) => {
4922
- if (isBrowser) {
4923
- // @ts-ignore - config coming from registerPlugin
4924
- if (this.config.receiveTranscription || options.receiveTranscription) {
4925
- if (this.isTranscriptionSupported()) {
5091
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.LLM_CONNECTION_AFTER_JOIN_FAILURE, {
5092
+ correlation_id: this.correlationId,
5093
+ reason: error?.message,
5094
+ stack: error.stack,
5095
+ });
5096
+ })
5097
+ .then(() => {
4926
5098
  LoggerProxy.logger.info(
4927
- 'Meeting:index#join --> Attempting to enabled to receive transcription!'
5099
+ 'Meeting:index#join --> Transcription Socket Connection Success'
4928
5100
  );
4929
- this.receiveTranscription().catch((error) => {
4930
- LoggerProxy.logger.error(
4931
- 'Meeting:index#join --> Receive Transcription Failed',
4932
- error
4933
- );
4934
-
4935
- Metrics.sendBehavioralMetric(
4936
- BEHAVIORAL_METRICS.RECEIVE_TRANSCRIPTION_AFTER_JOIN_FAILURE,
4937
- {
4938
- correlation_id: this.correlationId,
4939
- reason: error?.message,
4940
- stack: error.stack,
4941
- }
4942
- );
4943
- });
4944
- }
4945
- }
4946
- } else {
4947
- LoggerProxy.logger.error(
4948
- 'Meeting:index#join --> Receving transcription is not supported on this platform'
4949
- );
5101
+ Trigger.trigger(
5102
+ this,
5103
+ {
5104
+ file: 'meeting/index',
5105
+ function: 'join',
5106
+ },
5107
+ EVENT_TRIGGERS.MEETING_TRANSCRIPTION_CONNECTED,
5108
+ undefined
5109
+ );
5110
+ });
4950
5111
  }
4951
5112
 
4952
5113
  return join;
@@ -7377,7 +7538,7 @@ export default class Meeting extends StatelessWebexPlugin {
7377
7538
 
7378
7539
  if (layoutType) {
7379
7540
  if (!LAYOUT_TYPES.includes(layoutType)) {
7380
- this.rejectWithErrorLog(
7541
+ return this.rejectWithErrorLog(
7381
7542
  'Meeting:index#changeVideoLayout --> cannot change video layout, invalid layoutType received.'
7382
7543
  );
7383
7544
  }
@@ -7633,6 +7794,9 @@ export default class Meeting extends StatelessWebexPlugin {
7633
7794
  if (roles.includes(SELF_ROLES.COHOST)) {
7634
7795
  return 'cohost';
7635
7796
  }
7797
+ if (roles.includes(SELF_ROLES.PRESENTER)) {
7798
+ return 'presenter';
7799
+ }
7636
7800
  if (roles.includes(SELF_ROLES.ATTENDEE)) {
7637
7801
  return 'attendee';
7638
7802
  }
@@ -7723,8 +7887,7 @@ export default class Meeting extends StatelessWebexPlugin {
7723
7887
  this.queuedMediaUpdates = [];
7724
7888
 
7725
7889
  if (this.transcription) {
7726
- this.transcription.closeSocket();
7727
- this.triggerStopReceivingTranscriptionEvent();
7890
+ this.stopTranscription();
7728
7891
  this.transcription = undefined;
7729
7892
  }
7730
7893
  };
@@ -202,7 +202,11 @@ const MeetingUtil = {
202
202
  meeting.reconnectionManager.cleanUp();
203
203
  })
204
204
  .then(() => meeting.stopKeepAlive())
205
- .then(() => meeting.updateLLMConnection());
205
+ .then(() => {
206
+ if (meeting.config?.enableAutomaticLLM) {
207
+ meeting.updateLLMConnection();
208
+ }
209
+ });
206
210
  },
207
211
 
208
212
  disconnectPhoneAudio: (meeting, phoneUrl) => {