pc.removeStream(stream) not implemented Firefox + WebRTC - firefox

How can I remove a MediaStream from a peer connection in Firefox? The API says that the method pc.removeStream(stream) exists but when I use it, I receive the error: "removeStream not implemented"
I checked a solution here but I didn't understand the usage of the replaceTrack() function. I wan't to replace one audio track with another, and I can't figure out how to make it work ..

my understanding is Firefox is yet to implement adding/ removing streams from an PeerConnection, but you can add/ remove tracks( RTCRtpSender in this case) from within a single mediastream, so when you look at jib's answer takes all the tracks from input mediastream, checks if peerconnection's mediastream contains those tracks, removes them if present:
function removeTrack(pc, stream){
pc.getSenders().forEach(function(sender){
stream.getTracks.forEach(function(track){
if(track == sender.track){
pc.removeTrack(sender);
}
})
});
}
or basically you can just his polyfill:
window.mozRTCPeerConnection.prototype.removeStream = function(stream) {
this.getSenders().forEach(sender =>
stream.getTracks().includes(sender.track) && this.removeTrack(sender));
}

Related

Using Twilio SDK, Call Array of Numbers Consecutively Until Someone Picks Up

I'm working on building a call center functionality that when receiving an inbound call, it dials through a list of agent's phone numbers (as determined by a separate piece of code) and on the first number connection we break out of the code and run a separate function to connect the agent into a queue where the customer is waiting. This is being built in Laravel 5.7 since there are some other future dashboards that are being created and I wanted to place it in the code the team is going to use.
The initial customer queueing and connecting the agent to the call seems to be working.
I am looking for help with the following code:
public function findAgentByPriority($agentCount) {
$twilio = new Client(env('TWILIO_SID'),env('TWILIO_SECRET'));
// test numbers
$agentArr[] = ["agent" => env('TWILIO_TEST_AGENT1')];
$agentArr[] = ["agent" => env('TWILIO_TEST_AGENT2')];
// $agentArr = json_encode($agentArr);
//build the array by querying /api/v1/agile/users
$numbers = $agentArr;
// this part of the code will call one person after the next
// call the next number
if($agentCount == NULL){
$agentCount = count($numbers);
echo "in if<br>";
$call = $twilio->calls
->create(
$numbers[0],
env('TWILIO_MAIN_NUMBER'),
[
"url" => "https://{$_SERVER['HTTP_HOST']}/ivr/connect-agent",
"statusCallback" => "https://{$_SERVER['HTTP_HOST']}/ivr/next-agent?c=$agentCount",
"timeout" => 20
]
);
}elseif($agentCount > 0){
// when we run out of numbers move out of the loop
$agentCount = 0;
}else{
// when we run out of numbers move out of the loop
}
// once there are no more agents that were logged in today we
// will move to dial cell phone fallback for 40s
// and last we'll call on the voicemail function
}
And here is our connectAgent function that the connect-agent route is calling.
public function connectAgent() {
$response = new Twiml\VoiceResponse;
$dequeue = $response->dial('');
$dequeue->queue('main');
}
I was thinking basically have the "statusCallback" shoot to a new method when we read that there was "no-answer" and call the next number in line. I don't know how to keep track of what numbers are left by passing a variable through the callback. Would setting it into a database be better and do something like look for any remaining arrayIDs where records still exist and dial down the next one? I could establish the array and it's parameters in the database on the first function call.
I could avoid the issue of two people calling at once and messing up the code by just building out different arrayIDs and only going through one set at a time.
Any guidance is appreciated!
UPDATE
PHP's server was causing infinite loops. Moving testing to my vagrant box has resolved this so now I can call on the Laravel route from the same server without issue. This along with the answer of passing an array through the callback has helped me solve this problem. Updated code to follow for reference.
this following issue helped me figure out the second request getting stuck issue: Calling route from same server causes an infinite loop
Twilio developer evangelist here.
What you could do is add the current number as a query parameter to the statusCallback URL. That way, when the callback is called you can find that number in your list of numbers and then move on to the next one. This way you don't need to store anything in the database.
Let me know if that helps at all.

TB Plugin errors after updating TB from 38.7.2 to 45.1.0

