@eka-care/ekascribe-ts-sdk 1.4.39
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/README.md +437 -0
- package/dist/api/get-transaction-history.d.ts +5 -0
- package/dist/api/get-transaction-history.js +28 -0
- package/dist/api/get-voice-api-v2-config.d.ts +2 -0
- package/dist/api/get-voice-api-v2-config.js +26 -0
- package/dist/api/get-voice-api-v2-status.d.ts +51 -0
- package/dist/api/get-voice-api-v2-status.js +25 -0
- package/dist/api/get-voice-api-v3-status.d.ts +51 -0
- package/dist/api/get-voice-api-v3-status.js +26 -0
- package/dist/api/patch-transaction-status.d.ts +4 -0
- package/dist/api/patch-transaction-status.js +43 -0
- package/dist/api/post-cog-init.d.ts +3 -0
- package/dist/api/post-cog-init.js +15 -0
- package/dist/api/post-transaction-commit.d.ts +3 -0
- package/dist/api/post-transaction-commit.js +32 -0
- package/dist/api/post-transaction-init.d.ts +3 -0
- package/dist/api/post-transaction-init.js +40 -0
- package/dist/api/post-transaction-stop.d.ts +3 -0
- package/dist/api/post-transaction-stop.js +32 -0
- package/dist/audio-chunker/__tests__/audio-file-manager.test.d.ts +1 -0
- package/dist/audio-chunker/__tests__/audio-file-manager.test.js +5 -0
- package/dist/audio-chunker/audio-buffer-manager.d.ts +53 -0
- package/dist/audio-chunker/audio-buffer-manager.js +136 -0
- package/dist/audio-chunker/audio-file-manager.d.ts +96 -0
- package/dist/audio-chunker/audio-file-manager.js +579 -0
- package/dist/audio-chunker/vad-web.d.ts +90 -0
- package/dist/audio-chunker/vad-web.js +389 -0
- package/dist/aws-services/configure-aws.d.ts +5 -0
- package/dist/aws-services/configure-aws.js +13 -0
- package/dist/aws-services/get-files-s3.d.ts +10 -0
- package/dist/aws-services/get-files-s3.js +30 -0
- package/dist/aws-services/s3-retry-wrapper.d.ts +2 -0
- package/dist/aws-services/s3-retry-wrapper.js +38 -0
- package/dist/aws-services/translate-text-to-target-language.d.ts +6 -0
- package/dist/aws-services/translate-text-to-target-language.js +18 -0
- package/dist/aws-services/upload-file-to-s3.d.ts +13 -0
- package/dist/aws-services/upload-file-to-s3.js +48 -0
- package/dist/constants/constant.d.ts +27 -0
- package/dist/constants/constant.js +33 -0
- package/dist/constants/enums.d.ts +46 -0
- package/dist/constants/enums.js +51 -0
- package/dist/constants/setup-config.d.ts +14 -0
- package/dist/constants/setup-config.js +31 -0
- package/dist/constants/types.d.ts +224 -0
- package/dist/constants/types.js +1 -0
- package/dist/fetch-client/helper.d.ts +11 -0
- package/dist/fetch-client/helper.js +28 -0
- package/dist/fetch-client/index.d.ts +1 -0
- package/dist/fetch-client/index.js +36 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.js +267 -0
- package/dist/main/end-recording.d.ts +3 -0
- package/dist/main/end-recording.js +141 -0
- package/dist/main/init-transaction.d.ts +3 -0
- package/dist/main/init-transaction.js +86 -0
- package/dist/main/pause-recording.d.ts +3 -0
- package/dist/main/pause-recording.js +59 -0
- package/dist/main/resume-recording.d.ts +3 -0
- package/dist/main/resume-recording.js +33 -0
- package/dist/main/retry-upload-recording.d.ts +5 -0
- package/dist/main/retry-upload-recording.js +69 -0
- package/dist/main/start-recording.d.ts +3 -0
- package/dist/main/start-recording.js +55 -0
- package/dist/shared-worker/s3-file-upload.d.ts +1 -0
- package/dist/shared-worker/s3-file-upload.js +109 -0
- package/dist/shared-worker/s3-file-upload.ts +126 -0
- package/dist/store/store.d.ts +35 -0
- package/dist/store/store.js +121 -0
- package/dist/utils/compress-mp3-audio.d.ts +2 -0
- package/dist/utils/compress-mp3-audio.js +24 -0
- package/package.json +53 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import { MicVAD } from '@ricky0123/vad-web';
|
|
2
|
+
import { FRAME_SIZE, LONG_SILENCE_THRESHOLD, OUTPUT_FORMAT, PRE_SPEECH_PAD_FRAMES, SDK_STATUS_CODE, SHORT_SILENCE_THRESHOLD, } from '../constants/constant';
|
|
3
|
+
import EkaScribeStore from '../store/store';
|
|
4
|
+
import { ERROR_CODE } from '../constants/enums';
|
|
5
|
+
class VadWebClient {
|
|
6
|
+
/**
|
|
7
|
+
* Class that handle Vad functions and manages audio chunk
|
|
8
|
+
* @param pref_length Preferred length in seconds
|
|
9
|
+
* @param desp_length Desperate length in seconds
|
|
10
|
+
* @param max_length Maximum length in seconds
|
|
11
|
+
* @param sr Sample rate in Hz
|
|
12
|
+
*/
|
|
13
|
+
constructor(pref_length, desp_length, max_length, sr) {
|
|
14
|
+
Object.defineProperty(this, "vad_past", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: true,
|
|
18
|
+
value: void 0
|
|
19
|
+
});
|
|
20
|
+
Object.defineProperty(this, "last_clip_index", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: void 0
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(this, "clip_points", {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
configurable: true,
|
|
29
|
+
writable: true,
|
|
30
|
+
value: void 0
|
|
31
|
+
});
|
|
32
|
+
Object.defineProperty(this, "sil_duration_acc", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
writable: true,
|
|
36
|
+
value: void 0
|
|
37
|
+
});
|
|
38
|
+
Object.defineProperty(this, "pref_length_samples", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: true,
|
|
41
|
+
writable: true,
|
|
42
|
+
value: void 0
|
|
43
|
+
});
|
|
44
|
+
Object.defineProperty(this, "desp_length_samples", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
writable: true,
|
|
48
|
+
value: void 0
|
|
49
|
+
});
|
|
50
|
+
Object.defineProperty(this, "max_length_samples", {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
configurable: true,
|
|
53
|
+
writable: true,
|
|
54
|
+
value: void 0
|
|
55
|
+
});
|
|
56
|
+
Object.defineProperty(this, "shor_thsld", {
|
|
57
|
+
enumerable: true,
|
|
58
|
+
configurable: true,
|
|
59
|
+
writable: true,
|
|
60
|
+
value: void 0
|
|
61
|
+
});
|
|
62
|
+
Object.defineProperty(this, "long_thsld", {
|
|
63
|
+
enumerable: true,
|
|
64
|
+
configurable: true,
|
|
65
|
+
writable: true,
|
|
66
|
+
value: void 0
|
|
67
|
+
});
|
|
68
|
+
Object.defineProperty(this, "frame_size", {
|
|
69
|
+
enumerable: true,
|
|
70
|
+
configurable: true,
|
|
71
|
+
writable: true,
|
|
72
|
+
value: void 0
|
|
73
|
+
});
|
|
74
|
+
Object.defineProperty(this, "speech_pad_frames", {
|
|
75
|
+
enumerable: true,
|
|
76
|
+
configurable: true,
|
|
77
|
+
writable: true,
|
|
78
|
+
value: void 0
|
|
79
|
+
});
|
|
80
|
+
Object.defineProperty(this, "micVad", {
|
|
81
|
+
enumerable: true,
|
|
82
|
+
configurable: true,
|
|
83
|
+
writable: true,
|
|
84
|
+
value: void 0
|
|
85
|
+
}); // MicVad Object
|
|
86
|
+
Object.defineProperty(this, "is_vad_loading", {
|
|
87
|
+
enumerable: true,
|
|
88
|
+
configurable: true,
|
|
89
|
+
writable: true,
|
|
90
|
+
value: true
|
|
91
|
+
});
|
|
92
|
+
Object.defineProperty(this, "noSpeechStartTime", {
|
|
93
|
+
enumerable: true,
|
|
94
|
+
configurable: true,
|
|
95
|
+
writable: true,
|
|
96
|
+
value: null
|
|
97
|
+
});
|
|
98
|
+
Object.defineProperty(this, "recording_started", {
|
|
99
|
+
enumerable: true,
|
|
100
|
+
configurable: true,
|
|
101
|
+
writable: true,
|
|
102
|
+
value: false
|
|
103
|
+
});
|
|
104
|
+
Object.defineProperty(this, "lastWarningTime", {
|
|
105
|
+
enumerable: true,
|
|
106
|
+
configurable: true,
|
|
107
|
+
writable: true,
|
|
108
|
+
value: null
|
|
109
|
+
});
|
|
110
|
+
Object.defineProperty(this, "warningCooldownPeriod", {
|
|
111
|
+
enumerable: true,
|
|
112
|
+
configurable: true,
|
|
113
|
+
writable: true,
|
|
114
|
+
value: 2000
|
|
115
|
+
}); // 2 seconds cooldown after warning
|
|
116
|
+
this.vad_past = [];
|
|
117
|
+
this.last_clip_index = 0;
|
|
118
|
+
this.clip_points = [0];
|
|
119
|
+
this.sil_duration_acc = 0;
|
|
120
|
+
this.pref_length_samples = pref_length * sr;
|
|
121
|
+
this.desp_length_samples = desp_length * sr;
|
|
122
|
+
this.max_length_samples = max_length * sr;
|
|
123
|
+
this.shor_thsld = SHORT_SILENCE_THRESHOLD * sr;
|
|
124
|
+
this.long_thsld = LONG_SILENCE_THRESHOLD * sr;
|
|
125
|
+
this.frame_size = FRAME_SIZE;
|
|
126
|
+
this.speech_pad_frames = PRE_SPEECH_PAD_FRAMES;
|
|
127
|
+
this.micVad = {};
|
|
128
|
+
// instantiate MicVad
|
|
129
|
+
// TODO: initialise this in start recording
|
|
130
|
+
// this.initVad();
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Check for continuous silence and trigger periodic warnings
|
|
134
|
+
* @param isSpeech - vad probability (0 or 1)
|
|
135
|
+
*/
|
|
136
|
+
checkNoSpeech(isSpeech) {
|
|
137
|
+
if (!this.recording_started)
|
|
138
|
+
return;
|
|
139
|
+
const now = Date.now();
|
|
140
|
+
const errorCallback = EkaScribeStore.errorCallback;
|
|
141
|
+
const silenceThreshold = 10000; // 10 seconds
|
|
142
|
+
if (isSpeech === 0) {
|
|
143
|
+
if (this.noSpeechStartTime === null) {
|
|
144
|
+
this.noSpeechStartTime = now;
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
const silenceDuration = now - this.noSpeechStartTime;
|
|
148
|
+
// Check if we should show a warning (every 10 seconds of silence)
|
|
149
|
+
if (silenceDuration >= silenceThreshold) {
|
|
150
|
+
// Check if enough time has passed since the last warning (cooldown period)
|
|
151
|
+
if (this.lastWarningTime === null ||
|
|
152
|
+
now - this.lastWarningTime >= this.warningCooldownPeriod) {
|
|
153
|
+
if (errorCallback) {
|
|
154
|
+
errorCallback({
|
|
155
|
+
error_code: ERROR_CODE.NO_AUDIO_CAPTURE,
|
|
156
|
+
status_code: SDK_STATUS_CODE.AUDIO_ERROR,
|
|
157
|
+
message: 'No audio detected for a while. Please talk or stop the recording if done.',
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
this.lastWarningTime = now;
|
|
161
|
+
// Reset the silence timer to start counting the next 10 seconds
|
|
162
|
+
this.noSpeechStartTime = now;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// Reset timers when speech is detected
|
|
169
|
+
this.noSpeechStartTime = null;
|
|
170
|
+
this.lastWarningTime = null;
|
|
171
|
+
if (errorCallback) {
|
|
172
|
+
errorCallback({
|
|
173
|
+
error_code: ERROR_CODE.SPEECH_DETECTED,
|
|
174
|
+
status_code: SDK_STATUS_CODE.SUCCESS,
|
|
175
|
+
message: 'Audio captured. Recording continues.',
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Get the micVad instance
|
|
182
|
+
*/
|
|
183
|
+
getMicVad() {
|
|
184
|
+
return this.micVad;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Check if VAD is loading
|
|
188
|
+
*/
|
|
189
|
+
isVadLoading() {
|
|
190
|
+
return this.is_vad_loading;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Process a VAD frame and determine if it's a clip point
|
|
194
|
+
* @param vad_frame Voice activity detection frame (0 for silence, 1 for speech)
|
|
195
|
+
*/
|
|
196
|
+
processVadFrame(vad_frame) {
|
|
197
|
+
let is_clip_point_frame = false;
|
|
198
|
+
if (this.vad_past.length > 0) {
|
|
199
|
+
if (vad_frame === 0) {
|
|
200
|
+
this.sil_duration_acc += 1;
|
|
201
|
+
}
|
|
202
|
+
if (vad_frame === 1) {
|
|
203
|
+
this.sil_duration_acc = 0;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const sample_passed = this.vad_past.length - this.last_clip_index;
|
|
207
|
+
if (sample_passed > this.pref_length_samples) {
|
|
208
|
+
if (this.sil_duration_acc > this.long_thsld) {
|
|
209
|
+
this.last_clip_index =
|
|
210
|
+
this.vad_past.length - Math.min(Math.floor(this.sil_duration_acc / 2), 5);
|
|
211
|
+
this.clip_points.push(this.last_clip_index);
|
|
212
|
+
this.sil_duration_acc = 0;
|
|
213
|
+
is_clip_point_frame = true;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (sample_passed > this.desp_length_samples) {
|
|
217
|
+
if (this.sil_duration_acc > this.shor_thsld) {
|
|
218
|
+
this.last_clip_index =
|
|
219
|
+
this.vad_past.length - Math.min(Math.floor(this.sil_duration_acc / 2), 5);
|
|
220
|
+
this.clip_points.push(this.last_clip_index);
|
|
221
|
+
this.sil_duration_acc = 0;
|
|
222
|
+
is_clip_point_frame = true;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (sample_passed >= this.max_length_samples) {
|
|
226
|
+
this.last_clip_index = this.vad_past.length;
|
|
227
|
+
this.clip_points.push(this.last_clip_index);
|
|
228
|
+
this.sil_duration_acc = 0;
|
|
229
|
+
is_clip_point_frame = true;
|
|
230
|
+
}
|
|
231
|
+
this.vad_past.push(vad_frame);
|
|
232
|
+
if (is_clip_point_frame) {
|
|
233
|
+
return [true, this.clip_points[this.clip_points.length - 1]];
|
|
234
|
+
}
|
|
235
|
+
return [false, this.clip_points[this.clip_points.length - 1]];
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* initialize the VAD instance
|
|
239
|
+
*/
|
|
240
|
+
async initVad() {
|
|
241
|
+
const audioFileManager = EkaScribeStore.audioFileManagerInstance;
|
|
242
|
+
const audioBuffer = EkaScribeStore.audioBufferInstance;
|
|
243
|
+
this.is_vad_loading = true;
|
|
244
|
+
const vad = await MicVAD.new({
|
|
245
|
+
frameSamples: this.frame_size,
|
|
246
|
+
preSpeechPadFrames: this.speech_pad_frames,
|
|
247
|
+
onFrameProcessed: (prob, frames) => {
|
|
248
|
+
audioFileManager?.incrementTotalRawSamples(frames);
|
|
249
|
+
audioBuffer?.append(frames);
|
|
250
|
+
// Check if audio chunk needs to be clipped
|
|
251
|
+
const { isSpeech } = prob;
|
|
252
|
+
let vad_dec = 0;
|
|
253
|
+
if (isSpeech >= 0.5) {
|
|
254
|
+
vad_dec = 1;
|
|
255
|
+
}
|
|
256
|
+
// Call the new checkNoSpeech function
|
|
257
|
+
this.checkNoSpeech(vad_dec);
|
|
258
|
+
const vadResponse = this.processVadFrame(vad_dec);
|
|
259
|
+
const is_clip_point = vadResponse[0];
|
|
260
|
+
if (is_clip_point) {
|
|
261
|
+
// audio chunk is of float32 Array <ArrayBuffer>
|
|
262
|
+
const activeAudioChunk = audioBuffer?.getAudioData();
|
|
263
|
+
this.processAudioChunk({ audioFrames: activeAudioChunk });
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
onSpeechStart: () => {
|
|
267
|
+
EkaScribeStore.userSpeechCallback?.(true);
|
|
268
|
+
},
|
|
269
|
+
onSpeechEnd: () => {
|
|
270
|
+
EkaScribeStore.userSpeechCallback?.(false);
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
this.is_vad_loading = false;
|
|
274
|
+
this.micVad = vad;
|
|
275
|
+
return this.is_vad_loading;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* reinitialize the vad instance
|
|
279
|
+
*/
|
|
280
|
+
async reinitializeVad() {
|
|
281
|
+
const response = await this.initVad();
|
|
282
|
+
return response;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* process and upload audio chunk to s3
|
|
286
|
+
*/
|
|
287
|
+
async processAudioChunk({ audioFrames }) {
|
|
288
|
+
const audioFileManager = EkaScribeStore.audioFileManagerInstance;
|
|
289
|
+
const audioBuffer = EkaScribeStore.audioBufferInstance;
|
|
290
|
+
if (!audioFrames || !audioFileManager || !audioBuffer)
|
|
291
|
+
return;
|
|
292
|
+
// get the number of chunks already processed
|
|
293
|
+
const filenumber = (audioFileManager.audioChunks.length || 0) + 1;
|
|
294
|
+
const fileName = `${filenumber}.${OUTPUT_FORMAT}`;
|
|
295
|
+
const rawSampleDetails = audioFileManager.getRawSampleDetails();
|
|
296
|
+
const chunkTimestamps = audioBuffer?.calculateChunkTimestamps(rawSampleDetails.totalRawSamples);
|
|
297
|
+
try {
|
|
298
|
+
const chunkInfo = {
|
|
299
|
+
fileName,
|
|
300
|
+
timestamp: {
|
|
301
|
+
st: chunkTimestamps.start,
|
|
302
|
+
et: chunkTimestamps.end,
|
|
303
|
+
},
|
|
304
|
+
status: 'pending',
|
|
305
|
+
audioFrames,
|
|
306
|
+
};
|
|
307
|
+
const audioChunkLength = audioFileManager.updateAudioInfo(chunkInfo);
|
|
308
|
+
audioFileManager?.incrementInsertedSamples(audioBuffer.getCurrentSampleLength(), audioBuffer.getCurrentFrameLength());
|
|
309
|
+
audioBuffer.resetBufferState();
|
|
310
|
+
await audioFileManager.uploadAudioToS3({
|
|
311
|
+
audioFrames,
|
|
312
|
+
fileName,
|
|
313
|
+
chunkIndex: audioChunkLength - 1,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
console.error('Error uploading audio chunk:', error);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Start VAD
|
|
322
|
+
*/
|
|
323
|
+
startVad() {
|
|
324
|
+
this.micVad.start();
|
|
325
|
+
this.recording_started = true;
|
|
326
|
+
// this.monitorAudioCapture();
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Pause VAD
|
|
330
|
+
*/
|
|
331
|
+
pauseVad() {
|
|
332
|
+
this.micVad.pause();
|
|
333
|
+
this.recording_started = false;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* reset vadWeb instance
|
|
337
|
+
*/
|
|
338
|
+
resetVadWebInstance() {
|
|
339
|
+
this.vad_past = [];
|
|
340
|
+
this.last_clip_index = 0;
|
|
341
|
+
this.clip_points = [0];
|
|
342
|
+
this.sil_duration_acc = 0;
|
|
343
|
+
this.noSpeechStartTime = null;
|
|
344
|
+
this.lastWarningTime = null;
|
|
345
|
+
this.recording_started = false;
|
|
346
|
+
// TODO: will handle this - dont pass this callback - handle it properly - clear the error callback on client side on resetEkascribe
|
|
347
|
+
if (EkaScribeStore.errorCallback) {
|
|
348
|
+
EkaScribeStore.errorCallback({
|
|
349
|
+
error_code: ERROR_CODE.SPEECH_DETECTED,
|
|
350
|
+
status_code: SDK_STATUS_CODE.SUCCESS,
|
|
351
|
+
message: 'Audio captured. Recording continues.',
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* monitor initial audio capture within starting 4 seconds
|
|
357
|
+
*/
|
|
358
|
+
monitorAudioCapture() {
|
|
359
|
+
const audioBuffer = EkaScribeStore.audioBufferInstance;
|
|
360
|
+
const errorCallback = EkaScribeStore.errorCallback;
|
|
361
|
+
setTimeout(() => {
|
|
362
|
+
if (audioBuffer && audioBuffer.getCurrentSampleLength() <= 0) {
|
|
363
|
+
this.micVad.pause();
|
|
364
|
+
if (errorCallback) {
|
|
365
|
+
errorCallback({
|
|
366
|
+
error_code: ERROR_CODE.NO_AUDIO_CAPTURE,
|
|
367
|
+
status_code: SDK_STATUS_CODE.AUDIO_ERROR,
|
|
368
|
+
message: 'No audio is being captured. Please check your microphone.',
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
return true;
|
|
374
|
+
}, 5000);
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Callback to configure constants
|
|
378
|
+
*/
|
|
379
|
+
configureVadConstants({ pref_length, desp_length, max_length, sr, frame_size, pre_speech_pad_frames, short_thsld, long_thsld, }) {
|
|
380
|
+
this.pref_length_samples = pref_length * sr;
|
|
381
|
+
this.desp_length_samples = desp_length * sr;
|
|
382
|
+
this.max_length_samples = max_length * sr;
|
|
383
|
+
this.shor_thsld = short_thsld * sr;
|
|
384
|
+
this.long_thsld = long_thsld * sr;
|
|
385
|
+
this.frame_size = frame_size;
|
|
386
|
+
this.speech_pad_frames = pre_speech_pad_frames;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
export default VadWebClient;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as AWS from 'aws-sdk';
|
|
2
|
+
export function configureAWS({ accessKeyId, secretKey, sessionToken, }) {
|
|
3
|
+
try {
|
|
4
|
+
AWS.config.update({
|
|
5
|
+
sessionToken,
|
|
6
|
+
accessKeyId,
|
|
7
|
+
secretAccessKey: secretKey,
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
console.log(err, 'AWS config error');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as AWS from 'aws-sdk';
|
|
2
|
+
type TS3Response = {
|
|
3
|
+
response?: AWS.S3.GetObjectOutput;
|
|
4
|
+
error?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const getFilesS3: ({ fileName, maxPollingTime, }: {
|
|
7
|
+
fileName: string;
|
|
8
|
+
maxPollingTime: number;
|
|
9
|
+
}) => Promise<TS3Response>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as AWS from 'aws-sdk';
|
|
2
|
+
import s3RetryWrapper from './s3-retry-wrapper';
|
|
3
|
+
export const getFilesS3 = async ({ fileName, maxPollingTime, }) => {
|
|
4
|
+
try {
|
|
5
|
+
const requestBody = {
|
|
6
|
+
Bucket: 'm-prod-voice2rx',
|
|
7
|
+
Key: fileName,
|
|
8
|
+
};
|
|
9
|
+
const getFileCall = () => new Promise((resolve, reject) => {
|
|
10
|
+
const s3Bucket = new AWS.S3({
|
|
11
|
+
region: 'ap-south-1',
|
|
12
|
+
maxRetries: 0,
|
|
13
|
+
});
|
|
14
|
+
s3Bucket.getObject(requestBody, (err, data) => {
|
|
15
|
+
if (err)
|
|
16
|
+
return reject(err);
|
|
17
|
+
resolve(data);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
// retry upload with s3RetryWrapper
|
|
21
|
+
const result = await s3RetryWrapper(getFileCall, maxPollingTime, 1000, 0);
|
|
22
|
+
return {
|
|
23
|
+
response: result,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
console.error('getFilesS3 =>', error);
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import postCogInit from '../api/post-cog-init';
|
|
2
|
+
import { configureAWS } from './configure-aws';
|
|
3
|
+
async function s3RetryWrapper(s3Fn, maxRetryCount, delay, retryAttempt, is_shared_worker = false) {
|
|
4
|
+
try {
|
|
5
|
+
return await s3Fn();
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
if (retryAttempt >= maxRetryCount) {
|
|
9
|
+
throw error;
|
|
10
|
+
}
|
|
11
|
+
if (is_shared_worker) {
|
|
12
|
+
// eslint-disable-next-line
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
if (error.statusCode >= 400) {
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
// eslint-disable-next-line
|
|
20
|
+
// @ts-ignore
|
|
21
|
+
if (error.code === 'ExpiredToken') {
|
|
22
|
+
const cogResponse = await postCogInit();
|
|
23
|
+
const { credentials } = cogResponse;
|
|
24
|
+
if (credentials) {
|
|
25
|
+
configureAWS({
|
|
26
|
+
accessKeyId: credentials.AccessKeyId,
|
|
27
|
+
secretKey: credentials.SecretKey,
|
|
28
|
+
sessionToken: credentials.SessionToken,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
34
|
+
// Retry the operation
|
|
35
|
+
return s3RetryWrapper(s3Fn, maxRetryCount, delay, retryAttempt + 1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export default s3RetryWrapper;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { TranslateClient, TranslateTextCommand } from '@aws-sdk/client-translate';
|
|
2
|
+
const translateClient = new TranslateClient({ region: 'ap-south-1' }); // e.g., "us-east-1"
|
|
3
|
+
async function awsTranslateText({ source_language, target_language, content, }) {
|
|
4
|
+
const command = new TranslateTextCommand({
|
|
5
|
+
SourceLanguageCode: source_language,
|
|
6
|
+
TargetLanguageCode: target_language,
|
|
7
|
+
Text: content,
|
|
8
|
+
});
|
|
9
|
+
try {
|
|
10
|
+
const result = await translateClient.send(command);
|
|
11
|
+
console.log(result, 'result');
|
|
12
|
+
console.log('Translated text:', result.TranslatedText);
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
console.error('Translation error:', error);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export default awsTranslateText;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
declare const pushFileToS3: ({ fileBlob, fileName, txnID, businessID, is_shared_worker, }: {
|
|
2
|
+
fileBlob: Blob;
|
|
3
|
+
fileName: string;
|
|
4
|
+
txnID: string;
|
|
5
|
+
businessID: string;
|
|
6
|
+
is_shared_worker?: boolean;
|
|
7
|
+
}) => Promise<{
|
|
8
|
+
success?: string;
|
|
9
|
+
error?: string;
|
|
10
|
+
errorCode?: string;
|
|
11
|
+
code?: number;
|
|
12
|
+
}>;
|
|
13
|
+
export default pushFileToS3;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import * as AWS from 'aws-sdk';
|
|
3
|
+
import s3RetryWrapper from './s3-retry-wrapper';
|
|
4
|
+
import { S3_BUCKET_NAME } from '../constants/constant';
|
|
5
|
+
const pushFileToS3 = async ({ fileBlob, fileName, txnID, businessID, is_shared_worker = false, }) => {
|
|
6
|
+
try {
|
|
7
|
+
const requestBody = {
|
|
8
|
+
Bucket: S3_BUCKET_NAME,
|
|
9
|
+
Key: fileName,
|
|
10
|
+
Body: fileBlob,
|
|
11
|
+
Metadata: {
|
|
12
|
+
txnid: txnID,
|
|
13
|
+
bid: businessID,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
const uploadCall = () => new Promise((resolve, reject) => {
|
|
17
|
+
const s3Bucket = new AWS.S3({
|
|
18
|
+
region: 'ap-south-1',
|
|
19
|
+
maxRetries: 0,
|
|
20
|
+
});
|
|
21
|
+
s3Bucket.upload(requestBody, (err, data) => {
|
|
22
|
+
if (err)
|
|
23
|
+
return reject(err);
|
|
24
|
+
resolve(data);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
// retry upload with s3RetryWrapper
|
|
28
|
+
const result = await s3RetryWrapper(uploadCall, 3, 2000, 0, is_shared_worker);
|
|
29
|
+
// Return success with the data
|
|
30
|
+
return { success: result.ETag || 'Upload successful' };
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
const err = JSON.stringify(error, null, 2);
|
|
34
|
+
console.error('pushFilesToS3V2 error =>', err);
|
|
35
|
+
if (error.statusCode && error.statusCode >= 400) {
|
|
36
|
+
return {
|
|
37
|
+
error: `Expired token. Please re-authenticate! ${err}`,
|
|
38
|
+
errorCode: 'ExpiredToken',
|
|
39
|
+
code: error.statusCode,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
error: `Something went wrong! ${err}`,
|
|
44
|
+
code: error.statusCode,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
export default pushFileToS3;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare const PREF_CHUNK_LENGTH = 10;
|
|
2
|
+
export declare const DESP_CHUNK_LENGTH = 20;
|
|
3
|
+
export declare const MAX_CHUNK_LENGTH = 25;
|
|
4
|
+
export declare const FRAME_SIZE = 1024;
|
|
5
|
+
export declare const SAMPLING_RATE = 16000;
|
|
6
|
+
export declare const DURATION_PER_FRAME: number;
|
|
7
|
+
export declare const SILENCE_THRESHOLD = 0.01;
|
|
8
|
+
export declare const FRAME_RATE: number;
|
|
9
|
+
export declare const SHORT_SILENCE_THRESHOLD = 0.1;
|
|
10
|
+
export declare const LONG_SILENCE_THRESHOLD = 0.5;
|
|
11
|
+
export declare const SPEECH_DETECTION_THRESHOLD = 0.5;
|
|
12
|
+
export declare const PRE_SPEECH_PAD_FRAMES = 20;
|
|
13
|
+
export declare const BITRATE = 128;
|
|
14
|
+
export declare const QUALITY = 0;
|
|
15
|
+
export declare const CHANNELS = 1;
|
|
16
|
+
export declare const AUDIO_BUFFER_SIZE_IN_S: number;
|
|
17
|
+
export declare const OUTPUT_FORMAT = "mp3";
|
|
18
|
+
export declare const AUDIO_EXTENSION_TYPE_MAP: Record<string, string>;
|
|
19
|
+
export declare const S3_BUCKET_NAME = "m-prod-voice-record";
|
|
20
|
+
export declare const SDK_STATUS_CODE: {
|
|
21
|
+
AUDIO_ERROR: number;
|
|
22
|
+
SUCCESS: number;
|
|
23
|
+
TXN_ERROR: number;
|
|
24
|
+
BAD_REQUEST: number;
|
|
25
|
+
INTERNAL_SERVER_ERROR: number;
|
|
26
|
+
FORBIDDEN: number;
|
|
27
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const PREF_CHUNK_LENGTH = 10;
|
|
2
|
+
export const DESP_CHUNK_LENGTH = 20;
|
|
3
|
+
export const MAX_CHUNK_LENGTH = 25;
|
|
4
|
+
export const FRAME_SIZE = 1024;
|
|
5
|
+
export const SAMPLING_RATE = 16000;
|
|
6
|
+
export const DURATION_PER_FRAME = FRAME_SIZE / SAMPLING_RATE;
|
|
7
|
+
export const SILENCE_THRESHOLD = 0.01;
|
|
8
|
+
export const FRAME_RATE = SAMPLING_RATE / FRAME_SIZE;
|
|
9
|
+
export const SHORT_SILENCE_THRESHOLD = 0.1;
|
|
10
|
+
export const LONG_SILENCE_THRESHOLD = 0.5;
|
|
11
|
+
export const SPEECH_DETECTION_THRESHOLD = 0.5;
|
|
12
|
+
// export const REDEMPTION_FRAMES = 10;
|
|
13
|
+
export const PRE_SPEECH_PAD_FRAMES = 20;
|
|
14
|
+
// export const MIN_SPEECH_FRAMES = 80;
|
|
15
|
+
export const BITRATE = 128;
|
|
16
|
+
export const QUALITY = 0;
|
|
17
|
+
export const CHANNELS = 1;
|
|
18
|
+
export const AUDIO_BUFFER_SIZE_IN_S = DESP_CHUNK_LENGTH + 5;
|
|
19
|
+
export const OUTPUT_FORMAT = 'mp3';
|
|
20
|
+
export const AUDIO_EXTENSION_TYPE_MAP = {
|
|
21
|
+
m4a: 'audio/m4a',
|
|
22
|
+
wav: 'audio/wav',
|
|
23
|
+
mp3: 'audio/mpeg',
|
|
24
|
+
};
|
|
25
|
+
export const S3_BUCKET_NAME = 'm-prod-voice-record';
|
|
26
|
+
export const SDK_STATUS_CODE = {
|
|
27
|
+
AUDIO_ERROR: 1001,
|
|
28
|
+
SUCCESS: 200,
|
|
29
|
+
TXN_ERROR: 1003,
|
|
30
|
+
BAD_REQUEST: 1004,
|
|
31
|
+
INTERNAL_SERVER_ERROR: 1005,
|
|
32
|
+
FORBIDDEN: 1006,
|
|
33
|
+
};
|