Improve handling of receiver actions

This commit is contained in:
hensm
2022-08-26 06:09:52 +01:00
parent ee492802e0
commit 90b00c0ce4
10 changed files with 275 additions and 356 deletions

View File

@@ -8,7 +8,6 @@ import { getMediaTypesForPageUrl } from "../lib/utils";
import {
ReceiverDevice,
ReceiverSelectionActionType,
ReceiverSelectorMediaType,
ReceiverSelectorPageInfo
} from "../types";
@@ -22,16 +21,10 @@ import type { SenderMediaMessage, SenderMessage } from "../cast/sdk/types";
const POPUP_URL = browser.runtime.getURL("ui/popup/index.html");
export interface ReceiverSelectionCast {
actionType: ReceiverSelectionActionType.Cast;
export interface ReceiverSelection {
receiverDevice: ReceiverDevice;
mediaType: ReceiverSelectorMediaType;
}
export interface ReceiverSelectionStop {
actionType: ReceiverSelectionActionType.Stop;
receiverDevice: ReceiverDevice;
}
export type ReceiverSelection = ReceiverSelectionCast | ReceiverSelectionStop;
export interface ReceiverSelectorReceiverMessage {
deviceId: string;
@@ -43,24 +36,16 @@ export interface ReceiverSelectorMediaMessage {
}
interface ReceiverSelectorEvents {
selected: ReceiverSelectionCast;
error: string;
selected: ReceiverSelection;
cancelled: void;
stop: ReceiverSelectionStop;
stop: { deviceId: string };
error: string;
close: void;
receiverMessage: ReceiverSelectorReceiverMessage;
mediaMessage: ReceiverSelectorMediaMessage;
}
let baseConfig: BaseConfig;
baseConfigStorage
.get("baseConfig")
.then(value => {
baseConfig = value.baseConfig;
})
.catch(() => {
logger.error("Failed to get Chromecast base config!");
});
/**
* Manages the receiver selector popup window and communication with the
@@ -248,39 +233,27 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
/** Handles messages from the popup extension page. */
private onPopupMessage(message: Message) {
switch (message.subject) {
case "receiverSelector:selected": {
case "receiverSelector:selected":
this.wasReceiverSelected = true;
this.dispatchEvent(
new CustomEvent("selected", {
detail: message.data
})
new CustomEvent("selected", { detail: message.data })
);
break;
}
case "receiverSelector:stop": {
case "receiverSelector:stop":
this.dispatchEvent(
new CustomEvent("stop", {
detail: message.data
})
new CustomEvent("stop", { detail: message.data })
);
break;
}
case "receiverSelector:receiverMessage":
this.dispatchEvent(
new CustomEvent("receiverMessage", {
detail: message.data
})
new CustomEvent("receiverMessage", { detail: message.data })
);
break;
case "receiverSelector:mediaMessage":
this.dispatchEvent(
new CustomEvent("mediaMessage", {
detail: message.data
})
new CustomEvent("mediaMessage", { detail: message.data })
);
break;
}
@@ -307,13 +280,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
this.dispatchEvent(new CustomEvent("close"));
// Cleanup
delete this.windowId;
delete this.messagePort;
delete this.receiverDevices;
delete this.defaultMediaType;
delete this.availableMediaTypes;
this.wasReceiverSelected = false;
}
/**
@@ -334,7 +301,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
}
}
static shared = new ReceiverSelector();
static sharedInstance = new ReceiverSelector();
/**
* Opens a receiver selector with the specified default/available media
@@ -346,7 +313,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
* - Resolves to null if the selection is cancelled.
* - Rejects if the selection fails.
*/
static getSelection(
static async getSelection(
contextTabId: number,
contextFrameId = 0,
selectionOpts?: {
@@ -354,213 +321,110 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
withMediaSender?: boolean;
}
): Promise<ReceiverSelection | null> {
let castInstance = castManager.getInstance(
contextTabId,
contextFrameId
);
/**
* If the current context is running the mirroring app, pretend
* it doesn't exist because it shouldn't be launched like this.
*/
if (castInstance?.appId === (await options.get("mirroringAppId"))) {
castInstance = undefined;
}
let defaultMediaType = ReceiverSelectorMediaType.Tab;
let availableMediaTypes = ReceiverSelectorMediaType.None;
let pageUrl: string | undefined;
try {
pageUrl = (
await browser.webNavigation.getFrame({
tabId: contextTabId,
frameId: contextFrameId
})
).url;
availableMediaTypes = getMediaTypesForPageUrl(pageUrl);
} catch {
logger.error(
"Failed to locate frame, falling back to default available media types."
);
}
// Enable app media type if sender application is present
if (castInstance || selectionOpts?.withMediaSender) {
defaultMediaType = ReceiverSelectorMediaType.App;
availableMediaTypes |= ReceiverSelectorMediaType.App;
}
const opts = await options.getAll();
// Disable mirroring media types if mirroring is not enabled
if (!opts.mirroringEnabled) {
availableMediaTypes &= ~(
ReceiverSelectorMediaType.Tab | ReceiverSelectorMediaType.Screen
);
}
// Remove file media type if local media is not enabled
if (!opts.mediaEnabled || !opts.localMediaEnabled) {
availableMediaTypes &= ~ReceiverSelectorMediaType.File;
}
// Ensure status manager is initialized
await deviceManager.init();
let isRequestAppAudioCompatible: Optional<boolean>;
if (castInstance?.appId) {
if (!baseConfig) {
try {
baseConfig = (await baseConfigStorage.get("baseConfig"))
.baseConfig;
} catch (err) {
throw logger.error("Failed to get Chromecast base config!");
}
}
isRequestAppAudioCompatible = getAppTag(
baseConfig,
castInstance.appId
)?.supports_audio_only;
}
return new Promise(async (resolve, reject) => {
let castInstance = castManager.getInstance(
contextTabId,
contextFrameId
);
/**
* If the current context is running the mirroring app, pretend
* it doesn't exist because it shouldn't be launched like this.
*/
if (castInstance?.appId === (await options.get("mirroringAppId"))) {
castInstance = undefined;
}
let defaultMediaType = ReceiverSelectorMediaType.Tab;
let availableMediaTypes = ReceiverSelectorMediaType.None;
let pageUrl: string | undefined;
try {
pageUrl = (
await browser.webNavigation.getFrame({
tabId: contextTabId,
frameId: contextFrameId
})
).url;
availableMediaTypes = getMediaTypesForPageUrl(pageUrl);
} catch {
logger.error(
"Failed to locate frame, falling back to default available media types."
);
}
// Enable app media type if sender application is present
if (castInstance || selectionOpts?.withMediaSender) {
defaultMediaType = ReceiverSelectorMediaType.App;
availableMediaTypes |= ReceiverSelectorMediaType.App;
}
const opts = await options.getAll();
// Disable mirroring media types if mirroring is not enabled
if (!opts.mirroringEnabled) {
availableMediaTypes &= ~(
ReceiverSelectorMediaType.Tab |
ReceiverSelectorMediaType.Screen
);
}
// Remove file media type if local media is not enabled
if (!opts.mediaEnabled || !opts.localMediaEnabled) {
availableMediaTypes &= ~ReceiverSelectorMediaType.File;
}
// Close an existing open selector
if (ReceiverSelector.shared && ReceiverSelector.shared.isOpen) {
ReceiverSelector.shared.close();
if (ReceiverSelector.sharedInstance.isOpen) {
await ReceiverSelector.sharedInstance.close();
}
// Get a new selector for each selection
ReceiverSelector.shared = new ReceiverSelector();
const selector = createSelector();
ReceiverSelector.sharedInstance = selector;
function onReceiverChange() {
ReceiverSelector.shared.update(deviceManager.getDevices());
}
// Handle selected return value
const onSelected = (ev: CustomEvent<ReceiverSelection>) =>
resolve(ev.detail);
selector.addEventListener("selected", onSelected);
deviceManager.addEventListener(
"receiverDeviceUp",
onReceiverChange
);
deviceManager.addEventListener(
"receiverDeviceDown",
onReceiverChange
);
deviceManager.addEventListener(
"receiverDeviceUpdated",
onReceiverChange
);
deviceManager.addEventListener(
"receiverDeviceMediaUpdated",
onReceiverChange
// Handle cancelled return value
const onCancelled = () => resolve(null);
selector.addEventListener("cancelled", onCancelled);
const onError = (ev: CustomEvent<string>) => reject(ev.detail);
selector.addEventListener("error", onError);
// Cleanup listeners
selector.addEventListener(
"close",
() => {
selector.removeEventListener("selected", onSelected);
selector.removeEventListener("cancelled", onCancelled);
selector.removeEventListener("error", onError);
},
{ once: true }
);
function onSelectorSelected(
ev: CustomEvent<ReceiverSelectionCast>
) {
logger.info("Selected receiver", ev.detail);
resolve({
actionType: ReceiverSelectionActionType.Cast,
receiverDevice: ev.detail.receiverDevice,
mediaType: ev.detail.mediaType
});
}
function onSelectorStop(ev: CustomEvent<ReceiverSelectionStop>) {
logger.info("Stopping receiver app...", ev.detail);
deviceManager.stopReceiverApp(ev.detail.receiverDevice.id);
resolve({
actionType: ReceiverSelectionActionType.Stop,
receiverDevice: ev.detail.receiverDevice
});
}
function onSelectorCancelled() {
logger.info("Cancelled receiver selection");
resolve(null);
}
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
);
}
ReceiverSelector.shared.addEventListener(
"selected",
onSelectorSelected
);
ReceiverSelector.shared.addEventListener("stop", onSelectorStop);
ReceiverSelector.shared.addEventListener(
"cancelled",
onSelectorCancelled
);
ReceiverSelector.shared.addEventListener("error", onSelectorError);
ReceiverSelector.shared.addEventListener(
"receiverMessage",
onReceiverMessage
);
ReceiverSelector.shared.addEventListener(
"mediaMessage",
onMediaMessage
);
ReceiverSelector.shared.addEventListener("close", removeListeners);
function removeListeners() {
ReceiverSelector.shared.removeEventListener(
"selected",
onSelectorSelected
);
ReceiverSelector.shared.removeEventListener(
"stop",
onSelectorStop
);
ReceiverSelector.shared.removeEventListener(
"cancelled",
onSelectorCancelled
);
ReceiverSelector.shared.removeEventListener(
"error",
onSelectorError
);
ReceiverSelector.shared.removeEventListener(
"receiverMessage",
onReceiverMessage
);
ReceiverSelector.shared.removeEventListener(
"mediaMessage",
onMediaMessage
);
ReceiverSelector.shared.removeEventListener(
"close",
removeListeners
);
deviceManager.removeEventListener(
"receiverDeviceUp",
onReceiverChange
);
deviceManager.removeEventListener(
"receiverDeviceDown",
onReceiverChange
);
deviceManager.removeEventListener(
"receiverDeviceUpdated",
onReceiverChange
);
deviceManager.removeEventListener(
"receiverDeviceMediaUpdated",
onReceiverChange
);
}
// Ensure status manager is initialized
await deviceManager.init();
let isRequestAppAudioCompatible: Optional<boolean>;
if (castInstance?.appId) {
const appTag = getAppTag(baseConfig, castInstance.appId);
isRequestAppAudioCompatible = appTag?.supports_audio_only;
}
ReceiverSelector.shared.open({
selector.open({
receiverDevices: deviceManager.getDevices(),
defaultMediaType,
availableMediaTypes,
@@ -579,3 +443,74 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
});
}
}
/**
* Creates new ReceiverSelector object and adds listeners for
* updates/messages.
*/
function createSelector() {
// Get a new selector for each selection
const selector = new ReceiverSelector();
ReceiverSelector.sharedInstance = selector;
/**
* Sends message to cast instance to trigger stopped receiver action
* (if applicable).
*/
const onStop = (ev: CustomEvent<{ deviceId: string }>) => {
const castInstance = castManager.getInstanceByDeviceId(
ev.detail.deviceId
);
if (!castInstance) return;
castInstance.contentPort.postMessage({
subject: "cast:receiverStoppedAction",
data: { deviceId: ev.detail.deviceId }
});
};
selector.addEventListener("stop", onStop);
// Forward receiver messages
const onReceiverMessage = (
ev: CustomEvent<ReceiverSelectorReceiverMessage>
) =>
deviceManager.sendReceiverMessage(
ev.detail.deviceId,
ev.detail.message
);
selector.addEventListener("receiverMessage", onReceiverMessage);
// Forward media messages
const onMediaMessage = (ev: CustomEvent<ReceiverSelectorMediaMessage>) =>
deviceManager.sendMediaMessage(ev.detail.deviceId, ev.detail.message);
selector.addEventListener("mediaMessage", onMediaMessage);
// Update selector data whenever devices change/update
const onDeviceChange = () => selector.update(deviceManager.getDevices());
deviceManager.addEventListener("deviceUp", onDeviceChange);
deviceManager.addEventListener("deviceDown", onDeviceChange);
deviceManager.addEventListener("deviceUpdated", onDeviceChange);
deviceManager.addEventListener("deviceMediaUpdated", onDeviceChange);
// Cleanup listeners
selector.addEventListener(
"close",
() => {
deviceManager.removeEventListener("deviceUp", onDeviceChange);
deviceManager.removeEventListener("deviceDown", onDeviceChange);
deviceManager.removeEventListener("deviceUpdated", onDeviceChange);
deviceManager.removeEventListener(
"deviceMediaUpdated",
onDeviceChange
);
selector.removeEventListener("stop", onStop);
selector.removeEventListener("receiverMessage", onReceiverMessage);
selector.removeEventListener("mediaMessage", onMediaMessage);
},
{ once: true }
);
return selector;
}

View File

@@ -6,10 +6,7 @@ import messaging, { Message, Port } from "../messaging";
import options from "../lib/options";
import { stringify } from "../lib/utils";
import {
ReceiverSelectionActionType,
ReceiverSelectorMediaType
} from "../types";
import { ReceiverSelectorMediaType } from "../types";
import deviceManager from "./deviceManager";
import ReceiverSelector, { ReceiverSelection } from "./ReceiverSelector";
@@ -22,6 +19,12 @@ export interface CastInstance {
contentTabId?: number;
contentFrameId?: number;
appId?: string;
session?: CastSession;
}
interface CastSession {
sessionId: string;
deviceId: string;
}
/** Keeps track of cast API instances and provides bridge messaging. */
@@ -37,7 +40,7 @@ export default new (class {
});
// Forward receiver eventes to cast instances
deviceManager.addEventListener("receiverDeviceUp", ev => {
deviceManager.addEventListener("deviceUp", ev => {
for (const instance of this.activeInstances) {
instance.contentPort.postMessage({
subject: "cast:receiverDeviceUp",
@@ -45,7 +48,7 @@ export default new (class {
});
}
});
deviceManager.addEventListener("receiverDeviceDown", ev => {
deviceManager.addEventListener("deviceDown", ev => {
for (const instance of this.activeInstances) {
instance.contentPort.postMessage({
subject: "cast:receiverDeviceDown",
@@ -71,6 +74,12 @@ export default new (class {
}
}
public getInstanceByDeviceId(deviceId: string) {
for (const instance of this.activeInstances) {
if (instance.session?.deviceId === deviceId) return instance;
}
}
/**
* Creates a cast instance with a given port and connects messaging
* correctly depending on the type of port.
@@ -106,7 +115,7 @@ export default new (class {
// bridge -> content
instance.bridgePort.onMessage.addListener(message => {
contentPort.postMessage(message);
this.handleBridgeMessage(instance, message);
});
// content -> (any)
@@ -160,7 +169,7 @@ export default new (class {
};
// bridge -> content
const onBridgePortMessage = (message: Message) => {
contentPort.postMessage(message);
this.handleBridgeMessage(instance, message);
};
const onDisconnect = () => {
@@ -182,6 +191,23 @@ export default new (class {
return instance;
}
private async handleBridgeMessage(
instance: CastInstance,
message: Message
) {
// Intercept messages to store relevant info
switch (message.subject) {
case "cast:sessionCreated":
instance.session = {
deviceId: message.data.receiverId,
sessionId: message.data.sessionId
};
break;
}
instance.contentPort.postMessage(message);
}
/**
* Handle content messages from the cast instance. These will either
* be handled here in the background script or forwarded to the
@@ -238,48 +264,29 @@ export default new (class {
break;
}
switch (selection.actionType) {
case ReceiverSelectionActionType.Cast: {
/**
* If the media type returned from the
* selector has been changed, we need to
* cancel the current sender and switch it
* out for the right one.
*/
if (
selection.mediaType !==
ReceiverSelectorMediaType.App
) {
instance.contentPort.postMessage({
subject: "cast:selectReceiver/cancelled"
});
/**
* If the media type returned from the selector has
* been changed, we need to cancel the current
* sender and switch it out for the right one.
*/
if (selection.mediaType !== ReceiverSelectorMediaType.App) {
instance.contentPort.postMessage({
subject: "cast:selectReceiver/cancelled"
});
this.loadSender({
tabId: instance.contentTabId,
frameId: instance.contentFrameId,
selection
});
this.loadSender({
tabId: instance.contentTabId,
frameId: instance.contentFrameId,
selection
});
break;
}
instance.contentPort.postMessage({
subject: "cast:selectReceiver/selected",
data: selection
});
break;
}
case ReceiverSelectionActionType.Stop: {
instance.contentPort.postMessage({
subject: "cast:selectReceiver/stopped",
data: selection
});
break;
}
break;
}
instance.contentPort.postMessage({
subject: "cast:selectReceiver/selected",
data: selection
});
} catch (err) {
// TODO: Report errors properly
instance.contentPort.postMessage({
@@ -295,7 +302,7 @@ export default new (class {
* same one that caused the session creation.
*/
case "main:closeReceiverSelector": {
const selector = ReceiverSelector.shared;
const selector = ReceiverSelector.sharedInstance;
const shouldClose = await options.get(
"receiverSelectorWaitForConnection"
);
@@ -323,10 +330,6 @@ export default new (class {
return;
}
if (opts.selection.actionType !== ReceiverSelectionActionType.Cast) {
return;
}
switch (opts.selection.mediaType) {
case ReceiverSelectorMediaType.App: {
const instance = this.getInstance(opts.tabId, opts.frameId);

View File

@@ -16,13 +16,13 @@ import type {
import { PlayerState } from "../cast/sdk/media/enums";
interface EventMap {
receiverDeviceUp: { deviceInfo: ReceiverDevice };
receiverDeviceDown: { deviceId: string };
receiverDeviceUpdated: {
deviceUp: { deviceInfo: ReceiverDevice };
deviceDown: { deviceId: string };
deviceUpdated: {
deviceId: string;
status: ReceiverStatus;
};
receiverDeviceMediaUpdated: {
deviceMediaUpdated: {
deviceId: string;
status: MediaStatus;
};
@@ -139,7 +139,7 @@ export default new (class extends TypedEventTarget<EventMap> {
this.receiverDevices.set(deviceId, deviceInfo);
this.dispatchEvent(
new CustomEvent("receiverDeviceUp", {
new CustomEvent("deviceUp", {
detail: { deviceInfo }
})
);
@@ -154,7 +154,7 @@ export default new (class extends TypedEventTarget<EventMap> {
this.receiverDevices.delete(deviceId);
}
this.dispatchEvent(
new CustomEvent("receiverDeviceDown", {
new CustomEvent("deviceDown", {
detail: { deviceId: deviceId }
})
);
@@ -176,7 +176,7 @@ export default new (class extends TypedEventTarget<EventMap> {
device.status = status;
this.dispatchEvent(
new CustomEvent("receiverDeviceUpdated", {
new CustomEvent("deviceUpdated", {
detail: {
deviceId,
status: device.status
@@ -202,7 +202,7 @@ export default new (class extends TypedEventTarget<EventMap> {
}
this.dispatchEvent(
new CustomEvent("receiverDeviceMediaUpdated", {
new CustomEvent("deviceMediaUpdated", {
detail: {
deviceId,
status: device.mediaStatus
@@ -218,7 +218,7 @@ export default new (class extends TypedEventTarget<EventMap> {
private onBridgeDisconnect = () => {
// Notify listeners of device availablility
for (const [, receiverDevice] of this.receiverDevices) {
const event = new CustomEvent("receiverDeviceDown", {
const event = new CustomEvent("deviceDown", {
detail: { deviceId: receiverDevice.id }
});

View File

@@ -4,10 +4,7 @@ import logger from "../lib/logger";
import options from "../lib/options";
import { stringify } from "../lib/utils";
import {
ReceiverSelectionActionType,
ReceiverSelectorMediaType
} from "../types";
import { ReceiverSelectorMediaType } from "../types";
import ReceiverSelector, { ReceiverSelection } from "./ReceiverSelector";
import castManager from "./castManager";
@@ -167,12 +164,7 @@ async function onMenuClicked(
return;
}
// Invalid selection result
if (
!selection ||
selection.actionType !== ReceiverSelectionActionType.Cast
) {
return;
}
if (!selection) return;
if (castMenuClicked) {
castManager.loadSender({

View File

@@ -316,26 +316,6 @@ export default class {
break;
}
case "cast:selectReceiver/stopped": {
const { receiverDevice } = message.data;
logger.info("Stopped receiver");
if (this.#sessionRequest) {
this.#sessionRequest = undefined;
for (const listener of this.#receiverActionListeners) {
listener(
// TODO: Use existing receiver object?
createReceiver(receiverDevice),
ReceiverAction.STOP
);
}
}
break;
}
// Popup closed before session established
case "cast:selectReceiver/cancelled": {
if (this.#sessionRequest) {
@@ -349,6 +329,17 @@ export default class {
break;
}
case "cast:receiverStoppedAction": {
const device = this.#receiverDevices.get(message.data.deviceId);
if (!device) break;
for (const actionListener of this.#receiverActionListeners) {
actionListener(createReceiver(device), ReceiverAction.STOP);
}
break;
}
// Session request initiated via receiver selector
case "cast:launchApp": {
if (this.#sessionRequest) {

View File

@@ -92,7 +92,7 @@ interface ReqBase {
// NS: urn:x-cast:com.google.cast.receiver
export type SenderMessage =
| (ReqBase & { type: "LAUNCH"; appId: string })
| (ReqBase & { type: "STOP"; sessionId: string })
| (ReqBase & { type: "STOP"; sessionId?: string })
| (ReqBase & { type: "GET_STATUS" })
| (ReqBase & { type: "GET_APP_AVAILABILITY"; appId: string[] })
| (ReqBase & { type: "SET_VOLUME"; volume: Partial<Volume> });

View File

@@ -11,19 +11,21 @@ export class TypedEventTarget<T extends TypedEvents> extends EventTarget {
// @ts-ignore
public addEventListener<K extends keyof T>(
type: K,
listener: (ev: CustomEvent<T[K]>) => void
listener: (ev: CustomEvent<T[K]>) => void,
options?: boolean | AddEventListenerOptions
): void {
// @ts-ignore
super.addEventListener(type as string, listener);
super.addEventListener(type as string, listener, options);
}
// @ts-ignore
public removeEventListener<K extends keyof T>(
type: K,
listener: (ev: CustomEvent<T[K]>) => void
listener: (ev: CustomEvent<T[K]>) => void,
options?: boolean | EventListenerOptions
): void {
// @ts-ignore
super.removeEventListener(type as string, listener);
super.removeEventListener(type, listener, options);
}
public dispatchEvent<K extends keyof T>(ev: CustomEvent<T[K]>): boolean {

View File

@@ -5,8 +5,6 @@ import type { BridgeInfo } from "./lib/bridge";
import type {
ReceiverSelection,
ReceiverSelectionCast,
ReceiverSelectionStop,
ReceiverSelectorMediaMessage,
ReceiverSelectorReceiverMessage
} from "./background/receiverSelector";
@@ -57,16 +55,16 @@ type ExtMessageDefinitions = {
"popup:close": undefined;
"receiverSelector:selected": ReceiverSelection;
"receiverSelector:stop": ReceiverSelection;
"receiverSelector:stop": { deviceId: string };
"receiverSelector:receiverMessage": ReceiverSelectorReceiverMessage;
"receiverSelector:mediaMessage": ReceiverSelectorMediaMessage;
"main:selectReceiver": {
sessionRequest: SessionRequest;
};
"cast:selectReceiver/selected": ReceiverSelectionCast;
"cast:selectReceiver/stopped": ReceiverSelectionStop;
"cast:selectReceiver/selected": ReceiverSelection;
"cast:selectReceiver/cancelled": undefined;
"cast:receiverStoppedAction": { deviceId: string };
"main:closeReceiverSelector": undefined;
@@ -74,7 +72,7 @@ type ExtMessageDefinitions = {
"cast:initialized": BridgeInfo;
"cast:receiverDeviceUp": { receiverDevice: ReceiverDevice };
"cast:receiverDeviceDown": { receiverDeviceId: ReceiverDevice["id"] };
"cast:receiverDeviceDown": { receiverDeviceId: string };
"cast:launchApp": { receiverDevice: ReceiverDevice };
};

View File

@@ -30,11 +30,6 @@ export enum ReceiverSelectorMediaType {
Screen = 4,
File = 8
}
export enum ReceiverSelectionActionType {
Cast = 1,
Stop = 2
}
/** Info about sender page context. */
export interface ReceiverSelectorPageInfo {
url: string;

View File

@@ -8,7 +8,6 @@
import {
ReceiverDevice,
ReceiverDeviceCapabilities,
ReceiverSelectionActionType,
ReceiverSelectorMediaType,
ReceiverSelectorPageInfo
} from "../../types";
@@ -347,7 +346,6 @@
subject: "receiverSelector:selected",
data: {
receiverDevice,
actionType: ReceiverSelectionActionType.Cast,
mediaType
}
});
@@ -355,12 +353,17 @@
function onReceiverStop(receiverDevice: ReceiverDevice) {
port?.postMessage({
subject: "receiverSelector:stop",
subject: "receiverSelector:receiverMessage",
data: {
receiverDevice,
actionType: ReceiverSelectionActionType.Stop
deviceId: receiverDevice.id,
message: { requestId: 0, type: "STOP" }
}
});
port?.postMessage({
subject: "receiverSelector:stop",
data: { deviceId: receiverDevice.id }
});
}
</script>