Several years ago I made a private Thunderbird plugin for automatically processing paypal emails about subscriptions. The user has to put the paypal emails in a certain folder "PaypalMsgs", and the plugin reads them one by one, finds out if it is a payment, a cancellation etc. and then updates the "Other" field of the person in the address book.
The plugin got broken with the recent update of Thunderbird to 45.1.0 because it cannot find the folder PaypalMsgs any more.
This is the code for finding the folder:
// determine the local root folder
var localRootFolder = Components
.classes["#mozilla.org/messenger/account-manager;1"]
.getService(Components.interfaces.nsIMsgAccountManager)
.localFoldersServer
.rootFolder;
// start with root folder to find folder with given name
this.ppPaypalFldr = this.findFldrDeep(localRootFolder, "PaypalMsgs");
// recursive function to find a folder fldr with the name fldrName
findFldrDeep: function(fldr, fldrName) {
if(fldr.name == fldrName) {
return fldr;
} else {
if(fldr.hasSubFolders) {
var fldrEnum = fldr.subFolders;
while(fldrEnum.hasMoreElements()) {
var sfldr = fldrEnum.getNext();
var result = this.findFldrDeep(sfldr, fldrName);
if(result) {
return result;
}
}
} else {
return null;
}
}
},
When executed nothing happens and TB's error console shows:
Error: TypeError: this.ppPaypalFldr undefined
at the first location where this.ppPaypalFldr is used
It might be an easy thing, like the definition of the services of nsIMsgAccountManager might have changed or the folder type suddenly has different functions, but I have a really hard time to find reliable documentation or even the source for TB 45.
Thank you for any hints and support!
After more seach, debugging and thinking (sic!) I found the problem:
At the line
var sfldr = fldrEnum.getNext();
The interface is missing and it looks like in TB45 something has changed so the interface is not automatically retrieved from somewhere (the software worked without this interface since about 4 or 5 years).
So the correct line is:
var sfldr = fldrEnum.getNext().QueryInterface(Components.interfaces.nsIMsgFolder);
I also checked all of the plugin and added all interfaces - now it works like a charm.
Writing the problem here alone has helped me a lot to find the solution ;-)

Backbone navigate triggers twice in Firefox

