agora-appbuilder-core 4.1.9 → 4.1.11-beta.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/package.json +2 -2
- package/template/agora-rn-uikit/src/Contexts/PropsContext.tsx +1 -3
- package/template/agora-rn-uikit/src/Contexts/RtcContext.tsx +1 -2
- package/template/agora-rn-uikit/src/Reducer/index.ts +0 -2
- package/template/agora-rn-uikit/src/Rtc/Join.tsx +11 -25
- package/template/agora-rn-uikit/src/RtcConfigure.tsx +1 -14
- package/template/agora-rn-uikit/src/Utils/isBotUser.ts +1 -1
- package/template/android/app/build.gradle +0 -7
- package/template/bridge/rtm/web/Types.ts +0 -183
- package/template/bridge/rtm/web/index.ts +491 -423
- package/template/defaultConfig.js +3 -3
- package/template/ios/Podfile +0 -41
- package/template/package.json +5 -5
- package/template/src/assets/font-styles.css +4 -0
- package/template/src/assets/fonts/icomoon.ttf +0 -0
- package/template/src/assets/selection.json +1 -1
- package/template/src/atoms/ActionMenu.tsx +93 -13
- package/template/src/atoms/CustomIcon.tsx +1 -0
- package/template/src/atoms/DropDownMulti.tsx +80 -29
- package/template/src/atoms/Input.tsx +2 -1
- package/template/src/components/Controls.tsx +148 -143
- package/template/src/components/EventsConfigure.tsx +152 -97
- package/template/src/components/RTMConfigure.tsx +426 -644
- package/template/src/components/precall/joinCallBtn.native.tsx +7 -2
- package/template/src/components/precall/joinCallBtn.tsx +7 -2
- package/template/src/components/precall/joinWaitingRoomBtn.native.tsx +8 -3
- package/template/src/components/precall/joinWaitingRoomBtn.tsx +22 -4
- package/template/src/components/precall/textInput.tsx +45 -22
- package/template/src/components/precall/usePreCall.tsx +7 -0
- package/template/src/components/room-info/useRoomInfo.tsx +5 -0
- package/template/src/language/default-labels/videoCallScreenLabels.ts +27 -4
- package/template/src/pages/video-call/ActionSheetContent.tsx +77 -77
- package/template/src/pages/video-call/SidePanelHeader.tsx +81 -36
- package/template/src/rtm/RTMEngine.ts +33 -130
- package/template/src/rtm-events/constants.ts +6 -0
- package/template/src/rtm-events-api/Events.ts +30 -106
- package/template/src/subComponents/caption/Caption.tsx +48 -7
- package/template/src/subComponents/caption/CaptionContainer.tsx +324 -51
- package/template/src/subComponents/caption/CaptionIcon.tsx +35 -34
- package/template/src/subComponents/caption/CaptionText.tsx +103 -2
- package/template/src/subComponents/caption/LanguageSelectorPopup.tsx +179 -69
- package/template/src/subComponents/caption/Transcript.tsx +46 -11
- package/template/src/subComponents/caption/TranscriptIcon.tsx +27 -35
- package/template/src/subComponents/caption/TranscriptText.tsx +78 -3
- package/template/src/subComponents/caption/proto/ptoto.js +38 -4
- package/template/src/subComponents/caption/proto/test.proto +34 -19
- package/template/src/subComponents/caption/useCaption.tsx +753 -10
- package/template/src/subComponents/caption/useSTTAPI.tsx +118 -205
- package/template/src/subComponents/caption/useStreamMessageUtils.native.ts +152 -33
- package/template/src/subComponents/caption/useStreamMessageUtils.ts +165 -34
- package/template/src/subComponents/caption/utils.ts +171 -3
- package/template/src/utils/SdkEvents.ts +3 -0
- package/template/src/utils/useEndCall.ts +3 -5
- package/template/src/utils/useSpeechToText.ts +31 -20
- package/template/agora-rn-uikit/src/Reducer/Spotlight.ts +0 -11
- package/template/agora-rn-uikit/src/Reducer/UserBanned.ts +0 -11
- package/template/bridge/rtm/web/index-legacy.ts +0 -540
- package/template/src/components/RTMConfigure-legacy.tsx +0 -848
|
@@ -1,64 +1,97 @@
|
|
|
1
1
|
import React, {useContext} from 'react';
|
|
2
2
|
import StorageContext from '../../components/StorageContext';
|
|
3
3
|
import {useRoomInfo} from '../../components/room-info/useRoomInfo';
|
|
4
|
-
import {
|
|
5
|
-
import events, {PersistanceLevel} from '../../rtm-events-api';
|
|
6
|
-
import {EventNames} from '../../rtm-events';
|
|
7
|
-
import {getLanguageLabel, LanguageType} from './utils';
|
|
8
|
-
import useGetName from '../../utils/useGetName';
|
|
9
|
-
import {capitalizeFirstLetter} from '../../utils/common';
|
|
4
|
+
import {LanguageTranslationConfig} from './useCaption';
|
|
10
5
|
import {PropsContext, useLocalUid} from '../../../agora-rn-uikit';
|
|
11
6
|
import {logger, LogSource} from '../../logger/AppBuilderLogger';
|
|
12
7
|
import getUniqueID from '../../utils/getUniqueID';
|
|
13
8
|
|
|
9
|
+
export interface STTAPIResponse {
|
|
10
|
+
success: boolean;
|
|
11
|
+
data?: any;
|
|
12
|
+
error?: {
|
|
13
|
+
message: string;
|
|
14
|
+
code?: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
14
18
|
interface IuseSTTAPI {
|
|
15
|
-
start: (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
start: (
|
|
20
|
+
botUid: number,
|
|
21
|
+
translationConfig: LanguageTranslationConfig,
|
|
22
|
+
) => Promise<STTAPIResponse>;
|
|
23
|
+
update: (
|
|
24
|
+
botUid: number,
|
|
25
|
+
translationConfig: LanguageTranslationConfig,
|
|
26
|
+
) => Promise<STTAPIResponse>;
|
|
27
|
+
stop: (botUid: number) => Promise<STTAPIResponse>;
|
|
20
28
|
}
|
|
21
29
|
|
|
22
30
|
const useSTTAPI = (): IuseSTTAPI => {
|
|
23
31
|
const {store} = React.useContext(StorageContext);
|
|
24
32
|
const {
|
|
25
|
-
data: {roomId
|
|
33
|
+
data: {roomId},
|
|
26
34
|
} = useRoomInfo();
|
|
27
|
-
const {
|
|
28
|
-
language,
|
|
29
|
-
isSTTActive,
|
|
30
|
-
setIsSTTActive,
|
|
31
|
-
setIsLangChangeInProgress,
|
|
32
|
-
setLanguage,
|
|
33
|
-
setMeetingTranscript,
|
|
34
|
-
setIsSTTError,
|
|
35
|
-
} = useCaption();
|
|
36
|
-
|
|
37
|
-
const currentLangRef = React.useRef<LanguageType[]>([]);
|
|
35
|
+
const {rtcProps} = useContext(PropsContext);
|
|
38
36
|
const STT_API_URL = `${$config.BACKEND_ENDPOINT}/v1/stt`;
|
|
39
|
-
const username = useGetName();
|
|
40
37
|
const localUid = useLocalUid();
|
|
41
|
-
const {rtcProps} = useContext(PropsContext);
|
|
42
|
-
|
|
43
|
-
React.useEffect(() => {
|
|
44
|
-
currentLangRef.current = language;
|
|
45
|
-
}, [language]);
|
|
46
38
|
|
|
47
|
-
const apiCall = async (
|
|
39
|
+
const apiCall = async (
|
|
40
|
+
method: 'startv7' | 'update' | 'stopv7',
|
|
41
|
+
botUid: number,
|
|
42
|
+
translationConfig?: LanguageTranslationConfig,
|
|
43
|
+
): Promise<STTAPIResponse> => {
|
|
48
44
|
const requestId = getUniqueID();
|
|
49
45
|
const startReqTs = Date.now();
|
|
50
|
-
|
|
51
|
-
LogSource.NetworkRest,
|
|
52
|
-
'stt',
|
|
53
|
-
`Trying to ${method} stt for lang ${lang}`,
|
|
54
|
-
{
|
|
55
|
-
method,
|
|
56
|
-
lang,
|
|
57
|
-
requestId,
|
|
58
|
-
startReqTs,
|
|
59
|
-
},
|
|
60
|
-
);
|
|
46
|
+
|
|
61
47
|
try {
|
|
48
|
+
// Calculate which user this bot belongs to
|
|
49
|
+
const ownerUid = botUid - 900000000;
|
|
50
|
+
|
|
51
|
+
let requestBody: any = {
|
|
52
|
+
passphrase: roomId?.host || roomId?.attendee || '',
|
|
53
|
+
dataStream_uid: botUid,
|
|
54
|
+
encryption_mode: $config.ENCRYPTION_ENABLED
|
|
55
|
+
? rtcProps.encryption.mode
|
|
56
|
+
: null,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
console.log(
|
|
60
|
+
`[STT_BOT_SUBSCRIPTION] ${method.toUpperCase()} - Bot UID: ${botUid} will subscribe to User UID: ${ownerUid}`,
|
|
61
|
+
{
|
|
62
|
+
method,
|
|
63
|
+
botUid,
|
|
64
|
+
ownerUid,
|
|
65
|
+
translationConfig,
|
|
66
|
+
},
|
|
67
|
+
);
|
|
68
|
+
// Add translate_config only for start/update methods
|
|
69
|
+
if (translationConfig?.source?.[0]) {
|
|
70
|
+
requestBody.lang = translationConfig.source;
|
|
71
|
+
// Sanitize payload: remove source language from targets to avoid API errors
|
|
72
|
+
const sanitizedTargets =
|
|
73
|
+
translationConfig?.targets?.filter(
|
|
74
|
+
target => target !== translationConfig?.source[0],
|
|
75
|
+
) || [];
|
|
76
|
+
const shouldTranslate = sanitizedTargets.length > 0;
|
|
77
|
+
// Add translate_config payload only if targets exist
|
|
78
|
+
if (shouldTranslate) {
|
|
79
|
+
requestBody.translate_config = [
|
|
80
|
+
{
|
|
81
|
+
source_lang: translationConfig.source[0],
|
|
82
|
+
target_lang: sanitizedTargets,
|
|
83
|
+
},
|
|
84
|
+
];
|
|
85
|
+
if (method === 'update') {
|
|
86
|
+
requestBody.translate = true;
|
|
87
|
+
}
|
|
88
|
+
} else if (method === 'update') {
|
|
89
|
+
// If method is update and no targets are passed
|
|
90
|
+
requestBody.translate = false;
|
|
91
|
+
}
|
|
92
|
+
requestBody.subscribeAudioUids = [`${localUid}`];
|
|
93
|
+
}
|
|
94
|
+
|
|
62
95
|
const response = await fetch(`${STT_API_URL}/${method}`, {
|
|
63
96
|
method: 'POST',
|
|
64
97
|
headers: {
|
|
@@ -67,22 +100,17 @@ const useSTTAPI = (): IuseSTTAPI => {
|
|
|
67
100
|
'X-Request-Id': requestId,
|
|
68
101
|
'X-Session-Id': logger.getSessionId(),
|
|
69
102
|
},
|
|
70
|
-
body: JSON.stringify(
|
|
71
|
-
passphrase: roomId?.host || '',
|
|
72
|
-
lang: lang,
|
|
73
|
-
dataStream_uid: 111111, // bot ID
|
|
74
|
-
encryption_mode: $config.ENCRYPTION_ENABLED
|
|
75
|
-
? rtcProps.encryption.mode
|
|
76
|
-
: null,
|
|
77
|
-
}),
|
|
103
|
+
body: JSON.stringify(requestBody),
|
|
78
104
|
});
|
|
105
|
+
|
|
79
106
|
const res = await response.json();
|
|
80
107
|
const endReqTs = Date.now();
|
|
81
108
|
const latency = endReqTs - startReqTs;
|
|
109
|
+
|
|
82
110
|
logger.log(
|
|
83
111
|
LogSource.NetworkRest,
|
|
84
112
|
'stt',
|
|
85
|
-
`STT API Success - Called ${method}
|
|
113
|
+
`STT API Success - Called ${method}`,
|
|
86
114
|
{
|
|
87
115
|
responseData: res,
|
|
88
116
|
requestId,
|
|
@@ -91,14 +119,30 @@ const useSTTAPI = (): IuseSTTAPI => {
|
|
|
91
119
|
latency,
|
|
92
120
|
},
|
|
93
121
|
);
|
|
94
|
-
|
|
122
|
+
|
|
123
|
+
// Check if response has error
|
|
124
|
+
if (res?.error?.message) {
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
error: {
|
|
128
|
+
message: res.error.message,
|
|
129
|
+
code: res.error.code,
|
|
130
|
+
},
|
|
131
|
+
data: res,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
success: true,
|
|
137
|
+
data: res,
|
|
138
|
+
};
|
|
95
139
|
} catch (error) {
|
|
96
140
|
const endReqTs = Date.now();
|
|
97
141
|
const latency = endReqTs - startReqTs;
|
|
98
142
|
logger.error(
|
|
99
143
|
LogSource.NetworkRest,
|
|
100
144
|
'stt',
|
|
101
|
-
`STT API Failure - Called ${method}
|
|
145
|
+
`STT API Failure - Called ${method}`,
|
|
102
146
|
error,
|
|
103
147
|
{
|
|
104
148
|
requestId,
|
|
@@ -107,170 +151,39 @@ const useSTTAPI = (): IuseSTTAPI => {
|
|
|
107
151
|
latency,
|
|
108
152
|
},
|
|
109
153
|
);
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const startWithDelay = (lang: LanguageType[]): Promise<string> =>
|
|
114
|
-
new Promise(resolve => {
|
|
115
|
-
setTimeout(async () => {
|
|
116
|
-
const res = await start(lang);
|
|
117
|
-
resolve(res);
|
|
118
|
-
}, 1000); // Delay of 1 seconds (1000 milliseconds) to allow existing stt service to fully stop
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
const start = async (lang: LanguageType[]) => {
|
|
122
|
-
try {
|
|
123
|
-
setIsLangChangeInProgress(true);
|
|
124
|
-
const res = await apiCall('startv7', lang);
|
|
125
|
-
// null means stt startred successfully
|
|
126
|
-
const isSTTAlreadyActive =
|
|
127
|
-
res?.error?.message
|
|
128
|
-
?.toLowerCase()
|
|
129
|
-
.indexOf('current status is STARTED') !== -1 ||
|
|
130
|
-
res?.error?.code === 610 ||
|
|
131
|
-
false;
|
|
132
|
-
|
|
133
|
-
if (res?.error?.message && res?.error?.code !== 610) {
|
|
134
|
-
setIsSTTError(true);
|
|
135
|
-
logger.error(
|
|
136
|
-
LogSource.NetworkRest,
|
|
137
|
-
'stt',
|
|
138
|
-
`start stt for lang ${lang} failed`,
|
|
139
|
-
res?.error,
|
|
140
|
-
);
|
|
141
|
-
} else {
|
|
142
|
-
logger.log(
|
|
143
|
-
LogSource.NetworkRest,
|
|
144
|
-
'stt',
|
|
145
|
-
`start stt for lang ${lang} succesfull`,
|
|
146
|
-
res,
|
|
147
|
-
);
|
|
148
|
-
setIsSTTError(false);
|
|
149
|
-
}
|
|
150
|
-
if (res === null || isSTTAlreadyActive) {
|
|
151
|
-
// once STT is active in the channel , notify others so that they dont' trigger start again
|
|
152
|
-
events.send(
|
|
153
|
-
EventNames.STT_ACTIVE,
|
|
154
|
-
JSON.stringify({active: true}),
|
|
155
|
-
PersistanceLevel.Sender,
|
|
156
|
-
);
|
|
157
|
-
setIsSTTActive(true);
|
|
158
|
-
logger.debug(
|
|
159
|
-
LogSource.NetworkRest,
|
|
160
|
-
'stt',
|
|
161
|
-
`stt lang update from: ${language} to ${lang}`,
|
|
162
|
-
);
|
|
163
|
-
// inform about the language set for stt
|
|
164
|
-
events.send(
|
|
165
|
-
EventNames.STT_LANGUAGE,
|
|
166
|
-
JSON.stringify({
|
|
167
|
-
username: capitalizeFirstLetter(username),
|
|
168
|
-
uid: localUid,
|
|
169
|
-
prevLang: language,
|
|
170
|
-
newLang: lang,
|
|
171
|
-
}),
|
|
172
|
-
PersistanceLevel.Sender,
|
|
173
|
-
);
|
|
174
|
-
setLanguage(lang);
|
|
175
154
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
//const msg = `${capitalizeFirstLetter(username)} ${actionText} `;
|
|
184
|
-
setMeetingTranscript(prev => {
|
|
185
|
-
return [
|
|
186
|
-
...prev,
|
|
187
|
-
{
|
|
188
|
-
name: 'langUpdate',
|
|
189
|
-
time: new Date().getTime(),
|
|
190
|
-
uid: `langUpdate-${localUid}`,
|
|
191
|
-
text: actionText,
|
|
192
|
-
},
|
|
193
|
-
];
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
return res;
|
|
197
|
-
} catch (errorMsg) {
|
|
198
|
-
logger.error(
|
|
199
|
-
LogSource.NetworkRest,
|
|
200
|
-
'stt',
|
|
201
|
-
'There was error in start stt',
|
|
202
|
-
errorMsg,
|
|
203
|
-
);
|
|
204
|
-
throw errorMsg;
|
|
205
|
-
} finally {
|
|
206
|
-
setIsLangChangeInProgress(false);
|
|
155
|
+
return {
|
|
156
|
+
success: false,
|
|
157
|
+
error: {
|
|
158
|
+
message: error?.message || 'Unknown error occurred',
|
|
159
|
+
code: error?.code,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
207
162
|
}
|
|
208
163
|
};
|
|
209
164
|
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
// EventNames.STT_ACTIVE,
|
|
216
|
-
// JSON.stringify({active: false}),
|
|
217
|
-
// PersistanceLevel.Session,
|
|
218
|
-
// );
|
|
219
|
-
setIsSTTActive(false);
|
|
220
|
-
if (res?.error?.message) {
|
|
221
|
-
setIsSTTError(true);
|
|
222
|
-
} else {
|
|
223
|
-
logger.log(LogSource.NetworkRest, 'stt', 'stop stt succesfull', res);
|
|
224
|
-
setIsSTTError(false);
|
|
225
|
-
}
|
|
226
|
-
return res;
|
|
227
|
-
} catch (error) {
|
|
228
|
-
logger.error(
|
|
229
|
-
LogSource.NetworkRest,
|
|
230
|
-
'stt',
|
|
231
|
-
'There was error in stop stt',
|
|
232
|
-
error,
|
|
233
|
-
);
|
|
234
|
-
throw error;
|
|
235
|
-
}
|
|
236
|
-
};
|
|
237
|
-
const restart = async (lang: LanguageType[]) => {
|
|
238
|
-
try {
|
|
239
|
-
setIsLangChangeInProgress(true);
|
|
240
|
-
await stop();
|
|
241
|
-
await startWithDelay(lang);
|
|
242
|
-
return Promise.resolve();
|
|
243
|
-
} catch (error) {
|
|
244
|
-
logger.error(
|
|
245
|
-
LogSource.NetworkRest,
|
|
246
|
-
'stt',
|
|
247
|
-
'There was error error in re-starting STT',
|
|
248
|
-
error,
|
|
249
|
-
);
|
|
250
|
-
return Promise.reject(error);
|
|
251
|
-
} finally {
|
|
252
|
-
setIsLangChangeInProgress(false);
|
|
253
|
-
}
|
|
165
|
+
const start = async (
|
|
166
|
+
botUid: number,
|
|
167
|
+
translationConfig: LanguageTranslationConfig,
|
|
168
|
+
): Promise<STTAPIResponse> => {
|
|
169
|
+
return await apiCall('startv7', botUid, translationConfig);
|
|
254
170
|
};
|
|
255
171
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
172
|
+
const update = async (
|
|
173
|
+
botUid: number,
|
|
174
|
+
translationConfig: LanguageTranslationConfig,
|
|
175
|
+
): Promise<STTAPIResponse> => {
|
|
176
|
+
return await apiCall('update', botUid, translationConfig);
|
|
177
|
+
};
|
|
261
178
|
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
$config.ENABLE_MEETING_TRANSCRIPT &&
|
|
266
|
-
(isHost || (!isHost && isSTTActive));
|
|
179
|
+
const stop = async (botUid: number): Promise<STTAPIResponse> => {
|
|
180
|
+
return await apiCall('stopv7', botUid);
|
|
181
|
+
};
|
|
267
182
|
|
|
268
183
|
return {
|
|
269
184
|
start,
|
|
270
185
|
stop,
|
|
271
|
-
|
|
272
|
-
isAuthorizedSTTUser,
|
|
273
|
-
isAuthorizedTranscriptUser,
|
|
186
|
+
update,
|
|
274
187
|
};
|
|
275
188
|
};
|
|
276
189
|
|
|
@@ -6,10 +6,15 @@ type StreamMessageCallback = (args: [number, Uint8Array]) => void;
|
|
|
6
6
|
type FinalListType = {
|
|
7
7
|
[key: string]: string[];
|
|
8
8
|
};
|
|
9
|
-
type
|
|
10
|
-
|
|
11
|
-
time: number;
|
|
9
|
+
type TranslationData = {
|
|
10
|
+
lang: string;
|
|
12
11
|
text: string;
|
|
12
|
+
isFinal: boolean;
|
|
13
|
+
};
|
|
14
|
+
type FinalTranslationListType = {
|
|
15
|
+
[key: string]: {
|
|
16
|
+
[lang: string]: string[];
|
|
17
|
+
};
|
|
13
18
|
};
|
|
14
19
|
|
|
15
20
|
const useStreamMessageUtils = (): {
|
|
@@ -20,27 +25,33 @@ const useStreamMessageUtils = (): {
|
|
|
20
25
|
setMeetingTranscript,
|
|
21
26
|
activeSpeakerRef,
|
|
22
27
|
prevSpeakerRef,
|
|
28
|
+
// Use ref instead of state to avoid stale closure issues
|
|
29
|
+
// The ref always has the current value, even in callbacks created at mount time
|
|
30
|
+
selectedTranslationLanguageRef,
|
|
23
31
|
} = useCaption();
|
|
24
32
|
|
|
25
33
|
let captionStartTime: number = 0;
|
|
26
34
|
const finalList: FinalListType = {};
|
|
27
35
|
const finalTranscriptList: FinalListType = {};
|
|
36
|
+
const finalTranslationList: FinalTranslationListType = {};
|
|
28
37
|
|
|
29
38
|
const streamMessageCallback: StreamMessageCallback = args => {
|
|
30
39
|
/* uid - bot which sends stream message in channel
|
|
31
40
|
payload - stream message in Uint8Array format
|
|
32
41
|
*/
|
|
33
|
-
const [
|
|
42
|
+
const [botUid, payload] = args;
|
|
34
43
|
let nonFinalText = ''; // holds intermediate results
|
|
35
44
|
let finalText = ''; // holds final strings
|
|
36
45
|
let currentFinalText = ''; // holds current caption
|
|
37
46
|
let isInterjecting = false;
|
|
47
|
+
let translations: TranslationData[] = [];
|
|
38
48
|
|
|
39
49
|
const textstream = protoRoot
|
|
40
|
-
.lookupType('Text')
|
|
50
|
+
.lookupType('agora.audio2text.Text')
|
|
41
51
|
.decode(payload as Uint8Array) as any;
|
|
42
52
|
|
|
43
|
-
console.log('
|
|
53
|
+
console.log('[STT_PER_USER_BOT] stt v7 textstream', botUid, textstream);
|
|
54
|
+
// console.log('STT - Parsed Textstream : ', textstream);
|
|
44
55
|
|
|
45
56
|
// Identifing Current & Prev Speakers for the Captions
|
|
46
57
|
/*
|
|
@@ -81,6 +92,14 @@ const useStreamMessageUtils = (): {
|
|
|
81
92
|
// we have a speaker change so clear the context for prev speaker
|
|
82
93
|
if (prevSpeakerRef.current !== '') {
|
|
83
94
|
finalList[prevSpeakerRef.current] = [];
|
|
95
|
+
// Clear translations for previous speaker
|
|
96
|
+
if (finalTranslationList[prevSpeakerRef.current]) {
|
|
97
|
+
Object.keys(finalTranslationList[prevSpeakerRef.current]).forEach(
|
|
98
|
+
lang => {
|
|
99
|
+
finalTranslationList[prevSpeakerRef.current][lang] = [];
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
}
|
|
84
103
|
isInterjecting = true;
|
|
85
104
|
}
|
|
86
105
|
prevSpeakerRef.current = activeSpeakerRef.current;
|
|
@@ -96,6 +115,51 @@ const useStreamMessageUtils = (): {
|
|
|
96
115
|
finalTranscriptList[textstream.uid] = [];
|
|
97
116
|
}
|
|
98
117
|
|
|
118
|
+
/* Process translations if available */
|
|
119
|
+
if (textstream.trans && textstream.trans.length > 0) {
|
|
120
|
+
for (const trans of textstream.trans) {
|
|
121
|
+
const lang = trans.lang;
|
|
122
|
+
const texts = trans.texts || [];
|
|
123
|
+
const isFinal = trans.isFinal || false;
|
|
124
|
+
|
|
125
|
+
if (!finalTranslationList[textstream.uid]) {
|
|
126
|
+
finalTranslationList[textstream.uid] = {};
|
|
127
|
+
}
|
|
128
|
+
if (!finalTranslationList[textstream.uid][lang]) {
|
|
129
|
+
finalTranslationList[textstream.uid][lang] = [];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const currentTranslationText = texts.join(' ');
|
|
133
|
+
if (currentTranslationText) {
|
|
134
|
+
if (isFinal) {
|
|
135
|
+
finalTranslationList[textstream.uid][lang].push(
|
|
136
|
+
currentTranslationText,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Build complete translation text (final + current non-final)
|
|
141
|
+
const existingTranslationBuffer = isInterjecting
|
|
142
|
+
? ''
|
|
143
|
+
: finalTranslationList[textstream.uid][lang]?.join(' ');
|
|
144
|
+
const latestTranslationString = isFinal ? '' : currentTranslationText;
|
|
145
|
+
const completeTranslationText =
|
|
146
|
+
existingTranslationBuffer.length > 0
|
|
147
|
+
? latestTranslationString
|
|
148
|
+
? existingTranslationBuffer + ' ' + latestTranslationString
|
|
149
|
+
: existingTranslationBuffer
|
|
150
|
+
: latestTranslationString;
|
|
151
|
+
|
|
152
|
+
if (completeTranslationText || isFinal) {
|
|
153
|
+
translations.push({
|
|
154
|
+
lang,
|
|
155
|
+
text: completeTranslationText,
|
|
156
|
+
isFinal,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
99
163
|
const words = textstream.words; //[Word,Word]
|
|
100
164
|
|
|
101
165
|
/* categorize words into final & nonFinal objects per uid
|
|
@@ -105,7 +169,7 @@ const useStreamMessageUtils = (): {
|
|
|
105
169
|
"isFinal": true,
|
|
106
170
|
"confidence": 0.8549408316612244
|
|
107
171
|
}
|
|
108
|
-
|
|
172
|
+
*/
|
|
109
173
|
for (const word of words) {
|
|
110
174
|
if (word.isFinal) {
|
|
111
175
|
finalText = finalText + word.text;
|
|
@@ -130,32 +194,57 @@ const useStreamMessageUtils = (): {
|
|
|
130
194
|
}
|
|
131
195
|
|
|
132
196
|
/* Updating Meeting Transcript */
|
|
133
|
-
|
|
197
|
+
// Update transcript when: (1) new text finalized OR (2) final translations arrived
|
|
198
|
+
const hasFinalTranslations = textstream.trans?.some(
|
|
199
|
+
(t: any) => t.isFinal === true,
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
if (currentFinalText.length || hasFinalTranslations) {
|
|
203
|
+
// Prepare final translations for transcript
|
|
204
|
+
const finalTranslationsForTranscript: TranslationData[] = [];
|
|
205
|
+
if (finalTranslationList[textstream.uid]) {
|
|
206
|
+
Object.keys(finalTranslationList[textstream.uid]).forEach(lang => {
|
|
207
|
+
const translationText =
|
|
208
|
+
finalTranslationList[textstream.uid][lang]?.join(' ') || '';
|
|
209
|
+
|
|
210
|
+
if (translationText) {
|
|
211
|
+
finalTranslationsForTranscript.push({
|
|
212
|
+
lang: lang,
|
|
213
|
+
text: translationText,
|
|
214
|
+
isFinal: true,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
134
220
|
setMeetingTranscript(prevTranscript => {
|
|
135
221
|
const lastTranscriptIndex = prevTranscript.length - 1;
|
|
136
222
|
const lastTranscript =
|
|
137
223
|
lastTranscriptIndex >= 0 ? prevTranscript[lastTranscriptIndex] : null;
|
|
138
224
|
|
|
139
225
|
/*
|
|
140
|
-
checking if the last item transcript matches with current uid
|
|
141
|
-
If yes then updating the last transcript msg with current text
|
|
226
|
+
checking if the last item transcript matches with current uid
|
|
227
|
+
If yes then updating the last transcript msg with current text and translations
|
|
142
228
|
If no then adding a new entry in the transcript
|
|
143
229
|
*/
|
|
144
230
|
if (lastTranscript && lastTranscript.uid === textstream.uid) {
|
|
145
231
|
const updatedTranscript = {
|
|
146
232
|
...lastTranscript,
|
|
147
233
|
//text: lastTranscript.text + ' ' + currentFinalText, // missing few updates with reading prev values
|
|
148
|
-
text:
|
|
234
|
+
text: currentFinalText.length
|
|
235
|
+
? finalTranscriptList[textstream.uid].join(' ')
|
|
236
|
+
: lastTranscript.text, // Keep existing text if no new text
|
|
237
|
+
translations: finalTranslationsForTranscript,
|
|
238
|
+
// preserve the original translation language from when this transcript was created
|
|
239
|
+
selectedTranslationLanguage:
|
|
240
|
+
lastTranscript.selectedTranslationLanguage,
|
|
149
241
|
};
|
|
150
242
|
|
|
151
243
|
return [
|
|
152
244
|
...prevTranscript.slice(0, lastTranscriptIndex),
|
|
153
245
|
updatedTranscript,
|
|
154
246
|
];
|
|
155
|
-
} else {
|
|
156
|
-
const isLangUpdate =
|
|
157
|
-
lastTranscript?.uid.toString().indexOf('langUpdate') > -1;
|
|
158
|
-
|
|
247
|
+
} else if (currentFinalText.length) {
|
|
159
248
|
finalTranscriptList[textstream.uid] = [currentFinalText];
|
|
160
249
|
|
|
161
250
|
return [
|
|
@@ -164,36 +253,66 @@ const useStreamMessageUtils = (): {
|
|
|
164
253
|
uid: textstream.uid,
|
|
165
254
|
time: new Date().getTime(),
|
|
166
255
|
text: currentFinalText,
|
|
256
|
+
translations: finalTranslationsForTranscript,
|
|
257
|
+
// Store the current translation language with this transcript item
|
|
258
|
+
// This preserves which translation was active when this text was spoken
|
|
259
|
+
selectedTranslationLanguage:
|
|
260
|
+
selectedTranslationLanguageRef.current,
|
|
167
261
|
},
|
|
168
262
|
];
|
|
263
|
+
} else {
|
|
264
|
+
// No new text and uid doesn't match - don't modify transcript
|
|
265
|
+
// console.log(
|
|
266
|
+
// '[TRANSCRIPT_DEBUG] Skipping transcript update - no new text and uid mismatch',
|
|
267
|
+
// );
|
|
268
|
+
return prevTranscript;
|
|
169
269
|
}
|
|
170
270
|
});
|
|
171
271
|
}
|
|
172
272
|
|
|
173
|
-
/*
|
|
174
|
-
Previous final words of the uid are prepended and
|
|
273
|
+
/*
|
|
274
|
+
Previous final words of the uid are prepended and
|
|
175
275
|
then current non final words so that context of speech is not lost
|
|
176
276
|
*/
|
|
177
277
|
const existingStringBuffer = isInterjecting
|
|
178
278
|
? ''
|
|
179
279
|
: finalList[textstream.uid]?.join(' ');
|
|
180
280
|
const latestString = nonFinalText;
|
|
181
|
-
const captionText =
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
281
|
+
const captionText = isInterjecting
|
|
282
|
+
? latestString
|
|
283
|
+
: existingStringBuffer.length > 0
|
|
284
|
+
? existingStringBuffer + ' ' + latestString
|
|
285
|
+
: latestString;
|
|
286
|
+
|
|
287
|
+
// updating the captions with translations
|
|
288
|
+
setCaptionObj(prevState => {
|
|
289
|
+
const existingTranslations =
|
|
290
|
+
prevState[textstream.uid]?.translations || [];
|
|
291
|
+
|
|
292
|
+
// Update existing translations or add new ones
|
|
293
|
+
const updatedTranslations = [...existingTranslations];
|
|
294
|
+
|
|
295
|
+
for (const newTrans of translations) {
|
|
296
|
+
const existingIndex = updatedTranslations.findIndex(
|
|
297
|
+
t => t.lang === newTrans.lang,
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
if (existingIndex >= 0) {
|
|
301
|
+
updatedTranslations[existingIndex] = newTrans;
|
|
302
|
+
} else {
|
|
303
|
+
updatedTranslations.push(newTrans);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
...prevState,
|
|
309
|
+
[textstream.uid]: {
|
|
310
|
+
text: captionText || prevState[textstream.uid]?.text || '',
|
|
311
|
+
translations: updatedTranslations,
|
|
312
|
+
lastUpdated: new Date().getTime(),
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
});
|
|
197
316
|
|
|
198
317
|
console.group('STT-logs');
|
|
199
318
|
console.log('Recived uid =>', textstream.uid);
|