Trying to convert a script that involves webrtc into async/await failing - javascript

I am following this tutorial to make webrtc work. I would like to return the streams I get in resolve() to the component I call this getDrone() from. I created a returnStreamArray() function as a child promise which I tried to add as a then() to the stream to add it to the array, however I am stuck. I would appreciate any directions.
export default function getDrone() {
return new Promise((resolve, reject) => {
const drone = new Scaledrone('fBAkzbVAOmA2wbU0');
const roomName = 'observable-room';
const configuration = {
iceServers: [
{
urls: 'stun:stun.l.google.com:19302'
}
]
};
let room;
let pc;
const streamArr = [];
...
drone.on('open', error => {
if (error) {
console.error(error);
}
room = drone.subscribe(roomName);
room.on('open', error => {
if (error) {
onError(error);
}
});
..
..
pc.ontrack = event => {
const stream = event.streams[0];
streamArr.push(stream);
};
navigator.mediaDevices
.getUserMedia({
audio: true,
video: true
})
.then(stream => {
streamArr.push(stream);
stream.getTracks().forEach(track => pc.addTrack(track, stream));
}, onError)
.then(returnStreamArray => {
console.log(stream);
});
..
..
});
}
function returnStreamArray() {
return Promise.resolve({
streamArray: streamArr
});
}

Related

cancel multiple promises inside a promise on unmount?

hi i want to cancel promise on unmount since i received warning,
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
My code:
const makeCancelable = (promise: Promise<void>) => {
let hasCanceled_ = false;
const wrappedPromise = new Promise((resolve, reject) => {
promise.then(
(val) => (hasCanceled_ ? reject({ isCanceled: true }) : resolve(val)),
(error) => (hasCanceled_ ? reject({ isCanceled: true }) : reject(error))
);
});
return {
promise: wrappedPromise,
cancel() {
hasCanceled_ = true;
},
};
};
useEffect(() => {
const initialize = async () => {
const getImageFilesystemKey = (remoteUri: string) => {
const [_, fileName] = remoteUri.split('toolbox-talks/');
return `${cacheDirectory}${fileName}`;
};
const filesystemUri = getImageFilesystemKey(uri);
try {
// Use the cached image if it exists
const metadata = await getInfoAsync(filesystemUri);
if (metadata.exists) {
console.log('resolve 1');
setFileUri(filesystemUri);
} else {
const imageObject = await downloadAsync(uri, filesystemUri);
console.log('resolve 2');
setFileUri(imageObject.uri);
}
// otherwise download to cache
} catch (err) {
console.log('error 3');
setFileUri(uri);
}
};
const cancelable = makeCancelable(initialize());
cancelable.promise
.then(() => {
console.log('reslved');
})
.catch((e) => {
console.log('e ', e);
});
return () => {
cancelable.cancel();
};
}, []);
but i still get warning on fast press, help me please?
You're cancelling the promise, but you are not cancelling the axios call or any of the logic that happens after it inside initialize(). So while it is true that the console won't print resolved, setFileUri will be called regardless, which causes your problem.
A solution could look like this (untested):
const makeCancelable = (promise: Promise<void>) => {
let hasCanceled_ = false;
const wrappedPromise = new Promise((resolve, reject) => {
promise.then(
val => (hasCanceled_ ? reject({ isCanceled: true }) : resolve(val)),
error => (hasCanceled_ ? reject({ isCanceled: true }) : reject(error))
);
});
return {
promise: wrappedPromise,
cancel() {
hasCanceled_ = true;
}
};
};
const initialize = async () => {
const getImageFilesystemKey = (remoteUri: string) => {
const [_, fileName] = remoteUri.split("toolbox-talks/");
return `${cacheDirectory}${fileName}`;
};
const filesystemUri = getImageFilesystemKey(uri);
try {
// Use the cached image if it exists
const metadata = await getInfoAsync(filesystemUri);
if (metadata.exists) {
console.log("resolve 1");
return filesystemUri;
} else {
const imageObject = await downloadAsync(uri, filesystemUri);
console.log("resolve 2");
return imageObject.uri;
}
// otherwise download to cache
} catch (err) {
console.error("error 3", err);
return uri;
}
};
useEffect(() => {
const cancelable = makeCancelable(initialize());
cancelable.promise.then(
fileURI => {
console.log("resolved");
setFileUri(fileURI);
},
() => {
// Your logic is such that it's only possible to get here if the promise is cancelled
console.log("cancelled");
}
);
return () => {
cancelable.cancel();
};
}, []);
This ensures that you will only call setFileUri if the promise is not cancelled (I did not check the logic of makeCancelable).

module.exports returning undefined

I`m trying to make a translation function with IBM Watson API in "services/ibmWatson/index.js". I receive the response correctly, but when I return the response to the "IncidentController.js" it receives as undefined.
const LanguageTranslatorV3 = require('ibm-watson/language-translator/v3');
const { IamAuthenticator } = require('ibm-watson/auth');
module.exports = {
async translate(phrase, language) {
const languageTranslator = new LanguageTranslatorV3({
authenticator: new IamAuthenticator({ apikey: '<my_API_key>' }),
url: 'https://gateway.watsonplatform.net/language-translator/api/',
version: '2020-03-28',
});
await languageTranslator.translate(
{
text: phrase,
source: 'pt',
target: language
})
.then(response => {
if(response.status=200){
console.log(response.result.translations);
return(response.result.translations);
}
return (["error"]);
})
.catch(err => {
console.log('error: ', err);
return (["error"]);
});
}
}
In the above code the console.log(response.result.translations) returns correctly:
[ { translation: 'Dog run over.' },
{ translation: 'Castration' },
{ translation: 'Ticks' },
{ translation: 'Tuberculosis' } ]
In the in IncidentController.js:
const Watson = require('../../src/services/ibmWatson');
const db_connection = require('../database/connection');
module.exports = {
async index(request, response) {
const incidents = await db_connection('incidents').join('ongs', 'ongs.id', '=', 'incidents.ong_id')
.select([
'incidents.*',
'ongs.name',
'ongs.uf']
);
const titles = [];
incidents.forEach((incident) => { titles.push(incident.title) });
const translated_titles = await Watson.translate(titles, "en");
console.log(translated_titles);
return response.json(incidents);
}
}
In the above code the console.log(response.result.translations) returns undefined.
What is wrong with it?
You are returning response.result.translations to the response callback from then().
Since that callback cannot be accessed by your IncidentController, it returns undefined.
This is one way to solve this problem:
// services/ibmWatson/index.js
translate(phrase, language) {
return new Promise((resolve, reject) => {
try {
const response = await languageTranslator.translate({ /* options */ });
resolve(response); // Return the translations
} catch(error) {
reject(error); // If there's an error, return the error
}
});
}
// IncidentController.js
async index() {
// ...
const translatedTitles = await Watson.translate(titles, "en");
console.log(translatedTitles); // Should be working now
}
I hope I could help you or at least lead you in the right direction.

Webrtc how to set the video remote after receiver back an asnwer

hello to all I am new in this I have all almost done,but just on my initiator for say something I don't know the way for receiver the remote video stream after receiver the answer, can some one help me please ?
this is my client
$(document).ready(() => {
const configuration = {
iceServers: [{ url: 'stun:stun2.1.google.com:19302' }]
}
var peerConection = null;
// var btnCall = $('body #call');
var list = $('#mylist');
var TitlePrint = $('#titleUser');
var localVideo = document.getElementById('local');
var remoteVideo = document.getElementById('remote');
var userid = null;
var socket = io();
socket.on('connect', () => {
userid = socket.id
TitlePrint.text(userid);
});
socket.on('users', data => {
var users = [];
list.empty();
for (let index = 0; index < data.user.length; index++) {
if (data.user[index] != userid) {
users.push(`<button id="call" class="list-group-item list-group-item-action" data-ids="${data.user[index]}">${data.user[index]}</button>`);
}
}
if (users.length != 0) {
list.html(users);
} else {
list.html(`<div class="list-group-item"> Any users connected! </div>`);
}
});
$('body').on('click', '#call', function () {
let toId = $(this).attr('data-ids');
socket.emit('initiator', { initiatorid: userid, receiverid: toId });
});
socket.on('initiator', data => {
peerConection = createRTC(socket);
if (data.initiatorid === userid) {
console.log('this is the initiator');
initiateSignaling(socket, peerConection, data.receiverid, data.initiatorid);
} else {
console.log('this is the receiver');
prepareToReceiveOffer(socket, peerConection, data.initiatorid, data.receiverid);
}
});
// =============== HELPERS =====================//
function createRTC(socket) {
console.log('createRTC')
var peerConection = new RTCPeerConnection(configuration);
peerConection.onicecandidate = (e) => {
if (e.candidate) {
console.log('emit candidate')
socket.emit('send-candidate', e.candidate);
}
}
socket.on('receiver-candidate', (candidate) => {
peerConection.addIceCandidate(candidate);
});
return peerConection;
}
function initiateSignaling(socket, peerConection, targetID, from) {
navigator.mediaDevices.getUserMedia({ video: true, audio: false }).then((stream) => {
stream.getTracks().forEach(function (track) {
peerConection.addTrack(track, stream);
});
localVideo.srcObject = stream;
peerConection.createOffer().then(function (offer) {
return peerConection.setLocalDescription(offer);
})
.then(function () {
socket.emit('send-offer', {
from: from,
target: targetID,
type: "send-offer",
sdp: peerConection.localDescription
});
})
.catch(function (reason) {
console.log('error on create offer', reason);
});
})
socket.on('receiver-answer', (answer) => {
console.log(answer);
peerConection.setRemoteDescription(answer.sdp);
peerConection.ontrack = function (event) {
remoteVideo.srcObject = event.streams[0];
};
});
}
function prepareToReceiveOffer(socket, peerConection, targetID, from) {
socket.on('receiver-offer', (offer) => {
console.log(offer);
peerConection.setRemoteDescription(offer.sdp);
peerConection.createAnswer().then(function (answer) {
return peerConection.setLocalDescription(answer);
})
.then(function () {
socket.emit('send-answer', {
from: from,
target: targetID,
type: "send-answer",
sdp: peerConection.localDescription
});
});
peerConection.ontrack = function (event) {
remoteVideo.srcObject = event.streams[0];
};
navigator.mediaDevices.getUserMedia({ video: true, audio: false }).then((stream) => {
localVideo.srcObject = stream;
})
});
}
});
I just using socket.io I am handle the offer and answer then on my socket server I just set like this
socket.on('initiator', (init) => {
console.log(init);
io.to('video').emit('initiator', init);
});
socket.on('send-offer', offer => {
console.log('sending offer', offer);
socket.broadcast.emit('receiver-offer', offer);
});
socket.on('send-answer', answer => {
console.log('sending answer', answer);
socket.broadcast.emit('receiver-answer', answer);
});
socket.on('send-candidate', candidate => {
console.log(candidate);
socket.broadcast.emit('receiver-candidate',candidate);
});
I am get video remote on my receiver from the initiator but not in the initiator, I don't know what I miss for getting the remote video thanks so mush guys
The initiator calls addTrack(), but not the receiver doesn't, so this is only sending media one way.
In prepareToReceiveOffer you call getUserMedia() but never add resulting tracks to the peer connection. If you want a two-way call, it needs to call addTrack() as part of the offer/answer negotiation.
Just be sure to call getUserMedia() after setRemoteDescription to not miss ICE candidates:
function prepareToReceiveOffer(socket, peerConection, targetID, from) {
socket.on('receiver-offer', (offer) => {
console.log(offer);
peerConection.setRemoteDescription(offer.sdp)
.then(() => navigator.mediaDevices.getUserMedia({video: true, audio: false}))
.then(stream => {
localVideo.srcObject = stream;
for (const track of stream.getTracks()) {
peerConection.addTrack(track, stream);
}
return peerConection.createAnswer();
})
.then(function (answer) {
return peerConection.setLocalDescription(answer);
})
.then(function () {
socket.emit('send-answer', {
from: from,
target: targetID,
type: "send-answer",
sdp: peerConection.localDescription
});
})
.catch(err => console.log(err.message));
peerConection.ontrack = function (event) {
remoteVideo.srcObject = event.streams[0];
};
});
}
I get resolve it, in this particular case I my initiateSignaling function when I receiver the offer I has to include this
socket.on('receiver-answer', (answer) => {
console.log(answer);
peerConection.setRemoteDescription(answer.sdp)
.then(function () {
return navigator.mediaDevices.getUserMedia({video:true, audio: false});
})
.then(function (stream) {
return peerConection.addStream(stream);
})
peerConection.ontrack = function (event) {
remoteVideo.srcObject = event.streams[0];
};
});
now is working for me

sinon spy doesn't register call in a generator loop?

I want to check that a piece of code is being called, so I'm using a sinon spy to assert this. However, the spy seems to be failing, despite console.logs showing that the code has been called correctly.
I'm wondering if my function being a generator is causing my spy to misreport what it's doing.
my code (i've taken out some chunks for brevity):
isBlacklisted(release, jobUUID) {
names.forEach((name) => {
this._spawnPythonProcessGenerator(
this.IS_BLACKLISTED_SCRIPT,
name
).next().value
.then((data) => {
console.log(data);
})
.catch((err) => {
this._errorEvent(release, name, err, jobUUID);
});
}, this);
}
_errorEvent(release, name, err, jobUUID) {
console.log('got here');
}
*_spawnPythonProcessGenerator(scriptSrc, name) {
const pythonProcess = this._childProcess.spawn(
'python3',
[...arguments]
);
yield new Promise((resolve, reject) => {
pythonProcess.stderr.on('data', (err) => {
reject(err.toString());
});
pythonProcess.stdout.on('data', (data) => {
resolve(data.toString());
});
});
}
and my tests:
const Blacklist = require('../../src/Blacklist2');
const childProcess = require('child_process');
const uuid = require('uuid/v4');
describe('Blacklist', () => {
let blacklist;
beforeEach(() => {
blacklist = new Blacklist(childProcess);
blacklist.IS_BLACKLISTED_SCRIPT = './test/helpers/good.py';
});
describe('isBlacklisted', () => {
it('should call the _errorEvent for every name in a release when the blacklist application is not available', async () => {
let release = {
id: 1001,
asset_controller: {
id: 54321,
},
display_name: 'Blah',
names: [
{
id: 2001,
name: 'Blah',
},
],
};
blacklist.IS_BLACKLISTED_SCRIPT = './test/helpers/'+ uuid() +'.py';
const spy = sinon.spy(blacklist, '_errorEvent');
blacklist.isBlacklisted(release, uuid());
console.log(spy);
sinon.assert.calledTwice(spy);
spy.restore();
});
});
});
my spy reports:
notCalled: true
I'll expand my comment into an actual answer, hopefully that helps.
Your problem lies with asynchrony, not with the generator. You need isBlacklisted to return a promise you can wait on. Otherwise your assertion happens before the spy is called.
Something like this:
isBlacklisted(release, jobUUID) {
let promises = names.map((name) => {
return this._spawnPythonProcessGenerator(
this.IS_BLACKLISTED_SCRIPT,
name
).next().value
.then((data) => {
console.log(data);
})
.catch((err) => {
this._errorEvent(release, name, err, jobUUID);
});
}, this);
return Promise.all(promises);
}
Then, in your test:
return blacklist.isBlacklisted(release, uuid())
.then(() => {
sinon.assert.calledTwice(spy);
});
Also... This isn't related to your problem, but your _spawnPythonProcessGenerator method doesn't need to be a generator. You're only using the first value of it by calling next like that and calling the whole thing over again for each array item.
It will work the same if you take out the *, change yield to return, and skip the .next().value when you call it. You also probably want to rename it because it's not a generator.
_spawnPythonProcess(scriptSrc, name) {
const pythonProcess = this._childProcess.spawn(
'python3',
[...arguments]
);
return new Promise((resolve, reject) => {
pythonProcess.stderr.on('data', (err) => {
reject(err.toString());
});
pythonProcess.stdout.on('data', (data) => {
resolve(data.toString());
});
});
}
When you call it:
let promises = names.map((name) => {
return this._spawnPythonProcess(
this.IS_BLACKLISTED_SCRIPT,
name
)
.then((data) => {
console.log(data);
})
.catch((err) => {
this._errorEvent(release, name, err, jobUUID);
});
}, this);
return Promise.all(promises);

Mock stream finish on jest

I have a function that downloads a file using got and uses fs.createWritableStream to write file on disk. The code is working smooth but the unit test are being a pain:
download(twilioPath, destFile) {
return new Promise(function(resolve, reject) {
const uri = `${TWILIO_BASE_URL}${twilioPath}`.replace('json', 'mp3')
let file = fs.createWriteStream(`/tmp/${destFile}`)
console.log(got)
let str = got.stream(uri)
console.log(uri)
str.on('error', function(err) {
console.log('Error dl', err)
reject(err)
})
str.pipe(file)
file.on('finish', function() {
console.log('banana')
let cb = function() {
console.log('Download completed')
resolve(file)
}
file.close(cb)
})
})
My resolve or reject is not being called and I don't know how to fix that:
const Recording = require('./Recording')
const Readable = require('stream')
var mockedStream = new Readable();
var mockedWrite = new Readable.Writable()
jest.mock('fs', () => ({
createWriteStream: jest.fn((file_name) => {
return mockedWrite;
})
}));
jest.mock('got', () => {
return {
stream: () => {
return mockedStream
}
}
})
describe('Recording', () => {
test('should download a file from twilio', async () => {
...
mockedStream.emit('error')
mockedWrite.emit('finish')
console.log(result)
....
})
})
I've tried to force emit events but no lucky.

Categories

Resources