mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-12 18:39:58 +00:00
Pass Receiver objects instead of ReceiverDevice objects to cast API
This commit is contained in:
@@ -73,7 +73,7 @@ export default class Session extends CastClient {
|
|||||||
this.onSessionCreated?.(this.sessionId);
|
this.onSessionCreated?.(this.sessionId);
|
||||||
|
|
||||||
messaging.sendMessage({
|
messaging.sendMessage({
|
||||||
subject: "cast:sessionCreated",
|
subject: "main:castSessionCreated",
|
||||||
data: {
|
data: {
|
||||||
sessionId: this.sessionId,
|
sessionId: this.sessionId,
|
||||||
statusText: application.statusText,
|
statusText: application.statusText,
|
||||||
@@ -103,7 +103,7 @@ export default class Session extends CastClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
messaging.sendMessage({
|
messaging.sendMessage({
|
||||||
subject: "cast:sessionUpdated",
|
subject: "main:castSessionUpdated",
|
||||||
data: {
|
data: {
|
||||||
sessionId: this.sessionId,
|
sessionId: this.sessionId,
|
||||||
statusText: application.statusText,
|
statusText: application.statusText,
|
||||||
|
|||||||
@@ -110,8 +110,8 @@ type MessageDefinitions = {
|
|||||||
* updates. Updated details is a mutable subset of session details
|
* updates. Updated details is a mutable subset of session details
|
||||||
* otherwise fixed on creation.
|
* otherwise fixed on creation.
|
||||||
*/
|
*/
|
||||||
"cast:sessionCreated": CastSessionCreatedDetails;
|
"main:castSessionCreated": CastSessionCreatedDetails;
|
||||||
"cast:sessionUpdated": CastSessionUpdatedDetails;
|
"main:castSessionUpdated": CastSessionUpdatedDetails;
|
||||||
/**
|
/**
|
||||||
* Sent to cast API instances whenever a session is stopped.
|
* Sent to cast API instances whenever a session is stopped.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ import deviceManager from "./deviceManager";
|
|||||||
import castManager from "./castManager";
|
import castManager from "./castManager";
|
||||||
|
|
||||||
import { BaseConfig, baseConfigStorage, getAppTag } from "../cast/googleApi";
|
import { BaseConfig, baseConfigStorage, getAppTag } from "../cast/googleApi";
|
||||||
import type { SessionRequest } from "../cast/sdk/classes";
|
|
||||||
import type { SenderMediaMessage, SenderMessage } from "../cast/sdk/types";
|
import type { SenderMediaMessage, SenderMessage } from "../cast/sdk/types";
|
||||||
|
import type { SessionRequest } from "../cast/sdk/classes";
|
||||||
|
import { ReceiverAction } from "../cast/sdk/enums";
|
||||||
|
import { createReceiver } from "../cast/utils";
|
||||||
|
|
||||||
const POPUP_URL = browser.runtime.getURL("ui/popup/index.html");
|
const POPUP_URL = browser.runtime.getURL("ui/popup/index.html");
|
||||||
|
|
||||||
@@ -330,7 +332,10 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
|
|||||||
* If the current context is running the mirroring app, pretend
|
* If the current context is running the mirroring app, pretend
|
||||||
* it doesn't exist because it shouldn't be launched like this.
|
* it doesn't exist because it shouldn't be launched like this.
|
||||||
*/
|
*/
|
||||||
if (castInstance?.appId === (await options.get("mirroringAppId"))) {
|
if (
|
||||||
|
castInstance?.apiConfig?.sessionRequest.appId ===
|
||||||
|
(await options.get("mirroringAppId"))
|
||||||
|
) {
|
||||||
castInstance = undefined;
|
castInstance = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,7 +382,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
|
|||||||
await deviceManager.init();
|
await deviceManager.init();
|
||||||
|
|
||||||
let isRequestAppAudioCompatible: Optional<boolean>;
|
let isRequestAppAudioCompatible: Optional<boolean>;
|
||||||
if (castInstance?.appId) {
|
if (castInstance?.apiConfig?.sessionRequest.appId) {
|
||||||
if (!baseConfig) {
|
if (!baseConfig) {
|
||||||
try {
|
try {
|
||||||
baseConfig = (await baseConfigStorage.get("baseConfig"))
|
baseConfig = (await baseConfigStorage.get("baseConfig"))
|
||||||
@@ -389,7 +394,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
|
|||||||
|
|
||||||
isRequestAppAudioCompatible = getAppTag(
|
isRequestAppAudioCompatible = getAppTag(
|
||||||
baseConfig,
|
baseConfig,
|
||||||
castInstance.appId
|
castInstance.apiConfig?.sessionRequest.appId
|
||||||
)?.supports_audio_only;
|
)?.supports_audio_only;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +434,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
|
|||||||
receiverDevices: deviceManager.getDevices(),
|
receiverDevices: deviceManager.getDevices(),
|
||||||
defaultMediaType,
|
defaultMediaType,
|
||||||
availableMediaTypes,
|
availableMediaTypes,
|
||||||
appId: castInstance?.appId,
|
appId: castInstance?.apiConfig?.sessionRequest.appId,
|
||||||
// Create page info
|
// Create page info
|
||||||
pageInfo: pageUrl
|
pageInfo: pageUrl
|
||||||
? {
|
? {
|
||||||
@@ -464,9 +469,15 @@ function createSelector() {
|
|||||||
);
|
);
|
||||||
if (!castInstance) return;
|
if (!castInstance) return;
|
||||||
|
|
||||||
|
const device = deviceManager.getDeviceById(ev.detail.deviceId);
|
||||||
|
if (!device) return;
|
||||||
|
|
||||||
castInstance.contentPort.postMessage({
|
castInstance.contentPort.postMessage({
|
||||||
subject: "cast:receiverStoppedAction",
|
subject: "cast:sendReceiverAction",
|
||||||
data: { deviceId: ev.detail.deviceId }
|
data: {
|
||||||
|
receiver: createReceiver(device),
|
||||||
|
action: ReceiverAction.STOP
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
selector.addEventListener("stop", onStop);
|
selector.addEventListener("stop", onStop);
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import { stringify } from "../lib/utils";
|
|||||||
|
|
||||||
import { ReceiverSelectorMediaType } from "../types";
|
import { ReceiverSelectorMediaType } from "../types";
|
||||||
|
|
||||||
|
import type { ApiConfig } from "../cast/sdk/classes";
|
||||||
|
import { ReceiverAction } from "../cast/sdk/enums";
|
||||||
|
import { createReceiver } from "../cast/utils";
|
||||||
|
|
||||||
import deviceManager from "./deviceManager";
|
import deviceManager from "./deviceManager";
|
||||||
import ReceiverSelector, { ReceiverSelection } from "./ReceiverSelector";
|
import ReceiverSelector, { ReceiverSelection } from "./ReceiverSelector";
|
||||||
|
|
||||||
@@ -18,7 +22,10 @@ export interface CastInstance {
|
|||||||
contentPort: AnyPort;
|
contentPort: AnyPort;
|
||||||
contentTabId?: number;
|
contentTabId?: number;
|
||||||
contentFrameId?: number;
|
contentFrameId?: number;
|
||||||
appId?: string;
|
|
||||||
|
/** ApiConfig provided on initialization. */
|
||||||
|
apiConfig?: ApiConfig;
|
||||||
|
/** Established session details. */
|
||||||
session?: CastSession;
|
session?: CastSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,23 +46,23 @@ export default new (class {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Forward receiver eventes to cast instances
|
// Pass receiver availability updates to cast API.
|
||||||
deviceManager.addEventListener("deviceUp", ev => {
|
const updateReceiverAvailability = () => {
|
||||||
|
const isAvailable = deviceManager.getDevices().length > 0;
|
||||||
|
|
||||||
for (const instance of this.activeInstances) {
|
for (const instance of this.activeInstances) {
|
||||||
instance.contentPort.postMessage({
|
instance.contentPort.postMessage({
|
||||||
subject: "cast:receiverDeviceUp",
|
subject: "cast:updateReceiverAvailability",
|
||||||
data: { receiverDevice: ev.detail.deviceInfo }
|
data: { isAvailable }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
deviceManager.addEventListener("deviceDown", ev => {
|
|
||||||
for (const instance of this.activeInstances) {
|
deviceManager.addEventListener("deviceUp", updateReceiverAvailability);
|
||||||
instance.contentPort.postMessage({
|
deviceManager.addEventListener(
|
||||||
subject: "cast:receiverDeviceDown",
|
"deviceDown",
|
||||||
data: { receiverDeviceId: ev.detail.deviceId }
|
updateReceiverAvailability
|
||||||
});
|
);
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -197,7 +204,7 @@ export default new (class {
|
|||||||
) {
|
) {
|
||||||
// Intercept messages to store relevant info
|
// Intercept messages to store relevant info
|
||||||
switch (message.subject) {
|
switch (message.subject) {
|
||||||
case "cast:sessionCreated": {
|
case "main:castSessionCreated": {
|
||||||
// Close after session is created
|
// Close after session is created
|
||||||
const selector = ReceiverSelector.sharedInstance;
|
const selector = ReceiverSelector.sharedInstance;
|
||||||
if (
|
if (
|
||||||
@@ -211,12 +218,38 @@ export default new (class {
|
|||||||
selector.close();
|
selector.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { receiverId: deviceId } = message.data;
|
||||||
|
|
||||||
instance.session = {
|
instance.session = {
|
||||||
deviceId: message.data.receiverId,
|
deviceId,
|
||||||
sessionId: message.data.sessionId
|
sessionId: message.data.sessionId
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const device = deviceManager.getDeviceById(deviceId);
|
||||||
|
if (!device) {
|
||||||
|
logger.error(
|
||||||
|
"[on main:castSessionCreated]: Could not find device with ID:",
|
||||||
|
deviceId
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.contentPort.postMessage({
|
||||||
|
subject: "cast:sessionCreated",
|
||||||
|
data: {
|
||||||
|
...message.data,
|
||||||
|
receiver: createReceiver(device)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "main:castSessionUpdated":
|
||||||
|
instance.contentPort.postMessage({
|
||||||
|
subject: "cast:sessionUpdated",
|
||||||
|
data: message.data
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.contentPort.postMessage(message);
|
instance.contentPort.postMessage(message);
|
||||||
@@ -239,14 +272,14 @@ export default new (class {
|
|||||||
switch (message.subject) {
|
switch (message.subject) {
|
||||||
// Cast API has been initialized
|
// Cast API has been initialized
|
||||||
case "main:initializeCast": {
|
case "main:initializeCast": {
|
||||||
instance.appId = message.data.appId;
|
instance.apiConfig = message.data.apiConfig;
|
||||||
|
|
||||||
for (const receiverDevice of deviceManager.getDevices()) {
|
instance.contentPort.postMessage({
|
||||||
instance.contentPort.postMessage({
|
subject: "cast:updateReceiverAvailability",
|
||||||
subject: "cast:receiverDeviceUp",
|
data: {
|
||||||
data: { receiverDevice }
|
isAvailable: deviceManager.getDevices().length > 0
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -262,11 +295,13 @@ export default new (class {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { sessionRequest } = message.data;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const selection = await ReceiverSelector.getSelection(
|
const selection = await ReceiverSelector.getSelection(
|
||||||
instance.contentTabId,
|
instance.contentTabId,
|
||||||
instance.contentFrameId,
|
instance.contentFrameId,
|
||||||
{ sessionRequest: message.data.sessionRequest }
|
{ sessionRequest }
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle cancellation
|
// Handle cancellation
|
||||||
@@ -297,9 +332,12 @@ export default new (class {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.contentPort.postMessage({
|
instance.bridgePort.postMessage({
|
||||||
subject: "cast:selectReceiver/selected",
|
subject: "bridge:createCastSession",
|
||||||
data: selection
|
data: {
|
||||||
|
appId: sessionRequest.appId,
|
||||||
|
receiverDevice: selection.receiverDevice
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// TODO: Report errors properly
|
// TODO: Report errors properly
|
||||||
@@ -336,9 +374,24 @@ export default new (class {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!instance.apiConfig?.sessionRequest.appId) {
|
||||||
|
throw logger.error("Invalid session request");
|
||||||
|
}
|
||||||
|
|
||||||
instance.contentPort.postMessage({
|
instance.contentPort.postMessage({
|
||||||
subject: "cast:launchApp",
|
subject: "cast:sendReceiverAction",
|
||||||
data: { receiverDevice: opts.selection.receiverDevice }
|
data: {
|
||||||
|
receiver: createReceiver(opts.selection.receiverDevice),
|
||||||
|
action: ReceiverAction.CAST
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
instance.bridgePort.postMessage({
|
||||||
|
subject: "bridge:createCastSession",
|
||||||
|
data: {
|
||||||
|
appId: instance.apiConfig?.sessionRequest.appId,
|
||||||
|
receiverDevice: opts.selection.receiverDevice
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ interface EventMap {
|
|||||||
|
|
||||||
export default new (class extends TypedEventTarget<EventMap> {
|
export default new (class extends TypedEventTarget<EventMap> {
|
||||||
/**
|
/**
|
||||||
* Map of receiver device IDs to devices. Updated as
|
* Map of receiver device IDs to devices. Updated as receiverDevice
|
||||||
* receiverDevice messages are received from the bridge.
|
* messages are received from the bridge.
|
||||||
*/
|
*/
|
||||||
private receiverDevices = new Map<string, ReceiverDevice>();
|
private receiverDevices = new Map<string, ReceiverDevice>();
|
||||||
|
|
||||||
@@ -43,8 +43,8 @@ export default new (class extends TypedEventTarget<EventMap> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize (or re-initialize) a bridge connection to
|
* Initializes (or re-initializes) a bridge connection to start
|
||||||
* start dispatching events.
|
* dispatching events.
|
||||||
*/
|
*/
|
||||||
async refresh() {
|
async refresh() {
|
||||||
this.bridgePort?.disconnect();
|
this.bridgePort?.disconnect();
|
||||||
@@ -63,33 +63,16 @@ export default new (class extends TypedEventTarget<EventMap> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Gets a list of receiver devices. */
|
||||||
* Get a list of receiver devices
|
|
||||||
*/
|
|
||||||
getDevices() {
|
getDevices() {
|
||||||
return Array.from(this.receiverDevices.values());
|
return Array.from(this.receiverDevices.values());
|
||||||
}
|
}
|
||||||
|
/** Gets a device by ID. */
|
||||||
/**
|
getDeviceById(deviceId: string) {
|
||||||
* Stops a receiver app running on a given device.
|
return this.receiverDevices.get(deviceId);
|
||||||
*/
|
|
||||||
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:stopCastSession",
|
|
||||||
data: { receiverDevice }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sends an NS_RECEIVER message to a given device. */
|
||||||
sendReceiverMessage(deviceId: string, message: SenderMessage) {
|
sendReceiverMessage(deviceId: string, message: SenderMessage) {
|
||||||
if (!this.bridgePort) {
|
if (!this.bridgePort) {
|
||||||
logger.error(
|
logger.error(
|
||||||
@@ -112,6 +95,7 @@ export default new (class extends TypedEventTarget<EventMap> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sends an NS_MEDIA message to a given device. */
|
||||||
sendMediaMessage(deviceId: string, message: SenderMediaMessage) {
|
sendMediaMessage(deviceId: string, message: SenderMediaMessage) {
|
||||||
if (!this.bridgePort) {
|
if (!this.bridgePort) {
|
||||||
logger.error("Failed to send media message (no bridge connection)");
|
logger.error("Failed to send media message (no bridge connection)");
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import logger from "../../lib/logger";
|
|||||||
import type { Message } from "../../messaging";
|
import type { Message } from "../../messaging";
|
||||||
import eventMessaging from "../eventMessaging";
|
import eventMessaging from "../eventMessaging";
|
||||||
|
|
||||||
import type { ReceiverDevice } from "../../types";
|
|
||||||
import type { ErrorCallback, SuccessCallback } from "../types";
|
import type { ErrorCallback, SuccessCallback } from "../types";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -39,7 +38,6 @@ import {
|
|||||||
import Session from "./Session";
|
import Session from "./Session";
|
||||||
|
|
||||||
import media from "./media";
|
import media from "./media";
|
||||||
import { convertCapabilitiesFlags } from "../utils";
|
|
||||||
|
|
||||||
type ReceiverActionListener = (
|
type ReceiverActionListener = (
|
||||||
receiver: Receiver,
|
receiver: Receiver,
|
||||||
@@ -48,35 +46,21 @@ type ReceiverActionListener = (
|
|||||||
|
|
||||||
type RequestSessionSuccessCallback = (session: Session) => void;
|
type RequestSessionSuccessCallback = (session: Session) => void;
|
||||||
|
|
||||||
/**
|
|
||||||
* Create `chrome.cast.Receiver` object from receiver device info.
|
|
||||||
*/
|
|
||||||
function createReceiver(device: ReceiverDevice) {
|
|
||||||
const receiver = new Receiver(
|
|
||||||
device.id,
|
|
||||||
device.friendlyName,
|
|
||||||
convertCapabilitiesFlags(device.capabilities)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Currently only supports CAST receivers
|
|
||||||
receiver.receiverType = ReceiverType.CAST;
|
|
||||||
|
|
||||||
return receiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Cast SDK root class */
|
/** Cast SDK root class */
|
||||||
export default class {
|
export default class {
|
||||||
#receiverDevices = new Map<string, ReceiverDevice>();
|
|
||||||
|
|
||||||
#apiConfig?: ApiConfig;
|
#apiConfig?: ApiConfig;
|
||||||
#sessionRequest?: SessionRequest;
|
#sessionRequest?: SessionRequest;
|
||||||
|
|
||||||
#requestSessionSuccessCallback?: RequestSessionSuccessCallback;
|
#requestSessionSuccessCallback?: RequestSessionSuccessCallback;
|
||||||
#requestSessionErrorCallback?: ErrorCallback;
|
#requestSessionErrorCallback?: ErrorCallback;
|
||||||
|
|
||||||
|
#initializeSuccessCallback?: SuccessCallback;
|
||||||
|
|
||||||
#sessions = new Map<string, Session>();
|
#sessions = new Map<string, Session>();
|
||||||
#receiverActionListeners = new Set<ReceiverActionListener>();
|
#receiverActionListeners = new Set<ReceiverActionListener>();
|
||||||
|
|
||||||
|
#receiverAvailability = ReceiverAvailability.UNAVAILABLE;
|
||||||
|
|
||||||
// Enums
|
// Enums
|
||||||
AutoJoinPolicy = AutoJoinPolicy;
|
AutoJoinPolicy = AutoJoinPolicy;
|
||||||
Capability = Capability;
|
Capability = Capability;
|
||||||
@@ -114,29 +98,15 @@ export default class {
|
|||||||
eventMessaging.page.addListener(this.#onMessage.bind(this));
|
eventMessaging.page.addListener(this.#onMessage.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
#sendSessionRequest(
|
|
||||||
sessionRequest: SessionRequest,
|
|
||||||
receiverDevice: ReceiverDevice
|
|
||||||
) {
|
|
||||||
for (const listener of this.#receiverActionListeners) {
|
|
||||||
listener(createReceiver(receiverDevice), ReceiverAction.CAST);
|
|
||||||
}
|
|
||||||
|
|
||||||
eventMessaging.page.sendMessage({
|
|
||||||
subject: "bridge:createCastSession",
|
|
||||||
data: {
|
|
||||||
appId: sessionRequest.appId,
|
|
||||||
receiverDevice: receiverDevice
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#onMessage(message: Message) {
|
#onMessage(message: Message) {
|
||||||
switch (message.subject) {
|
switch (message.subject) {
|
||||||
case "cast:initialized": {
|
case "cast:initialized":
|
||||||
|
this.#initializeSuccessCallback?.();
|
||||||
|
this.#apiConfig?.receiverListener(this.#receiverAvailability);
|
||||||
|
|
||||||
this.isAvailable = true;
|
this.isAvailable = true;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Once the bridge detects a session creation, session info
|
* Once the bridge detects a session creation, session info
|
||||||
@@ -144,19 +114,9 @@ export default class {
|
|||||||
*/
|
*/
|
||||||
case "cast:sessionCreated": {
|
case "cast:sessionCreated": {
|
||||||
const status = message.data;
|
const status = message.data;
|
||||||
const receiverDevice = this.#receiverDevices.get(
|
|
||||||
status.receiverId
|
|
||||||
);
|
|
||||||
if (!receiverDevice) {
|
|
||||||
logger.error(
|
|
||||||
`Could not find receiver device "${status.receiverFriendlyName}" (${status.receiverId})`
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const receiver = createReceiver(receiverDevice);
|
status.receiver.volume = status.volume;
|
||||||
receiver.volume = status.volume;
|
status.receiver.displayStatus = new ReceiverDisplayStatus(
|
||||||
receiver.displayStatus = new ReceiverDisplayStatus(
|
|
||||||
status.statusText,
|
status.statusText,
|
||||||
status.appImages
|
status.appImages
|
||||||
);
|
);
|
||||||
@@ -166,7 +126,7 @@ export default class {
|
|||||||
status.appId,
|
status.appId,
|
||||||
status.displayName,
|
status.displayName,
|
||||||
status.appImages,
|
status.appImages,
|
||||||
receiver
|
status.receiver
|
||||||
);
|
);
|
||||||
|
|
||||||
session.namespaces = status.namespaces;
|
session.namespaces = status.namespaces;
|
||||||
@@ -178,9 +138,9 @@ export default class {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* If session created via requestSession, the success
|
* If session created via requestSession, the success
|
||||||
* callback will be set, otherwise the session was created
|
* callback will be set, otherwise the session was
|
||||||
* by the extension and the session listener should be
|
* created by the extension and the session listener
|
||||||
* called instead.
|
* should be called instead.
|
||||||
*/
|
*/
|
||||||
if (this.#requestSessionSuccessCallback) {
|
if (this.#requestSessionSuccessCallback) {
|
||||||
this.#requestSessionSuccessCallback(session);
|
this.#requestSessionSuccessCallback(session);
|
||||||
@@ -263,49 +223,15 @@ export default class {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "cast:receiverDeviceUp": {
|
case "cast:updateReceiverAvailability": {
|
||||||
const { receiverDevice } = message.data;
|
const availability = message.data.isAvailable
|
||||||
if (this.#receiverDevices.has(receiverDevice.id)) {
|
? ReceiverAvailability.AVAILABLE
|
||||||
break;
|
: ReceiverAvailability.UNAVAILABLE;
|
||||||
}
|
|
||||||
|
|
||||||
this.#receiverDevices.set(receiverDevice.id, receiverDevice);
|
// If availability has changed, call receiver listeners
|
||||||
|
if (availability !== this.#receiverAvailability) {
|
||||||
if (this.#apiConfig) {
|
this.#receiverAvailability = availability;
|
||||||
// Notify listeners of new cast destination
|
this.#apiConfig?.receiverListener(availability);
|
||||||
this.#apiConfig.receiverListener(
|
|
||||||
ReceiverAvailability.AVAILABLE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "cast:receiverDeviceDown": {
|
|
||||||
const { receiverDeviceId } = message.data;
|
|
||||||
|
|
||||||
this.#receiverDevices.delete(receiverDeviceId);
|
|
||||||
|
|
||||||
if (this.#receiverDevices.size === 0) {
|
|
||||||
if (this.#apiConfig) {
|
|
||||||
this.#apiConfig.receiverListener(
|
|
||||||
ReceiverAvailability.UNAVAILABLE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "cast:selectReceiver/selected": {
|
|
||||||
logger.info("Selected receiver");
|
|
||||||
|
|
||||||
if (this.#sessionRequest) {
|
|
||||||
this.#sendSessionRequest(
|
|
||||||
this.#sessionRequest,
|
|
||||||
message.data.receiverDevice
|
|
||||||
);
|
|
||||||
this.#sessionRequest = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -324,35 +250,13 @@ export default class {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "cast:receiverStoppedAction": {
|
case "cast:sendReceiverAction": {
|
||||||
const device = this.#receiverDevices.get(message.data.deviceId);
|
|
||||||
if (!device) break;
|
|
||||||
|
|
||||||
for (const actionListener of this.#receiverActionListeners) {
|
for (const actionListener of this.#receiverActionListeners) {
|
||||||
actionListener(createReceiver(device), ReceiverAction.STOP);
|
actionListener(message.data.receiver, message.data.action);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Session request initiated via receiver selector
|
|
||||||
case "cast:launchApp": {
|
|
||||||
if (this.#sessionRequest) {
|
|
||||||
logger.error("Session request already in progress.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!this.#apiConfig?.sessionRequest) {
|
|
||||||
logger.error("Session request not found!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.#sendSessionRequest(
|
|
||||||
this.#apiConfig.sessionRequest,
|
|
||||||
message.data.receiverDevice
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,25 +275,20 @@ export default class {
|
|||||||
|
|
||||||
this.#apiConfig = apiConfig;
|
this.#apiConfig = apiConfig;
|
||||||
|
|
||||||
|
if (successCallback) {
|
||||||
|
this.#initializeSuccessCallback = successCallback;
|
||||||
|
}
|
||||||
|
|
||||||
eventMessaging.page.sendMessage({
|
eventMessaging.page.sendMessage({
|
||||||
subject: "main:initializeCast",
|
subject: "main:initializeCast",
|
||||||
data: { appId: this.#apiConfig.sessionRequest.appId }
|
data: { apiConfig: this.#apiConfig }
|
||||||
});
|
});
|
||||||
|
|
||||||
successCallback?.();
|
|
||||||
|
|
||||||
this.#apiConfig.receiverListener(
|
|
||||||
this.#receiverDevices.size
|
|
||||||
? ReceiverAvailability.AVAILABLE
|
|
||||||
: ReceiverAvailability.UNAVAILABLE
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
requestSession(
|
requestSession(
|
||||||
successCallback: RequestSessionSuccessCallback,
|
successCallback: RequestSessionSuccessCallback,
|
||||||
errorCallback: ErrorCallback,
|
errorCallback: ErrorCallback,
|
||||||
newSessionRequest?: SessionRequest,
|
newSessionRequest?: SessionRequest
|
||||||
receiverDevice?: ReceiverDevice
|
|
||||||
) {
|
) {
|
||||||
logger.info("cast.requestSession");
|
logger.info("cast.requestSession");
|
||||||
|
|
||||||
@@ -410,40 +309,23 @@ export default class {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No receivers available
|
if (this.#receiverAvailability === ReceiverAvailability.UNAVAILABLE) {
|
||||||
if (!this.#receiverDevices.size) {
|
|
||||||
errorCallback?.(new Error_(ErrorCode.RECEIVER_UNAVAILABLE));
|
errorCallback?.(new Error_(ErrorCode.RECEIVER_UNAVAILABLE));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Store used session request
|
||||||
* Store session request for use in return message from
|
|
||||||
* receiver selection.
|
|
||||||
*/
|
|
||||||
this.#sessionRequest =
|
this.#sessionRequest =
|
||||||
newSessionRequest ?? this.#apiConfig.sessionRequest;
|
newSessionRequest ?? this.#apiConfig.sessionRequest;
|
||||||
|
|
||||||
this.#requestSessionSuccessCallback = successCallback;
|
this.#requestSessionSuccessCallback = successCallback;
|
||||||
this.#requestSessionErrorCallback = errorCallback;
|
this.#requestSessionErrorCallback = errorCallback;
|
||||||
|
|
||||||
/**
|
// Open receiver selector UI
|
||||||
* If a receiver was provided, skip the receiver selector
|
eventMessaging.page.sendMessage({
|
||||||
* process.
|
subject: "main:selectReceiver",
|
||||||
*/
|
data: { sessionRequest: this.#sessionRequest }
|
||||||
if (receiverDevice) {
|
});
|
||||||
if (
|
|
||||||
receiverDevice?.id &&
|
|
||||||
this.#receiverDevices.has(receiverDevice.id)
|
|
||||||
) {
|
|
||||||
this.#sendSessionRequest(this.#sessionRequest, receiverDevice);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Open receiver selector UI
|
|
||||||
eventMessaging.page.sendMessage({
|
|
||||||
subject: "main:selectReceiver",
|
|
||||||
data: { sessionRequest: this.#sessionRequest }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
requestSessionById(_sessionId: string): void {
|
requestSessionById(_sessionId: string): void {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { ReceiverDevice, ReceiverDeviceCapabilities } from "../types";
|
import { ReceiverDevice, ReceiverDeviceCapabilities } from "../types";
|
||||||
import { Capability } from "./sdk/enums";
|
import { Receiver } from "./sdk/classes";
|
||||||
|
import { Capability, ReceiverType } from "./sdk/enums";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check receiver device capabilities bitflags against array of
|
* Check receiver device capabilities bitflags against array of
|
||||||
@@ -64,3 +65,19 @@ export function getEstimatedTime(opts: GetEstimatedTimeOpts) {
|
|||||||
|
|
||||||
return estimatedTime;
|
return estimatedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create `chrome.cast.Receiver` object from receiver device info.
|
||||||
|
*/
|
||||||
|
export function createReceiver(device: ReceiverDevice) {
|
||||||
|
const receiver = new Receiver(
|
||||||
|
device.id,
|
||||||
|
device.friendlyName,
|
||||||
|
convertCapabilitiesFlags(device.capabilities)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Currently only supports CAST receivers
|
||||||
|
receiver.receiverType = ReceiverType.CAST;
|
||||||
|
|
||||||
|
return receiver;
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ import type {
|
|||||||
SenderMediaMessage,
|
SenderMediaMessage,
|
||||||
SenderMessage
|
SenderMessage
|
||||||
} from "./cast/sdk/types";
|
} from "./cast/sdk/types";
|
||||||
import type { SessionRequest } from "./cast/sdk/classes";
|
import type { ApiConfig, Receiver, SessionRequest } from "./cast/sdk/classes";
|
||||||
|
|
||||||
import type { ReceiverDevice, ReceiverSelectorMediaType } from "./types";
|
import type { ReceiverDevice, ReceiverSelectorMediaType } from "./types";
|
||||||
|
import type { ReceiverAction } from "./cast/sdk/enums";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Messages are JSON objects with a `subject` string key and a
|
* Messages are JSON objects with a `subject` string key and a
|
||||||
@@ -76,24 +77,26 @@ type ExtMessageDefinitions = {
|
|||||||
"main:selectReceiver": {
|
"main:selectReceiver": {
|
||||||
sessionRequest: SessionRequest;
|
sessionRequest: SessionRequest;
|
||||||
};
|
};
|
||||||
/** Return message to the cast API when a receiver is selected. */
|
|
||||||
"cast:selectReceiver/selected": ReceiverSelection;
|
|
||||||
/** Return message to the cast API when a selection is cancelled. */
|
/** Return message to the cast API when a selection is cancelled. */
|
||||||
"cast:selectReceiver/cancelled": undefined;
|
"cast:selectReceiver/cancelled": undefined;
|
||||||
|
|
||||||
/** Sent to the cast API when a receiver app is stopped. */
|
/**
|
||||||
"cast:receiverStoppedAction": { deviceId: string };
|
* Sent to the cast API when a session is requested or stopped via
|
||||||
|
* the extension UI.
|
||||||
|
*/
|
||||||
|
"cast:sendReceiverAction": { receiver: Receiver; action: ReceiverAction };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells the cast manager to provide the cast API instance with
|
* Tells the cast manager to provide the cast API instance with
|
||||||
* receiver data.
|
* receiver data.
|
||||||
*/
|
*/
|
||||||
"main:initializeCast": { appId: string };
|
"main:initializeCast": { apiConfig: ApiConfig };
|
||||||
"cast:initialized": { isAvailable: boolean };
|
"cast:initialized": { isAvailable: boolean };
|
||||||
|
|
||||||
"cast:receiverDeviceUp": { receiverDevice: ReceiverDevice };
|
"cast:sessionCreated": CastSessionCreatedDetails & { receiver: Receiver };
|
||||||
"cast:receiverDeviceDown": { receiverDeviceId: string };
|
"cast:sessionUpdated": CastSessionUpdatedDetails;
|
||||||
"cast:launchApp": { receiverDevice: ReceiverDevice };
|
|
||||||
|
"cast:updateReceiverAvailability": { isAvailable: boolean };
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -189,8 +192,9 @@ type AppMessageDefinitions = {
|
|||||||
* updates. Updated details is a mutable subset of session details
|
* updates. Updated details is a mutable subset of session details
|
||||||
* otherwise fixed on creation.
|
* otherwise fixed on creation.
|
||||||
*/
|
*/
|
||||||
"cast:sessionCreated": CastSessionCreatedDetails;
|
"main:castSessionCreated": CastSessionCreatedDetails;
|
||||||
"cast:sessionUpdated": CastSessionUpdatedDetails;
|
"main:castSessionUpdated": CastSessionUpdatedDetails;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent to cast API instances whenever a session is stopped.
|
* Sent to cast API instances whenever a session is stopped.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user