I have this piece of code, that activates my webcam:
var video = document.getElementById('video');
// Get access to the camera!
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
// Not adding `{ audio: true }` since we only want video now
navigator.mediaDevices.getUserMedia({ video: true }).then(function (stream) {
video.srcObject = stream;
video.play();
});
}
When running the code above, the browser asks for permission to use the webcam. Let's assume that I allow it. The webcam is active now.
What I want now, is to write some code that checks if the webcam is active/being used. So I want to do something like this:
if(navigator.mediaDevices.getUserMedia == true) {
alert("The camera is active");
}
I found a similar post which has a solution, but I guess I am doing something wrong, even though I tried to follow the same solution. The post is here: How to check with JavaScript that webcam is being used in Chrome
Here is what I tried:
function isCamAlreadyActive() {
if (navigator.getUserMedia) {
navigator.getUserMedia({
video: true
}), function (stream) {
// returns true if any tracks have active state of true
var result = stream.getVideoTracks().some(function (track) {
return track.enabled && track.readyState === 'live';
});
}
if (result) {
alert("Already active");
return true;
}
}
alert("Not active");
return false;
}
My solution always returns false, even when the webcam is active
I am not too sure as to what you are trying to detect exactly.
If you want to know if the webcam is already used by some other program or some other pages, or some other scripts on the same page, then there is in my knowledge no bullet proof solution: different devices and different OSes will have different abilities with regard to request the same device simultaneously. So yes, requesting the device and keeping the stream alive, you could do function isActive() { return true; } but...
But if I read correctly between the lines of your question, I feel that what you want to know is if you have been granted the authorization from the user, and thus if they will see the prompt or not.
In this case, you can make use of the MediaDevices.enumerateDevices method, which will (IMM unfortunately) not request for user permission, while it needs it to get the full informations about the user's devices.
Indeed, the label property of the MediaDeviceInfo object should remain anonymised to avoid finger-printing. So if when you do request for this information, the MediaDeviceInfo all return an empty string (""), it means that you don't have user authorization, and thus, that they will get a prompt.
function checkIsApproved(){
return navigator.mediaDevices.enumerateDevices()
.then(infos =>
// if any of the MediaDeviceInfo has a label, we're good to go
[...infos].some(info=>info.label!=="")
);
}
check.onclick = e => {
checkIsApproved().then(console.log);
};
req.onclick = e => {
navigator.mediaDevices.getUserMedia({video:true}).catch(e=>console.error('you might be better using the jsfiddle'));
}
<button id="check">check</button>
<button id="req">request approval</button>
But since StackSnippets don't work well with getUserMedia, here is a jsfiddle demonstrating this.
In the code which you have shared, there is some problem with the syntax of calling navigator.getMediaUserMedia(). navigator.getMediaUserMedia() expects 3 parameters. You can check here for more details.
You can modify your function to:
function isCamAlreadyActive() {
if (navigator.getUserMedia) {
navigator.getUserMedia({
video: true
}, function (stream) {
// returns true if any tracks have active state of true
var result = stream.getVideoTracks().some(function (track) {
return track.enabled && track.readyState === 'live';
});
if (result) {
alert("Already active");
}else{
alert("No")
}
},
function(e) {
alert("Error: " + e.name);
});
}
}
PS : Since navigator.getMediaUserMedia() is deprecated, you can use navigator.mediaDevices.getUserMedia instead. You can checkout here for more details.
You can use navigator.mediaDevices.getUserMedia as :
function isCamAlreadyActive(){
navigator.mediaDevices.getUserMedia({video: true}).then(function(stream){
result = stream.getVideoTracks().some(function (track) {
return track.enabled && track.readyState === 'live';
});
if(result){
alert("On");
}else{
alert("Off");
}
}).catch(function(err) { console.log(err.name + ": " + err.message); });
}
Hope it helps.
Related
bot.on('message', message => {
if (message.content === 'spam') {
message.channel.send('spam');
while (message.channel.send('spam')) {
if (message.content === 'stop spam') {
return message.channel.send('stopped');
}
}
}
});
im still fairly new to javascript so im not sure if this is even possible the way ive been trying to do it ive looked through w3schools developers.mozilla and even a few questions that are already on here; ive tried using do while, and for loops and ive tried multiple versions of the code i have up there
the ultimate goal is if a user sends the word 'spam' the bot should continuously send the word 'spam' and keep doing so till the bot is turned off or a user sends the words 'stop spam'
Here are some things you should know about the code you're working with:
message.channel.send returns a Promise, so you can't put that in a while loop, because you need to have something that is true or false (a Boolean).
Right now you're trying to check if a message's content is equal to 'stop spam' while you're inside the if-statement checking if the content is equal to 'spam' - So you'll never get inside the inner if-statement.
I would recommend practicing basic javascript a bit more, then moving to Node.js, then coming back to Discord.js - However, it might be cool for you to see a spammer work, so I wrote a some spam code you could use - check this out:
First, create a new file named spamCtrl.js that looks like this (see comments in code for descriptions of what's going on):
let spamming = false;
let spamChannel = undefined;
// spam function repeats until variable spamming is false
function spam() {
return new Promise((resolve, reject) => {
// add check to make sure discord channel exists
if (!spamChannel)
reject('Channel is undefined!');
// send message on spam channel
spamChannel.send('spam')
.then(msg => {
// wait 100 ms until sending next spam message
setTimeout(() => {
// continue spamming if spamming variable is true
if (spamming) {
spam()
.then(resolve) // not entirely necessary, but good practice
.catch(console.log); // log error to console in case one shows up
}
// otherwise, just resolve promise to end this looping
else {
resolve();
}
}, 100)
})
.catch(console.log);
});
}
// public functions that will be used in your index.js file
module.exports = {
// pass in discord.js channel for spam function
setChannel: function(channel) {
spamChannel = channel;
},
// set spam status (true = start spamming, false = stop spamming)
setStatus: function (statusFlag) {
// get current status
let currentStatus = spamming;
// update spamming flag
spamming = statusFlag;
// if spamming should start, and it hasn't started already, call spam()
if (statusFlag && currentStatus != statusFlag) {
spam();
}
},
// not used in my commands, but you may find this useful somewhere
getStatus: function() {
return spamming;
}
};
Next, import that file into your index.js file (Must be in the same directory as your spamCtrl.js file - unless you change the require statement below).
// in index.js file, get controller for spam messages
let spamCtrl = require('./spamCtrl');
Final step: In your index.js file (or wherever you're handling your spam commands) set up your commands (this can be renamed as you like):
// 2 commands together make spamming work :)
case '?SPAM':
spamCtrl.setChannel(message.channel);
spamCtrl.setStatus(true);
break;
case '?STOP-SPAM':
spamCtrl.setStatus(false);
break;
Let me know if you would like any additional explanations on anything, or if you want to see some tweaks here 'n there.
Try using a variable instead. You cannot use message.channel.send('spam') for the while loop.
var spam = false;
if (message.content === 'spam') {
if (message.author.id !== bot.user.id) { // Replace bot with the instance of your bot Client.
spam = true;
} else {
if(spam) {
message.channel.send('spam');
}
}
if (message.content === 'stop spam') {
if(spam) {
message.channel.send('stopped');
}
spam = false;
}
}
Typically you would set constraints as follow
{ audio: true, video: true }
but I'd like to be more permissive then that. I'd like to only get the audio, if it is able to get it i.e. if a microphone is available, otherwise it will throw me a NotFoundError error.
Any ideas if that's possible?
Yes.
You can use the MediaDevices.enumerateDevices() method to get the list of all audio and video devices.
This will return an Array of MediaDeviceInfo objects which will have a kind property letting you know which constraint you can use.
So you could do something like
navigator.mediaDevices.enumerateDevices()
.then(getDevicesTypes)
.then(getUserMedia)
.then(console.log)
.catch(console.error);
function getDevicesTypes(list){
return new Set(list
.filter(device => device.kind.indexOf('input') > -1)
.map(device => device.kind.replace('input', ''))
);
}
function getUserMedia(deviceKinds){
const constraint = {};
deviceKinds.forEach(kind => constraint[kind] = true);
console.log(constraint);
return navigator.mediaDevices.getUserMedia(constraint);
}
How do you mute and no-video the local participant?
https://www.twilio.com/docs/api/video/getting-started
this code has been proposed elsewhere - but is this the track data?
var localMedia = conversation.localMedia;
localMedia.mute();
So would I get the tracks of the local participant
var tracks = Array.from(participant.tracks.values())
audioTrack.mute()?
videoTrack.mute()?
document.getElementById('button-mute').onclick = function () {
log('Mute call...')
console.log('mute call')
// var localMedia = conversation.localMedia
// localMedia.mute()
}
document.getElementById('button-no-video').onclick = function () {
log('No Vid call...')
console.log('no vid')
}
Twilio developer evangelist here.
You actually want to use the disable method on the local media tracks to mute them.
First, make sure you're using the Twilio Video Rooms API (you mention conversation in the question, the conversations API is deprecated).
Then, when you connect you can get hold of the local participants media tracks and disable or enable them when you like. Something like this:
Video.connect(token, { name: 'room-name' }).then(room => {
const localParticipant = room.localParticipant;
$button.on('click', event => {
localParticipant.tracks.forEach((trackId, track) => {
if (track.isEnabled) {
track.disable();
} else {
track.enable();
}
})
})
});
Let me know if that helps at all.
Try following code, it worked for me.
// name is video or audio
vm.toggleMyDevices = function(kind){
var localMedia = activeRoom.localParticipant;
localMedia.tracks.forEach(function (track) {
console.log(track);
if(track.kind === kind){
if (track.isEnabled) {
track.disable();
$scope.isPaused = true;
} else {
track.enable();
$scope.isPaused = false;
}
}
},kind);
}
I am building a project similar to this example with jsartoolkit5, and I would like to be able to select the back camera of my device instead of letting Chrome on Android select the front one as default.
According to the example in this demo, I have added the code below to switch camera automatically if the device has a back camera.
var videoElement = document.querySelector('canvas');
function successCallback(stream) {
window.stream = stream; // make stream available to console
videoElement.src = window.URL.createObjectURL(stream);
videoElement.play();
}
function errorCallback(error) {
console.log('navigator.getUserMedia error: ', error);
}
navigator.mediaDevices.enumerateDevices().then(
function(devices) {
for (var i = 0; i < devices.length; i++) {
if (devices[i].kind == 'videoinput' && devices[i].label.indexOf('back') !== -1) {
if (window.stream) {
videoElement.src = null;
window.stream.stop();
}
var constraints = {
video: {
optional: [{
sourceId: devices[i].deviceId
}]
}
};
navigator.getUserMedia(constraints, successCallback, errorCallback);
}
}
}
);
The issue is that it works perfectly for a <video> tag, but unluckily jsartoolkit renders the content inside a canvas and it consequently throws an error.
I have also tried to follow the instructions in this closed issue in the Github repository, but this time I get the following error: DOMException: play() can only be initiated by a user gesture.
Do you know or have any suggestion on how to solve this issue?
Thanks in advance for your replies!
Main problem :
You are mixing old and new getUserMedia syntax.
navigator.getUserMedia is deprecated, and navigator.mediaDevices.getUserMedia should be preferred.
Also, I think that optional is not part of the constraints dictionary anymore.
Default Solution
This part is almost a duplicate of this answer : https://stackoverflow.com/a/32364912/3702797
You should be able to call directly
navigator.mediaDevices.getUserMedia({
video: {
facingMode: {
exact: 'environment'
}
}
})
But chrome still has this bug, and even if #jib's answer states that it should work with adpater.js polyfill, I myself were unable to make it work on my chrome for Android.
So previous syntax will currently work only on Firefox for Android.
For chrome, you'll indeed need to use enumerateDevices, along with adapter.js to make it work, but don't mix up the syntax, and everything should be fine :
let handleStream = s => {
document.body.append(
Object.assign(document.createElement('video'), {
autoplay: true,
srcObject: s
})
);
}
navigator.mediaDevices.enumerateDevices().then(devices => {
let sourceId = null;
// enumerate all devices
for (var device of devices) {
// if there is still no video input, or if this is the rear camera
if (device.kind == 'videoinput' &&
(!sourceId || device.label.indexOf('back') !== -1)) {
sourceId = device.deviceId;
}
}
// we didn't find any video input
if (!sourceId) {
throw 'no video input';
}
let constraints = {
video: {
sourceId: sourceId
}
};
navigator.mediaDevices.getUserMedia(constraints)
.then(handleStream);
});
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
Fiddle for chrome which need https.
Make it work with jsartoolkit
You'll have to fork jsartoolkit project and edit artoolkit.api.js.
The main project currently disables mediaDevices.getUserMedia(), so you'll need to enable it again, and you'll also have to add a check for an sourceId option, that we'll add later in the ARController.getUserMediaThreeScene() call.
You can find a rough and ugly implementation of these edits in this fork.
So once it is done, you'll have to rebuild the js files, and then remember to include adapter.js polyfill in your code.
Here is a working fiddle that uses one of the project's demo.
I gotta a companion script for a serviceworker and I'm trialling right now.
The script works like so:
((n, d) => {
if (!(n.serviceWorker && (typeof Cache !== 'undefined' && Cache.prototype.addAll))) return;
n.serviceWorker.register('/serviceworker.js', { scope: './book/' })
.then(function(reg) {
if (!n.serviceWorker.controller) return;
reg.onupdatefound = () => {
let installingWorker = reg.installing;
installingWorker.onstatechange = () => {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
updateReady(reg.waiting);
} else {
// This is the initial serviceworker…
console.log('May be skipwaiting here?');
}
break;
case 'waiting':
updateReady(reg.waiting);
break;
case 'redundant':
// Something went wrong?
console.log('[Companion] new SW could not install…')
break;
}
};
};
}).catch((err) => {
//console.log('[Companion] Something went wrong…', err);
});
function updateReady(worker) {
d.getElementById('swNotifier').classList.remove('hidden');
λ('refreshServiceWorkerButton').on('click', function(event) {
event.preventDefault();
worker.postMessage({ 'refreshServiceWorker': true } );
});
λ('cancelRefresh').on('click', function(event) {
event.preventDefault();
d.getElementById('swNotifier').classList.add('hidden');
});
}
function λ(selector) {
let self = {};
self.selector = selector;
self.element = d.getElementById(self.selector);
self.on = function(type, callback) {
self.element['on' + type] = callback;
};
return self;
}
let refreshing;
n.serviceWorker.addEventListener('controllerchange', function() {
if (refreshing) return;
window.location.reload();
refreshing = true;
});
})(navigator, document);
I'm a bit overwhelmed right now by the enormity of the service workers api and unable to "see" what one would do with reg.installing returning a redundant state?
Apologies if this seems like a dumb question but I'm new to serviceworkers.
It's kinda difficult to work out what your intent is here so I'll try and answer the question generally.
A service worker will become redundant if it fails to install or if it's superseded by a newer service worker.
What you do when this happens is up to you. What do you want to do in these cases?
Based on the definition here https://www.w3.org/TR/service-workers/#service-worker-state-attribute I am guessing just print a log in case it comes up in debugging otherwise do nothing.
You should remove any UI prompts you created that ask the user to do something in order to activate the latest service worker. And be patient a little longer.
You have 3 service workers, as you can see on the registration:
active: the one that is running
waiting: the one that was downloaded, and is ready to become active
installing: the one that we just found, being downloaded, after which it becomes waiting
When a service worker reaches #2, you may display a prompt to the user about the new version of the app being just a click away. Let's say they don't act on it.
Then you publish a new version. Your app detects the new version, and starts to download it. At this point, you have 3 service workers. The one at #2 changes to redundant. The one at #3 is not ready yet. You should remove that prompt.
Once #3 is downloaded, it takes the place of #2, and you can show that prompt again.
Write catch function to see the error. It could be SSL issue.
/* In main.js */
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./sw.js')
.then(function(registration) {
console.log("Service Worker Registered", registration);
})
.catch(function(err) {
console.log("Service Worker Failed to Register", err);
})
}