Add initial status bridge implementation

This commit is contained in:
hensm
2019-04-13 04:35:08 +01:00
committed by Matt Hensman
parent 0b494fa908
commit 01546e81dd
8 changed files with 191 additions and 42 deletions

View File

@@ -59,9 +59,11 @@ export default class StatusListener extends EventEmitter {
} }
case "RECEIVER_STATUS": { case "RECEIVER_STATUS": {
// Send update message this.emit("receiverStatus", data.status);
this.emit("statusUpdate", data.status); break;
}
case "MEDIA_STATUS": {
this.emit("mediaStatus", data.status);
break; break;
} }
} }

54
app/src/castTypes.ts Normal file
View File

@@ -0,0 +1,54 @@
"use strict";
export interface ReceiverStatus {
volume: {
muted: boolean;
stepInterval: number;
controlType: string;
level: number;
};
applications?: {
displayName: string;
statusText: string;
transportId: string;
isIdleScreen: boolean;
sessionId: string;
namespaces: { name: string }[];
appId: string;
}[];
userEq?: {};
}
export interface MediaStatus {
mediaSessionId: number;
supportedMediaCommands: number;
currentTime: number;
media: {
duration: number;
contentId: string;
streamType: string;
contentType: string;
};
playbackRate: number;
volume: {
muted: boolean;
level: number;
}
currentItemId: number;
idleReason: string;
playerState: string;
extendedStatus: {
playerState: string;
media: {
contentId: string;
streamType: string;
contentType: string;
metadata: {
images: { url: string }[];
metadataType: number;
artist: string;
title: string;
};
}
}
}

View File