Trying to use Backbone's navigate property.
this.navigate("week/" + companyName + "/" + employeeNo + "/" + weekEnd, { trigger: true, replace: false });
The code above is executed once.
It hits this:
routes: {
"week/:companyName/:employeeNo/:weekEnd": "getWeek"
},
And then this function gets hit twice:
getWeek: function (companyName, employeeNo, weekEnd) {
console.log('getWeek:', companyName, employeeNo, weekEnd);
}
It is logged twice in Firefox, only once in IE and Chrome.
What's the issue here? I originally didn't even have trigger set to true, and Firefox ignored that and still triggered the URL.
I had a similar issue recently with Firefox doing two server calls after a Backbone.navigate. In my case it was because we had not encoded the string. Does your company name have any characters which should be encoded?
You could try:
this.navigate("week/" + escape(companyName) + "/" + employeeNo + "/" + weekEnd, { trigger: true, replace: false });
Stepping in as I've run into the same issue and got to the underlying problem here.
As everyone mentioned before, the problem comes from URL encoding. Now as to why the issue only appears in Firefox...
Let's start by summarizing quickly how the routes are called when the hash changes. There are 3 key functions here:
loadUrl: this function is the one that will call your route handler.
navigate: this is the function used to change the route manually. If the trigger flag is set to true, the function will call loadUrl.
checkUrl: this function is set as callback for the onhashchange event on the window object (when it's available of course). It also runs loadUrl on certain conditions.
Now, we're getting to the interesting part.
When you run navigate, Backbone will cache the fragment you navigated to. The hash changing, checkUrl will also be called. This function will then check if the cached hash equals the current one, so as not to execute loadUrl if you called navigate before, because it would mean it has already been called. To make that comparison, checkUrl gets the current hash with the function getFragment, which uses getHash. Here is getHash's code:
getHash: function(window) {
var match = (window || this).location.href.match(/#(.*)$/);
return match ? match[1] : '';
},
And you got your problem. location.href is URI-encoded in firefox, but is not in chrome. So if you navigated to another hash (with or without the trigger flag), in firefox, Backbone will cache the unencoded version of your hash, and then compare it with the encoded version. If your hash contained a should-be-encoded character, the result of the comparison will be negative, and Backbone will execute the route handler it should not execute.
As per the solution, well, folks said it before, your URIs should be encoded.
Question may be old, but for me this was still relevant. Encoding the url wasn't enough in my case. I replaced the GetHash() function in Backbone with:
getHash: function (t) {
var e = (t || this).location.href.match(/#(.*)$/);
return match ? this.decodeFragment(match[1]) : '';
}

How to print validation error outside of field constructor in Play framework 2

How can I show a validation error for a form field outside of a field constructor in Play framework 2? Here is what I tried:
#eventForm.("name").error.message
And I get this error:
value message is not a member of Option[play.api.data.FormError]
I'm confused because in the api docs it says message is a member of FormError. Also this works fine for global errors:
#eventForm.globalError.message
You can get a better grasp of it checking Form's sourcecode here
Form defines an apply method:
def apply(key: String): Field = Field(
this,
key,
constraints.get(key).getOrElse(Nil),
formats.get(key),
errors.collect { case e if e.key == key => e },
data.get(key))
That, as said in the doc, returns any field, even if it doesn't exist. And a Field has an errors member which returns a Seq[FormError]:
So, you could do something like that (for the Seq[FormError]):
eventForm("name").errors.foreach { error =>
<div>#error.message</div>
}
Or (for the Option[FormError])
eventForm("name").error.map { error =>
<div>#error.message</div>
}
Or, you could use Form errors:
def errors(key: String): Seq[FormError] = errors.filter(_.key == key)
And get all errors of a given key. Like this (for the Seq[FormError]):
eventForm.errors("name").foreach { error =>
<div>#error.message</div>
}
Or (for the Option[FormError])
eventForm.error("name").map { error =>
<div>#error.message</div>
}
If you want more details, check the source code. It's well written and well commented.
Cheers!
EDIT:
As biesior commented: to show human readable pretty messages with different languages you have to check how play works I18N out here
To be thorough you're probably going to have to deal with I18N. It's not hard at all to get it all working.
After reading the documentation you may still find yourself a bit consufed. I'll give you a little push. Add a messages file to your conf folder and you can copy its content from here. That way you'll have more control over the default messages. Now, in your view, you should be able to do something like that:
eventForm.errors("name").foreach { error =>
<div>#Messages(error.message, error.args: _*)</div>
}
For instance, if error.message were error.invalid it would show the message previously defined in the conf/messages file Invalid value. args define some arguments that your error message may handle. For instance, if you were handling an error.min, an arg could be the minimum value required. In your message you just have to follow the {n} pattern, where n is the order of your argument.
Of course, you're able to define your own messages like that:
error.futureBirthday=Are you sure you're born in the future? Oowww hay, we got ourselves a time traveler!
And in your controller you could check your form like that (just one line of code to show you the feeling of it)
"year" -> number.verifying("error.furtureBirthday", number <= 2012) // 2012 being the current year
If you want to play around with languages, just follow the documentation.
Cheers, again!
As you said yourself, message is a member of FormError, but you have an Option[FormError]. You could use
eventForm("name").error.map(_.message).getOrElse("")
That gives you the message, if there is an error, and "" if there isn't.

casperjs download file without specifying url

Is there any way to download CSV file with casperjs without specifying download URL? I am trying to download CSV file whose URL is dynamically generated when I click the download button. So, I may not be able to use download() well under the situation.
For the record, it's already possible using 'resource.received' event.
If you receive header like this one:
Content-Disposition: Attachment; Filename="ExportData.csv"
The file generated can be downloaded using following event listener:
casper.on('resource.received', function(resource) {
if (resource.stage !== "end") {
console.log("resource.stage !== 'end'");
return;
}
if (resource.url.indexOf('ExportData.csv') > -1) {
console.log("Downloading csv file");
this.download(resource.url, 'ExportData.csv');
}
});
I have found a solution for that. It is not very clean but it works :
You need to build an global array, which associates every resource.received having a filename attached.
fileInfos[url]= parse_filename_from_responseHeader(resource)
If url is the resource you want to download, try open(url) first. This will trigger the resiyrce.received event, parse the header and update the global array.
Now, before launching the casper.download(url), look for fileInfos[url].
You will find the filename corresponding to the url in the fileInfos array.
I can elaborate on the solution if needed, but since the question is already a few years old,
I'll wait for the next poke to elaborate.
This won't be possible until this phantomjs issue will be solved http://code.google.com/p/phantomjs/issues/detail?id=52
What I would do is something like this but I know the filename that I am gonna receive:
this.waitForResource(function check(resource){
res = resource;
// regular expression to test
return /thefilename/.test(resource.url);
}, function(){
this.echo("Resource found: " + res.url);
// download the file
this.download(res.url, path);
}, null, 10000);

Resources