mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-08 08:39:59 +00:00
Add better support for handling device capabilities and receiver objects
This commit is contained in:
@@ -27,13 +27,15 @@ export default class Remote extends CastClient {
|
|||||||
|
|
||||||
constructor(private host: string, private options?: CastRemoteOptions) {
|
constructor(private host: string, private options?: CastRemoteOptions) {
|
||||||
super();
|
super();
|
||||||
super.connect(host, {
|
super
|
||||||
onReceiverMessage: message => {
|
.connect(host, {
|
||||||
this.onReceiverMessage(message);
|
onReceiverMessage: message => {
|
||||||
}
|
this.onReceiverMessage(message);
|
||||||
}).then(() => {
|
}
|
||||||
this.sendReceiverMessage({ type: "GET_STATUS" });
|
})
|
||||||
});
|
.then(() => {
|
||||||
|
this.sendReceiverMessage({ type: "GET_STATUS" });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
disconnect() {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ interface CastRecord {
|
|||||||
md: string;
|
md: string;
|
||||||
// Friendly name (user-visible)
|
// Friendly name (user-visible)
|
||||||
fn: string;
|
fn: string;
|
||||||
|
// Capabilities
|
||||||
|
ca: string;
|
||||||
// Version (?)
|
// Version (?)
|
||||||
ve: string;
|
ve: string;
|
||||||
// Icon path (?)
|
// Icon path (?)
|
||||||
@@ -23,7 +25,6 @@ interface CastRecord {
|
|||||||
|
|
||||||
cd: string;
|
cd: string;
|
||||||
rm: string;
|
rm: string;
|
||||||
ca: string;
|
|
||||||
st: string;
|
st: string;
|
||||||
bs: string;
|
bs: string;
|
||||||
nf: string;
|
nf: string;
|
||||||
@@ -71,16 +72,18 @@ browser.on("serviceUp", service => {
|
|||||||
|
|
||||||
const record = service.txtRecord as CastRecord;
|
const record = service.txtRecord as CastRecord;
|
||||||
const device: ReceiverDevice = {
|
const device: ReceiverDevice = {
|
||||||
|
id: record.id,
|
||||||
|
friendlyName: record.fn,
|
||||||
|
modelName: record.md,
|
||||||
|
capabilities: parseInt(record.ca),
|
||||||
host: service.addresses[0],
|
host: service.addresses[0],
|
||||||
port: service.port,
|
port: service.port
|
||||||
id: service.name,
|
|
||||||
friendlyName: record.fn
|
|
||||||
};
|
};
|
||||||
|
|
||||||
sendMessage({
|
sendMessage({
|
||||||
subject: "main:receiverDeviceUp",
|
subject: "main:receiverDeviceUp",
|
||||||
data: {
|
data: {
|
||||||
deviceId: service.name,
|
deviceId: device.id,
|
||||||
deviceInfo: device
|
deviceInfo: device
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,10 +7,21 @@ import {
|
|||||||
Volume
|
Volume
|
||||||
} from "./components/cast/types";
|
} from "./components/cast/types";
|
||||||
|
|
||||||
|
export enum ReceiverDeviceCapabilities {
|
||||||
|
NONE = 0,
|
||||||
|
VIDEO_OUT = 1,
|
||||||
|
VIDEO_IN = 2,
|
||||||
|
AUDIO_OUT = 4,
|
||||||
|
AUDIO_IN = 8,
|
||||||
|
MULTIZONE_GROUP = 32
|
||||||
|
}
|
||||||
|
|
||||||
export interface ReceiverDevice {
|
export interface ReceiverDevice {
|
||||||
host: string;
|
|
||||||
friendlyName: string;
|
|
||||||
id: string;
|
id: string;
|
||||||
|
friendlyName: string;
|
||||||
|
modelName: string;
|
||||||
|
capabilities: ReceiverDeviceCapabilities;
|
||||||
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
status?: ReceiverStatus;
|
status?: ReceiverStatus;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ browser.menus.onClicked.addListener(async (info, tab) => {
|
|||||||
if (selection.mediaType === ReceiverSelectorMediaType.App) {
|
if (selection.mediaType === ReceiverSelectorMediaType.App) {
|
||||||
await browser.tabs.executeScript(tab.id, {
|
await browser.tabs.executeScript(tab.id, {
|
||||||
code: stringify`
|
code: stringify`
|
||||||
window.receiver = ${selection.receiver};
|
window.receiver = ${selection.receiverDevice};
|
||||||
window.mediaUrl = ${info.srcUrl};
|
window.mediaUrl = ${info.srcUrl};
|
||||||
window.targetElementId = ${info.targetElementId};
|
window.targetElementId = ${info.targetElementId};
|
||||||
`,
|
`,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import logger from "../lib/logger";
|
|||||||
import { TypedEventTarget } from "../lib/TypedEventTarget";
|
import { TypedEventTarget } from "../lib/TypedEventTarget";
|
||||||
|
|
||||||
import { Message, Port } from "../messaging";
|
import { Message, Port } from "../messaging";
|
||||||
import { ReceiverDevice } from "../types";
|
import { ReceiverDevice, ReceiverDeviceCapabilities } from "../types";
|
||||||
import { ReceiverStatus } from "../cast/api/types";
|
import { ReceiverStatus } from "../cast/api/types";
|
||||||
|
|
||||||
interface EventMap {
|
interface EventMap {
|
||||||
@@ -84,6 +84,16 @@ export default new (class extends TypedEventTarget<EventMap> {
|
|||||||
case "main:receiverDeviceUp": {
|
case "main:receiverDeviceUp": {
|
||||||
const { deviceId, deviceInfo } = message.data;
|
const { deviceId, deviceInfo } = message.data;
|
||||||
|
|
||||||
|
// TODO: Add proper support for Chromecast Audio devices
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
deviceInfo.capabilities &
|
||||||
|
ReceiverDeviceCapabilities.VIDEO_OUT
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
this.receiverDevices.set(deviceId, deviceInfo);
|
this.receiverDevices.set(deviceId, deviceInfo);
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent("receiverDeviceUp", {
|
new CustomEvent("receiverDeviceUp", {
|
||||||
@@ -112,9 +122,7 @@ export default new (class extends TypedEventTarget<EventMap> {
|
|||||||
case "main:receiverDeviceStatusUpdated": {
|
case "main:receiverDeviceStatusUpdated": {
|
||||||
const { deviceId, status } = message.data;
|
const { deviceId, status } = message.data;
|
||||||
const receiverDevice = this.receiverDevices.get(deviceId);
|
const receiverDevice = this.receiverDevices.get(deviceId);
|
||||||
|
|
||||||
if (!receiverDevice) {
|
if (!receiverDevice) {
|
||||||
logger.error(`Receiver ID \`${deviceId}\` not found!`);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
|
|||||||
private messagePort?: Port;
|
private messagePort?: Port;
|
||||||
private messagePortDisconnected?: boolean;
|
private messagePortDisconnected?: boolean;
|
||||||
|
|
||||||
private receivers?: ReceiverDevice[];
|
private receiverDevices?: ReceiverDevice[];
|
||||||
private defaultMediaType?: ReceiverSelectorMediaType;
|
private defaultMediaType?: ReceiverSelectorMediaType;
|
||||||
private availableMediaTypes?: ReceiverSelectorMediaType;
|
private availableMediaTypes?: ReceiverSelectorMediaType;
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async open(
|
public async open(
|
||||||
receivers: ReceiverDevice[],
|
receiverDevices: ReceiverDevice[],
|
||||||
defaultMediaType: ReceiverSelectorMediaType,
|
defaultMediaType: ReceiverSelectorMediaType,
|
||||||
availableMediaTypes: ReceiverSelectorMediaType,
|
availableMediaTypes: ReceiverSelectorMediaType,
|
||||||
appId?: string,
|
appId?: string,
|
||||||
@@ -83,7 +83,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
|
|||||||
await browser.windows.remove(this.windowId);
|
await browser.windows.remove(this.windowId);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.receivers = receivers;
|
this.receiverDevices = receiverDevices;
|
||||||
this.defaultMediaType = defaultMediaType;
|
this.defaultMediaType = defaultMediaType;
|
||||||
this.availableMediaTypes = availableMediaTypes;
|
this.availableMediaTypes = availableMediaTypes;
|
||||||
|
|
||||||
@@ -135,12 +135,12 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(receivers: ReceiverDevice[]) {
|
public update(receiverDevices: ReceiverDevice[]) {
|
||||||
this.receivers = receivers;
|
this.receiverDevices = receiverDevices;
|
||||||
this.messagePort?.postMessage({
|
this.messagePort?.postMessage({
|
||||||
subject: "popup:update",
|
subject: "popup:update",
|
||||||
data: {
|
data: {
|
||||||
receivers: this.receivers
|
receiverDevices: this.receiverDevices
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -176,7 +176,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.receivers === undefined ||
|
this.receiverDevices === undefined ||
|
||||||
this.defaultMediaType === undefined ||
|
this.defaultMediaType === undefined ||
|
||||||
this.availableMediaTypes === undefined
|
this.availableMediaTypes === undefined
|
||||||
) {
|
) {
|
||||||
@@ -191,7 +191,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
|
|||||||
this.messagePort.postMessage({
|
this.messagePort.postMessage({
|
||||||
subject: "popup:update",
|
subject: "popup:update",
|
||||||
data: {
|
data: {
|
||||||
receivers: this.receivers,
|
receiverDevices: this.receiverDevices,
|
||||||
defaultMediaType: this.defaultMediaType,
|
defaultMediaType: this.defaultMediaType,
|
||||||
availableMediaTypes: this.availableMediaTypes
|
availableMediaTypes: this.availableMediaTypes
|
||||||
}
|
}
|
||||||
@@ -250,7 +250,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
|
|||||||
// Cleanup
|
// Cleanup
|
||||||
this.windowId = undefined;
|
this.windowId = undefined;
|
||||||
this.messagePort = undefined;
|
this.messagePort = undefined;
|
||||||
this.receivers = undefined;
|
this.receiverDevices = undefined;
|
||||||
this.defaultMediaType = undefined;
|
this.defaultMediaType = undefined;
|
||||||
this.availableMediaTypes = undefined;
|
this.availableMediaTypes = undefined;
|
||||||
this.wasReceiverSelected = false;
|
this.wasReceiverSelected = false;
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ async function getSelection(
|
|||||||
logger.info("Selected receiver", ev.detail);
|
logger.info("Selected receiver", ev.detail);
|
||||||
resolve({
|
resolve({
|
||||||
actionType: ReceiverSelectionActionType.Cast,
|
actionType: ReceiverSelectionActionType.Cast,
|
||||||
receiver: ev.detail.receiver,
|
receiverDevice: ev.detail.receiverDevice,
|
||||||
mediaType: ev.detail.mediaType,
|
mediaType: ev.detail.mediaType,
|
||||||
filePath: ev.detail.filePath
|
filePath: ev.detail.filePath
|
||||||
});
|
});
|
||||||
@@ -203,11 +203,11 @@ async function getSelection(
|
|||||||
"stop",
|
"stop",
|
||||||
storeListener("stop", async ev => {
|
storeListener("stop", async ev => {
|
||||||
logger.info("Stopping receiver app...", ev.detail);
|
logger.info("Stopping receiver app...", ev.detail);
|
||||||
receiverDevices.stopReceiverApp(ev.detail.receiver.id);
|
receiverDevices.stopReceiverApp(ev.detail.receiverDevice.id);
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
actionType: ReceiverSelectionActionType.Stop,
|
actionType: ReceiverSelectionActionType.Stop,
|
||||||
receiver: ev.detail.receiver
|
receiverDevice: ev.detail.receiverDevice
|
||||||
});
|
});
|
||||||
removeListeners();
|
removeListeners();
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ export enum ReceiverSelectionActionType {
|
|||||||
|
|
||||||
export interface ReceiverSelectionCast {
|
export interface ReceiverSelectionCast {
|
||||||
actionType: ReceiverSelectionActionType.Cast;
|
actionType: ReceiverSelectionActionType.Cast;
|
||||||
receiver: ReceiverDevice;
|
receiverDevice: ReceiverDevice;
|
||||||
mediaType: ReceiverSelectorMediaType;
|
mediaType: ReceiverSelectorMediaType;
|
||||||
filePath?: string;
|
filePath?: string;
|
||||||
}
|
}
|
||||||
export interface ReceiverSelectionStop {
|
export interface ReceiverSelectionStop {
|
||||||
actionType: ReceiverSelectionActionType.Stop;
|
actionType: ReceiverSelectionActionType.Stop;
|
||||||
receiver: ReceiverDevice;
|
receiverDevice: ReceiverDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ReceiverSelection = ReceiverSelectionCast | ReceiverSelectionStop;
|
export type ReceiverSelection = ReceiverSelectionCast | ReceiverSelectionStop;
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
import logger from "../../lib/logger";
|
import logger from "../../lib/logger";
|
||||||
|
|
||||||
import { ReceiverDevice } from "../../types";
|
import {
|
||||||
|
ReceiverDevice,
|
||||||
|
ReceiverDeviceCapabilities as ReceiverDeviceCapabilities
|
||||||
|
} from "../../types";
|
||||||
import { ErrorCallback, SuccessCallback } from "../types";
|
import { ErrorCallback, SuccessCallback } from "../types";
|
||||||
|
|
||||||
import { onMessage, sendMessageResponse } from "../eventMessageChannel";
|
import { onMessage, sendMessageResponse } from "../eventMessageChannel";
|
||||||
@@ -92,17 +95,40 @@ export const timeout = new Timeout();
|
|||||||
// chrome.cast.media namespace
|
// chrome.cast.media namespace
|
||||||
export * as media from "./media";
|
export * as media from "./media";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create `chrome.cast.Receiver` object from receiver device info.
|
||||||
|
*/
|
||||||
|
function createReceiver(device: ReceiverDevice) {
|
||||||
|
// Convert capabilities bitflag to string array
|
||||||
|
const capabilities: Capability[] = [];
|
||||||
|
if (device.capabilities & ReceiverDeviceCapabilities.VIDEO_OUT) {
|
||||||
|
capabilities.push(Capability.VIDEO_OUT);
|
||||||
|
} else if (device.capabilities & ReceiverDeviceCapabilities.VIDEO_IN) {
|
||||||
|
capabilities.push(Capability.VIDEO_IN);
|
||||||
|
} else if (device.capabilities & ReceiverDeviceCapabilities.AUDIO_OUT) {
|
||||||
|
capabilities.push(Capability.AUDIO_OUT);
|
||||||
|
} else if (device.capabilities & ReceiverDeviceCapabilities.AUDIO_IN) {
|
||||||
|
capabilities.push(Capability.AUDIO_IN);
|
||||||
|
} else if (
|
||||||
|
device.capabilities & ReceiverDeviceCapabilities.MULTIZONE_GROUP
|
||||||
|
) {
|
||||||
|
capabilities.push(Capability.MULTIZONE_GROUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
const receiver = new Receiver(device.id, device.friendlyName, capabilities);
|
||||||
|
|
||||||
|
// Currently only supports CAST receivers
|
||||||
|
receiver.receiverType = ReceiverType.CAST;
|
||||||
|
|
||||||
|
return receiver;
|
||||||
|
}
|
||||||
|
|
||||||
function sendSessionRequest(
|
function sendSessionRequest(
|
||||||
sessionRequest: SessionRequest,
|
sessionRequest: SessionRequest,
|
||||||
receiverDevice: ReceiverDevice
|
receiverDevice: ReceiverDevice
|
||||||
) {
|
) {
|
||||||
for (const listener of receiverActionListeners) {
|
for (const listener of receiverActionListeners) {
|
||||||
const receiver = new Receiver(
|
listener(createReceiver(receiverDevice), ReceiverAction.CAST);
|
||||||
receiverDevice.id,
|
|
||||||
receiverDevice.friendlyName
|
|
||||||
);
|
|
||||||
|
|
||||||
listener(receiver, ReceiverAction.CAST);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessageResponse({
|
sendMessageResponse({
|
||||||
@@ -258,13 +284,28 @@ onMessage(message => {
|
|||||||
const status = message.data;
|
const status = message.data;
|
||||||
|
|
||||||
// TODO: Implement persistent per-origin receiver IDs
|
// TODO: Implement persistent per-origin receiver IDs
|
||||||
const receiver = new Receiver(
|
const receiver1 = new Receiver(
|
||||||
status.receiverId, // label
|
status.receiverId, // label
|
||||||
status.receiverFriendlyName, // friendlyName
|
status.receiverFriendlyName, // friendlyName
|
||||||
[Capability.VIDEO_OUT, Capability.AUDIO_OUT], // capabilities
|
[Capability.VIDEO_OUT, Capability.AUDIO_OUT], // capabilities
|
||||||
status.volume // volume
|
status.volume // volume
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const receiverDevice = receiverDevices.get(status.receiverId);
|
||||||
|
if (!receiverDevice) {
|
||||||
|
logger.error(
|
||||||
|
`Could not find receiver device "${status.receiverFriendlyName}" (${status.receiverId})`
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const receiver = createReceiver(receiverDevice);
|
||||||
|
receiver.volume = status.volume;
|
||||||
|
receiver.displayStatus = new ReceiverDisplayStatus(
|
||||||
|
status.statusText,
|
||||||
|
status.appImages
|
||||||
|
);
|
||||||
|
|
||||||
const session = new Session(
|
const session = new Session(
|
||||||
status.sessionId, // sessionId
|
status.sessionId, // sessionId
|
||||||
status.appId, // appId
|
status.appId, // appId
|
||||||
@@ -401,7 +442,7 @@ onMessage(message => {
|
|||||||
logger.info("Selected receiver");
|
logger.info("Selected receiver");
|
||||||
|
|
||||||
if (sessionRequest) {
|
if (sessionRequest) {
|
||||||
sendSessionRequest(sessionRequest, message.data.receiver);
|
sendSessionRequest(sessionRequest, message.data.receiverDevice);
|
||||||
sessionRequest = null;
|
sessionRequest = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,7 +450,7 @@ onMessage(message => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "cast:selectReceiver/stopped": {
|
case "cast:selectReceiver/stopped": {
|
||||||
const { receiver } = message.data;
|
const { receiverDevice } = message.data;
|
||||||
|
|
||||||
logger.info("Stopped receiver");
|
logger.info("Stopped receiver");
|
||||||
|
|
||||||
@@ -417,12 +458,11 @@ onMessage(message => {
|
|||||||
sessionRequest = null;
|
sessionRequest = null;
|
||||||
|
|
||||||
for (const listener of receiverActionListeners) {
|
for (const listener of receiverActionListeners) {
|
||||||
const castReceiver = new Receiver(
|
listener(
|
||||||
receiver.id,
|
// TODO: Use existing receiver object?
|
||||||
receiver.friendlyName
|
createReceiver(receiverDevice),
|
||||||
|
ReceiverAction.STOP
|
||||||
);
|
);
|
||||||
|
|
||||||
listener(castReceiver, ReceiverAction.STOP);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,7 +491,10 @@ onMessage(message => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendSessionRequest(apiConfig.sessionRequest, message.data.receiver);
|
sendSessionRequest(
|
||||||
|
apiConfig.sessionRequest,
|
||||||
|
message.data.receiverDevice
|
||||||
|
);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export default async function loadSender(opts: LoadSenderOptions) {
|
|||||||
|
|
||||||
instance.contentPort.postMessage({
|
instance.contentPort.postMessage({
|
||||||
subject: "cast:launchApp",
|
subject: "cast:launchApp",
|
||||||
data: { receiver: opts.selection.receiver }
|
data: { receiverDevice: opts.selection.receiverDevice }
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -53,7 +53,7 @@ export default async function loadSender(opts: LoadSenderOptions) {
|
|||||||
await browser.tabs.executeScript(opts.tabId, {
|
await browser.tabs.executeScript(opts.tabId, {
|
||||||
code: stringify`
|
code: stringify`
|
||||||
window.selectedMedia = ${opts.selection.mediaType};
|
window.selectedMedia = ${opts.selection.mediaType};
|
||||||
window.selectedReceiver = ${opts.selection.receiver};
|
window.selectedReceiver = ${opts.selection.receiverDevice};
|
||||||
`,
|
`,
|
||||||
frameId: opts.frameId
|
frameId: opts.frameId
|
||||||
});
|
});
|
||||||
@@ -72,7 +72,7 @@ export default async function loadSender(opts: LoadSenderOptions) {
|
|||||||
|
|
||||||
init({
|
init({
|
||||||
mediaUrl: fileUrl.href,
|
mediaUrl: fileUrl.href,
|
||||||
receiver: opts.selection.receiver
|
receiver: opts.selection.receiverDevice
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ type ExtMessageDefinitions = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
"popup:update": {
|
"popup:update": {
|
||||||
receivers: ReceiverDevice[];
|
receiverDevices: ReceiverDevice[];
|
||||||
defaultMediaType?: ReceiverSelectorMediaType;
|
defaultMediaType?: ReceiverSelectorMediaType;
|
||||||
availableMediaTypes?: ReceiverSelectorMediaType;
|
availableMediaTypes?: ReceiverSelectorMediaType;
|
||||||
};
|
};
|
||||||
@@ -68,7 +68,7 @@ type ExtMessageDefinitions = {
|
|||||||
|
|
||||||
"cast:receiverDeviceUp": { receiverDevice: ReceiverDevice };
|
"cast:receiverDeviceUp": { receiverDevice: ReceiverDevice };
|
||||||
"cast:receiverDeviceDown": { receiverDeviceId: ReceiverDevice["id"] };
|
"cast:receiverDeviceDown": { receiverDeviceId: ReceiverDevice["id"] };
|
||||||
"cast:launchApp": { receiver: ReceiverDevice };
|
"cast:launchApp": { receiverDevice: ReceiverDevice };
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,10 +2,21 @@
|
|||||||
|
|
||||||
import { ReceiverStatus } from "./cast/api/types";
|
import { ReceiverStatus } from "./cast/api/types";
|
||||||
|
|
||||||
|
export enum ReceiverDeviceCapabilities {
|
||||||
|
NONE = 0,
|
||||||
|
VIDEO_OUT = 1,
|
||||||
|
VIDEO_IN = 2,
|
||||||
|
AUDIO_OUT = 4,
|
||||||
|
AUDIO_IN = 8,
|
||||||
|
MULTIZONE_GROUP = 32
|
||||||
|
}
|
||||||
|
|
||||||
export interface ReceiverDevice {
|
export interface ReceiverDevice {
|
||||||
host: string;
|
|
||||||
friendlyName: string;
|
|
||||||
id: string;
|
id: string;
|
||||||
|
friendlyName: string;
|
||||||
|
modelName: string;
|
||||||
|
capabilities: ReceiverDeviceCapabilities;
|
||||||
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
status?: ReceiverStatus;
|
status?: ReceiverStatus;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ browser.runtime.getPlatformInfo().then(platformInfo => {
|
|||||||
|
|
||||||
interface PopupAppProps {}
|
interface PopupAppProps {}
|
||||||
interface PopupAppState {
|
interface PopupAppState {
|
||||||
receivers: ReceiverDevice[];
|
receiverDevices: ReceiverDevice[];
|
||||||
mediaType: ReceiverSelectorMediaType;
|
mediaType: ReceiverSelectorMediaType;
|
||||||
availableMediaTypes: ReceiverSelectorMediaType;
|
availableMediaTypes: ReceiverSelectorMediaType;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
@@ -58,7 +58,7 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
receivers: [],
|
receiverDevices: [],
|
||||||
mediaType: ReceiverSelectorMediaType.App,
|
mediaType: ReceiverSelectorMediaType.App,
|
||||||
availableMediaTypes: ReceiverSelectorMediaType.App,
|
availableMediaTypes: ReceiverSelectorMediaType.App,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
@@ -111,10 +111,13 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "popup:update": {
|
case "popup:update": {
|
||||||
const { receivers, availableMediaTypes, defaultMediaType } =
|
const {
|
||||||
message.data;
|
receiverDevices: receivers,
|
||||||
|
availableMediaTypes,
|
||||||
|
defaultMediaType
|
||||||
|
} = message.data;
|
||||||
|
|
||||||
this.setState({ receivers });
|
this.setState({ receiverDevices: receivers });
|
||||||
|
|
||||||
if (
|
if (
|
||||||
availableMediaTypes !== undefined &&
|
availableMediaTypes !== undefined &&
|
||||||
@@ -332,10 +335,11 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul className="receivers">
|
<ul className="receivers">
|
||||||
{this.state.receivers && this.state.receivers.length ? (
|
{this.state.receiverDevices &&
|
||||||
this.state.receivers.map((receiver, i) => (
|
this.state.receiverDevices.length ? (
|
||||||
|
this.state.receiverDevices.map((receiver, i) => (
|
||||||
<ReceiverEntry
|
<ReceiverEntry
|
||||||
receiver={receiver}
|
receiverDevice={receiver}
|
||||||
onCast={this.onCast}
|
onCast={this.onCast}
|
||||||
onStop={this.onStop}
|
onStop={this.onStop}
|
||||||
isLoading={this.state.isLoading}
|
isLoading={this.state.isLoading}
|
||||||
@@ -368,7 +372,7 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onCast(receiver: ReceiverDevice) {
|
private onCast(receiverDevice: ReceiverDevice) {
|
||||||
this.setState({
|
this.setState({
|
||||||
isLoading: true
|
isLoading: true
|
||||||
});
|
});
|
||||||
@@ -376,20 +380,20 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
|||||||
this.port?.postMessage({
|
this.port?.postMessage({
|
||||||
subject: "receiverSelector:selected",
|
subject: "receiverSelector:selected",
|
||||||
data: {
|
data: {
|
||||||
|
receiverDevice,
|
||||||
actionType: ReceiverSelectionActionType.Cast,
|
actionType: ReceiverSelectionActionType.Cast,
|
||||||
receiver,
|
|
||||||
mediaType: this.state.mediaType,
|
mediaType: this.state.mediaType,
|
||||||
filePath: this.state.filePath
|
filePath: this.state.filePath
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private onStop(receiver: ReceiverDevice) {
|
private onStop(receiverDevice: ReceiverDevice) {
|
||||||
this.port?.postMessage({
|
this.port?.postMessage({
|
||||||
subject: "receiverSelector:stop",
|
subject: "receiverSelector:stop",
|
||||||
data: {
|
data: {
|
||||||
actionType: ReceiverSelectionActionType.Stop,
|
receiverDevice,
|
||||||
receiver
|
actionType: ReceiverSelectionActionType.Stop
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -427,11 +431,11 @@ class PopupApp extends Component<PopupAppProps, PopupAppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ReceiverEntryProps {
|
interface ReceiverEntryProps {
|
||||||
receiver: ReceiverDevice;
|
receiverDevice: ReceiverDevice;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
canCast: boolean;
|
canCast: boolean;
|
||||||
onCast(receiver: ReceiverDevice): void;
|
onCast(receiverDevice: ReceiverDevice): void;
|
||||||
onStop(receiver: ReceiverDevice): void;
|
onStop(receiverDevice: ReceiverDevice): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ReceiverEntryState {
|
interface ReceiverEntryState {
|
||||||
@@ -472,18 +476,18 @@ class ReceiverEntry extends Component<ReceiverEntryProps, ReceiverEntryState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { status } = this.props.receiver;
|
const { status } = this.props.receiverDevice;
|
||||||
const application = status?.applications?.[0];
|
const application = status?.applications?.[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className="receiver">
|
<li className="receiver">
|
||||||
<div className="receiver__name">
|
<div className="receiver__name">
|
||||||
{this.props.receiver.friendlyName}
|
{this.props.receiverDevice.friendlyName}
|
||||||
</div>
|
</div>
|
||||||
<div className="receiver__address">
|
<div className="receiver__address">
|
||||||
{application && !application.isIdleScreen
|
{application && !application.isIdleScreen
|
||||||
? application.statusText
|
? application.statusText
|
||||||
: `${this.props.receiver.host}:${this.props.receiver.port}`}
|
: `${this.props.receiverDevice.host}:${this.props.receiverDevice.port}`}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className="button receiver__connect"
|
className="button receiver__connect"
|
||||||
@@ -508,7 +512,7 @@ class ReceiverEntry extends Component<ReceiverEntryProps, ReceiverEntryState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private handleCast() {
|
private handleCast() {
|
||||||
const { status } = this.props.receiver;
|
const { status } = this.props.receiverDevice;
|
||||||
if (!status) {
|
if (!status) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -516,9 +520,9 @@ class ReceiverEntry extends Component<ReceiverEntryProps, ReceiverEntryState> {
|
|||||||
const application = status.applications?.[0];
|
const application = status.applications?.[0];
|
||||||
|
|
||||||
if (this.state.showAlternateAction) {
|
if (this.state.showAlternateAction) {
|
||||||
this.props.onStop(this.props.receiver);
|
this.props.onStop(this.props.receiverDevice);
|
||||||
} else {
|
} else {
|
||||||
this.props.onCast(this.props.receiver);
|
this.props.onCast(this.props.receiverDevice);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
isLoading: true
|
isLoading: true
|
||||||
|
|||||||
Reference in New Issue
Block a user