Related
I am unable to use specific profile in Puppeteer. It always open the chrome as a new user.
For example: I have 3 profiles for my chrome. Following is the code I am using to open chrome in specific profile:
const browser = await puppeteer.launch({
headless: false,
executablePath: 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
// args: ['--profile-directory="Profile 1"'],
userDataDir:"C:\\Users\\USER_NAME\\AppData\\Local\\Google\\Chrome\\User Data\\Profile 1"
});
But it always open the chrome as "Current user" profile.
Try below code:
const browser = await puppeteer.launch({headless:false, args:[
'--user-data-dir=/user/data/directory/profile_n']
});
Full explanation is given here:
In Puppeteer how to switch to chrome window from default profile to desired profile
Nowadays this should work with:
args: ['--profile-directory="Profile 1"'],
userDataDir:"C:\\Users\\USER_NAME\\AppData\\Local\\Google\\Chrome\\User Data"
Edit:
As the end of the year and the end of Manifest V2 is approaching I did a bit more research on this and found the following workarounds:
The example here that uses the desktopCapture API:
https://github.com/GoogleChrome/chrome-extensions-samples/issues/627
The problem with this approach is that it requires the user to select a capture source via some UI which can be disruptive. The --auto-select-desktop-capture-source command line switch can apparently be used to bypass this but I haven't been able to use it with success.
The example extension here that works around tabCapture not working in
service workers by creating its own inactive tab from
which to access the tabCapture API and record the currently
active tab:
https://github.com/zhw2590582/chrome-audio-capture
So far this seems to be the best solution I've found so far in terms of UX. The background page provided in Manifest V2 is essentially replaced with a phantom tab.
The roundaboutedness of the second solution also seems to suggest that the tabCapture API is essentially not intended for use in Manifest V3, or else there would have been a more straightforward way to use it. I am disappointed that Manifest V3 is being enforced while essentially leaving behind Manifest V2 features such as this one.
Original Post:
I'm trying to write a manifest v3 Chrome extension that captures tab audio. However as far as I can tell, with manifest v3 there are some changes that make this a bit difficult:
Background scripts are replaced by service workers.
Service workers do not have access to the chrome.tabCapture API.
Despite this I managed to get something that nearly works as popup scripts still have access to chrome.tabCapture. However, there is a drawback - the audio of the tab is muted and there doesn't seem to be a way to unmute it. This is what I have so far:
Query the service worker current tab from the popup script.
let tabId;
// Fetch tab immediately
chrome.runtime.sendMessage({command: 'query-active-tab'}, (response) => {
tabId = response.id;
});
This is the service worker, which response with the current tab ID.
chrome.runtime.onMessage.addListener(
(request, sender, sendResponse) => {
// Popup asks for current tab
if (request.command === 'query-active-tab') {
chrome.tabs.query({active: true}, (tabs) => {
if (tabs.length > 0) {
sendResponse({id: tabs[0].id});
}
});
return true;
}
...
Again in the popup script, from a keyboard shortcut command, use chrome.tabCapture.getMediaStreamId to get a media stream ID to be consumed by the current tab, and send that stream ID back to the service worker.
// On command, get the stream ID and forward it back to the service worker
chrome.commands.onCommand.addListener((command) => {
chrome.tabCapture.getMediaStreamId({consumerTabId: tabId}, (streamId) => {
chrome.runtime.sendMessage({
command: 'tab-media-stream',
tabId: tabId,
streamId: streamId
})
});
});
The service worker forwards that stream ID to the content script.
chrome.runtime.onMessage.addListener(
(request, sender, sendResponse) => {
...
// Popup sent back media stream ID, forward it to the content script
if (request.command === 'tab-media-stream') {
chrome.tabs.sendMessage(request.tabId, {
command: 'tab-media-stream',
streamId: request.streamId
});
}
}
);
The content script uses navigator.mediaDevices.getUserMedia to get the stream.
// Service worker sent us the stream ID, use it to get the stream
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
navigator.mediaDevices.getUserMedia({
video: false,
audio: true,
audio: {
mandatory: {
chromeMediaSource: 'tab',
chromeMediaSourceId: request.streamId
}
}
})
.then((stream) => {
// Once we're here, the audio in the tab is muted
// However, recording the audio works!
const recorder = new MediaRecorder(stream);
const chunks = [];
recorder.ondataavailable = (e) => {
chunks.push(e.data);
};
recorder.onstop = (e) => saveToFile(new Blob(chunks), "test.wav");
recorder.start();
setTimeout(() => recorder.stop(), 5000);
});
});
Here is the code that implements the above: https://github.com/killergerbah/-test-tab-capture-extension
This actually does produce a MediaStream, but the drawback is that the sound of the tab is muted. I've tried playing the stream through an audio element, but that seems to do nothing.
Is there a way to obtain a stream of the tab audio in a manifest v3 extension without muting the audio in the tab?
I suspect that this approach might be completely wrong as it's so roundabout, but this is the best I could come up with after reading through the docs and various StackOverflow posts.
I've also read that the tabCapture API is going to be moved for manifest v3 at some point, so maybe the question doesn't even make sense to ask - however if there is a way to still properly use it I would like to know.
I found your post very useful in progressing my implementation of an audio tab recorder.
Regarding the specific muting issue you were running into, I resolved it by looking here: Original audio of tab gets muted while using chrome.tabCapture.capture() and MediaRecorder()
// Service worker sent us the stream ID, use it to get the stream
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
navigator.mediaDevices.getUserMedia({
video: false,
audio: true,
audio: {
mandatory: {
chromeMediaSource: 'tab',
chromeMediaSourceId: request.streamId
}
}
})
.then((stream) => {
// To resolve original audio muting
context = new AudioContext();
var audio = context.createMediaStreamSource(stream);
audio.connect(context.destination);
const recorder = new MediaRecorder(stream);
const chunks = [];
recorder.ondataavailable = (e) => {
chunks.push(e.data);
};
recorder.onstop = (e) => saveToFile(new Blob(chunks), "test.wav");
recorder.start();
setTimeout(() => recorder.stop(), 5000);
});
});
This may not be exactly what you are looking for, but perhaps it may provide some insight.
I've tried playing the stream through an audio element, but that seems to do nothing.
Ironically this is how I managed to get around the issue; by creating an object in the popup itself. When using tabCapture in the popup script, it returns the stream, and I set the audio srcObject to that stream.
HTML:
<audio id="audioObject" autoplay> No source detected </audio>
JS:
chrome.tabCapture.capture({audio: true, video: false}, function(stream) {
var audio = document.getElementById("audioObject");
audio.srcObject = stream
})
According to this post on Manifest V3, chrome.capture will be the new namespace for tabCapture and the like, but I haven't seen anything beyond that.
I had this problem too, and I resolve it by using Web Audio API. Just create a new context and conect it to a media stream source using the captures MediaStream, this is an example:
avoidSilenceInTab: (desktopStream: MediaStream) => {
var contextTab = new AudioContext();
contextTab
.createMediaStreamSource(desktopStream)
.connect(contextTab.destination);
}
On different systems (Windows/Android/etc.) there are some "built-in" audio enhancements. For example AEC (autmatic echo cancellation), NR (noise reduction) and Automatic Gain Control. Everyone can have those turned off or on in any combination.
There are also audio enhancements on some browsers (i know about Chrome and Firefox)
It is possible to turn them all off using webRTC?
For all I know, it is possible to turn off those "browser enhancements" and I think I managed it by specifying mediaConstraints. Example for Chrome:
var mediaConstraints = {
audio: {
echoCancellation: { exact: false },
googEchoCancellation: { exact: false },
googAutoGainControl: { exact: false },
googNoiseSuppression: { exact: false },
}
}
Can't find solution to turn off system/device-specific audio enhancements.
There is similar question: WebRTC - disable all audio processing, but I think it addresses only those browser enhancements.
I have to make an extension that when clicked on text in the context menu, in callback opens the extension menu popup.
chrome.runtime.onInstalled.addListener(function() {
var context = "selection";
var title = "Google for Selected Text";
var id = chrome.contextMenus.create({"title": title, "contexts":["selection"],
"id": "context" + context});
});
// add click event
chrome.contextMenus.onClicked.addListener(onClickHandler);
// The onClicked callback function.
function onClickHandler(info, tab) {
var sText = info.selectionText;
var url = "https://www.google.com/search?q=" + encodeURIComponent(sText);
//what i have put here to open extension popup
};
In this case, when I click on the menu I open a new tab with this search.
There is no way of opening the default browser action popup programmatically. A work around is use content scripts to open a modal or a lightbox and show the contents of your popup.
Another way would be - within the clickhandler of your context menu item, create a new tab and make it inactive and then pass that tab to chrome.windows.create api to create a new popup window.
chrome.tabs.create({
url: chrome.extension.getURL('popup.html'),
active: false
}, function(tab) {
// After the tab has been created, open a window to inject the tab
chrome.windows.create({
tabId: tab.id,
type: 'popup',
focused: true
});
});
It is just a work around. Hope it helps.
It is now possible to open a browser action popup programmatically from inside the handler for a user action.
browser.menus.create({
id: "open-popup",
title: "open popup",
contexts: ["all"]
});
browser.menus.onClicked.addListener(() => {
browser.browserAction.openPopup();
});
You can read more about it here.
Edit:
This feature is only available as of Firefox 57. In Chrome, it is only available in the dev channel.
Sources: chrome/common/extensions/api/_api_features.json - chromium/src - Git at Google
Unfortunately, it cannot be done.
Chrome API doesn't provide a method to open extension popup programmatically. The Chromium team rejected the feature request for such an option with an explanation that:
The philosophy for browser and page action popups is that they must be
triggered by user action.
Here's the source.
You can use the chrome.window API (documentation here).
What you want is something like this :
chrome.windows.create({
url : "http://yourPopupUrl.com"
focused : true
type : "popup"});
This will open a new windows in popup mode (without the top menu bar) and load the "http://yourPopupUrl.com".
I'm using ruby on rails to develop this webapp that only supports the latest browser.
How do i show a pop-up message when user is using unsupported browser.
The message will show when the page is not loaded in the following browser:
Firefox 4 and above
IE9 and above
Safari 5 and above
Chrome 10 and above
jQuery Browser Rejection Plugin can help you. it supports following browsers disable/enable options:
msie: false,msie5: true,msie6: true,msie7: false,msie8: false, // MSIE Flags (Global, 5-8)
firefox: false,firefox1: false,firefox2: false,firefox3: false, // Firefox Flags (Global, 1-3)
konqueror: false,konqueror1: false,konqueror2: false,konqueror3: false, // Konqueror Flags (Global, 1-3)
chrome: false,chrome1: false,chrome2: false,chrome3: false,chrome4: false, // Chrome Flags (Global, 1-4)
safari: false,safari2: false,safari3: false,safari4: false, // Safari Flags (Global, 1-4)
opera: false,opera7: false,opera8: false,opera9: false,opera10: false, // Opera Flags (Global, 7-10)
gecko: false,webkit: false,trident: false,khtml: false,presto: false, // Rendering Engines (Gecko, Webkit, Trident, KHTML, Presto)
win: false,mac: false,linux : false,solaris : false,iphone: false, // Operating Systems (Win, Mac, Linux, Solaris, iPhone)
unknown: false // Unknown covers everything else
It is very simple using jreject_rails gem
Just need to call function into your javascript file.
$(function() {
$.reject({
reject: {
msie5: true,
msie6: true,
msie7: true
}
});
});
Make those versions true which your site don't support.
Let me know If you any issue to implement. It works for me.