mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-08 08:39:59 +00:00
Replace StatusManager
This commit is contained in:
@@ -27,7 +27,7 @@
|
||||
, "comma-style": [ "error", "first" ]
|
||||
, "no-multiple-empty-lines": [ "error", { "max": 2 }]
|
||||
, "no-console": [ "error", {
|
||||
"allow": [ "info", "error" ]
|
||||
"allow": [ "info", "warn", "error" ]
|
||||
}]
|
||||
, "operator-linebreak": [ "error", "after", {
|
||||
"overrides": {
|
||||
|
||||
@@ -4,7 +4,7 @@ import castv2 from "castv2";
|
||||
|
||||
import Session, { NS_CONNECTION, NS_RECEIVER } from "./Session";
|
||||
import Media from "./Media";
|
||||
import { Receiver } from "../../types";
|
||||
import { ReceiverDevice } from "../../types";
|
||||
|
||||
|
||||
// Existing counterpart Media/Session objects
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
|
||||
import mdns from "mdns";
|
||||
|
||||
import StatusListener from "./chromecast/StatusListener";
|
||||
import { ReceiverStatus } from "../types";
|
||||
import { sendMessage } from "../lib/nativeMessaging";
|
||||
|
||||
import { ReceiverStatus } from "../types";
|
||||
import { Message } from "../messaging";
|
||||
|
||||
import StatusListener from "./chromecast/StatusListener";
|
||||
|
||||
|
||||
interface CastTxtRecord {
|
||||
id: string; cd: string; rm: string;
|
||||
@@ -34,12 +37,14 @@ function onBrowserServiceUp(service: mdns.Service) {
|
||||
const txtRecord = service.txtRecord as CastTxtRecord;
|
||||
|
||||
sendMessage({
|
||||
subject: "main:serviceUp"
|
||||
subject: "main:receiverDeviceUp"
|
||||
, data: {
|
||||
host: service.addresses[0]
|
||||
, port: service.port
|
||||
, id: txtRecord.id
|
||||
, friendlyName: txtRecord.fn
|
||||
receiverDevice: {
|
||||
host: service.addresses[0]
|
||||
, port: service.port
|
||||
, id: txtRecord.id
|
||||
, friendlyName: txtRecord.fn
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -72,41 +77,20 @@ export function startDiscovery(options: InitializeOptions) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { id } = service.txtRecord;
|
||||
|
||||
const listener = new StatusListener(
|
||||
service.addresses[0]
|
||||
, service.port);
|
||||
service.addresses[0], service.port);
|
||||
|
||||
listener.on("receiverStatus", (status: ReceiverStatus) => {
|
||||
const receiverStatusMessage: any = {
|
||||
subject: "main:updateReceiverStatus"
|
||||
sendMessage({
|
||||
subject: "main:receiverDeviceUpdated"
|
||||
, data: {
|
||||
id
|
||||
, status: {
|
||||
volume: {
|
||||
level: status.volume.level
|
||||
, muted: status.volume.muted
|
||||
}
|
||||
}
|
||||
receiverDeviceId: service.txtRecord.id
|
||||
, status
|
||||
}
|
||||
};
|
||||
|
||||
if (status.applications && status.applications.length) {
|
||||
const application = status.applications[0];
|
||||
|
||||
receiverStatusMessage.data.status.application = {
|
||||
appId: application.appId
|
||||
, displayName: application.displayName
|
||||
, isIdleScreen: application.isIdleScreen
|
||||
, statusText: application.statusText
|
||||
};
|
||||
}
|
||||
|
||||
sendMessage(receiverStatusMessage);
|
||||
});
|
||||
});
|
||||
|
||||
statusListeners.set(id, listener);
|
||||
statusListeners.set(service.txtRecord.id, listener);
|
||||
}
|
||||
|
||||
function onStatusBrowserServiceDown(_service: mdns.Service) {
|
||||
|
||||
@@ -51,8 +51,8 @@ decodeTransform.on("data", (message: Message) => {
|
||||
}
|
||||
|
||||
case "bridge:stopReceiverApp": {
|
||||
const { host, port } = message.data.receiver;
|
||||
stopReceiverApp(host, port);
|
||||
const { receiverDevice } = message.data;
|
||||
stopReceiverApp(receiverDevice.host, receiverDevice.port);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
import { Receiver
|
||||
import { ReceiverDevice
|
||||
, ReceiverSelectionCast
|
||||
, ReceiverSelectionStop
|
||||
, ReceiverStatus } from "./types";
|
||||
@@ -127,7 +127,7 @@ type MessageDefinitions = {
|
||||
, "bridge:openReceiverSelector": string
|
||||
, "bridge:closeReceiverSelector": {}
|
||||
|
||||
, "bridge:stopReceiverApp": { receiver: Receiver }
|
||||
, "bridge:stopReceiverApp": { receiverDevice: ReceiverDevice }
|
||||
|
||||
|
||||
, "bridge:startMediaServer": {
|
||||
@@ -145,13 +145,21 @@ type MessageDefinitions = {
|
||||
, "mediaCast:mediaServerError": {}
|
||||
|
||||
|
||||
, "main:serviceUp": Receiver
|
||||
, "main:serviceUp": ReceiverDevice
|
||||
, "main:serviceDown": { id: string }
|
||||
|
||||
, "main:updateReceiverStatus": {
|
||||
id: string
|
||||
, status: ReceiverStatus
|
||||
}
|
||||
|
||||
|
||||
, "main:receiverDeviceUp": { receiverDevice: ReceiverDevice }
|
||||
, "main:receiverDeviceDown": { receiverDeviceId: string }
|
||||
, "main:receiverDeviceUpdated": {
|
||||
receiverDeviceId: string
|
||||
, status: ReceiverStatus
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ export interface ReceiverStatus {
|
||||
, transportId: string
|
||||
, universalAppId: string
|
||||
}>
|
||||
, isActiveInput: boolean
|
||||
, isStandBy: boolean
|
||||
, isActiveInput?: boolean
|
||||
, isStandBy?: boolean
|
||||
, userEq: unknown
|
||||
, volume: Volume
|
||||
}
|
||||
@@ -68,17 +68,17 @@ export enum ReceiverSelectionActionType {
|
||||
|
||||
export interface ReceiverSelectionCast {
|
||||
actionType: ReceiverSelectionActionType.Cast;
|
||||
receiver: Receiver;
|
||||
receiver: ReceiverDevice;
|
||||
mediaType: ReceiverSelectorMediaType;
|
||||
filePath?: string;
|
||||
}
|
||||
|
||||
export interface ReceiverSelectionStop {
|
||||
actionType: ReceiverSelectionActionType.Stop;
|
||||
receiver: Receiver;
|
||||
receiver: ReceiverDevice;
|
||||
}
|
||||
|
||||
export interface Receiver {
|
||||
export interface ReceiverDevice {
|
||||
host: string;
|
||||
friendlyName: string;
|
||||
id: string;
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ReceiverSelectionActionType
|
||||
import ReceiverSelectorManager
|
||||
from "./receiverSelector/ReceiverSelectorManager";
|
||||
|
||||
import StatusManager from "./StatusManager";
|
||||
import receiverDevices from "./receiverDevices";
|
||||
|
||||
|
||||
type AnyPort = Port | MessagePort;
|
||||
@@ -37,20 +37,20 @@ export default new class ShimManager {
|
||||
}
|
||||
});
|
||||
|
||||
StatusManager.addEventListener("serviceUp", ev => {
|
||||
receiverDevices.addEventListener("receiverDeviceUp", ev => {
|
||||
for (const shim of this.activeShims) {
|
||||
shim.contentPort.postMessage({
|
||||
subject: "shim:serviceUp"
|
||||
, data: { id: ev.detail.id }
|
||||
, data: { id: ev.detail.receiverDevice.id }
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
StatusManager.addEventListener("serviceDown", ev => {
|
||||
receiverDevices.addEventListener("receiverDeviceDown", ev => {
|
||||
for (const shim of this.activeShims) {
|
||||
shim.contentPort.postMessage({
|
||||
subject: "shim:serviceDown"
|
||||
, data: { id: ev.detail.id }
|
||||
, data: { id: ev.detail.receiverDeviceId }
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -170,10 +170,10 @@ export default new class ShimManager {
|
||||
case "main:shimReady": {
|
||||
shim.appId = message.data.appId;
|
||||
|
||||
for (const receiver of StatusManager.getReceivers()) {
|
||||
for (const receiverDevice of receiverDevices.getDevices()) {
|
||||
shim.contentPort.postMessage({
|
||||
subject: "shim:serviceUp"
|
||||
, data: { id: receiver.id }
|
||||
, data: { id: receiverDevice.id }
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
import bridge from "../lib/bridge";
|
||||
import logger from "../lib/logger";
|
||||
import { Message, Port } from "../messaging";
|
||||
|
||||
import { TypedEventTarget } from "../lib/TypedEventTarget";
|
||||
import { Receiver, ReceiverStatus } from "../types";
|
||||
|
||||
|
||||
interface EventMap {
|
||||
"serviceUp": Receiver;
|
||||
"serviceDown": { id: string };
|
||||
"statusUpdate": { id: string, status: ReceiverStatus };
|
||||
}
|
||||
|
||||
export default new class StatusManager
|
||||
extends TypedEventTarget<EventMap> {
|
||||
|
||||
private bridgePort: (Port | null) = null;
|
||||
private receivers = new Map<string, Receiver>();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// Bind listeners
|
||||
this.onBridgePortMessage = this.onBridgePortMessage.bind(this);
|
||||
this.onBridgePortDisconnect = this.onBridgePortDisconnect.bind(this);
|
||||
}
|
||||
|
||||
public async init() {
|
||||
if (!this.bridgePort) {
|
||||
this.bridgePort = await this.createBridgePort();
|
||||
}
|
||||
}
|
||||
|
||||
public *getReceivers() {
|
||||
for (const [ , receiver ] of this.receivers) {
|
||||
if (receiver.status && receiver.status.application
|
||||
&& receiver.status.volume) {
|
||||
yield receiver;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async stopReceiverApp(receiver: Receiver) {
|
||||
if (!this.bridgePort) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.bridgePort.postMessage({
|
||||
subject: "bridge:stopReceiverApp"
|
||||
, data: { receiver }
|
||||
});
|
||||
}
|
||||
|
||||
private async createBridgePort() {
|
||||
const bridgePort = await bridge.connect();
|
||||
bridgePort.onMessage.addListener(this.onBridgePortMessage);
|
||||
bridgePort.onDisconnect.addListener(this.onBridgePortDisconnect);
|
||||
|
||||
bridgePort.postMessage({
|
||||
subject: "bridge:initialize"
|
||||
, data: {
|
||||
shouldWatchStatus: true
|
||||
}
|
||||
});
|
||||
|
||||
return bridgePort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming bridge status messages, manages the
|
||||
* receiver list, and dispatches events.
|
||||
*/
|
||||
private onBridgePortMessage(message: Message) {
|
||||
switch (message.subject) {
|
||||
case "main:serviceUp": {
|
||||
const { data: receiver } = message;
|
||||
this.receivers.set(receiver.id, receiver);
|
||||
|
||||
this.dispatchEvent(new CustomEvent("serviceUp", {
|
||||
detail: receiver
|
||||
}));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "main:serviceDown": {
|
||||
const { data: { id }} = message;
|
||||
|
||||
if (this.receivers.has(id)) {
|
||||
this.receivers.delete(id);
|
||||
}
|
||||
|
||||
this.dispatchEvent(new CustomEvent("serviceDown", {
|
||||
detail: { id }
|
||||
}));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "main:updateReceiverStatus": {
|
||||
const { data: { id, status }} = message;
|
||||
const receiver = this.receivers.get(id);
|
||||
|
||||
if (!receiver) {
|
||||
throw logger.error(`Could not find receiver (${id}) specified in status message.`);
|
||||
}
|
||||
|
||||
// Merge with existing
|
||||
this.receivers.set(id, {
|
||||
...receiver
|
||||
, status: {
|
||||
...receiver.status
|
||||
, ...status
|
||||
}
|
||||
});
|
||||
|
||||
this.dispatchEvent(new CustomEvent("statusUpdate", {
|
||||
detail: { id, status }
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs once the status bridge has disconnected. Sends
|
||||
* serviceDown messages for all receivers to all shims to
|
||||
* update receiver availability, then clears the receiver
|
||||
* list.
|
||||
*
|
||||
* Attempts to reinitialize the status bridge after 10
|
||||
* seconds. If it fails immediately, this handler will be
|
||||
* triggered again and the timer is reset for another 10
|
||||
* seconds.
|
||||
*/
|
||||
private onBridgePortDisconnect() {
|
||||
for (const [ , receiver ] of this.receivers) {
|
||||
const serviceDownEvent = new CustomEvent("serviceDown", {
|
||||
detail: { id: receiver.id }
|
||||
});
|
||||
|
||||
this.dispatchEvent(serviceDownEvent);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
this.receivers.clear();
|
||||
|
||||
if (this.bridgePort) {
|
||||
this.bridgePort.onDisconnect.removeListener(
|
||||
this.onBridgePortDisconnect);
|
||||
this.bridgePort.onMessage.removeListener(
|
||||
this.onBridgePortMessage);
|
||||
|
||||
this.bridgePort = null;
|
||||
}
|
||||
|
||||
window.setTimeout(async () => {
|
||||
this.bridgePort = await this.createBridgePort();
|
||||
}, 10000);
|
||||
}
|
||||
};
|
||||
@@ -11,7 +11,8 @@ import ReceiverSelectorManager
|
||||
from "./receiverSelector/ReceiverSelectorManager";
|
||||
|
||||
import ShimManager from "./ShimManager";
|
||||
import StatusManager from "./StatusManager";
|
||||
|
||||
import receiverDevices from "./receiverDevices";
|
||||
|
||||
import { initMenus } from "./menus";
|
||||
import { initWhitelist } from "./whitelist";
|
||||
@@ -155,7 +156,7 @@ async function init() {
|
||||
|
||||
await notifyBridgeCompat();
|
||||
|
||||
await StatusManager.init();
|
||||
await receiverDevices.init();
|
||||
await ShimManager.init();
|
||||
|
||||
await initMenus();
|
||||
|
||||
167
ext/src/background/receiverDevices.ts
Normal file
167
ext/src/background/receiverDevices.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
"use strict";
|
||||
|
||||
import bridge from "../lib/bridge";
|
||||
import logger from "../lib/logger";
|
||||
import { TypedEventTarget } from "../lib/TypedEventTarget";
|
||||
|
||||
import messaging, { Message, Port } from "../messaging";
|
||||
import { ReceiverDevice, ReceiverStatus } from "../types";
|
||||
|
||||
|
||||
interface EventMap {
|
||||
"receiverDeviceUp": { receiverDevice: ReceiverDevice }
|
||||
, "receiverDeviceDown": { receiverDeviceId: string }
|
||||
, "receiverDeviceUpdated": {
|
||||
receiverDeviceId: string
|
||||
, status: ReceiverStatus
|
||||
}
|
||||
}
|
||||
|
||||
export default new class extends TypedEventTarget<EventMap> {
|
||||
/**
|
||||
* Map of receiver device IDs to devices. Updated as
|
||||
* receiverDevice messages are received from the bridge.
|
||||
*/
|
||||
private receiverDevices = new Map<string, ReceiverDevice>();
|
||||
|
||||
private bridgePort?: Port;
|
||||
|
||||
|
||||
async init() {
|
||||
if (!this.bridgePort) {
|
||||
await this.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize (or re-initialize) a bridge connection to
|
||||
* start dispatching events.
|
||||
*/
|
||||
async refresh() {
|
||||
this.bridgePort?.disconnect();
|
||||
|
||||
const port = await bridge.connect();
|
||||
|
||||
port.onMessage.addListener(this.onBridgeMessage);
|
||||
port.onDisconnect.addListener(this.onBridgeDisconnect);
|
||||
|
||||
port.postMessage({
|
||||
subject: "bridge:initialize"
|
||||
, data: {
|
||||
// Also send back status messages
|
||||
shouldWatchStatus: true
|
||||
}
|
||||
});
|
||||
|
||||
this.bridgePort = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of receiver devices
|
||||
*/
|
||||
getDevices() {
|
||||
return Array.from(this.receiverDevices.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops a receiver app running on a given device.
|
||||
*/
|
||||
stopReceiverApp(receiverDeviceId: string) {
|
||||
if (!this.bridgePort) {
|
||||
logger.error("Failed to stop receiver device, no bridge connection");
|
||||
return;
|
||||
}
|
||||
|
||||
const receiverDevice = this.receiverDevices.get(receiverDeviceId);
|
||||
if (receiverDevice) {
|
||||
this.bridgePort.postMessage({
|
||||
subject: "bridge:stopReceiverApp"
|
||||
, data: { receiverDevice }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private onBridgeMessage = (message: Message) => {
|
||||
switch (message.subject) {
|
||||
case "main:receiverDeviceUp": {
|
||||
const { receiverDevice } = message.data;
|
||||
|
||||
this.receiverDevices.set(receiverDevice.id, receiverDevice);
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("receiverDeviceUp"
|
||||
, {
|
||||
detail: { receiverDevice }
|
||||
}));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "main:receiverDeviceDown": {
|
||||
const { receiverDeviceId } = message.data;
|
||||
|
||||
if (this.receiverDevices.has(receiverDeviceId)) {
|
||||
this.receiverDevices.delete(receiverDeviceId);
|
||||
}
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("receiverDeviceDown"
|
||||
, {
|
||||
detail: { receiverDeviceId }
|
||||
}));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "main:receiverDeviceUpdated": {
|
||||
const { receiverDeviceId, status } = message.data;
|
||||
const receiverDevice =
|
||||
this.receiverDevices.get(receiverDeviceId);
|
||||
|
||||
if (!receiverDevice) {
|
||||
logger.error(`Receiver ID \`${receiverDeviceId}\` not found!`);
|
||||
break;
|
||||
}
|
||||
|
||||
if (receiverDevice.status) {
|
||||
receiverDevice.status.isActiveInput = status.isActiveInput;
|
||||
receiverDevice.status.isStandBy = status.isStandBy;
|
||||
receiverDevice.status.userEq = status.userEq;
|
||||
receiverDevice.status.volume = status.volume;
|
||||
|
||||
if (status.applications) {
|
||||
receiverDevice.status.applications =
|
||||
status.applications;
|
||||
}
|
||||
} else {
|
||||
receiverDevice.status = status;
|
||||
}
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("receiverDeviceUpdated"
|
||||
, {
|
||||
detail: {
|
||||
receiverDeviceId
|
||||
, status: receiverDevice.status
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onBridgeDisconnect = () => {
|
||||
// Notify listeners of device availablility
|
||||
for (const [ , receiverDevice ] of this.receiverDevices) {
|
||||
const event = new CustomEvent("receiverDeviceDown", {
|
||||
detail: { receiverDeviceId: receiverDevice.id }
|
||||
});
|
||||
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
|
||||
this.receiverDevices.clear();
|
||||
|
||||
// Re-initialize after 10 seconds
|
||||
window.setTimeout(() => {
|
||||
this.refresh();
|
||||
}, 10000);
|
||||
}
|
||||
};
|
||||
@@ -8,7 +8,7 @@ import options from "../../lib/options";
|
||||
|
||||
import { TypedEventTarget } from "../../lib/TypedEventTarget";
|
||||
import { getWindowCenteredProps } from "../../lib/utils";
|
||||
import { Receiver } from "../../types";
|
||||
import { ReceiverDevice } from "../../types";
|
||||
|
||||
import ReceiverSelector, {
|
||||
ReceiverSelection
|
||||
@@ -34,7 +34,7 @@ export default class NativeReceiverSelector extends ReceiverSelector {
|
||||
}
|
||||
|
||||
public async open(
|
||||
receivers: Receiver[]
|
||||
receivers: ReceiverDevice[]
|
||||
, defaultMediaType: ReceiverSelectorMediaType
|
||||
, availableMediaTypes: ReceiverSelectorMediaType
|
||||
, appId?: string): Promise<void> {
|
||||
|
||||
@@ -9,7 +9,7 @@ import options from "../../lib/options";
|
||||
|
||||
import { TypedEventTarget } from "../../lib/TypedEventTarget";
|
||||
import { getWindowCenteredProps, WindowCenteredProps } from "../../lib/utils";
|
||||
import { Receiver } from "../../types";
|
||||
import { ReceiverDevice } from "../../types";
|
||||
|
||||
|
||||
const POPUP_URL = browser.runtime.getURL("ui/popup/index.html");
|
||||
@@ -20,7 +20,7 @@ export default class PopupReceiverSelector extends ReceiverSelector {
|
||||
private messagePort?: Port;
|
||||
private messagePortDisconnected?: boolean;
|
||||
|
||||
private receivers?: Receiver[];
|
||||
private receivers?: ReceiverDevice[];
|
||||
private defaultMediaType?: ReceiverSelectorMediaType;
|
||||
private availableMediaTypes?: ReceiverSelectorMediaType;
|
||||
|
||||
@@ -53,7 +53,7 @@ export default class PopupReceiverSelector extends ReceiverSelector {
|
||||
}
|
||||
|
||||
public async open(
|
||||
receivers: Receiver[]
|
||||
receivers: ReceiverDevice[]
|
||||
, defaultMediaType: ReceiverSelectorMediaType
|
||||
, availableMediaTypes: ReceiverSelectorMediaType
|
||||
, appId?: string): Promise<void> {
|
||||
@@ -115,7 +115,7 @@ export default class PopupReceiverSelector extends ReceiverSelector {
|
||||
}
|
||||
}
|
||||
|
||||
public update(receivers: Receiver[]) {
|
||||
public update(receivers: ReceiverDevice[]) {
|
||||
this.receivers = receivers;
|
||||
this.messagePort?.postMessage({
|
||||
subject: "popup:update"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
import { TypedEventTarget } from "../../lib/TypedEventTarget";
|
||||
import { Receiver } from "../../types";
|
||||
import { ReceiverDevice } from "../../types";
|
||||
|
||||
|
||||
export enum ReceiverSelectorMediaType {
|
||||
@@ -18,13 +18,13 @@ export enum ReceiverSelectionActionType {
|
||||
|
||||
export interface ReceiverSelectionCast {
|
||||
actionType: ReceiverSelectionActionType.Cast;
|
||||
receiver: Receiver;
|
||||
receiver: ReceiverDevice;
|
||||
mediaType: ReceiverSelectorMediaType;
|
||||
filePath?: string;
|
||||
}
|
||||
export interface ReceiverSelectionStop {
|
||||
actionType: ReceiverSelectionActionType.Stop;
|
||||
receiver: Receiver;
|
||||
receiver: ReceiverDevice;
|
||||
}
|
||||
|
||||
export type ReceiverSelection = ReceiverSelectionCast | ReceiverSelectionStop;
|
||||
@@ -43,12 +43,12 @@ export default abstract class ReceiverSelector
|
||||
abstract readonly isOpen: boolean;
|
||||
|
||||
abstract open (
|
||||
receivers: Receiver[]
|
||||
receivers: ReceiverDevice[]
|
||||
, defaultMediaType: ReceiverSelectorMediaType
|
||||
, availableMediaTypes: ReceiverSelectorMediaType
|
||||
, appId?: string): void;
|
||||
|
||||
abstract update (receivers: Receiver[]): void;
|
||||
abstract update (receivers: ReceiverDevice[]): void;
|
||||
|
||||
abstract close (): void;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import options from "../../lib/options";
|
||||
import logger from "../../lib/logger";
|
||||
|
||||
import ShimManager from "../ShimManager";
|
||||
import StatusManager from "../StatusManager";
|
||||
import receiverDevices from "../receiverDevices";
|
||||
|
||||
import { getMediaTypesForPageUrl } from "../../lib/utils";
|
||||
|
||||
@@ -120,12 +120,15 @@ async function getSelection(
|
||||
|
||||
|
||||
function onReceiverChange() {
|
||||
sharedSelector.update(Array.from(StatusManager.getReceivers()));
|
||||
sharedSelector.update(receiverDevices.getDevices());
|
||||
}
|
||||
|
||||
StatusManager.addEventListener("serviceUp", onReceiverChange);
|
||||
StatusManager.addEventListener("serviceDown", onReceiverChange);
|
||||
StatusManager.addEventListener("statusUpdate", onReceiverChange);
|
||||
receiverDevices.addEventListener(
|
||||
"receiverDeviceUp", onReceiverChange);
|
||||
receiverDevices.addEventListener(
|
||||
"receiverDeviceDown", onReceiverChange);
|
||||
receiverDevices.addEventListener(
|
||||
"receiverDeviceUpdated", onReceiverChange);
|
||||
|
||||
|
||||
let onSelected: any;
|
||||
@@ -156,9 +159,12 @@ async function getSelection(
|
||||
sharedSelector.removeEventListener("error", onError);
|
||||
sharedSelector.removeEventListener("stop", onStop);
|
||||
|
||||
StatusManager.removeEventListener("serviceUp", onReceiverChange);
|
||||
StatusManager.removeEventListener("serviceDown", onReceiverChange);
|
||||
StatusManager.removeEventListener("statusUpdate", onReceiverChange);
|
||||
receiverDevices.removeEventListener(
|
||||
"receiverDeviceUp", onReceiverChange);
|
||||
receiverDevices.removeEventListener(
|
||||
"receiverDeviceDown", onReceiverChange);
|
||||
receiverDevices.removeEventListener(
|
||||
"receiverDeviceUpdated", onReceiverChange);
|
||||
}
|
||||
|
||||
sharedSelector.addEventListener("selected"
|
||||
@@ -191,10 +197,9 @@ async function getSelection(
|
||||
sharedSelector.addEventListener("stop"
|
||||
, storeListener("stop", async ev => {
|
||||
|
||||
logger.info("Stopped receiver app", ev.detail);
|
||||
logger.info("Stopping receiver app...", ev.detail);
|
||||
|
||||
await StatusManager.init();
|
||||
await StatusManager.stopReceiverApp(ev.detail.receiver);
|
||||
receiverDevices.stopReceiverApp(ev.detail.receiver.id);
|
||||
|
||||
resolve({
|
||||
actionType: ReceiverSelectionActionType.Stop
|
||||
@@ -205,10 +210,10 @@ async function getSelection(
|
||||
|
||||
|
||||
// Ensure status manager is initialized
|
||||
await StatusManager.init();
|
||||
await receiverDevices.init();
|
||||
|
||||
sharedSelector.open(
|
||||
Array.from(StatusManager.getReceivers())
|
||||
receiverDevices.getDevices()
|
||||
, defaultMediaType
|
||||
, availableMediaTypes
|
||||
, currentShim?.appId);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Message, Port } from "../messaging";
|
||||
import nativeMessaging from "./nativeMessaging";
|
||||
import options from "./options";
|
||||
|
||||
import { Receiver } from "../types";
|
||||
import { ReceiverDevice } from "../types";
|
||||
import { ReceiverSelectionCast
|
||||
, ReceiverSelectionStop } from "../background/receiverSelector/ReceiverSelector";
|
||||
|
||||
|
||||
@@ -21,6 +21,14 @@ export class Logger {
|
||||
console.info(formattedMessage);
|
||||
}
|
||||
}
|
||||
public warn(message: string, data?: any) {
|
||||
const formattedMessage = `${this.prefix} (Warning): ${message}`;
|
||||
if (data) {
|
||||
console.warn(formattedMessage, data);
|
||||
} else {
|
||||
console.warn(formattedMessage);
|
||||
}
|
||||
}
|
||||
public error(message: string, data?: any) {
|
||||
const formattedMessage = `${this.prefix} (Error): ${message}`;
|
||||
if (data) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import Messenger from "./lib/Messenger";
|
||||
import { TypedPort } from "./lib/TypedPort";
|
||||
|
||||
import { BridgeInfo } from "./lib/bridge";
|
||||
import { Receiver, ReceiverStatus } from "./types";
|
||||
import { ReceiverDevice, ReceiverStatus } from "./types";
|
||||
import { ReceiverSelectorMediaType } from "./background/receiverSelector";
|
||||
import { ReceiverSelection, ReceiverSelectionCast, ReceiverSelectionStop }
|
||||
from "./background/receiverSelector/ReceiverSelector";
|
||||
@@ -34,7 +34,7 @@ import { MediaInfo } from "./shim/cast/media";
|
||||
type ExtMessageDefinitions = {
|
||||
"popup:init": { appId?: string }
|
||||
, "popup:update": {
|
||||
receivers: Receiver[]
|
||||
receivers: ReceiverDevice[]
|
||||
, defaultMediaType?: ReceiverSelectorMediaType
|
||||
, availableMediaTypes?: ReceiverSelectorMediaType
|
||||
}
|
||||
@@ -53,10 +53,10 @@ type ExtMessageDefinitions = {
|
||||
, "main:sessionCreated": {}
|
||||
|
||||
, "shim:initialized": BridgeInfo
|
||||
, "shim:serviceUp": { id: Receiver["id"] }
|
||||
, "shim:serviceDown": { id: Receiver["id"] }
|
||||
, "shim:serviceUp": { id: ReceiverDevice["id"] }
|
||||
, "shim:serviceDown": { id: ReceiverDevice["id"] }
|
||||
|
||||
, "shim:launchApp": { receiver: Receiver }
|
||||
, "shim:launchApp": { receiver: ReceiverDevice }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,7 +184,7 @@ type AppMessageDefinitions = {
|
||||
, "bridge:openReceiverSelector": string
|
||||
, "bridge:closeReceiverSelector": {}
|
||||
|
||||
, "bridge:stopReceiverApp": { receiver: Receiver }
|
||||
, "bridge:stopReceiverApp": { receiverDevice: ReceiverDevice }
|
||||
|
||||
|
||||
, "bridge:startMediaServer": {
|
||||
@@ -202,11 +202,10 @@ type AppMessageDefinitions = {
|
||||
, "mediaCast:mediaServerError": {}
|
||||
|
||||
|
||||
, "main:serviceUp": Receiver
|
||||
, "main:serviceDown": { id: string }
|
||||
|
||||
, "main:updateReceiverStatus": {
|
||||
id: string
|
||||
, "main:receiverDeviceUp": { receiverDevice: ReceiverDevice }
|
||||
, "main:receiverDeviceDown": { receiverDeviceId: string }
|
||||
, "main:receiverDeviceUpdated": {
|
||||
receiverDeviceId: string
|
||||
, status: ReceiverStatus
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import options from "../../lib/options";
|
||||
import cast, { ensureInit } from "../../shim/export";
|
||||
|
||||
import { Message } from "../../messaging";
|
||||
import { Receiver } from "../../types";
|
||||
import { ReceiverDevice } from "../../types";
|
||||
|
||||
|
||||
function startMediaServer(filePath: string, port: number)
|
||||
@@ -343,7 +343,7 @@ async function registerMediaElementListeners() {
|
||||
|
||||
interface InitOptions {
|
||||
mediaUrl: string;
|
||||
receiver: Receiver;
|
||||
receiver: ReceiverDevice;
|
||||
targetElementId?: number;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,14 @@ import options from "../lib/options";
|
||||
import cast, { ensureInit } from "../shim/export";
|
||||
|
||||
import { ReceiverSelectorMediaType } from "../background/receiverSelector";
|
||||
import { Receiver } from "../types";
|
||||
import { ReceiverDevice } from "../types";
|
||||
|
||||
|
||||
// Variables passed from background
|
||||
const { selectedMedia
|
||||
, selectedReceiver }
|
||||
: { selectedMedia: ReceiverSelectorMediaType
|
||||
, selectedReceiver: Receiver } = (window as any);
|
||||
, selectedReceiver: ReceiverDevice } = (window as any);
|
||||
|
||||
|
||||
const FX_CAST_RECEIVER_APP_NAMESPACE = "urn:x-cast:fx_cast";
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import logger from "../../lib/logger";
|
||||
|
||||
import { Receiver } from "../../types";
|
||||
import { ReceiverDevice } from "../../types";
|
||||
import { onMessage, sendMessageResponse } from "../eventMessageChannel";
|
||||
|
||||
import Session from "./Session";
|
||||
@@ -177,7 +177,7 @@ export function requestSession(
|
||||
}
|
||||
|
||||
export function _requestSession(
|
||||
_receiver: Receiver
|
||||
_receiver: ReceiverDevice
|
||||
, successCallback?: RequestSessionSuccessCallback
|
||||
, errorCallback?: ErrorCallback): void {
|
||||
|
||||
@@ -414,7 +414,7 @@ onMessage(async message => {
|
||||
}
|
||||
|
||||
case "shim:launchApp": {
|
||||
const receiver: Receiver = message.data.receiver;
|
||||
const receiver: ReceiverDevice = message.data.receiver;
|
||||
_requestSession(receiver
|
||||
, session => {
|
||||
apiConfig.sessionListener(session);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Volume } from "./shim/cast/dataClasses";
|
||||
|
||||
|
||||
export interface Receiver {
|
||||
export interface ReceiverDevice {
|
||||
host: string
|
||||
friendlyName: string
|
||||
, id: string
|
||||
@@ -25,8 +25,8 @@ export interface ReceiverStatus {
|
||||
, transportId: string
|
||||
, universalAppId: string
|
||||
}>
|
||||
, isActiveInput: boolean
|
||||
, isStandBy: boolean
|
||||
, isActiveInput?: boolean
|
||||
, isStandBy?: boolean
|
||||
, userEq: unknown
|
||||
, volume: Volume
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import options from "../../lib/options";
|
||||
|
||||
import messaging, { Message, Port } from "../../messaging";
|
||||
import { getNextEllipsis } from "../../lib/utils";
|
||||
import { Receiver } from "../../types";
|
||||
import { ReceiverDevice } from "../../types";
|
||||
|
||||
import { ReceiverSelectionActionType
|
||||
, ReceiverSelectorMediaType } from "../../background/receiverSelector";
|
||||
@@ -31,7 +31,7 @@ browser.runtime.getPlatformInfo()
|
||||
|
||||
interface PopupAppProps {}
|
||||
interface PopupAppState {
|
||||
receivers: Receiver[];
|
||||
receivers: ReceiverDevice[];
|
||||
mediaType: ReceiverSelectorMediaType;
|
||||
availableMediaTypes: ReceiverSelectorMediaType;
|
||||
isLoading: boolean;
|
||||
@@ -92,7 +92,7 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
||||
|
||||
if (availableMediaTypes && defaultMediaType) {
|
||||
this.setState({
|
||||
availableMediaTypes: availableMediaTypes
|
||||
availableMediaTypes
|
||||
, mediaType: defaultMediaType
|
||||
});
|
||||
}
|
||||
@@ -214,7 +214,7 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
||||
</>;
|
||||
}
|
||||
|
||||
private onCast(receiver: Receiver) {
|
||||
private onCast(receiver: ReceiverDevice) {
|
||||
this.setState({
|
||||
isLoading: true
|
||||
});
|
||||
@@ -230,7 +230,7 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
||||
});
|
||||
}
|
||||
|
||||
private onStop(receiver: Receiver) {
|
||||
private onStop(receiver: ReceiverDevice) {
|
||||
this.port?.postMessage({
|
||||
subject: "receiverSelector:stop"
|
||||
, data: {
|
||||
@@ -274,11 +274,11 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
||||
|
||||
|
||||
interface ReceiverEntryProps {
|
||||
receiver: Receiver;
|
||||
receiver: ReceiverDevice;
|
||||
isLoading: boolean;
|
||||
canCast: boolean;
|
||||
onCast (receiver: Receiver): void;
|
||||
onStop (receiver: Receiver): void;
|
||||
onCast (receiver: ReceiverDevice): void;
|
||||
onStop (receiver: ReceiverDevice): void;
|
||||
}
|
||||
|
||||
interface ReceiverEntryState {
|
||||
@@ -320,28 +320,27 @@ class ReceiverEntry extends Component<ReceiverEntryProps, ReceiverEntryState> {
|
||||
}
|
||||
|
||||
public render() {
|
||||
if (!this.props.receiver.status) {
|
||||
const { status } = this.props.receiver;
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { application } = this.props.receiver.status;
|
||||
const application = status.applications?.[0];
|
||||
|
||||
return (
|
||||
<li className="receiver">
|
||||
<div className="receiver__name">
|
||||
{ this.props.receiver.friendlyName }
|
||||
</div>
|
||||
<div className="receiver__address"
|
||||
title={ !application.isIdleScreen ? application.statusText : "" }>
|
||||
{ application.isIdleScreen
|
||||
? `${this.props.receiver.host}:${this.props.receiver.port}`
|
||||
: application.statusText }
|
||||
<div className="receiver__address">
|
||||
{ application && !application.isIdleScreen
|
||||
? application.statusText
|
||||
: `${this.props.receiver.host}:${this.props.receiver.port}` }
|
||||
</div>
|
||||
<button className="button receiver__connect"
|
||||
onClick={ this.handleCast }
|
||||
disabled={ this.state.showAlternateAction
|
||||
? application.isIdleScreen
|
||||
: (this.props.isLoading || !this.props.canCast) }>
|
||||
disabled={ (application && application.isIdleScreen)
|
||||
?? (this.props.isLoading || !this.props.canCast) }>
|
||||
{ this.state.isLoading
|
||||
? _("popupCastingButtonTitle"
|
||||
, (this.state.isLoading
|
||||
@@ -356,13 +355,14 @@ class ReceiverEntry extends Component<ReceiverEntryProps, ReceiverEntryState> {
|
||||
}
|
||||
|
||||
private handleCast() {
|
||||
if (!this.props.receiver.status) {
|
||||
const { status } = this.props.receiver;
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { application } = this.props.receiver.status;
|
||||
const application = status.applications?.[0];
|
||||
|
||||
if (!application.isIdleScreen && this.state.showAlternateAction) {
|
||||
if (!application?.isIdleScreen && this.state.showAlternateAction) {
|
||||
this.props.onStop(this.props.receiver);
|
||||
} else {
|
||||
this.props.onCast(this.props.receiver);
|
||||
|
||||
Reference in New Issue
Block a user