Optionally forward status updates from bridge to extension

This commit is contained in:
hensm
2019-04-10 06:51:48 +01:00
parent 7a36160bf2
commit 82f68a1366
4 changed files with 161 additions and 22 deletions

View File

@@ -46,7 +46,7 @@ Cast SDK API calls are translated into Chromecast protocol messages and sent via
| No. | Subject | Origin | Destination | Description |
| --: | --------------------------------------------- | ---------- | ----------- | ----------- |
| 1 | `shim:/initialized` | background | shim | Sent once bridge has been created. |
| 2 | `bridge:/startDiscovery` | shim | bridge | Starts network discovery. |
| 2 | `bridge:/initialize` | shim | bridge | Starts network discovery. |
| 3 | `shim:/serviceUp` | bridge | shim | Sent once a receiver device has been found. |
| 4 | `shim:/serviceDown` | bridge | shim | Sent once a receiver device has been lost. |
| 5 | `main:/openPopup` | shim | background | Opens the receiver selection popup. |

View File

@@ -10,12 +10,19 @@ import Media from "./Media";
import Session from "./Session";
import * as transforms from "./transforms";
import { Channel, Client } from "castv2";
import { Message } from "./types";
import { __applicationName
, __applicationVersion } from "../package.json";
const NS_CONNECTION = "urn:x-cast:com.google.cast.tp.connection";
const NS_HEARTBEAT = "urn:x-cast:com.google.cast.tp.heartbeat";
const NS_RECEIVER = "urn:x-cast:com.google.cast.receiver";
// Increase listener limit
events.EventEmitter.defaultMaxListeners = 50;
@@ -50,6 +57,10 @@ function sendMessage (message: object) {
}
interface InitializeOptions {
shouldWatchStatus?: boolean;
}
// Existing counterpart Media/Session objects
const existingSessions: Map<string, Session> = new Map();
const existingMedia: Map<string, Media> = new Map();
@@ -111,11 +122,14 @@ async function handleMessage (message: Message) {
return __applicationVersion;
}
case "bridge:/startDiscovery": {
browser.start();
case "bridge:/initialize": {
const options: InitializeOptions = message.data;
initialize(options);
break;
}
case "bridge:/startHttpServer": {
const { filePath, port } = message.data;
@@ -173,25 +187,128 @@ async function handleMessage (message: Message) {
}
}
function initialize (options: InitializeOptions) {
browser.on("serviceUp", (service: dnssd.Service) => {
const address = service.addresses[0];
const port = service.port;
const id = service.txt.id;
browser.on("serviceUp", (service: dnssd.Service) => {
transforms.encode.write({
subject: "shim:/serviceUp"
, data: {
address: service.addresses[0]
, port: service.port
, id: service.txt.id
, friendlyName: service.txt.fn
, currentApp: service.txt.rs
if (options.shouldWatchStatus) {
registerStatusListener(address, port, id);
}
});
});
browser.on("serviceDown", (service: dnssd.Service) => {
transforms.encode.write({
subject: "shim:/serviceDown"
, data: {
id: service.txt.id
}
transforms.encode.write({
subject: "shim:/serviceUp"
, data: {
address, port, id
, friendlyName: service.txt.fn
, currentApp: service.txt.rs
}
});
});
});
browser.on("serviceDown", (service: dnssd.Service) => {
const id = service.txt.id;
if (options.shouldWatchStatus) {
deregisterStatusListener(id);
}
transforms.encode.write({
subject: "shim:/serviceDown"
, data: { id }
});
});
browser.start();
}
interface StatusListener {
client: Client;
clientReceiver: Channel;
}
// Map of client connections
const statusListeners = new Map<string, StatusListener>();
/**
* Creates a connection to a receiver device and forwards
* RECEIVER_STATUS updates to the extension.
*/
function registerStatusListener (
host: string
, port: number
, id: string) {
const client = new Client();
const sourceId = "sender-0";
const destinationId = "receiver-0";
let heartbeatIntervalId: number;
client.connect({ host, port }, () => {
const clientConnection = client.createChannel(
sourceId, destinationId, NS_CONNECTION, "JSON");
const clientHeartbeat = client.createChannel(
sourceId, destinationId, NS_HEARTBEAT, "JSON");
const clientReceiver = client.createChannel(
sourceId, destinationId, NS_RECEIVER, "JSON");
clientReceiver.on("message", data => {
switch (data.type) {
case "CLOSE": {
client.close();
break;
}
case "RECEIVER_STATUS": {
// Send update message
transforms.encode.write({
subject: "main:/receiverStatusUpdate"
, data: {
id
, status: data.status
}
});
break;
}
}
});
clientConnection.send({ type: "CONNECT" });
clientHeartbeat.send({ type: "PING" });
clientReceiver.send({ type: "GET_STATUS", requestId: 1 });
heartbeatIntervalId = setInterval(() => {
clientHeartbeat.send({ type: "PING" });
});
statusListeners.set(id, {
client
, clientReceiver
});
});
client.on("close", () => {
clearInterval(heartbeatIntervalId);
});
}
/**
* Closes status listener connection for a given receiver
* device.
*/
function deregisterStatusListener (id: string) {
const { client, clientReceiver } = statusListeners.get(id);
// Cleanup
clientReceiver.send({ type: "CLOSE" });
client.close();
// Remove from map
statusListeners.delete(id);
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -539,6 +539,28 @@ browser.runtime.onConnect.addListener(port => {
});
const statusBridge = browser.runtime.connectNative(APPLICATION_NAME);
const receiverStatusMap = new Map<string, any>();
statusBridge.onMessage.addListener((message: Message) => {
switch (message.subject) {
case "main:/receiverStatusUpdate": {
const { id, status } = message.data;
receiverStatusMap.set(id, status);
break;
}
}
});
statusBridge.postMessage({
subject: "bridge:/initialize"
, data: {
shouldWatchStatus: true
}
});
messageRouter.register("mirrorCast", message => {
browser.tabs.sendMessage(mirrorCastTabId, message
, { frameId: mirrorCastFrameId });