Skip to content

Commit

Permalink
fix: Convert audio file to mp3 before sending it to WhatsApp (#9432)
Browse files Browse the repository at this point in the history
Last week, the WhatsApp API has started returning an error message for the voice notes, Unsupported Audio mime type audio/opus. Please use one of audio/ogg; codecs=opus, audio/mpeg, audio/amr, audio/mp4, audio/aac. This error began appearing on May 1. Even though there was no change in the files and content type, FB API started rejecting the file.

In this PR, we are converting the audio recordings from Wav to Mp3 from frontend itself.
  • Loading branch information
pranavrajs committed May 8, 2024
1 parent d649bd2 commit 9977bcc
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 11 deletions.
Expand Up @@ -5,6 +5,7 @@
</template>

<script>
import getUuid from 'widget/helpers/uuid';
import 'video.js/dist/video-js.css';
import 'videojs-record/dist/css/videojs.record.css';
Expand All @@ -28,6 +29,7 @@ import OpusRecorderEngine from 'videojs-record/dist/plugins/videojs.record.opus-
import { format, addSeconds } from 'date-fns';
import { AUDIO_FORMATS } from 'shared/constants/messages';
import { convertWavToMp3 } from './utils/mp3ConversionUtils';
WaveSurfer.microphone = MicrophonePlugin;
Expand All @@ -39,6 +41,10 @@ export default {
type: String,
default: AUDIO_FORMATS.WAV,
},
isAWhatsAppChannel: {
type: Boolean,
default: false,
},
},
data() {
return {
Expand Down Expand Up @@ -145,12 +151,17 @@ export default {
stopRecord() {
this.fireStateRecorderChanged('stopped');
},
finishRecord() {
const file = new File(
[this.player.recordedData],
this.player.recordedData.name,
{ type: this.player.recordedData.type }
);
async finishRecord() {
let recordedContent = this.player.recordedData;
let fileName = this.player.recordedData.name;
if (this.isAWhatsAppChannel) {
recordedContent = await convertWavToMp3(this.player.recordedData);
fileName = `${getUuid()}.mp3`;
}
const type = !this.isAWhatsAppChannel
? this.player.recordedData.type
: 'audio/mp3';
const file = new File([recordedContent], fileName, { type });
this.fireRecorderBlob(file);
},
progressRecord() {
Expand Down Expand Up @@ -231,11 +242,13 @@ export default {
@apply bg-transparent max-h-60 min-h-[3rem] pt-4 px-0 pb-0 resize-none;
}
}
// Added to override the default text and bg style to support dark and light mode.
.video-js .vjs-control-bar,
.vjs-record.video-js .vjs-control.vjs-record-indicator:before {
@apply text-slate-600 dark:text-slate-200 bg-transparent dark:bg-transparent;
}
// Added to fix div overlays the screen and takes over the button clicks
// https://github.com/collab-project/videojs-record/issues/688
// https://github.com/collab-project/videojs-record/pull/709
Expand Down
@@ -0,0 +1,53 @@
import lamejs from 'lamejs';
/**
* Encodes a mono channel audio stream to MP3 format.
* @param {number} channels - Number of audio channels.
* @param {number} sampleRate - Sample rate in Hz.
* @param {Int16Array} samples - Audio samples to be encoded.
* @returns {Blob} - The MP3 encoded audio as a Blob.
*/
export const encodeToMP3 = (channels, sampleRate, samples) => {
const outputBuffer = [];
const encoder = new lamejs.Mp3Encoder(channels, sampleRate, 128);
const maxSamplesPerFrame = 1152;

for (let offset = 0; offset < samples.length; offset += maxSamplesPerFrame) {
const sliceEnd = Math.min(offset + maxSamplesPerFrame, samples.length);
const sampleSlice = samples.subarray(offset, sliceEnd);
const mp3Buffer = encoder.encodeBuffer(sampleSlice);

if (mp3Buffer.length > 0) {
outputBuffer.push(new Int8Array(mp3Buffer));
}
}

const remainingData = encoder.flush();
if (remainingData.length > 0) {
outputBuffer.push(new Int8Array(remainingData));
}

return new Blob(outputBuffer, { type: 'audio/mp3' });
};

/**
* Converts a WAV audio Blob to an MP3 format Blob.
* @param {Blob} blob - The audio data in WAV format as a Blob.
* @returns {Promise<Blob>} - A Blob containing the MP3 encoded audio.
*/
export const convertWavToMp3 = async blob => {
try {
const audioBuffer = await blob.arrayBuffer();
const wavHeader = lamejs.WavHeader.readHeader(new DataView(audioBuffer));
const samples = new Int16Array(
audioBuffer,
wavHeader.dataOffset,
wavHeader.dataLen / 2
);

return encodeToMP3(wavHeader.channels, wavHeader.sampleRate, samples);
} catch (error) {
// eslint-disable-next-line
console.log('Failed to convert WAV to MP3:', error);
throw new Error('Conversion from WAV to MP3 failed.');
}
};
Expand Up @@ -53,6 +53,7 @@
v-if="showAudioRecorderEditor"
ref="audioRecorderInput"
:audio-record-format="audioRecordFormat"
:is-a-whats-app-channel="isAWhatsAppChannel"
@state-recorder-progress-changed="onStateProgressRecorderChanged"
@state-recorder-changed="onStateRecorderChanged"
@finish-record="onFinishRecorder"
Expand Down Expand Up @@ -501,11 +502,7 @@ export default {
return `draft-${this.conversationIdByRoute}-${this.replyType}`;
},
audioRecordFormat() {
if (
this.isAWhatsAppChannel ||
this.isAPIInbox ||
this.isATelegramChannel
) {
if (this.isAPIInbox || this.isATelegramChannel) {
return AUDIO_FORMATS.OGG;
}
return AUDIO_FORMATS.WAV;
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -62,6 +62,7 @@
"idb": "^7.1.1",
"ionicons": "~2.0.1",
"js-cookie": "^3.0.5",
"lamejs": "1.2.0",
"libphonenumber-js": "^1.10.24",
"markdown-it": "^13.0.2",
"markdown-it-link-attributes": "^4.0.1",
Expand Down
12 changes: 12 additions & 0 deletions yarn.lock
Expand Up @@ -14062,6 +14062,13 @@ klona@^2.0.4:
resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0"
integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==

lamejs@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/lamejs/-/lamejs-1.2.0.tgz#0259f83db4666141a7b671b8caa6369d95177d08"
integrity sha512-xEYvsB2sZ9jIccqfUQpQEBdB6UYQDG/ta2xQkH7oEpENb0JHFJii965WVF8ErUMdZzoe7EdIruz1WpICdhZ9Pw==
dependencies:
use-strict "1.0.1"

language-subtag-registry@~0.3.2:
version "0.3.21"
resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a"
Expand Down Expand Up @@ -20289,6 +20296,11 @@ urlpattern-polyfill@^6.0.2:
dependencies:
braces "^3.0.2"

use-strict@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/use-strict/-/use-strict-1.0.1.tgz#0bb80d94f49a4a05192b84a8c7d34e95f1a7e3a0"
integrity sha512-IeiWvvEXfW5ltKVMkxq6FvNf2LojMKvB2OCeja6+ct24S1XOmQw2dGr2JyndwACWAGJva9B7yPHwAmeA9QCqAQ==

use@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
Expand Down

0 comments on commit 9977bcc

Please sign in to comment.