mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-12 18:39:58 +00:00
Add initial status bridge implementation
This commit is contained in:
@@ -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
54
app/src/castTypes.ts
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 }
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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
25
ext/src/messageTypes.ts
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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 () {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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}>
|
||||||
|
|||||||
Reference in New Issue
Block a user