Chrome screen sharing get monitor info - google-chrome-extension

I implemented chrome extension which using chrome.desktopCapture.chooseDesktopMedia to retrieve screen id.
This is my background script:
chrome.runtime.onConnect.addListener(function (port) {
port.onMessage.addListener(messageHandler);
// listen to "content-script.js"
function messageHandler(message) {
if(message == 'get-screen-id') {
chrome.desktopCapture.chooseDesktopMedia(['screen', 'window'], port.sender.tab, onUserAction);
}
}
function onUserAction(sourceId) {
//Access denied
if(!sourceId || !sourceId.length) {
return port.postMessage('permission-denie');
}
port.postMessage({
sourceId: sourceId
});
}
});
I need to get shared monitor info(resolution, landscape or portrait).
My question is: If customer using more than one monitor, how can i determine which monitor he picked?
Can i add for example "system.display" permissions to my extension and get picked monitor info from "chrome.system.display.getInfo"?

You are right. You could add system.display permission and call chrome.system.display.getDisplayLayout(callbackFuncion(DisplayLayout)) and handle the DisplayLayout.position in the callback to get the layout and the chrome.system.display.getInfo to handle the array of displayInfo in the callback. You should look for 'isPrimary' value

This is a year old question, but I came across it since I was after the same information and I finally managed to figure how you can identify which monitor the user selected for screen-sharing in Chrome.
First of all: this information will not come from the extension that you probably built for screen-sharing in Chrome, because:
The chrome.desktopCapture.chooseDesktopMedia API callback only returns a sourceId, which is a string that represents a stream id, that you can then use to call the getMediaSource API to build the media stream.
The chrome.system.display.getInfo will give you a list of the displays, yes, but from that info you can't tell which one is being shared, and there is no way to match the sourceId with any of the fields returned for each display.
So... the solution I've found comes from the MediaStream object itself. Once you have the stream, after calling getMediaSource, you need to get the video track, and in there you will find a property called "label". This label gives you an idea of which screen the user picked.
You can get the video track with something like:
const videoTrack = mediaStream.getVideoTracks()[0];
(Check the getVideoTracks API here: https://developer.mozilla.org/en-US/docs/Web/API/MediaStream/getVideoTracks).
If you print that object, you will see the "label" field. In Chrome screen 1 shows as "0:0", whereas screen 2 shows as "1:0", and I assume screen i would be "i-1:0" (I've only tested with 2 screens).
Here is a capture of that object printed in the console:
And not only works for Chrome, but for other browsers that implement it! In Firefox they show up as "Screen i":
Also, if you check Chrome chrome://webrtc-internals you'll see this is what they show in the addStream event:
And that's it! It's not ideal, since this is a label, more than a real screen identifier, but well, it's something to work with. Once you have the screen identified, in Chrome you can work with the chrome.system.display.getInfo to get information for that display.

Related

How to navigate Edge extension local storage callback requirements

I'm trying to access the local storage data in Edge set by my options page, using my popup script. Is there a current, working example of this available?
I was using localStorage, but it would only update the popup if I reloaded the extension after saving changes in my options page. I want to make it easier on the user by allowing the popup to access it immediately after saving, without reloading. I found the browser.storage.local.get(), but documentation conflicts everywhere I look, and I can't find viable working examples.
I have used, per Edge documentation:
browser.storage.local.get("sample");
But it throws an error requiring a callback function. So then I used:
let sample = browser.storage.local.get("example");
sample.then(ifGood, ifBad);
I get an error regarding property "then".
Then I simply tried adding a callback to the action itself:
sample = browser.storage.local.get("example", callbackFunction);
function callbackFunction(data){
alert(data);
}
The end alert should display a string, but it just displays an empty Object. How do I access the data returned in the callback? I tried callbackFunction(this) as an argument in the get, but it throws an error about the syntax of the get.
I found a work-around using browser.runtime.reload() in the Options page when saving the changes. It still reloads the extension, but it does it without requiring the user to do it manually.
You should use this syntax:
browser.storage.local.get(propertyName | null, callbackFn)
where
callbackFn = function fn(resultObject) {...}
When you pass null you will get whole storage object.
Look for example 1 or example 2 in my Edge extension.

Actions SDK conv.hasScreen not working as expected

I am building an action using DialogFlow and Firebase cloud functions. I have a simple check to either ask a question or close the conversation depending on user's device type.
if (conv.hasScreen) {
response += `Do you want to see a picture?`;
conv.ask(response);
return;
}
else{
conv.close(response);
return;
}
I tested using Google Home mini, as expected, the conversation gracefully closed. But when I tested on a phone, the if check failed and the conversation was closed again. I was expecting the contestation to continue and assistant would ask me to show a picture but it did not happen. What am I doing wrong?
It looks like the syntax is simply conv.screen. As the property hasScreen does not exist, the conditional always returns undefined, which is a falsey value.
Take a look at the following to understand Surface Capabilities.
Are you using the following statement or not?
const hasScreen =
conv.surface.capabilities.has('actions.capability.SCREEN_OUTPUT');

Set Spotify playing position

I'm using the SpotifyAPI-NET on GitHub from JohnnyCrazy to play and pause songs on my Spotify desktop client. This works fine.
Now I want to change the playing position of the currently playing song. So I only want to say something like "SetPlayingPosition(64)" to play the current song from position "01:04". It seems that the SpotifyLocalAPI didn't support this feature.
To play and pause a song the API uses a message with the following format:
http://127.0.0.1:4381/remote/pause.json?pause=true&ref=&cors=&_=1520448230&oauth=oauth&csrf=csrf
I tried to find a summary of possible commands in this format, but I didn't find anything.
Is there something like http://127.0.0.1:4381/remote/seek.json... that I can use to seek to a specific position?
EDIT:
I tried to write my own method in the RemoteHandler class in the local portion of the SpotifyAPI. With this method I can set the position in the current playback.
Here's my code:
internal async Task SendPositionRequest(double playingPositionSec) //The desired playback position in seconds
{
StatusResponse status = GetNewStatus(); //Get the current status of the local desktop API
string trackUri = "spotify:track:" + status.Track.TrackResource.ParseUri().Id; //The URI of the current track
TimeSpan playingPositionTimeSpan = TimeSpan.FromSeconds(playingPositionSec);
string playingPosStr = playingPositionTimeSpan.ToString(#"mm\:ss"); //Convert the playingPosition to a string (Format mm:ss)
string playingContext = "spotify:artist:1EfwyuCzDQpCslZc8C9gkG";
await SendPlayRequest(trackUri + "#" + playingPosStr, playingContext);
if (!status.Playing) { await SendPauseRequest(); }
}
I need to call the SendPlayRequest() method with the correct playingContext because when the current song is part of a playlist and you call SendPlayRequest() without the context, the next song isn't from the playlist anymore.
But you can see that I use a fixed context at the moment.
So my question is now: How can I get the context (playlist, artist, ...) of the currently played song with the SpotifyLocalAPI?
The SeekPlayback method of the library you mentioned lets you seek through playback on whatever device your user is listening on. You can find the docs here.
Seeking playback is not currently possible using the Spotify Local API portion of that library.

how to connect extension with content script in Firefox for android? [duplicate]

I just noticed that the tabs API is only available for the desktop not for Android. In the past I have used this code to send messages to my content scripts:
sendMsgToTabs(msg) {
return browser.tabs.query({}).then(tabs => {
let msgPromises = []
for (let tab of tabs) {
let msgPromise = browser.tabs.sendMessage(tab.id, msg)
msgPromises.push(msgPromise)
}
return Promise.all(msgPromises)
})
}
But how am I supposed to do that when the tabs API is not available?
I mean the only thing I can think of is to constantly send empty messages from the content scripts to the background script and whenever the background script has new information then it can send a direct response to one of these messages. But that sounds horribly inefficient. There must be a better way, right?
As of Firefox 54, use .tabs.sendMessage()
As of Firefox 54, the tabs API is supported on Firefox for Android.
Alternative for versions of Firefox prior to Firefox 54.
The storage API is stated as supported in Firefox for Android. Thus, while I have not tested it, a method you could use to send data to content script would be to save a value using chrome.storage.local.set(). By listening to the chrome.storage.onChanged event in your content script(s), you can then be notified of that data being stored/changed. This will provide an event driven way to send a message (i.e. stored data) to the content script.
In order to differentiate between receiving the data in different tabs, you will need to establish a protocol for what the data you save means. This could be as simple as just a particular saved key/value meaning that all content scripts should send a message to the background script to get more information, or more complex where you send/store something like:
{
contentScriptMessage: {
tab: 14,
frame: 1234,
message: 'Some data'
}
}
In each content script's chrome.storage.onChanged listener, it can then ignore any changes that are not to the tab/frame in which it is running.
This methodology will require fleshing out as you try to implement it. Hopefully, at least part of the chrome.tabs API will be implemented for Android in the near future.

How to use the experimental offscreenTab API?

I've been searching for examples and reference and have come up with nothing. I found a note in offscreenTab source code mentioning it cannot be instantiated from a background page (it doesn't have a tab for the offscreenTab to relate to). Elsewhere I found mention that popup also has no tie to a tab.
How do you successfully create an offscreenTab in a Chrome extension?
According to the documentation, offscreenTabs.create won't function in a background page. Although not explicitly mentioned, the API cannot be used in a Content script either. Through a simple test, it seems that the popup has the same limitation as a background page.
The only leftover option is a tab which runs in the context of a Chrome extension. The easiest way to do that is by using the following code in the background/popup:
chrome.tabs.create({url: chrome.extension.getURL('ost.htm'), active:false});
// active:false, so that the window do not jump to the front
ost.htm is a helper page, which creates the tab:
chrome.experimental.offscreenTabs.create({url: '...'}, function(offscreenTab) {
// Do something with offscreenTab.id !
});
To change the URL, use chrome.experimental.offscreenTabs.update.
offscreenTab.id is a tabId, which ought to be used with the chrome.tabs API. However, at least in Chrome 20.0.1130.1, this is not the case. All methods of the tabs API do not recognise the returned tabID.
A work-around is to inject a content script using a manifest file, eg:
{"content_scripts": {"js":["contentscript.js"], "matches":["<all_urls>"]}}
// contentscript.js:
chrome.extension.sendMessage({ .. any request .. }, function(response) {
// Do something with response.
});
Appendum to the background page:
chrome.extension.onMessage.addListener(function(message, sender, sendResponse) {
// Instead of checking for index == -1, you can also see if the ID matches
// the ID of a previously created offscreenTab
if (sender.tab && sender.tab.index === -1) {
// index is negative if the tab is invisible
// ... do something (logic) ...
sendResponse( /* .. some response .. */ );
}
});
With content scripts, you've got full access to a page's DOM. But not to the global object. You'll have to inject scripts (see this answer) if you want to run code in the context of the page.
Another API which might be useful is the chrome.webRequest API. It can be used to modify headers/abort/redirect requests. Note: It cannot be used to read or modify the response.
Currently, the offscreenTabs API is experimental. To play with it, you have to enable the experimental APIs via chrome://flags, and add "permissions":["experimental"] to your manifest file. Once it's not experimental any more, use "permissions":["offscreenTabs"].

Resources