I am using react-media-recorder to record audio. It works fine on localhost but when deployed to a server and accessed, the onStop method stops working. It doesn't get invoked.
this is my file:
import { useReactMediaRecorder } from "react-media-recorder"
const { status, startRecording, stopRecording, error, mediaBlobUrl, clearBlobUrl } = useReactMediaRecorder({
audio: true,
type: "audio/wav",
onStop: (blobUrl, blob) => {
console.log("onStop recording")
const url = URL.createObjectURL(blob)
let formData = new FormData()
//person_name-person_id-language_id-sentence.id-date
const today = new Date()
// const file_name = `${id}-${language_id}-${sentence.id}-${today.toISOString()}.wav`
const file_name = `${name}-${id}-${language_id}-${
sentence.id
}-${today.toDateString()}-${language_name}.wav`
console.log("-------------------------------------------------------")
console.log("file_name :>>", file_name)
console.log("-------------------------------------------------------")
formData.append("file", blob, file_name)
let upload_url
if (sample) {
upload_url = "sentence/upload_audio_sample"
} else {
upload_url = "sentence/upload_audio"
}
console.log(`upload_url`, upload_url)
axios
.post(upload_url, formData)
.then((d) => console.log("after post blob :>>", d))
.catch((e) => console.log("error in post blob :>>", e))
},
})
const handleStartRecording = () => {
setRecording(!recording)
if (!recording) {
clearBlobUrl()
startRecording()
} else {
stopRecording()
}
}
any help is much appreciated
for this to work both the frontend and backend need to be on https.
Make sure your backend server is running on https
Related
I have an mp3 file that I am caching as such:
const request = URL_TO_MY_MP3_FILE
caches.open("my-cache").then(cache => {
cache.add(request);
});
I can see that the mp3 file is being cached in the Application tab:
By the way, how do I ensure that file is Content-Type cached as audio/mp3 and not audio/mpeg?
Later on, I would like to retrieve the mp3 file from cache so I can play it in the browser:
caches.open("my-cache").then(cache => {
const responsePromise = cache.match(request);
responsePromise.then(result => {
console.log(result.body)
this.audio = new Audio(result.body);
const playPromise = this.audio.play();
playPromise.then(function() {
console.log('success!')
}).catch(function(error) {
console.log(error)
}.bind(this), false);
});
})
This is the output of console.log(result.body):
After loading the mp3 file with new Audio(result.body) I try to play the file with this.audio.play() but this results in an error:
DOMException: Failed to load because no supported source was found.
How can I retrieve the mp3 file from cache and play it in the browser?
You have to call reader.read()
caches.open("my-cache").then(cache => {
const responsePromise = cache.match(request);
responsePromise.then(result => {
var reader = result.body.getReader();
reader.read().then((result) => {
const mimeType = 'audio/mpeg'
const url = URL.createObjectURL(new Blob([result.value.buffer], {type: mimeType}))
this.audio = new Audio(url);
const playPromise = this.audio.play();
playPromise.then(function() {
console.log('success!')
}).catch(function(error) {
console.log(error)
}.bind(this), false);
});
});
});
I'm not sure if this is even possible, but here's what I'm trying to do:
Let the user enter some text
Generate a PNG from that text
Upload it to Pinata, which requires it to be in ReadStream format
Do all of this on the front-end
I've managed to accomplish (1) and (2) using html2canvas.
The tricky part is (3). The reason it has to be in ReadStream format is because that's the format Pinata's SDK wants:
const fs = require('fs');
const readableStreamForFile = fs.createReadStream('./yourfile.png');
const options = {
pinataMetadata: {
name: MyCustomName,
keyvalues: {
customKey: 'customValue',
customKey2: 'customValue2'
}
},
pinataOptions: {
cidVersion: 0
}
};
pinata.pinFileToIPFS(readableStreamForFile, options).then((result) => {
//handle results here
console.log(result);
}).catch((err) => {
//handle error here
console.log(err);
});
I realize that this would be no problem to do on the backend with node, but I'd like to do it on the front-end. Is that at all possible? Or am I crazy?
I'm specifically using Vue if that matters.
For anyone interested the solution ended up being using fetch+blob:
const generateImg = async () => {
const canvas = await html2canvas(document.getElementById('hello'));
const img = canvas.toDataURL('image/png');
const res = await fetch(img);
return res.blob();
};
This blob can then be passed into a more manual version of their SDK:
const uploadImg = (blob: Blob) => {
const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`;
const data = new FormData();
data.append('file', blob);
const metadata = JSON.stringify({
name: 'testname',
});
data.append('pinataMetadata', metadata);
const pinataOptions = JSON.stringify({
cidVersion: 0,
});
data.append('pinataOptions', pinataOptions);
return axios
.post(url, data, {
maxBodyLength: 'Infinity' as any, // this is needed to prevent axios from erroring out with large files
headers: {
// #ts-ignore
'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
pinata_api_key: apiKey,
pinata_secret_api_key: apiSecret,
},
})
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
});
};
I have this code in js witch recod an audio from the browser and I need to send it back from js to flask
start: function () {
var options = {audio: true, video: false};
navigator.mediaDevices.getUserMedia(options).then(function (stream) {
myRecorder.objects.stream = stream;
myRecorder.objects.recorder = new Recorder(
myRecorder.objects.context.createMediaStreamSource(stream),
{numChannels: 1}
);
myRecorder.objects.recorder.record();
}).catch(function (err) {});
How I should do that while making the file in wav format?
The following example creates a limited time audio recording and uploads it when finished. A form containing a blob is used for this.
It would also be possible to transmit the pure blob to the server, but since there are differences in the audio format used depending on the browser, this is the more general variant.
(function() {
const uploadURL = "{{ url_for('upload') }}";
const startButton = document.getElementById("toggle-rec-btn");
startButton.addEventListener("click", function() {
if (!navigator.mediaDevices) {
console.error("getUserMedia not supported.")
return;
}
const constraints = { audio: true };
navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream) {
let chunks = []
let recorder = new MediaRecorder(stream);
recorder.ondataavailable = event => {
// Collect all the chunks of the recording in an array.
chunks.push(event.data);
};
recorder.onstop = event => {
console.log("Recording stopped.")
// Create a blob with all the chunks of the recording.
let blob = new Blob(chunks, { type: recorder.mimeType });
chunks = [];
startButton.disabled = false;
// Create form data that contain the recording.
let formData = new FormData();
formData.append("audio_file", blob);
// Send the form data to the server.
fetch(uploadURL, {
method: "POST",
cache: "no-cache",
body: formData
}).then(resp => {
if (resp.status === 200) {
window.location.reload(true);
} else {
console.error("Error:", resp)
}
}).catch(err => {
console.error(err);
});
};
recorder.onstart = event => {
console.log("Recording started.");
startButton.disabled = true;
// Stop recording when the time is up.
setTimeout(function() { recorder.stop(); }, 10000);
};
recorder.start();
})
.catch(function(err) {
console.error(err);
});
});
})();
All recordings are saved on the server in a directory with the default name "var/app-instance/uploads".
import os
from flask import abort, current_app, make_response, request
from mimetypes import guess_extension
from werkzeug.utils import secure_filename
#app.route('/upload', methods=['POST'])
def upload():
if 'audio_file' in request.files:
file = request.files['audio_file']
# Get the file suffix based on the mime type.
extname = guess_extension(file.mimetype)
if not extname:
abort(400)
# Test here for allowed file extensions.
# Generate a unique file name with the help of consecutive numbering.
i = 1
while True:
dst = os.path.join(
current_app.instance_path,
current_app.config.get('UPLOAD_FOLDER', 'uploads'),
secure_filename(f'audio_record_{i}{extname}'))
if not os.path.exists(dst): break
i += 1
# Save the file to disk.
file.save(dst)
return make_response('', 200)
abort(400)
I wish you every success in implementing your project.
I am working on VUI interface with Reactjs frontend. I got a BLOB file that I can play but I want to convert it to .WAV file using REACT or Javascript to send it to my server.
I tried lot of things, but found no solution
toggleRecording() {
if (this.state.start === 1) {
console.log("we start recording", this.state.start)
this.setState({ start: 0, recognition: "" })
const constraints = {
audio: {
sampleRate: 16000,
channelCount: 1,
}
}
navigator.mediaDevices.getUserMedia(constraints)
.then(stream => {
console.log(this);
this.recorder = new MediaRecorder(stream);
this.recorder.start();
const audioChunks = [];
this.recorder.addEventListener("dataavailable", event => {
audioChunks.push(event.data);
});
this.recorder.addEventListener("stop", () => {
const audioBlob = new Blob(audioChunks, { 'type': 'audio/wav' });
const audioUrl = URL.createObjectURL(audioBlob);
console.log("test: ", audioUrl)
console.log(audioBlob.type)
fetch('http://127.0.0.1:6060/api/sendaudio', {
method: "post",
headers: { 'Content-Type': 'audio/wav' },
body: audioBlob
})
.then(response => {
return response.text()
}).then(text => {
console.log(text);
this.setState({ recognition: text })
});
//to play the audio file:
const audio = new Audio(audioUrl);
audio.play();
});
});
}
I expect to get a Wav file to post to my server but don't know how to do that ....
You can try this package if you don't have a problem to add new dependency: https://www.npmjs.com/package/audiobuffer-to-wav
Hope it will work for you
I am trying to upload a file to web api which takes the file as byte array using angular 2 application.
I am not able to pass the byte array from angular 2 page to web api. It looks like the File Reader read method is asynchronous. How do I make this as synchronous call or wait for the file content to be loaded before executing the next line of code?
Below is my code
//attachment on browse - when the browse button is clicked
//It only assign the file to a local variable (attachment)
fileChange = (event) => {
var files = event.target.files;
if (files.length > 0) {
this.attachment = files[0];
}
}
//when the submit button is clicked
onSubmit = () => {
//Read the content of the file and store it in local variable (fileData)
let fr = new FileReader();
let data = new Blob([this.attachment]);
fr.readAsArrayBuffer(data);
fr.onloadend = () => {
this.fileData = fr.result; //Note : This always "undefined"
};
//build the attachment object which will be sent to Web API
let attachment: Attachment = {
AttachmentId: '0',
FileName: this.form.controls["attachmentName"].value,
FileData: this.fileData
}
//build the purchase order object
let order: UpdatePurchaseOrder = {
SendEmail: true,
PurchaseOrderNumber: this.form.controls["purchaseOrderNumber"].value,
Attachment: attachment
}
//call the web api and pass the purchaseorder object
this.updatePoService
.updatePurchaseOrder(this.form.controls["purchaseOrderRequestId"].value, order)
.subscribe(data => {
if (data) {
this.saveSuccess = true;
}
else {
this.saveSuccess = false;
}
},
error => this.errors = error,
() => this.res = 'Completed'
);
}
Any hint would be useful.
regards,
-Alan-
You cannot make this async call synchronous. But you can take advantage of the observables to wait for the files to be read:
//when the submit button is clicked
onSubmit = () => {
let file = Observable.create((observer) => {
let fr = new FileReader();
let data = new Blob([this.attachment]);
fr.readAsArrayBuffer(data);
fr.onloadend = () => {
observer.next(fr.result);
observer.complete()
};
fr.onerror = (err) => {
observer.error(err)
}
fr.onabort = () => {
observer.error("aborted")
}
});
file.map((fileData) => {
//build the attachment object which will be sent to Web API
let attachment: Attachment = {
AttachmentId: '0',
FileName: this.form.controls["attachmentName"].value,
FileData: fileData
}
//build the purchase order object
let order: UpdatePurchaseOrder = {
SendEmail: true,
PurchaseOrderNumber: this.form.controls["purchaseOrderNumber"].value,
Attachment: attachment
}
return order;
})
.switchMap(order => this.updatePoService.updatePurchaseOrder(this.form.controls["purchaseOrderRequestId"].value, order))
.subscribe(data => {
if (data) {
this.saveSuccess = true;
} else {
this.saveSuccess = false;
}
},
error => this.errors = error,
() => this.res = 'Completed'
);
}
I arrived here looking for a solution for a similar issue. I'm performing requests to an endpoint which can response a binary blob if anything goes well or a JSON file in event of error.
this.httpClient.post(urlService, bodyRequest,
{responseType: 'blob', headers: headers})
.pipe(map((response: Response) => response),
catchError((err: Error | HttpErrorResponse) => {
if (err instanceof HttpErrorResponse) {
// here, err.error is a BLOB containing a JSON String with the error message
} else {
return throwError(ErrorDataService.overLoadError(err, message));
}
}));
As FileReaderSync apparently doesn't work in Angular6 I took n00dl3's solution (above) to throw the error after parsing the Blob content:
return this.httpClient.post(urlService, bodyRequest,
{responseType: 'blob', headers: headers})
.pipe(map((response: Response) => response),
catchError((err: Error | HttpErrorResponse) => {
const message = `In TtsService.getTts(${locale},${outputFormat}). ${err.message}`;
if (err instanceof HttpErrorResponse) {
const $errBlobReader: Observable<HttpErrorResponse> = Observable.create((observer) => {
const fr = new FileReader();
const errorBlob = err.error;
fr.readAsText(errorBlob, 'utf8');
fr.onloadend = () => {
const errMsg = JSON.parse(fr.result).message;
const msg = `In TtsService.getTts(${locale},${outputFormat}). ${errMsg}`;
observer.error(ErrorDataService.overLoadError(err, msg));
};
fr.onerror = (blobReadError) => {
observer.error(blobReadError);
};
fr.onabort = () => {
observer.error('aborted');
};
});
return $errBlobReader;
} else {
return throwError(ErrorDataService.overLoadError(err, message));
}
}));
Thanks! You really saved my day!