@webex/internal-plugin-voicea 3.0.0-beta.4 → 3.0.0-beta.400

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/src/constants.ts CHANGED
@@ -8,7 +8,7 @@ export const EVENT_TRIGGERS = {
8
8
 
9
9
  NEW_CAPTION: 'voicea:newCaption',
10
10
  EVA_COMMAND: 'voicea:wxa',
11
- HIGHLIGHT_CREATED: 'voicea:highlightCreated'
11
+ HIGHLIGHT_CREATED: 'voicea:highlightCreated',
12
12
  };
13
13
 
14
14
  export const VOICEA_RELAY_TYPES = {
@@ -16,7 +16,7 @@ export const VOICEA_RELAY_TYPES = {
16
16
  CLIENT_ANNOUNCEMENT: 'client.annc',
17
17
  TRANSLATION_REQUEST: 'voicea.transl.req',
18
18
  TRANSLATION_RESPONSE: 'voicea.transl.rsp',
19
- TRANSCRIPTION: 'voicea.transcription'
19
+ TRANSCRIPTION: 'voicea.transcription',
20
20
  };
21
21
 
22
22
  export const TRANSCRIPTION_TYPE = {
@@ -26,7 +26,19 @@ export const TRANSCRIPTION_TYPE = {
26
26
  EVA_CANCEL: 'eva_cancel',
27
27
  HIGHLIGHT_CREATED: 'highlight_created',
28
28
  TRANSCRIPT_INTERIM_RESULTS: 'transcript_interim_results',
29
- TRANSCRIPT_FINAL_RESULT: 'transcript_final_result'
29
+ TRANSCRIPT_FINAL_RESULT: 'transcript_final_result',
30
30
  };
31
31
 
32
32
  export const VOICEA = 'voicea';
33
+
34
+ export const ANNOUNCE_STATUS = {
35
+ IDLE: 'idle',
36
+ JOINED: 'joined',
37
+ JOINING: 'joining',
38
+ };
39
+
40
+ export const TURN_ON_CAPTION_STATUS = {
41
+ IDLE: 'idle',
42
+ ENABLED: 'enabled',
43
+ SENDING: 'sending',
44
+ };
package/src/index.ts CHANGED
@@ -5,3 +5,4 @@ import VoiceaChannel from './voicea';
5
5
  WebexCore.registerInternalPlugin('voicea', VoiceaChannel, {});
6
6
 
7
7
  export {default} from './voicea';
8
+ export {EVENT_TRIGGERS, TURN_ON_CAPTION_STATUS} from './constants';
package/src/utils.ts CHANGED
@@ -3,6 +3,5 @@ export const millisToMinutesAndSeconds = (millis: number) => {
3
3
  const minutes = Math.floor(millis / 60000);
4
4
  const seconds = ((millis % 60000) / 1000).toFixed(0);
5
5
 
6
- return `${minutes}:${+(seconds) < 10 ? '0' : ''}${seconds}`;
6
+ return `${minutes}:${+seconds < 10 ? '0' : ''}${seconds}`;
7
7
  };
8
-
package/src/voicea.ts CHANGED
@@ -2,7 +2,14 @@ import uuid from 'uuid';
2
2
  import {TriggerProxy as Trigger} from '@webex/plugin-meetings';
3
3
  import {WebexPlugin, config} from '@webex/webex-core';
4
4
 
5
- import {EVENT_TRIGGERS, VOICEA_RELAY_TYPES, TRANSCRIPTION_TYPE, VOICEA} from './constants';
5
+ import {
6
+ EVENT_TRIGGERS,
7
+ VOICEA_RELAY_TYPES,
8
+ TRANSCRIPTION_TYPE,
9
+ VOICEA,
10
+ ANNOUNCE_STATUS,
11
+ TURN_ON_CAPTION_STATUS,
12
+ } from './constants';
6
13
  // eslint-disable-next-line no-unused-vars
7
14
  import {
8
15
  AnnouncementPayload,
@@ -13,30 +20,36 @@ import {
13
20
  import {millisToMinutesAndSeconds} from './utils';
14
21
 
15
22
  /**
16
- * VoiceaChannel to hold single instance of LLM
23
+ * @description VoiceaChannel to hold single instance of LLM
24
+ * @export
25
+ * @class VoiceaChannel
17
26
  */
18
27
  export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
19
28
  namespace = VOICEA;
20
29
 
21
30
  private seqNum: number;
22
31
 
23
- private hasVoiceaJoined: boolean;
24
-
25
32
  private areCaptionsEnabled: boolean;
26
33
 
27
- private isTranscribingEnabled: boolean;
28
-
29
- private hasSubscribedToEvents: boolean = false;
34
+ private hasSubscribedToEvents = false;
30
35
 
31
36
  private vmcDeviceId?: string;
32
37
 
38
+ private announceStatus: string;
39
+
40
+ private captionStatus: string;
41
+
42
+ /**
43
+ * @param {Object} e
44
+ * @returns {undefined}
45
+ */
46
+
33
47
  private eventProcessor = (e) => {
34
- e;
35
48
  this.seqNum = e.sequenceNumber + 1;
36
49
  switch (e.data.relayType) {
37
50
  case VOICEA_RELAY_TYPES.ANNOUNCEMENT:
38
51
  this.vmcDeviceId = e.headers.from;
39
- this.hasVoiceaJoined = true;
52
+ this.announceStatus = ANNOUNCE_STATUS.JOINED;
40
53
  this.processAnnouncementMessage(e.data.voiceaPayload);
41
54
  break;
42
55
  case VOICEA_RELAY_TYPES.TRANSLATION_RESPONSE:
@@ -49,23 +62,31 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
49
62
  break;
50
63
  }
51
64
  };
65
+
52
66
  /**
53
67
  * Listen to websocket messages
68
+ * @returns {undefined}
54
69
  */
55
70
  private listenToEvents() {
56
71
  if (!this.hasSubscribedToEvents) {
72
+ // @ts-ignore
57
73
  this.webex.internal.llm.on('event:relay.event', this.eventProcessor);
58
74
  this.hasSubscribedToEvents = true;
59
75
  }
60
76
  }
61
77
 
78
+ /**
79
+ * Listen to websocket messages
80
+ * @returns {void}
81
+ */
62
82
  public deregisterEvents() {
63
- this.hasVoiceaJoined = false;
64
83
  this.areCaptionsEnabled = false;
65
- this.isTranscribingEnabled = false;
66
84
  this.vmcDeviceId = undefined;
85
+ // @ts-ignore
67
86
  this.webex.internal.llm.off('event:relay.event', this.eventProcessor);
68
87
  this.hasSubscribedToEvents = false;
88
+ this.announceStatus = ANNOUNCE_STATUS.IDLE;
89
+ this.captionStatus = TURN_ON_CAPTION_STATUS.IDLE;
69
90
  }
70
91
 
71
92
  /**
@@ -75,10 +96,10 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
75
96
  constructor(...args) {
76
97
  super(...args);
77
98
  this.seqNum = 1;
78
- this.hasVoiceaJoined = false;
79
99
  this.areCaptionsEnabled = false;
80
- this.isTranscribingEnabled = false;
81
100
  this.vmcDeviceId = undefined;
101
+ this.announceStatus = ANNOUNCE_STATUS.IDLE;
102
+ this.captionStatus = TURN_ON_CAPTION_STATUS.IDLE;
82
103
  }
83
104
 
84
105
  /**
@@ -115,6 +136,7 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
115
136
  {
116
137
  isFinal: true,
117
138
  transcriptId: voiceaPayload.transcript_id,
139
+ translations: voiceaPayload.translations,
118
140
  transcript: {
119
141
  csis: voiceaPayload.csis,
120
142
  text: voiceaPayload.transcript.text,
@@ -236,14 +258,14 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
236
258
  * @returns {void}
237
259
  */
238
260
  private sendAnnouncement = (): void => {
239
- if (this.hasVoiceaJoined || !this.webex.internal.llm.isConnected()) return;
240
-
261
+ this.announceStatus = ANNOUNCE_STATUS.JOINING;
241
262
  this.listenToEvents();
242
-
263
+ // @ts-ignore
243
264
  this.webex.internal.llm.socket.send({
244
265
  id: `${this.seqNum}`,
245
266
  type: 'publishRequest',
246
267
  recipients: {
268
+ // @ts-ignore
247
269
  route: this.webex.internal.llm.getBinding(),
248
270
  },
249
271
  headers: {},
@@ -265,11 +287,15 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
265
287
  * @returns {Promise}
266
288
  */
267
289
  public setSpokenLanguage = (languageCode: string): Promise<void> =>
290
+ // @ts-ignore
268
291
  this.request({
269
292
  method: 'PUT',
293
+ // @ts-ignore
270
294
  url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,
271
295
  body: {
272
- languageCode,
296
+ transcribe: {
297
+ spokenLanguage: languageCode,
298
+ },
273
299
  },
274
300
  }).then(() => {
275
301
  Trigger.trigger(
@@ -289,11 +315,14 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
289
315
  * @returns {void}
290
316
  */
291
317
  public requestLanguage = (languageCode: string): void => {
318
+ // @ts-ignore
292
319
  if (!this.webex.internal.llm.isConnected()) return;
320
+ // @ts-ignore
293
321
  this.webex.internal.llm.socket.send({
294
322
  id: `${this.seqNum}`,
295
323
  type: 'publishRequest',
296
324
  recipients: {
325
+ // @ts-ignore
297
326
  route: this.webex.internal.llm.getBinding(),
298
327
  },
299
328
  headers: {
@@ -313,59 +342,129 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
313
342
  };
314
343
 
315
344
  /**
316
- * Turn on Captions
345
+ * request turn on Captions
346
+ * @param {string} [languageCode] - Optional Parameter for spoken language code. Defaults to English
317
347
  * @returns {Promise}
318
348
  */
319
- public turnOnCaptions = async (): undefined | Promise<void> => {
320
- if (this.hasVoiceaJoined && this.areCaptionsEnabled) return undefined;
349
+ private requestTurnOnCaptions = (languageCode?): undefined | Promise<void> => {
350
+ this.captionStatus = TURN_ON_CAPTION_STATUS.SENDING;
351
+
352
+ // only set the spoken language if it is provided
353
+ const body = {
354
+ transcribe: {caption: true},
355
+ languageCode,
356
+ };
321
357
 
358
+ // @ts-ignore
359
+ // eslint-disable-next-line newline-before-return
322
360
  return this.request({
323
361
  method: 'PUT',
362
+ // @ts-ignore
324
363
  url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,
325
- body: {
326
- transcribe: {caption: true},
327
- },
328
- }).then(() => {
329
- Trigger.trigger(
330
- this,
331
- {
332
- file: 'voicea',
333
- function: 'turnOnCaptions',
334
- },
335
- EVENT_TRIGGERS.CAPTIONS_TURNED_ON
336
- );
337
- this.areCaptionsEnabled = true;
338
- this.sendAnnouncement();
339
- });
364
+ body,
365
+ })
366
+ .then(() => {
367
+ Trigger.trigger(
368
+ this,
369
+ {
370
+ file: 'voicea',
371
+ function: 'turnOnCaptions',
372
+ },
373
+ EVENT_TRIGGERS.CAPTIONS_TURNED_ON
374
+ );
375
+ this.areCaptionsEnabled = true;
376
+ this.captionStatus = TURN_ON_CAPTION_STATUS.ENABLED;
377
+ this.announce();
378
+ })
379
+ .catch(() => {
380
+ this.captionStatus = TURN_ON_CAPTION_STATUS.IDLE;
381
+ throw new Error('turn on captions fail');
382
+ });
383
+ };
384
+
385
+ /**
386
+ * is announce processing
387
+ * @returns {boolean}
388
+ */
389
+ private isAnnounceProcessing = () =>
390
+ [ANNOUNCE_STATUS.JOINING, ANNOUNCE_STATUS.JOINED].includes(this.announceStatus);
391
+
392
+ /**
393
+ * announce to voicea data chanel
394
+ * @returns {void}
395
+ */
396
+ public announce = () => {
397
+ if (this.isAnnounceProcessing()) return;
398
+ // @ts-ignore
399
+ if (!this.webex.internal.llm.isConnected()) {
400
+ throw new Error('voicea can not announce before llm connected');
401
+ }
402
+ this.sendAnnouncement();
403
+ };
404
+
405
+ /**
406
+ * is turn on caption processing
407
+ * @returns {boolean}
408
+ */
409
+ private isCaptionProcessing = () =>
410
+ [TURN_ON_CAPTION_STATUS.SENDING, TURN_ON_CAPTION_STATUS.ENABLED].includes(this.captionStatus);
411
+
412
+ /**
413
+ * Turn on Captions
414
+ * @param {string} [spokenLanguage] - Optional Spoken language code
415
+ * @returns {Promise}
416
+ */
417
+ public turnOnCaptions = async (spokenLanguage?): undefined | Promise<void> => {
418
+ if (this.isCaptionProcessing()) return undefined;
419
+ // @ts-ignore
420
+ if (!this.webex.internal.llm.isConnected()) {
421
+ throw new Error('can not turn on captions before llm connected');
422
+ }
423
+
424
+ return this.requestTurnOnCaptions(spokenLanguage);
340
425
  };
341
426
 
342
427
  /**
343
428
  * Toggle transcribing for highlights
344
429
  * @param {bool} activate if true transcribing is turned on
430
+ * @param {string} spokenLanguage language code for spoken language
345
431
  * @returns {Promise}
346
432
  */
347
- public toggleTranscribing = async (activate: boolean): undefined | Promise<void> => {
348
- if (this.isTranscribingEnabled === activate) return undefined;
349
-
433
+ public toggleTranscribing = (
434
+ activate: boolean,
435
+ spokenLanguage?: string
436
+ ): undefined | Promise<void> => {
437
+ // @ts-ignore
350
438
  return this.request({
351
439
  method: 'PUT',
440
+ // @ts-ignore
352
441
  url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,
353
442
  body: {
354
- transcribe: {transcribing: activate},
355
- },
356
- }).then(() => {
357
- Trigger.trigger(
358
- this,
359
- {
360
- file: 'voicea',
361
- function: 'toggleTranscribing',
443
+ transcribe: {
444
+ transcribing: activate,
362
445
  },
363
- activate ? EVENT_TRIGGERS.TRANSCRIBING_ON : EVENT_TRIGGERS.TRANSCRIBING_OFF
364
- );
365
- this.isTranscribingEnabled = activate;
366
- if (activate && !this.areCaptionsEnabled && !this.hasVoiceaJoined) this.turnOnCaptions();
446
+ spokenLanguage,
447
+ },
448
+ }).then((): undefined | Promise<void> => {
449
+ if (activate && !this.areCaptionsEnabled) {
450
+ return this.turnOnCaptions(spokenLanguage);
451
+ }
452
+
453
+ return undefined;
367
454
  });
368
455
  };
456
+
457
+ /**
458
+ * get caption status
459
+ * @returns {string}
460
+ */
461
+ public getCaptionStatus = () => this.captionStatus;
462
+
463
+ /**
464
+ * get announce status
465
+ * @returns {string}
466
+ */
467
+ public getAnnounceStatus = () => this.announceStatus;
369
468
  }
370
469
 
371
470
  export default VoiceaChannel;
@@ -78,11 +78,11 @@ interface IVoiceaChannel {
78
78
  setSpokenLanguage: (languageCode: string) => Promise<void>;
79
79
  requestLanguage: (languageCode: string) => void;
80
80
  turnOnCaptions: () => undefined | Promise<void>;
81
- toggleTranscribing: (activate: boolean) => undefined | Promise<void>;
82
- deregisterEvents: () => undefined | Promise<void>;
81
+ toggleTranscribing: (activate: boolean, spokenLanguage: string) => undefined | Promise<void>;
82
+ deregisterEvents: () => void;
83
83
  }
84
84
 
85
- export {
85
+ export type {
86
86
  AnnouncementPayload,
87
87
  CaptionLanguageResponse,
88
88
  TranscriptionResponse,