sera-ai 1.0.22 → 1.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var React3 = require('react');
4
+ var ffmpeg = require('@ffmpeg/ffmpeg');
4
5
  var pRetry = require('p-retry');
5
6
  var lucideReact = require('lucide-react');
6
7
  var jsxRuntime = require('react/jsx-runtime');
@@ -78,14 +79,14 @@ var createFFmpegWorker = () => {
78
79
 
79
80
  processAudioData: function(audioBuffer, options = {}) {
80
81
  try {
81
- const { quality = 1, bitRate = 128000 } = options;
82
+ const { quality = 1, bitRate = 128000, sampleRate = 44100 } = options;
82
83
  const float32Array = new Float32Array(audioBuffer);
83
- const wavBuffer = this.float32ToWavFile(float32Array);
84
-
84
+ const wavBuffer = this.float32ToWavFile(float32Array, sampleRate);
85
+
85
86
  return {
86
87
  buffer: wavBuffer,
87
88
  size: wavBuffer.byteLength,
88
- duration: float32Array.length / 44100
89
+ duration: float32Array.length / sampleRate
89
90
  };
90
91
  } catch (error) {
91
92
  throw new Error('Failed to process audio data: ' + error.message);
@@ -292,14 +293,38 @@ var createFFmpegWorker = () => {
292
293
  };
293
294
  var useFFmpegConverter = () => {
294
295
  const [isLoaded, setIsLoaded] = React3.useState(true);
296
+ const [ffmpegLoaded, setFfmpegLoaded] = React3.useState(false);
295
297
  const [isConverting, setIsConverting] = React3.useState(false);
296
298
  const [progress, setProgress] = React3.useState(0);
297
299
  const [error, setError] = React3.useState(null);
298
300
  const [statusMessage, setStatusMessage] = React3.useState("");
301
+ const ffmpegRef = React3.useRef(null);
299
302
  const loadFFmpeg = React3.useCallback(async () => {
300
- setIsLoaded(true);
301
- return true;
302
- }, []);
303
+ if (ffmpegRef.current && ffmpegLoaded) {
304
+ return true;
305
+ }
306
+ try {
307
+ setStatusMessage("Loading FFmpeg...");
308
+ const ffmpeg$1 = ffmpeg.createFFmpeg({
309
+ log: false,
310
+ progress: ({ ratio }) => {
311
+ setProgress(Math.round(ratio * 100));
312
+ }
313
+ });
314
+ await ffmpeg$1.load();
315
+ ffmpegRef.current = ffmpeg$1;
316
+ setFfmpegLoaded(true);
317
+ setIsLoaded(true);
318
+ setStatusMessage("");
319
+ console.log("FFmpeg WASM loaded successfully");
320
+ return true;
321
+ } catch (err) {
322
+ console.error("Failed to load FFmpeg:", err);
323
+ setError("Failed to load FFmpeg");
324
+ setStatusMessage("");
325
+ return false;
326
+ }
327
+ }, [ffmpegLoaded]);
303
328
  const convertToWav = React3.useCallback(
304
329
  async (audioData, sampleRate = AUDIO_SAMPLE_RATE, fileName = "recording.wav") => {
305
330
  setIsConverting(true);
@@ -344,8 +369,7 @@ var useFFmpegConverter = () => {
344
369
  worker.postMessage({
345
370
  type: "convertWav",
346
371
  audioBuffer: audioData.buffer,
347
- sampleRate,
348
- options: { quality: 1, bitRate: 128e3 }
372
+ options: { quality: 1, bitRate: 128e3, sampleRate }
349
373
  });
350
374
  });
351
375
  } catch (err) {
@@ -357,6 +381,69 @@ var useFFmpegConverter = () => {
357
381
  },
358
382
  []
359
383
  );
384
+ const convertToFlac = React3.useCallback(
385
+ async (wavFile) => {
386
+ if (!ffmpegRef.current || !ffmpegLoaded) {
387
+ const loaded = await loadFFmpeg();
388
+ if (!loaded) {
389
+ console.error("Failed to load FFmpeg for FLAC conversion");
390
+ return wavFile;
391
+ }
392
+ }
393
+ const ffmpeg$1 = ffmpegRef.current;
394
+ if (!ffmpeg$1) {
395
+ console.error("FFmpeg not available");
396
+ return wavFile;
397
+ }
398
+ try {
399
+ setIsConverting(true);
400
+ setProgress(0);
401
+ setError(null);
402
+ setStatusMessage("Converting to FLAC...");
403
+ const inputFileName = "input.wav";
404
+ const outputFileName = "output.flac";
405
+ const wavData = await ffmpeg.fetchFile(wavFile);
406
+ ffmpeg$1.FS("writeFile", inputFileName, wavData);
407
+ setProgress(30);
408
+ setStatusMessage("Encoding FLAC...");
409
+ await ffmpeg$1.run(
410
+ "-i",
411
+ inputFileName,
412
+ "-c:a",
413
+ "flac",
414
+ "-compression_level",
415
+ "5",
416
+ // 0-12, higher = smaller file but slower
417
+ outputFileName
418
+ );
419
+ setProgress(80);
420
+ setStatusMessage("Finalizing...");
421
+ const flacData = ffmpeg$1.FS("readFile", outputFileName);
422
+ ffmpeg$1.FS("unlink", inputFileName);
423
+ ffmpeg$1.FS("unlink", outputFileName);
424
+ const flacBlob = new Blob([new Uint8Array(flacData)], { type: "audio/flac" });
425
+ const flacFileName = wavFile.name.replace(/\.wav$/i, ".flac");
426
+ const flacFile = new File([flacBlob], flacFileName, { type: "audio/flac" });
427
+ setProgress(100);
428
+ setStatusMessage("FLAC conversion complete");
429
+ console.log(`[FLAC] Converted ${wavFile.size} bytes WAV to ${flacFile.size} bytes FLAC (${Math.round((1 - flacFile.size / wavFile.size) * 100)}% reduction)`);
430
+ setTimeout(() => {
431
+ setIsConverting(false);
432
+ setProgress(0);
433
+ setStatusMessage("");
434
+ }, 500);
435
+ return flacFile;
436
+ } catch (err) {
437
+ console.error("FLAC conversion failed:", err);
438
+ setError("FLAC conversion failed");
439
+ setIsConverting(false);
440
+ setProgress(0);
441
+ setStatusMessage("");
442
+ return wavFile;
443
+ }
444
+ },
445
+ [ffmpegLoaded, loadFFmpeg]
446
+ );
360
447
  const removeSilence = React3.useCallback(async (file) => {
361
448
  if (!file) {
362
449
  setError("No file provided for processing");
@@ -529,14 +616,16 @@ var useFFmpegConverter = () => {
529
616
  setStatusMessage("");
530
617
  }, []);
531
618
  return {
532
- ffmpeg: null,
619
+ ffmpeg: ffmpegRef.current,
533
620
  isLoaded,
621
+ ffmpegLoaded,
534
622
  isConverting,
535
623
  progress,
536
624
  error,
537
625
  statusMessage,
538
626
  loadFFmpeg,
539
627
  convertToWav,
628
+ convertToFlac,
540
629
  removeSilence,
541
630
  reset
542
631
  };
@@ -2960,7 +3049,8 @@ var useAudioRecorder = ({
2960
3049
  loadFFmpeg,
2961
3050
  progress,
2962
3051
  statusMessage,
2963
- convertToWav
3052
+ convertToWav,
3053
+ convertToFlac
2964
3054
  } = useFFmpegConverter_default();
2965
3055
  const isLoadedRef = React3__namespace.default.useRef(isLoaded);
2966
3056
  React3__namespace.default.useEffect(() => {
@@ -3168,12 +3258,27 @@ var useAudioRecorder = ({
3168
3258
  } else {
3169
3259
  console.log("[SILENCE] Silence removal disabled or not loaded");
3170
3260
  }
3261
+ let audioFile = wavFile;
3262
+ try {
3263
+ console.log("[FLAC] Converting WAV to FLAC...");
3264
+ const flacFile = await convertToFlac(wavFile);
3265
+ if (flacFile && flacFile.type === "audio/flac") {
3266
+ audioFile = flacFile;
3267
+ console.log(
3268
+ `[FLAC] Conversion successful: ${wavFile.size} bytes WAV \u2192 ${flacFile.size} bytes FLAC`
3269
+ );
3270
+ } else {
3271
+ console.warn("[FLAC] Conversion failed, using WAV file");
3272
+ }
3273
+ } catch (flacError) {
3274
+ console.warn("[FLAC] Conversion error, using WAV file:", flacError);
3275
+ }
3171
3276
  console.log(
3172
- `[OUT] Final file for transcription: ${wavFile.size} bytes, ${wavFile.name}`
3277
+ `[OUT] Final file for transcription: ${audioFile.size} bytes, ${audioFile.name}`
3173
3278
  );
3174
- if (wavFile.size < 500) {
3279
+ if (audioFile.size < 500) {
3175
3280
  console.warn(
3176
- `[WARN] Small audio file (${wavFile.size} bytes) - may contain minimal audio data, sending to server anyway`
3281
+ `[WARN] Small audio file (${audioFile.size} bytes) - may contain minimal audio data, sending to server anyway`
3177
3282
  );
3178
3283
  }
3179
3284
  const requestData = {
@@ -3194,13 +3299,13 @@ var useAudioRecorder = ({
3194
3299
  let contentType;
3195
3300
  switch (selectedFormatRef.current) {
3196
3301
  case "hl7":
3197
- formData = createHL7TranscriptionRequest(wavFile, requestData);
3302
+ formData = createHL7TranscriptionRequest(audioFile, requestData);
3198
3303
  contentType = "multipart/form-data; hl7-request=true";
3199
3304
  console.log("Created HL7-formatted request");
3200
3305
  console.log("HL7 FormData entries:", Array.from(formData.entries()));
3201
3306
  break;
3202
3307
  case "fhir":
3203
- formData = createFHIRTranscriptionRequest(wavFile, requestData);
3308
+ formData = createFHIRTranscriptionRequest(audioFile, requestData);
3204
3309
  contentType = "multipart/form-data; fhir-request=true";
3205
3310
  console.log("Created FHIR-formatted request");
3206
3311
  console.log("FHIR FormData entries:", Array.from(formData.entries()));
@@ -3213,7 +3318,7 @@ var useAudioRecorder = ({
3213
3318
  } else if (sessionIdRef.current) {
3214
3319
  formData.append("sessionId", sessionIdRef.current);
3215
3320
  }
3216
- formData.append("audio", wavFile);
3321
+ formData.append("audio", audioFile);
3217
3322
  formData.append("model", selectedModelRef.current);
3218
3323
  formData.append("doctorName", doctorName);
3219
3324
  formData.append("patientName", patientName || "");