Add media controls (#229)

This commit is contained in:
Matt Hensman
2022-08-24 02:17:35 +01:00
committed by GitHub
parent cbc039a355
commit ac46802431
37 changed files with 1694 additions and 432 deletions

View File

@@ -5,6 +5,7 @@ import messaging, { Port, Message } from "../messaging";
import options from "../lib/options";
import { TypedEventTarget } from "../lib/TypedEventTarget";
import { SenderMediaMessage, SenderMessage } from "../cast/sdk/types";
import {
ReceiverDevice,
ReceiverSelectionActionType,
@@ -25,13 +26,25 @@ export interface ReceiverSelectionStop {
}
export type ReceiverSelection = ReceiverSelectionCast | ReceiverSelectionStop;
export interface ReceiverSelectorReceiverMessage {
deviceId: string;
message: SenderMessage;
}
export interface ReceiverSelectorMediaMessage {
deviceId: string;
message: SenderMediaMessage;
}
interface ReceiverSelectorEvents {
selected: ReceiverSelectionCast;
error: string;
cancelled: void;
stop: ReceiverSelectionStop;
close: void;
receiverMessage: ReceiverSelectorReceiverMessage;
mediaMessage: ReceiverSelectorMediaMessage;
}
/**
* Manages the receiver selector popup window and communication with the
* extension page hosted within.
@@ -100,7 +113,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
this.availableMediaTypes = opts.availableMediaTypes;
const popupSizePosition = {
width: 350,
width: 400,
height: 200,
left: 100,
top: 100
@@ -238,6 +251,21 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
break;
}
case "receiverSelector:receiverMessage":
this.dispatchEvent(
new CustomEvent("receiverMessage", {
detail: message.data
})
);
break;
case "receiverSelector:mediaMessage":
this.dispatchEvent(
new CustomEvent("mediaMessage", {
detail: message.data
})
);
break;
}
}

View File

@@ -6,7 +6,13 @@ import { TypedEventTarget } from "../lib/TypedEventTarget";
import { Message, Port } from "../messaging";
import { ReceiverDevice } from "../types";
import { ReceiverStatus } from "../cast/sdk/types";
import {
MediaStatus,
ReceiverStatus,
SenderMediaMessage,
SenderMessage
} from "../cast/sdk/types";
import { PlayerState } from "../cast/sdk/media/enums";
interface EventMap {
receiverDeviceUp: { deviceInfo: ReceiverDevice };
@@ -15,6 +21,10 @@ interface EventMap {
deviceId: string;
status: ReceiverStatus;
};
receiverDeviceMediaUpdated: {
deviceId: string;
status: MediaStatus;
};
}
export default new (class extends TypedEventTarget<EventMap> {
@@ -79,6 +89,48 @@ export default new (class extends TypedEventTarget<EventMap> {
}
}
sendReceiverMessage(deviceId: string, message: SenderMessage) {
if (!this.bridgePort) {
logger.error(
"Failed to send receiver message (no bridge connection)"
);
return;
}
const device = this.receiverDevices.get(deviceId);
if (!device) {
logger.error(
"Failed to send receiver message (could not find device)"
);
return;
}
this.bridgePort?.postMessage({
subject: "bridge:sendReceiverMessage",
data: { deviceId, message }
});
}
sendMediaMessage(deviceId: string, message: SenderMediaMessage) {
if (!this.bridgePort) {
logger.error("Failed to send media message (no bridge connection)");
return;
}
const device = this.receiverDevices.get(deviceId);
if (!device) {
logger.error(
"Failed to send media message (could not find device)"
);
return;
}
this.bridgePort?.postMessage({
subject: "bridge:sendMediaMessage",
data: { deviceId, message }
});
}
private onBridgeMessage = (message: Message) => {
switch (message.subject) {
case "main:receiverDeviceUp": {
@@ -111,29 +163,22 @@ export default new (class extends TypedEventTarget<EventMap> {
case "main:receiverDeviceStatusUpdated": {
const { deviceId, status } = message.data;
const receiverDevice = this.receiverDevices.get(deviceId);
if (!receiverDevice) {
break;
const device = this.receiverDevices.get(deviceId);
if (!device) break;
// Clear media status when app status changes
const application = status.applications?.[0];
if (!application || application.isIdleScreen) {
delete device.mediaStatus;
}
if (receiverDevice.status) {
receiverDevice.status.isActiveInput = status.isActiveInput;
receiverDevice.status.isStandBy = status.isStandBy;
receiverDevice.status.volume = status.volume;
if (status.applications) {
receiverDevice.status.applications =
status.applications;
}
} else {
receiverDevice.status = status;
}
device.status = status;
this.dispatchEvent(
new CustomEvent("receiverDeviceUpdated", {
detail: {
deviceId,
status: receiverDevice.status
status: device.status
}
})
);
@@ -142,6 +187,28 @@ export default new (class extends TypedEventTarget<EventMap> {
}
case "main:receiverDeviceMediaStatusUpdated": {
const { deviceId, status } = message.data;
const device = this.receiverDevices.get(deviceId);
if (!device) break;
if (device.mediaStatus) {
device.mediaStatus = { ...device.mediaStatus, ...status };
if (status.playerState === PlayerState.IDLE) {
delete device.mediaStatus.media;
}
} else {
device.mediaStatus = status;
}
this.dispatchEvent(
new CustomEvent("receiverDeviceMediaUpdated", {
detail: {
deviceId,
status: device.mediaStatus
}
})
);
break;
}
}

View File

@@ -12,7 +12,9 @@ import deviceManager from "./deviceManager";
import ReceiverSelector, {
ReceiverSelection,
ReceiverSelectionCast,
ReceiverSelectionStop
ReceiverSelectionStop,
ReceiverSelectorMediaMessage,
ReceiverSelectorReceiverMessage
} from "./ReceiverSelector";
import {
@@ -122,6 +124,10 @@ async function getSelection(
"receiverDeviceUpdated",
onReceiverChange
);
deviceManager.addEventListener(
"receiverDeviceMediaUpdated",
onReceiverChange
);
function onSelectorSelected(ev: CustomEvent<ReceiverSelectionCast>) {
logger.info("Selected receiver", ev.detail);
@@ -150,11 +156,27 @@ async function getSelection(
function onSelectorError(ev: CustomEvent<string>) {
reject(ev.detail);
}
function onReceiverMessage(
ev: CustomEvent<ReceiverSelectorReceiverMessage>
) {
deviceManager.sendReceiverMessage(
ev.detail.deviceId,
ev.detail.message
);
}
function onMediaMessage(ev: CustomEvent<ReceiverSelectorMediaMessage>) {
deviceManager.sendMediaMessage(
ev.detail.deviceId,
ev.detail.message
);
}
sharedSelector.addEventListener("selected", onSelectorSelected);
sharedSelector.addEventListener("stop", onSelectorStop);
sharedSelector.addEventListener("cancelled", onSelectorCancelled);
sharedSelector.addEventListener("error", onSelectorError);
sharedSelector.addEventListener("receiverMessage", onReceiverMessage);
sharedSelector.addEventListener("mediaMessage", onMediaMessage);
sharedSelector.addEventListener("close", removeListeners);
function removeListeners() {
@@ -165,6 +187,11 @@ async function getSelection(
onSelectorCancelled
);
sharedSelector.removeEventListener("error", onSelectorError);
sharedSelector.removeEventListener(
"receiverMessage",
onReceiverMessage
);
sharedSelector.removeEventListener("mediaMessage", onMediaMessage);
sharedSelector.removeEventListener("close", removeListeners);
deviceManager.removeEventListener(
@@ -179,6 +206,10 @@ async function getSelection(
"receiverDeviceUpdated",
onReceiverChange
);
deviceManager.removeEventListener(
"receiverDeviceMediaUpdated",
onReceiverChange
);
}
// Ensure status manager is initialized