@@ -11,6 +11,8 @@ import Session from "./Session";
import StatusListener from "./StatusListener"; import StatusListener from "./StatusListener";
import * as transforms from "./transforms"; import * as transforms from "./transforms";
import { ReceiverStatus, MediaStatus } from "./castTypes";
import { Message } from "./types"; import { Message } from "./types";
import { __applicationName import { __applicationName
@@ -185,29 +187,48 @@ function initialize (options: InitializeOptions) {
const statusListeners = new Map<string, StatusListener>(); const statusListeners = new Map<string, StatusListener>();
browser.on("serviceUp", (service: dnssd.Service) => { browser.on("serviceUp", (service: dnssd.Service) => {
const address = service.addresses[0]; const host = service.addresses[0];
const port = service.port; const port = service.port;
const id = service.txt.id; const id = service.txt.id;
if (options.shouldWatchStatus) { if (options.shouldWatchStatus) {
const listener = new StatusListener(address, port); const listener = new StatusListener(host, port);
listener.on("statusUpdate", (status: any) => { listener.on("receiverStatus", (status: ReceiverStatus) => {
sendMessage({ const receiverStatusMessage: any = {
subject: "main:/receiverStatusUpdate" subject: "receiverStatus"
, data: { id, status } , data: {
}); id
, status: {
volume: {
level: status.volume.level
, muted: status.volume.muted
}
}
}
};
if ("applications" in status) {
const application = status.applications[0];
receiverStatusMessage.data.status.application = {
displayName: application.displayName
, isIdleScreen: application.isIdleScreen
, statusText: application.statusText
};
}
sendMessage(receiverStatusMessage);
}); });
statusListeners.set(id, listener); statusListeners.set(id, listener);
} }
transforms.encode.write({ sendMessage({
subject: "shim:/serviceUp" subject: "shim:/serviceUp"
, data: { , data: {
address, port, id host, port, id
, friendlyName: service.txt.fn , friendlyName: service.txt.fn
, currentApp: service.txt.rs
} }
}); });
}); });
@@ -215,11 +236,12 @@ function initialize (options: InitializeOptions) {
browser.on("serviceDown", (service: dnssd.Service) => { browser.on("serviceDown", (service: dnssd.Service) => {
const id = service.txt.id; const id = service.txt.id;
// De-register status listener
if (options.shouldWatchStatus && statusListeners.has(id)) { if (options.shouldWatchStatus && statusListeners.has(id)) {
statusListeners.get(id).deregister(); statusListeners.get(id).deregister();
} }
transforms.encode.write({ sendMessage({
subject: "shim:/serviceDown" subject: "shim:/serviceDown"
, data: { id } , data: { id }
}); });

View File

@@ -7,7 +7,11 @@ import messageRouter from "./lib/messageRouter";
import { getChromeUserAgent } from "./lib/userAgents"; import { getChromeUserAgent } from "./lib/userAgents";
import { getWindowCenteredProps } from "./lib/utils"; import { getWindowCenteredProps } from "./lib/utils";
import { Message } from "./types"; import { Message, Receiver } from "./types";
import { ReceiverStatusMessage
, ServiceDownMessage
, ServiceUpMessage } from "./messageTypes";
import semver from "semver"; import semver from "semver";
@@ -540,13 +544,45 @@ browser.runtime.onConnect.addListener(port => {
const statusBridge = browser.runtime.connectNative(APPLICATION_NAME); const statusBridge = browser.runtime.connectNative(APPLICATION_NAME);
const receiverStatusMap = new Map<string, any>(); const statusBridgeReceivers = new Map<string, Receiver>();
statusBridge.onMessage.addListener((message: Message) => { statusBridge.onMessage.addListener((message: Message) => {
switch (message.subject) { switch (message.subject) {
case "main:/receiverStatusUpdate": {
const { id, status } = message.data; case "shim:/serviceUp": {
receiverStatusMap.set(id, status); const serviceUpMessage = message as ServiceUpMessage;
const receiver = serviceUpMessage.data;
statusBridgeReceivers.set(receiver.id, receiver);
break;
}
case "shim:/serviceDown": {
const serviceDownMessage = (message as ServiceDownMessage);
const { id } = serviceDownMessage.data;
if (statusBridgeReceivers.has(id)) {
statusBridgeReceivers.delete(id);
}
break;
}
case "receiverStatus": {
const receiverStatusMessage = message as ReceiverStatusMessage;
const { id, status } = receiverStatusMessage.data;
const receiver = statusBridgeReceivers.get(id);
// Merge new status with old status
statusBridgeReceivers.set(id, {
...receiver
, status: {
...receiver.status
, ...status
}
});
break; break;
} }

25
ext/src/messageTypes.ts Normal file
View File

@@ -0,0 +1,25 @@
"use strict";
import { Message, Receiver, ReceiverStatus } from "./types";
export interface ReceiverStatusMessage extends Message {
subject: "receiverStatus";
data: {
id: string;
status: ReceiverStatus;
}
}
export interface ServiceDownMessage extends Message {
subject: "shim:/serviceDown";
data: {
id: string;
};
}
export interface ServiceUpMessage extends Message {
subject: "shim:/serviceUp";
data: Receiver;
}

View File

@@ -235,7 +235,7 @@ onMessage(message => {
message.data.receiver.id message.data.receiver.id
, message.data.receiver.friendlyName); , message.data.receiver.friendlyName);
(selectedReceiver as any)._address = message.data.receiver.address; (selectedReceiver as any)._address = message.data.receiver.host;
(selectedReceiver as any)._port = message.data.receiver.port; (selectedReceiver as any)._port = message.data.receiver.port;
function createSession () { function createSession () {

View File

@@ -7,11 +7,24 @@ export interface Message {
} }
export interface Receiver { export interface Receiver {
address: string; host: string;
currentApp: string;
friendlyName: string; friendlyName: string;
id: string; id: string;
port: number; port: number;
status?: ReceiverStatus;
}
export interface ReceiverStatus {
application: {
displayName: string;
isIdleScreen: string;
statusText: string;
};
id: string;
volume: {
level: number;
muted: boolean
};
} }
export interface DownloadDelta { export interface DownloadDelta {

View File

@@ -5,7 +5,7 @@ import React, { Component } from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import { getNextEllipsis } from "../../lib/utils"; import { getNextEllipsis } from "../../lib/utils";
import * as types from "../../types"; import { Message, Receiver } from "../../types";
const _ = browser.i18n.getMessage; const _ = browser.i18n.getMessage;
@@ -29,7 +29,7 @@ let frameWidth: number;
interface PopupAppState { interface PopupAppState {
receivers: types.Receiver[]; receivers: Receiver[];
selectedMedia: string; selectedMedia: string;
isLoading: boolean; isLoading: boolean;
} }
@@ -63,7 +63,7 @@ class PopupApp extends Component<{}, PopupAppState> {
name: "popup" name: "popup"
}); });
backgroundPort.onMessage.addListener((message: types.Message) => { backgroundPort.onMessage.addListener((message: Message) => {
if (message.subject === "popup:/assignShim") { if (message.subject === "popup:/assignShim") {
this.setPort(message.data.tabId this.setPort(message.data.tabId
, message.data.frameId); , message.data.frameId);
@@ -92,10 +92,10 @@ class PopupApp extends Component<{}, PopupAppState> {
<ul className="receivers"> <ul className="receivers">
{ this.state.receivers.map((receiver, i) => { { this.state.receivers.map((receiver, i) => {
return ( return (
<Receiver receiver={ receiver } <ReceiverEntry receiver={ receiver }
onCast={ this.onCast } onCast={ this.onCast }
isLoading={ this.state.isLoading } isLoading={ this.state.isLoading }
key={ i }/> key={ i }/>
); );
})} })}
</ul> </ul>
@@ -117,7 +117,7 @@ class PopupApp extends Component<{}, PopupAppState> {
subject: "shim:/popupReady" subject: "shim:/popupReady"
}); });
this.port.onMessage.addListener((message: types.Message) => { this.port.onMessage.addListener((message: Message) => {
switch (message.subject) { switch (message.subject) {
case "popup:/populateReceiverList": { case "popup:/populateReceiverList": {
this.setState({ this.setState({
@@ -145,7 +145,7 @@ class PopupApp extends Component<{}, PopupAppState> {
}); });
} }
private onCast (receiver: types.Receiver) { private onCast (receiver: Receiver) {
this.setState({ this.setState({
isLoading: true isLoading: true
}); });
@@ -168,19 +168,19 @@ class PopupApp extends Component<{}, PopupAppState> {
} }
interface ReceiverProps { interface ReceiverEntryProps {
receiver: types.Receiver; receiver: Receiver;
isLoading: boolean; isLoading: boolean;
onCast (receiver: types.Receiver): void; onCast (receiver: Receiver): void;
} }
interface ReceiverState { interface ReceiverEntryState {
ellipsis: string; ellipsis: string;
isLoading: boolean; isLoading: boolean;
} }
class Receiver extends Component<ReceiverProps, ReceiverState> { class ReceiverEntry extends Component<ReceiverEntryProps, ReceiverEntryState> {
constructor (props: ReceiverProps) { constructor (props: ReceiverEntryProps) {
super(props); super(props);
this.state = { this.state = {
@@ -198,12 +198,9 @@ class Receiver extends Component<ReceiverProps, ReceiverState> {
{ this.props.receiver.friendlyName } { this.props.receiver.friendlyName }
</div> </div>
<div className="receiver-address"> <div className="receiver-address">
{ `${this.props.receiver.address}:${this.props.receiver.port}` } { `${this.props.receiver.host}:${this.props.receiver.port}` }
</div>
<div className="receiver-status">
{ this.props.receiver.currentApp &&
`- ${this.props.receiver.currentApp}` }
</div> </div>
<div className="receiver-status"></div>
<button className="receiver-connect" <button className="receiver-connect"
onClick={ this.handleCast } onClick={ this.handleCast }
disabled={this.props.isLoading}> disabled={this.props.isLoading}>