Handle audio device support checking

This commit is contained in:
hensm
2022-08-25 05:20:02 +01:00
parent 04c3dbf397
commit 41094ca4b3
8 changed files with 170 additions and 54 deletions

60
ext/src/cast/googleApi.ts Normal file
View File

@@ -0,0 +1,60 @@
import logger from "../lib/logger";
import { TypedStorageArea } from "../lib/TypedStorageArea";
const ENDPOINT = "https://clients3.google.com/cast/chromecast/device";
export interface BaseConfig {
app_tags: Array<{
supports_audio_only: boolean;
suports_video: boolean;
app_id: number;
}>;
}
export const baseConfigStorage = new TypedStorageArea<{
baseConfig: BaseConfig;
baseConfigUpdated: number;
}>(browser.storage.local);
/**
* Fetches Chromecast base config data subset.
*/
export async function fetchBaseConfig(): Promise<BaseConfig | null> {
try {
const res = await fetch(`${ENDPOINT}/baseconfig`);
const baseConfig = JSON.parse((await res.text()).slice(4));
// Strip other properties
return { app_tags: baseConfig.app_tags };
} catch (err) {
logger.error("Failed to fetch Chromecast base config!");
return null;
}
}
/**
* Get app tag from base config.
* @param baseConfig Base config data.
* @param appId Chromecast app ID.
*/
export function getAppTag(baseConfig: BaseConfig, appId: string) {
// App tag IDs are represented as 32-bit signed integers
const signedAppId = (parseInt(appId, 16) << 32) >> 32;
return baseConfig.app_tags.find(tag => tag.app_id === signedAppId);
}
/**
* Fetches Chromecast app config.
*
* @param appId Chromecast app ID
* @returns
*/
export async function fetchAppConfig(appId: string) {
try {
const res = await fetch(`${ENDPOINT}/app?a=${appId}`);
return JSON.parse((await res.text()).slice(4));
} catch (err) {
logger.error("Failed to fetch Chromecast app config!", { appId });
return null;
}
}

View File

@@ -2,11 +2,12 @@
import logger from "../../lib/logger";
import { ReceiverDevice, ReceiverDeviceCapabilities } from "../../types";
import { ErrorCallback, SuccessCallback } from "../types";
import { Message } from "../../messaging";
import eventMessaging from "../eventMessaging";
import { ReceiverDevice } from "../../types";
import { ErrorCallback, SuccessCallback } from "../types";
import {
AutoJoinPolicy,
Capability,
@@ -38,7 +39,7 @@ import {
import Session from "./Session";
import media from "./media";
import { Message } from "../../messaging";
import { convertCapabilitiesFlags } from "../utils";
type ReceiverActionListener = (
receiver: Receiver,
@@ -51,23 +52,11 @@ type RequestSessionSuccessCallback = (session: Session) => void;
* 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);
const receiver = new Receiver(
device.id,
device.friendlyName,
convertCapabilitiesFlags(device.capabilities)
);
// Currently only supports CAST receivers
receiver.receiverType = ReceiverType.CAST;
@@ -178,11 +167,11 @@ export default class {
);
const session = new Session(
status.sessionId, // sessionId
status.appId, // appId
status.displayName, // displayName
status.appImages, // appImages
receiver // receiver
status.sessionId,
status.appId,
status.displayName,
status.appImages,
receiver
);
session.namespaces = status.namespaces;
@@ -221,11 +210,8 @@ export default class {
session.namespaces = status.namespaces;
session.receiver.volume = status.volume;
const updateListeners = session?._updateListeners;
if (updateListeners) {
for (const listener of updateListeners) {
listener(session.status !== SessionStatus.STOPPED);
}
for (const listener of session._updateListeners) {
listener(session.status !== SessionStatus.STOPPED);
}
break;
@@ -236,12 +222,8 @@ export default class {
const session = this.#sessions.get(sessionId);
if (session) {
session.status = SessionStatus.STOPPED;
const updateListeners = session?._updateListeners;
if (updateListeners) {
for (const listener of updateListeners) {
listener(false);
}
for (const listener of session._updateListeners) {
listener(false);
}
}
@@ -252,9 +234,7 @@ export default class {
const { sessionId, namespace, messageData } = message.data;
const session = this.#sessions.get(sessionId);
if (session) {
const _messageListeners = session._messageListeners;
const listeners = _messageListeners.get(namespace);
const listeners = session._messageListeners.get(namespace);
if (listeners) {
for (const listener of listeners) {
listener(namespace, messageData);

View File

@@ -140,7 +140,7 @@ export default class Media {
* information reported by the receiver.
*/
getEstimatedTime(): number {
if (this.playerState === PlayerState.PLAYING && this._lastUpdateTime) {
if (this.playerState === PlayerState.PLAYING) {
return getEstimatedTime({
currentTime: this.currentTime,
lastUpdateTime: this._lastUpdateTime,

View File

@@ -28,6 +28,24 @@ export function hasRequiredCapabilities(
});
}
export function convertCapabilitiesFlags(flags: ReceiverDeviceCapabilities) {
// Convert capabilities bitflag to string array
const capabilities: Capability[] = [];
if (flags & ReceiverDeviceCapabilities.VIDEO_OUT)
capabilities.push(Capability.VIDEO_OUT);
if (flags & ReceiverDeviceCapabilities.VIDEO_IN)
capabilities.push(Capability.VIDEO_IN);
if (flags & ReceiverDeviceCapabilities.AUDIO_OUT)
capabilities.push(Capability.AUDIO_OUT);
if (flags & ReceiverDeviceCapabilities.AUDIO_IN)
capabilities.push(Capability.AUDIO_IN);
if (flags & ReceiverDeviceCapabilities.MULTIZONE_GROUP)
capabilities.push(Capability.MULTIZONE_GROUP);
return capabilities;
}
interface GetEstimatedTimeOpts {
currentTime: number;
lastUpdateTime: number;