mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-11 18:19:58 +00:00
Hide Session/Media private data and preserve Media constructor signature
This commit is contained in:
@@ -5,13 +5,13 @@ import { v4 as uuid } from "uuid";
|
|||||||
import logger from "../../lib/logger";
|
import logger from "../../lib/logger";
|
||||||
|
|
||||||
import eventMessaging from "../pageMessenging";
|
import eventMessaging from "../pageMessenging";
|
||||||
|
import { convertSupportedMediaCommandsFlags } from "../utils";
|
||||||
|
|
||||||
import {
|
import type {
|
||||||
MediaStatus,
|
MediaStatus,
|
||||||
ReceiverMediaMessage,
|
ReceiverMediaMessage,
|
||||||
SenderMediaMessage,
|
SenderMediaMessage,
|
||||||
SenderMessage,
|
SenderMessage
|
||||||
_MediaCommand
|
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
import { SessionStatus } from "./enums";
|
import { SessionStatus } from "./enums";
|
||||||
@@ -22,9 +22,13 @@ import type {
|
|||||||
SenderApplication
|
SenderApplication
|
||||||
} from "./classes";
|
} from "./classes";
|
||||||
|
|
||||||
import { MediaCommand } from "./media/enums";
|
|
||||||
import type { LoadRequest, QueueLoadRequest, QueueItem } from "./media/classes";
|
import type { LoadRequest, QueueLoadRequest, QueueItem } from "./media/classes";
|
||||||
import Media, { NS_MEDIA } from "./media/Media";
|
import Media, {
|
||||||
|
createMedia,
|
||||||
|
MediaLastUpdateTimes,
|
||||||
|
MediaUpdateListeners,
|
||||||
|
NS_MEDIA
|
||||||
|
} from "./media/Media";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a media object and a media status object and merges the status
|
* Takes a media object and a media status object and merges the status
|
||||||
@@ -32,7 +36,7 @@ import Media, { NS_MEDIA } from "./media/Media";
|
|||||||
*/
|
*/
|
||||||
function updateMedia(media: Media, status: MediaStatus) {
|
function updateMedia(media: Media, status: MediaStatus) {
|
||||||
if (status.currentTime) {
|
if (status.currentTime) {
|
||||||
media._lastUpdateTime = Date.now();
|
MediaLastUpdateTimes.set(media, Date.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy basic props
|
// Copy basic props
|
||||||
@@ -46,23 +50,9 @@ function updateMedia(media: Media, status: MediaStatus) {
|
|||||||
if (status.repeatMode) media.repeatMode = status.repeatMode;
|
if (status.repeatMode) media.repeatMode = status.repeatMode;
|
||||||
if (status.volume) media.volume = status.volume;
|
if (status.volume) media.volume = status.volume;
|
||||||
|
|
||||||
// Convert supportedMediaCommands bitflags to string array
|
media.supportedMediaCommands = convertSupportedMediaCommandsFlags(
|
||||||
const supportedMediaCommands: string[] = [];
|
status.supportedMediaCommands
|
||||||
if (status.supportedMediaCommands & _MediaCommand.PAUSE) {
|
);
|
||||||
supportedMediaCommands.push(MediaCommand.PAUSE);
|
|
||||||
} else if (status.supportedMediaCommands & _MediaCommand.SEEK) {
|
|
||||||
supportedMediaCommands.push(MediaCommand.SEEK);
|
|
||||||
} else if (status.supportedMediaCommands & _MediaCommand.STREAM_VOLUME) {
|
|
||||||
supportedMediaCommands.push(MediaCommand.STREAM_VOLUME);
|
|
||||||
} else if (status.supportedMediaCommands & _MediaCommand.STREAM_MUTE) {
|
|
||||||
supportedMediaCommands.push(MediaCommand.STREAM_MUTE);
|
|
||||||
} else if (status.supportedMediaCommands & _MediaCommand.QUEUE_NEXT) {
|
|
||||||
supportedMediaCommands.push("queue_next");
|
|
||||||
} else if (status.supportedMediaCommands & _MediaCommand.QUEUE_PREV) {
|
|
||||||
supportedMediaCommands.push("queue_prev");
|
|
||||||
}
|
|
||||||
|
|
||||||
media.supportedMediaCommands = supportedMediaCommands;
|
|
||||||
|
|
||||||
// Update queue state
|
// Update queue state
|
||||||
if (status.items) {
|
if (status.items) {
|
||||||
@@ -97,21 +87,57 @@ function updateMedia(media: Media, status: MediaStatus) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SessionMessageListeners = new WeakMap<
|
||||||
|
Session,
|
||||||
|
Map<string, Set<MessageListener>>
|
||||||
|
>();
|
||||||
|
export const SessionUpdateListeners = new WeakMap<
|
||||||
|
Session,
|
||||||
|
Set<UpdateListener>
|
||||||
|
>();
|
||||||
|
export const SessionSendMessageCallbacks = new WeakMap<
|
||||||
|
Session,
|
||||||
|
Map<string, SendMessageCallback>
|
||||||
|
>();
|
||||||
|
|
||||||
|
/** Creates a Session object and initializes private data. */
|
||||||
|
export function createSession(
|
||||||
|
sessionArgs: ConstructorParameters<typeof Session>
|
||||||
|
) {
|
||||||
|
const session = new Session(...sessionArgs);
|
||||||
|
SessionUpdateListeners.set(session, new Set());
|
||||||
|
SessionSendMessageCallbacks.set(session, new Map());
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
type MessageListener = (namespace: string, message: string) => void;
|
type MessageListener = (namespace: string, message: string) => void;
|
||||||
type UpdateListener = (isAlive: boolean) => void;
|
type UpdateListener = (isAlive: boolean) => void;
|
||||||
|
type SendMessageCallback = [(() => void)?, ((err: CastError) => void)?];
|
||||||
|
|
||||||
export default class Session {
|
export default class Session {
|
||||||
#loadMediaRequest?: LoadRequest;
|
#loadMediaRequest?: LoadRequest;
|
||||||
#loadMediaSuccessCallback?: (media: Media) => void;
|
#loadMediaSuccessCallback?: (media: Media) => void;
|
||||||
#loadMediaErrorCallback?: (err: CastError) => void;
|
#loadMediaErrorCallback?: (err: CastError) => void;
|
||||||
|
|
||||||
_messageListeners = new Map<string, Set<MessageListener>>();
|
get #messageListeners() {
|
||||||
_updateListeners = new Set<UpdateListener>();
|
const messageListeners = SessionMessageListeners.get(this);
|
||||||
|
if (!messageListeners)
|
||||||
_sendMessageCallbacks = new Map<
|
throw logger.error("Missing session message listeners!");
|
||||||
string,
|
return messageListeners;
|
||||||
[(() => void)?, ((err: CastError) => void)?]
|
}
|
||||||
>();
|
get #updateListeners() {
|
||||||
|
const updateListeners = SessionUpdateListeners.get(this);
|
||||||
|
if (!updateListeners)
|
||||||
|
throw logger.error("Missing session update listeners!");
|
||||||
|
return updateListeners;
|
||||||
|
}
|
||||||
|
get #sendMessageCallbacks() {
|
||||||
|
const sendMessageCallback = SessionSendMessageCallbacks.get(this);
|
||||||
|
if (!sendMessageCallback)
|
||||||
|
throw logger.error("Missing session sendMessage callback!");
|
||||||
|
return sendMessageCallback;
|
||||||
|
}
|
||||||
|
|
||||||
media: Media[] = [];
|
media: Media[] = [];
|
||||||
namespaces: Array<{ name: string }> = [];
|
namespaces: Array<{ name: string }> = [];
|
||||||
@@ -129,6 +155,7 @@ export default class Session {
|
|||||||
) {
|
) {
|
||||||
this.transportId = sessionId || "";
|
this.transportId = sessionId || "";
|
||||||
|
|
||||||
|
SessionMessageListeners.set(this, new Map());
|
||||||
this.addMessageListener(NS_MEDIA, this.#mediaMessageListener);
|
this.addMessageListener(NS_MEDIA, this.#mediaMessageListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,9 +174,8 @@ export default class Session {
|
|||||||
|
|
||||||
// Handle Media creation
|
// Handle Media creation
|
||||||
if (!media) {
|
if (!media) {
|
||||||
media = new Media(
|
media = createMedia(
|
||||||
this.sessionId,
|
[this.sessionId, mediaStatus.mediaSessionId],
|
||||||
mediaStatus.mediaSessionId,
|
|
||||||
this.#sendMediaMessage
|
this.#sendMediaMessage
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -158,8 +184,11 @@ export default class Session {
|
|||||||
this.#loadMediaSuccessCallback?.(media);
|
this.#loadMediaSuccessCallback?.(media);
|
||||||
} else {
|
} else {
|
||||||
updateMedia(media, mediaStatus);
|
updateMedia(media, mediaStatus);
|
||||||
for (const listener of media._updateListeners) {
|
const updateListeners = MediaUpdateListeners.get(media);
|
||||||
listener(true);
|
if (updateListeners) {
|
||||||
|
for (const listener of updateListeners) {
|
||||||
|
listener(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,7 +227,7 @@ export default class Session {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._sendMessageCallbacks.set(messageId, [resolve, reject]);
|
this.#sendMessageCallbacks.set(messageId, [resolve, reject]);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -210,21 +239,21 @@ export default class Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addMessageListener(namespace: string, listener: MessageListener) {
|
addMessageListener(namespace: string, listener: MessageListener) {
|
||||||
if (!this._messageListeners.has(namespace)) {
|
if (!this.#messageListeners.has(namespace)) {
|
||||||
this._messageListeners.set(namespace, new Set());
|
this.#messageListeners.set(namespace, new Set());
|
||||||
}
|
}
|
||||||
|
|
||||||
this._messageListeners.get(namespace)?.add(listener);
|
this.#messageListeners.get(namespace)?.add(listener);
|
||||||
}
|
}
|
||||||
removeMessageListener(namespace: string, listener: MessageListener) {
|
removeMessageListener(namespace: string, listener: MessageListener) {
|
||||||
this._messageListeners.get(namespace)?.delete(listener);
|
this.#messageListeners.get(namespace)?.delete(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
addUpdateListener(listener: UpdateListener) {
|
addUpdateListener(listener: UpdateListener) {
|
||||||
this._updateListeners.add(listener);
|
this.#updateListeners.add(listener);
|
||||||
}
|
}
|
||||||
removeUpdateListener(listener: UpdateListener) {
|
removeUpdateListener(listener: UpdateListener) {
|
||||||
this._updateListeners.delete(listener);
|
this.#updateListeners.delete(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
leave(
|
leave(
|
||||||
@@ -272,7 +301,7 @@ export default class Session {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._sendMessageCallbacks.set(messageId, [
|
this.#sendMessageCallbacks.set(messageId, [
|
||||||
successCallback,
|
successCallback,
|
||||||
errorCallback
|
errorCallback
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
AutoJoinPolicy,
|
AutoJoinPolicy,
|
||||||
Capability,
|
Capability,
|
||||||
DefaultActionPolicy,
|
DefaultActionPolicy,
|
||||||
|
ErrorCode,
|
||||||
ReceiverAvailability,
|
ReceiverAvailability,
|
||||||
ReceiverType,
|
ReceiverType,
|
||||||
VolumeControlType
|
VolumeControlType
|
||||||
@@ -35,7 +36,7 @@ export class DialRequest {
|
|||||||
|
|
||||||
export class Error {
|
export class Error {
|
||||||
constructor(
|
constructor(
|
||||||
public code: string,
|
public code: ErrorCode,
|
||||||
public description: Nullable<string> = null,
|
public description: Nullable<string> = null,
|
||||||
public details: unknown = null
|
public details: unknown = null
|
||||||
) {}
|
) {}
|
||||||
|
|||||||
@@ -33,9 +33,14 @@ import {
|
|||||||
Volume
|
Volume
|
||||||
} from "./classes";
|
} from "./classes";
|
||||||
|
|
||||||
import Session from "./Session";
|
import Session, {
|
||||||
|
createSession,
|
||||||
|
SessionMessageListeners,
|
||||||
|
SessionSendMessageCallbacks,
|
||||||
|
SessionUpdateListeners
|
||||||
|
} from "./Session";
|
||||||
|
|
||||||
import media from "./media";
|
import * as media from "./media";
|
||||||
|
|
||||||
type ReceiverActionListener = (
|
type ReceiverActionListener = (
|
||||||
receiver: Receiver,
|
receiver: Receiver,
|
||||||
@@ -49,6 +54,7 @@ export default class {
|
|||||||
#apiConfig?: ApiConfig;
|
#apiConfig?: ApiConfig;
|
||||||
#sessionRequest?: SessionRequest;
|
#sessionRequest?: SessionRequest;
|
||||||
|
|
||||||
|
/** Current receiver availability. */
|
||||||
#receiverAvailability = ReceiverAvailability.UNAVAILABLE;
|
#receiverAvailability = ReceiverAvailability.UNAVAILABLE;
|
||||||
|
|
||||||
#initializeSuccessCallback?: () => void;
|
#initializeSuccessCallback?: () => void;
|
||||||
@@ -87,7 +93,7 @@ export default class {
|
|||||||
Volume = Volume;
|
Volume = Volume;
|
||||||
Session = Session;
|
Session = Session;
|
||||||
|
|
||||||
media = media;
|
media = { ...media };
|
||||||
|
|
||||||
VERSION = [1, 2];
|
VERSION = [1, 2];
|
||||||
isAvailable = false;
|
isAvailable = false;
|
||||||
@@ -101,10 +107,19 @@ export default class {
|
|||||||
switch (message.subject) {
|
switch (message.subject) {
|
||||||
case "cast:initialized":
|
case "cast:initialized":
|
||||||
this.isAvailable = true;
|
this.isAvailable = true;
|
||||||
|
|
||||||
this.#initializeSuccessCallback?.();
|
this.#initializeSuccessCallback?.();
|
||||||
this.#apiConfig?.receiverListener(this.#receiverAvailability);
|
this.#apiConfig?.receiverListener(this.#receiverAvailability);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Popup closed before session established
|
||||||
|
case "cast:sessionRequestCancelled":
|
||||||
|
if (this.#sessionRequest) {
|
||||||
|
this.#sessionRequest = undefined;
|
||||||
|
|
||||||
|
this.#requestSessionErrorCallback?.(
|
||||||
|
new CastError(ErrorCode.CANCEL)
|
||||||
|
);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,13 +135,13 @@ export default class {
|
|||||||
status.appImages
|
status.appImages
|
||||||
);
|
);
|
||||||
|
|
||||||
const session = new Session(
|
const session = createSession([
|
||||||
status.sessionId,
|
status.sessionId,
|
||||||
status.appId,
|
status.appId,
|
||||||
status.displayName,
|
status.displayName,
|
||||||
status.appImages,
|
status.appImages,
|
||||||
status.receiver
|
status.receiver
|
||||||
);
|
]);
|
||||||
|
|
||||||
session.namespaces = status.namespaces;
|
session.namespaces = status.namespaces;
|
||||||
session.senderApps = status.senderApps;
|
session.senderApps = status.senderApps;
|
||||||
@@ -164,8 +179,11 @@ export default class {
|
|||||||
session.namespaces = status.namespaces;
|
session.namespaces = status.namespaces;
|
||||||
session.receiver.volume = status.volume;
|
session.receiver.volume = status.volume;
|
||||||
|
|
||||||
for (const listener of session._updateListeners) {
|
const updateListeners = SessionUpdateListeners.get(session);
|
||||||
listener(session.status !== SessionStatus.STOPPED);
|
if (updateListeners) {
|
||||||
|
for (const listener of updateListeners) {
|
||||||
|
listener(session.status !== SessionStatus.STOPPED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -176,8 +194,12 @@ export default class {
|
|||||||
const session = this.#sessions.get(sessionId);
|
const session = this.#sessions.get(sessionId);
|
||||||
if (session) {
|
if (session) {
|
||||||
session.status = SessionStatus.STOPPED;
|
session.status = SessionStatus.STOPPED;
|
||||||
for (const listener of session._updateListeners) {
|
|
||||||
listener(false);
|
const updateListeners = SessionUpdateListeners.get(session);
|
||||||
|
if (updateListeners) {
|
||||||
|
for (const listener of updateListeners) {
|
||||||
|
listener(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +210,8 @@ export default class {
|
|||||||
const { sessionId, namespace, messageData } = message.data;
|
const { sessionId, namespace, messageData } = message.data;
|
||||||
const session = this.#sessions.get(sessionId);
|
const session = this.#sessions.get(sessionId);
|
||||||
if (session) {
|
if (session) {
|
||||||
const listeners = session._messageListeners.get(namespace);
|
const listeners =
|
||||||
|
SessionMessageListeners.get(session)?.get(namespace);
|
||||||
if (listeners) {
|
if (listeners) {
|
||||||
for (const listener of listeners) {
|
for (const listener of listeners) {
|
||||||
listener(namespace, messageData);
|
listener(namespace, messageData);
|
||||||
@@ -207,12 +230,16 @@ export default class {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const callbacks = session._sendMessageCallbacks.get(messageId);
|
const sendMessageCallback =
|
||||||
if (callbacks) {
|
SessionSendMessageCallbacks.get(session)?.get(messageId);
|
||||||
const [successCallback, errorCallback] = callbacks;
|
if (sendMessageCallback) {
|
||||||
|
const [successCallback, errorCallback] =
|
||||||
|
sendMessageCallback;
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
errorCallback?.(new CastError(error));
|
errorCallback?.(
|
||||||
|
new CastError(ErrorCode.CHANNEL_ERROR, error)
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,26 +263,11 @@ export default class {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Popup closed before session established
|
case "cast:receiverAction":
|
||||||
case "cast:sessionRequestCancelled": {
|
|
||||||
if (this.#sessionRequest) {
|
|
||||||
this.#sessionRequest = undefined;
|
|
||||||
|
|
||||||
this.#requestSessionErrorCallback?.(
|
|
||||||
new CastError(ErrorCode.CANCEL)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "cast:receiverAction": {
|
|
||||||
for (const actionListener of this.#receiverActionListeners) {
|
for (const actionListener of this.#receiverActionListeners) {
|
||||||
actionListener(message.data.receiver, message.data.action);
|
actionListener(message.data.receiver, message.data.action);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,15 @@
|
|||||||
|
|
||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
|
|
||||||
import logger from "../../../lib/logger";
|
import { Logger } from "../../../lib/logger";
|
||||||
|
const logger = new Logger("fx_cast [sdk :: cast.Media]");
|
||||||
|
|
||||||
|
import { getEstimatedTime } from "../../utils";
|
||||||
|
import type { SenderMediaMessage } from "../types";
|
||||||
|
|
||||||
import { Volume, Error as CastError } from "../classes";
|
import { Volume, Error as CastError } from "../classes";
|
||||||
|
import { ErrorCode } from "../enums";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BreakStatus,
|
BreakStatus,
|
||||||
EditTracksInfoRequest,
|
EditTracksInfoRequest,
|
||||||
@@ -26,23 +32,52 @@ import {
|
|||||||
VideoInformation,
|
VideoInformation,
|
||||||
VolumeRequest
|
VolumeRequest
|
||||||
} from "./classes";
|
} from "./classes";
|
||||||
|
|
||||||
import { PlayerState, RepeatMode } from "./enums";
|
import { PlayerState, RepeatMode } from "./enums";
|
||||||
import { ErrorCode } from "../enums";
|
|
||||||
|
|
||||||
import type { SenderMediaMessage } from "../types";
|
|
||||||
import { getEstimatedTime } from "../../utils";
|
|
||||||
|
|
||||||
export const NS_MEDIA = "urn:x-cast:com.google.cast.media";
|
export const NS_MEDIA = "urn:x-cast:com.google.cast.media";
|
||||||
|
|
||||||
|
type MediaMessageCallback = (
|
||||||
|
message: DistributiveOmit<SenderMediaMessage, "requestId">
|
||||||
|
) => Promise<void>;
|
||||||
|
|
||||||
|
const MediaMessageCallbacks = new WeakMap<Media, MediaMessageCallback>();
|
||||||
|
export const MediaUpdateListeners = new WeakMap<Media, Set<UpdateListener>>();
|
||||||
|
export const MediaLastUpdateTimes = new WeakMap<Media, number>();
|
||||||
|
|
||||||
|
/** Creates a Media object and initializes private data. */
|
||||||
|
export function createMedia(
|
||||||
|
mediaArgs: ConstructorParameters<typeof Media>,
|
||||||
|
mediaMessageCallback: MediaMessageCallback
|
||||||
|
) {
|
||||||
|
const media = new Media(...mediaArgs);
|
||||||
|
MediaMessageCallbacks.set(media, mediaMessageCallback);
|
||||||
|
MediaUpdateListeners.set(media, new Set());
|
||||||
|
MediaLastUpdateTimes.set(media, 0);
|
||||||
|
|
||||||
|
return media;
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateListener = (isAlive: boolean) => void;
|
type UpdateListener = (isAlive: boolean) => void;
|
||||||
|
|
||||||
export default class Media {
|
export default class Media {
|
||||||
#id = uuid();
|
#id = uuid();
|
||||||
|
|
||||||
// Timestamp of last status update
|
get #updateListeners() {
|
||||||
_lastUpdateTime = 0;
|
const updateListeners = MediaUpdateListeners.get(this);
|
||||||
_updateListeners = new Set<UpdateListener>();
|
if (!updateListeners)
|
||||||
|
throw logger.error("Missing media update listeners!");
|
||||||
|
return updateListeners;
|
||||||
|
}
|
||||||
|
get #mediaMessageCallback() {
|
||||||
|
const callback = MediaMessageCallbacks.get(this);
|
||||||
|
if (!callback) throw logger.error("Missing media message callback!");
|
||||||
|
return callback;
|
||||||
|
}
|
||||||
|
get #lastUpdateTime() {
|
||||||
|
const lastUpdateTime = MediaLastUpdateTimes.get(this);
|
||||||
|
if (!lastUpdateTime) throw logger.error("Missing last update time!");
|
||||||
|
return lastUpdateTime;
|
||||||
|
}
|
||||||
|
|
||||||
activeTrackIds: Nullable<number[]> = null;
|
activeTrackIds: Nullable<number[]> = null;
|
||||||
breakStatus?: BreakStatus;
|
breakStatus?: BreakStatus;
|
||||||
@@ -65,19 +100,13 @@ export default class Media {
|
|||||||
preloadedItemId: Nullable<number> = null;
|
preloadedItemId: Nullable<number> = null;
|
||||||
queueData?: QueueData;
|
queueData?: QueueData;
|
||||||
|
|
||||||
constructor(
|
constructor(public sessionId: string, public mediaSessionId: number) {}
|
||||||
public sessionId: string,
|
|
||||||
public mediaSessionId: number,
|
|
||||||
public _sendMediaMessage: (
|
|
||||||
message: DistributiveOmit<SenderMediaMessage, "requestId">
|
|
||||||
) => Promise<void>
|
|
||||||
) {}
|
|
||||||
|
|
||||||
addUpdateListener(listener: UpdateListener) {
|
addUpdateListener(listener: UpdateListener) {
|
||||||
this._updateListeners.add(listener);
|
this.#updateListeners?.add(listener);
|
||||||
}
|
}
|
||||||
removeUpdateListener(listener: UpdateListener) {
|
removeUpdateListener(listener: UpdateListener) {
|
||||||
this._updateListeners.delete(listener);
|
this.#updateListeners?.delete(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
editTracksInfo(
|
editTracksInfo(
|
||||||
@@ -85,7 +114,7 @@ export default class Media {
|
|||||||
successCallback?: () => void,
|
successCallback?: () => void,
|
||||||
errorCallback?: (err: CastError) => void
|
errorCallback?: (err: CastError) => void
|
||||||
) {
|
) {
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...editTracksInfoRequest,
|
...editTracksInfoRequest,
|
||||||
type: "EDIT_TRACKS_INFO",
|
type: "EDIT_TRACKS_INFO",
|
||||||
mediaSessionId: this.mediaSessionId
|
mediaSessionId: this.mediaSessionId
|
||||||
@@ -108,7 +137,7 @@ export default class Media {
|
|||||||
|
|
||||||
return getEstimatedTime({
|
return getEstimatedTime({
|
||||||
currentTime: this.breakStatus.currentBreakClipTime,
|
currentTime: this.breakStatus.currentBreakClipTime,
|
||||||
lastUpdateTime: this._lastUpdateTime,
|
lastUpdateTime: this.#lastUpdateTime,
|
||||||
duration: currentBreakClip.duration
|
duration: currentBreakClip.duration
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -127,7 +156,7 @@ export default class Media {
|
|||||||
|
|
||||||
return getEstimatedTime({
|
return getEstimatedTime({
|
||||||
currentTime: this.breakStatus.currentBreakTime,
|
currentTime: this.breakStatus.currentBreakTime,
|
||||||
lastUpdateTime: this._lastUpdateTime,
|
lastUpdateTime: this.#lastUpdateTime,
|
||||||
duration: currentBreak.duration
|
duration: currentBreak.duration
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -144,7 +173,7 @@ export default class Media {
|
|||||||
if (this.playerState === PlayerState.PLAYING) {
|
if (this.playerState === PlayerState.PLAYING) {
|
||||||
return getEstimatedTime({
|
return getEstimatedTime({
|
||||||
currentTime: this.currentTime,
|
currentTime: this.currentTime,
|
||||||
lastUpdateTime: this._lastUpdateTime,
|
lastUpdateTime: this.#lastUpdateTime,
|
||||||
duration: this.media?.duration
|
duration: this.media?.duration
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -161,7 +190,7 @@ export default class Media {
|
|||||||
successCallback?: () => void,
|
successCallback?: () => void,
|
||||||
errorCallback?: (err: CastError) => void
|
errorCallback?: (err: CastError) => void
|
||||||
) {
|
) {
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...getStatusRequest,
|
...getStatusRequest,
|
||||||
type: "MEDIA_GET_STATUS",
|
type: "MEDIA_GET_STATUS",
|
||||||
mediaSessionId: this.mediaSessionId
|
mediaSessionId: this.mediaSessionId
|
||||||
@@ -175,7 +204,7 @@ export default class Media {
|
|||||||
successCallback?: () => void,
|
successCallback?: () => void,
|
||||||
errorCallback?: (err: CastError) => void
|
errorCallback?: (err: CastError) => void
|
||||||
) {
|
) {
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...pauseRequest,
|
...pauseRequest,
|
||||||
type: "PAUSE",
|
type: "PAUSE",
|
||||||
mediaSessionId: this.mediaSessionId
|
mediaSessionId: this.mediaSessionId
|
||||||
@@ -189,7 +218,7 @@ export default class Media {
|
|||||||
successCallback?: () => void,
|
successCallback?: () => void,
|
||||||
errorCallback?: (err: CastError) => void
|
errorCallback?: (err: CastError) => void
|
||||||
) {
|
) {
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...playRequest,
|
...playRequest,
|
||||||
type: "PLAY",
|
type: "PLAY",
|
||||||
mediaSessionId: this.mediaSessionId
|
mediaSessionId: this.mediaSessionId
|
||||||
@@ -203,7 +232,7 @@ export default class Media {
|
|||||||
successCallback?: () => void,
|
successCallback?: () => void,
|
||||||
errorCallback?: (err: CastError) => void
|
errorCallback?: (err: CastError) => void
|
||||||
) {
|
) {
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...new QueueInsertItemsRequest([item]),
|
...new QueueInsertItemsRequest([item]),
|
||||||
type: "QUEUE_INSERT",
|
type: "QUEUE_INSERT",
|
||||||
sessionId: this.sessionId,
|
sessionId: this.sessionId,
|
||||||
@@ -218,7 +247,7 @@ export default class Media {
|
|||||||
successCallback?: () => void,
|
successCallback?: () => void,
|
||||||
errorCallback?: (err: CastError) => void
|
errorCallback?: (err: CastError) => void
|
||||||
) {
|
) {
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...queueInsertItemsRequest,
|
...queueInsertItemsRequest,
|
||||||
type: "QUEUE_INSERT",
|
type: "QUEUE_INSERT",
|
||||||
sessionId: this.sessionId,
|
sessionId: this.sessionId,
|
||||||
@@ -237,7 +266,7 @@ export default class Media {
|
|||||||
const jumpRequest = new QueueJumpRequest();
|
const jumpRequest = new QueueJumpRequest();
|
||||||
jumpRequest.currentItemId = itemId;
|
jumpRequest.currentItemId = itemId;
|
||||||
|
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...jumpRequest,
|
...jumpRequest,
|
||||||
type: "QUEUE_UPDATE",
|
type: "QUEUE_UPDATE",
|
||||||
sessionId: this.sessionId,
|
sessionId: this.sessionId,
|
||||||
@@ -283,7 +312,7 @@ export default class Media {
|
|||||||
reorderItemsRequest.insertBefore = existingItem.itemId;
|
reorderItemsRequest.insertBefore = existingItem.itemId;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...reorderItemsRequest,
|
...reorderItemsRequest,
|
||||||
type: "QUEUE_REORDER",
|
type: "QUEUE_REORDER",
|
||||||
sessionId: this.sessionId,
|
sessionId: this.sessionId,
|
||||||
@@ -301,7 +330,7 @@ export default class Media {
|
|||||||
const jumpRequest = new QueueJumpRequest();
|
const jumpRequest = new QueueJumpRequest();
|
||||||
jumpRequest.jump = 1;
|
jumpRequest.jump = 1;
|
||||||
|
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...jumpRequest,
|
...jumpRequest,
|
||||||
type: "QUEUE_UPDATE",
|
type: "QUEUE_UPDATE",
|
||||||
sessionId: this.sessionId,
|
sessionId: this.sessionId,
|
||||||
@@ -318,7 +347,7 @@ export default class Media {
|
|||||||
const jumpRequest = new QueueJumpRequest();
|
const jumpRequest = new QueueJumpRequest();
|
||||||
jumpRequest.jump = -1;
|
jumpRequest.jump = -1;
|
||||||
|
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...jumpRequest,
|
...jumpRequest,
|
||||||
type: "QUEUE_UPDATE",
|
type: "QUEUE_UPDATE",
|
||||||
sessionId: this.sessionId,
|
sessionId: this.sessionId,
|
||||||
@@ -348,7 +377,7 @@ export default class Media {
|
|||||||
successCallback?: () => void,
|
successCallback?: () => void,
|
||||||
errorCallback?: (err: CastError) => void
|
errorCallback?: (err: CastError) => void
|
||||||
) {
|
) {
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...queueRemoveItemsRequest,
|
...queueRemoveItemsRequest,
|
||||||
|
|
||||||
mediaSessionId: this.mediaSessionId,
|
mediaSessionId: this.mediaSessionId,
|
||||||
@@ -364,7 +393,7 @@ export default class Media {
|
|||||||
successCallback?: () => void,
|
successCallback?: () => void,
|
||||||
errorCallback?: (err: CastError) => void
|
errorCallback?: (err: CastError) => void
|
||||||
) {
|
) {
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...queueReorderItemsRequest,
|
...queueReorderItemsRequest,
|
||||||
|
|
||||||
mediaSessionId: this.mediaSessionId,
|
mediaSessionId: this.mediaSessionId,
|
||||||
@@ -383,7 +412,7 @@ export default class Media {
|
|||||||
const setPropertiesRequest = new QueueSetPropertiesRequest();
|
const setPropertiesRequest = new QueueSetPropertiesRequest();
|
||||||
setPropertiesRequest.repeatMode = repeatMode;
|
setPropertiesRequest.repeatMode = repeatMode;
|
||||||
|
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...setPropertiesRequest,
|
...setPropertiesRequest,
|
||||||
type: "QUEUE_UPDATE",
|
type: "QUEUE_UPDATE",
|
||||||
sessionId: this.sessionId,
|
sessionId: this.sessionId,
|
||||||
@@ -398,7 +427,7 @@ export default class Media {
|
|||||||
successCallback?: () => void,
|
successCallback?: () => void,
|
||||||
errorCallback?: (err: CastError) => void
|
errorCallback?: (err: CastError) => void
|
||||||
) {
|
) {
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...queueUpdateItemsRequest,
|
...queueUpdateItemsRequest,
|
||||||
type: "QUEUE_UPDATE",
|
type: "QUEUE_UPDATE",
|
||||||
sessionId: this.sessionId,
|
sessionId: this.sessionId,
|
||||||
@@ -413,7 +442,7 @@ export default class Media {
|
|||||||
successCallback?: () => void,
|
successCallback?: () => void,
|
||||||
errorCallback?: (err: CastError) => void
|
errorCallback?: (err: CastError) => void
|
||||||
) {
|
) {
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...seekRequest,
|
...seekRequest,
|
||||||
type: "SEEK",
|
type: "SEEK",
|
||||||
mediaSessionId: this.mediaSessionId
|
mediaSessionId: this.mediaSessionId
|
||||||
@@ -427,7 +456,7 @@ export default class Media {
|
|||||||
successCallback?: () => void,
|
successCallback?: () => void,
|
||||||
errorCallback?: (err: CastError) => void
|
errorCallback?: (err: CastError) => void
|
||||||
) {
|
) {
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...volumeRequest,
|
...volumeRequest,
|
||||||
type: "MEDIA_SET_VOLUME",
|
type: "MEDIA_SET_VOLUME",
|
||||||
mediaSessionId: this.mediaSessionId
|
mediaSessionId: this.mediaSessionId
|
||||||
@@ -445,7 +474,7 @@ export default class Media {
|
|||||||
stopRequest = new StopRequest();
|
stopRequest = new StopRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._sendMediaMessage({
|
this.#mediaMessageCallback?.({
|
||||||
...stopRequest,
|
...stopRequest,
|
||||||
type: "STOP",
|
type: "STOP",
|
||||||
mediaSessionId: this.mediaSessionId
|
mediaSessionId: this.mediaSessionId
|
||||||
|
|||||||
@@ -1,140 +1,20 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import Media from "./Media";
|
export * from "./enums";
|
||||||
|
export * from "./classes";
|
||||||
|
|
||||||
import {
|
export { default as Media } from "./Media";
|
||||||
ContainerType,
|
|
||||||
HdrType,
|
|
||||||
HlsSegmentFormat,
|
|
||||||
HlsVideoSegmentFormat,
|
|
||||||
IdleReason,
|
|
||||||
MediaCommand,
|
|
||||||
MetadataType,
|
|
||||||
PlayerState,
|
|
||||||
QueueType,
|
|
||||||
RepeatMode,
|
|
||||||
ResumeState,
|
|
||||||
StreamType,
|
|
||||||
TextTrackEdgeType,
|
|
||||||
TextTrackFontGenericFamily,
|
|
||||||
TextTrackFontStyle,
|
|
||||||
TextTrackType,
|
|
||||||
TextTrackWindowType,
|
|
||||||
TrackType,
|
|
||||||
UserAction
|
|
||||||
} from "./enums";
|
|
||||||
|
|
||||||
import {
|
export const DEFAULT_MEDIA_RECEIVER_APP_ID = "CC1AD845";
|
||||||
AudiobookChapterMediaMetadata,
|
|
||||||
AudiobookContainerMetadata,
|
|
||||||
Break,
|
|
||||||
BreakClip,
|
|
||||||
BreakStatus,
|
|
||||||
ContainerMetadata,
|
|
||||||
EditTracksInfoRequest,
|
|
||||||
GenericMediaMetadata,
|
|
||||||
GetStatusRequest,
|
|
||||||
LiveSeekableRange,
|
|
||||||
LoadRequest,
|
|
||||||
MediaInfo,
|
|
||||||
MediaMetadata,
|
|
||||||
MovieMediaMetadata,
|
|
||||||
MusicTrackMediaMetadata,
|
|
||||||
PauseRequest,
|
|
||||||
PhotoMediaMetadata,
|
|
||||||
PlayRequest,
|
|
||||||
QueueData,
|
|
||||||
QueueInsertItemsRequest,
|
|
||||||
QueueItem,
|
|
||||||
QueueJumpRequest,
|
|
||||||
QueueLoadRequest,
|
|
||||||
QueueRemoveItemsRequest,
|
|
||||||
QueueReorderItemsRequest,
|
|
||||||
QueueSetPropertiesRequest,
|
|
||||||
QueueUpdateItemsRequest,
|
|
||||||
SeekRequest,
|
|
||||||
StopRequest,
|
|
||||||
TextTrackStyle,
|
|
||||||
Track,
|
|
||||||
TvShowMediaMetadata,
|
|
||||||
UserActionState,
|
|
||||||
VastAdsRequest,
|
|
||||||
VideoInformation,
|
|
||||||
VolumeRequest
|
|
||||||
} from "./classes";
|
|
||||||
|
|
||||||
export default {
|
export const timeout = {
|
||||||
DEFAULT_MEDIA_RECEIVER_APP_ID: "CC1AD845",
|
editTracksInfo: 0,
|
||||||
timeout: {
|
getStatus: 0,
|
||||||
editTracksInfo: 0,
|
load: 0,
|
||||||
getStatus: 0,
|
pause: 0,
|
||||||
load: 0,
|
play: 0,
|
||||||
pause: 0,
|
queue: 0,
|
||||||
play: 0,
|
seek: 0,
|
||||||
queue: 0,
|
setVolume: 0,
|
||||||
seek: 0,
|
stop: 0
|
||||||
setVolume: 0,
|
|
||||||
stop: 0
|
|
||||||
},
|
|
||||||
|
|
||||||
Media,
|
|
||||||
|
|
||||||
// Enums
|
|
||||||
ContainerType,
|
|
||||||
HdrType,
|
|
||||||
HlsSegmentFormat,
|
|
||||||
HlsVideoSegmentFormat,
|
|
||||||
IdleReason,
|
|
||||||
MediaCommand,
|
|
||||||
MetadataType,
|
|
||||||
PlayerState,
|
|
||||||
QueueType,
|
|
||||||
RepeatMode,
|
|
||||||
ResumeState,
|
|
||||||
StreamType,
|
|
||||||
TextTrackEdgeType,
|
|
||||||
TextTrackFontGenericFamily,
|
|
||||||
TextTrackFontStyle,
|
|
||||||
TextTrackType,
|
|
||||||
TextTrackWindowType,
|
|
||||||
TrackType,
|
|
||||||
UserAction,
|
|
||||||
|
|
||||||
// Classes
|
|
||||||
AudiobookChapterMediaMetadata,
|
|
||||||
AudiobookContainerMetadata,
|
|
||||||
Break,
|
|
||||||
BreakClip,
|
|
||||||
BreakStatus,
|
|
||||||
ContainerMetadata,
|
|
||||||
EditTracksInfoRequest,
|
|
||||||
GenericMediaMetadata,
|
|
||||||
GetStatusRequest,
|
|
||||||
LiveSeekableRange,
|
|
||||||
LoadRequest,
|
|
||||||
MediaInfo,
|
|
||||||
MediaMetadata,
|
|
||||||
MovieMediaMetadata,
|
|
||||||
MusicTrackMediaMetadata,
|
|
||||||
PauseRequest,
|
|
||||||
PhotoMediaMetadata,
|
|
||||||
PlayRequest,
|
|
||||||
QueueData,
|
|
||||||
QueueInsertItemsRequest,
|
|
||||||
QueueItem,
|
|
||||||
QueueJumpRequest,
|
|
||||||
QueueLoadRequest,
|
|
||||||
QueueRemoveItemsRequest,
|
|
||||||
QueueReorderItemsRequest,
|
|
||||||
QueueSetPropertiesRequest,
|
|
||||||
QueueUpdateItemsRequest,
|
|
||||||
SeekRequest,
|
|
||||||
StopRequest,
|
|
||||||
TextTrackStyle,
|
|
||||||
Track,
|
|
||||||
TvShowMediaMetadata,
|
|
||||||
UserActionState,
|
|
||||||
VastAdsRequest,
|
|
||||||
VideoInformation,
|
|
||||||
VolumeRequest
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const logger = new Logger("fx_cast [media sender]");
|
|||||||
interface MediaSenderOpts {
|
interface MediaSenderOpts {
|
||||||
mediaUrl: string;
|
mediaUrl: string;
|
||||||
contextTabId?: number;
|
contextTabId?: number;
|
||||||
targetElementId?: number;
|
mediaElement?: HTMLMediaElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MediaSender {
|
export default class MediaSender {
|
||||||
@@ -23,6 +23,7 @@ export default class MediaSender {
|
|||||||
private mediaUrl: string;
|
private mediaUrl: string;
|
||||||
private contextTabId?: number;
|
private contextTabId?: number;
|
||||||
|
|
||||||
|
/** Target media element if loaded as a content script. */
|
||||||
private mediaElement?: HTMLMediaElement;
|
private mediaElement?: HTMLMediaElement;
|
||||||
|
|
||||||
private isLocalMedia = false;
|
private isLocalMedia = false;
|
||||||
@@ -34,12 +35,7 @@ export default class MediaSender {
|
|||||||
constructor(opts: MediaSenderOpts) {
|
constructor(opts: MediaSenderOpts) {
|
||||||
this.mediaUrl = opts.mediaUrl;
|
this.mediaUrl = opts.mediaUrl;
|
||||||
this.contextTabId = opts.contextTabId;
|
this.contextTabId = opts.contextTabId;
|
||||||
|
this.mediaElement = opts.mediaElement;
|
||||||
if (opts.targetElementId) {
|
|
||||||
this.mediaElement = browser.menus.getTargetElement(
|
|
||||||
opts.targetElementId
|
|
||||||
) as HTMLMediaElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
@@ -79,7 +75,7 @@ export default class MediaSender {
|
|||||||
),
|
),
|
||||||
undefined,
|
undefined,
|
||||||
err => {
|
err => {
|
||||||
logger.error("Failed to initialize cast API", err);
|
logger.error("Failed to initialize cast SDK", err);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -270,10 +266,21 @@ export default class MediaSender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If loaded as a content script, opts are stored on the window object.
|
||||||
|
*/
|
||||||
if (window.location.protocol !== "moz-extension:") {
|
if (window.location.protocol !== "moz-extension:") {
|
||||||
const window_ = window as any;
|
const window_ = window as any;
|
||||||
|
|
||||||
|
let mediaElement: Optional<HTMLMediaElement>;
|
||||||
|
if (window_.targetElementId) {
|
||||||
|
mediaElement = browser.menus.getTargetElement(
|
||||||
|
window_.targetElementId
|
||||||
|
) as HTMLMediaElement;
|
||||||
|
}
|
||||||
|
|
||||||
new MediaSender({
|
new MediaSender({
|
||||||
mediaUrl: window_.mediaUrl,
|
mediaUrl: window_.mediaUrl,
|
||||||
targetElementId: window_.targetElementId
|
mediaElement
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { ReceiverDevice, ReceiverDeviceCapabilities } from "../types";
|
import { ReceiverDevice, ReceiverDeviceCapabilities } from "../types";
|
||||||
import { Receiver } from "./sdk/classes";
|
import { Receiver } from "./sdk/classes";
|
||||||
import { Capability, ReceiverType } from "./sdk/enums";
|
import { Capability, ReceiverType } from "./sdk/enums";
|
||||||
|
import { MediaCommand } from "./sdk/media/enums";
|
||||||
|
import { _MediaCommand } from "./sdk/types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check receiver device capabilities bitflags against array of
|
* Check receiver device capabilities bitflags against array of
|
||||||
@@ -29,8 +31,8 @@ export function hasRequiredCapabilities(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Convert capabilities bitflags to string array. */
|
||||||
export function convertCapabilitiesFlags(flags: ReceiverDeviceCapabilities) {
|
export function convertCapabilitiesFlags(flags: ReceiverDeviceCapabilities) {
|
||||||
// Convert capabilities bitflag to string array
|
|
||||||
const capabilities: Capability[] = [];
|
const capabilities: Capability[] = [];
|
||||||
if (flags & ReceiverDeviceCapabilities.VIDEO_OUT)
|
if (flags & ReceiverDeviceCapabilities.VIDEO_OUT)
|
||||||
capabilities.push(Capability.VIDEO_OUT);
|
capabilities.push(Capability.VIDEO_OUT);
|
||||||
@@ -47,6 +49,26 @@ export function convertCapabilitiesFlags(flags: ReceiverDeviceCapabilities) {
|
|||||||
return capabilities;
|
return capabilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Convert media commands bitflags to string array. */
|
||||||
|
export function convertSupportedMediaCommandsFlags(flags: _MediaCommand) {
|
||||||
|
const supportedMediaCommands: string[] = [];
|
||||||
|
if (flags & _MediaCommand.PAUSE) {
|
||||||
|
supportedMediaCommands.push(MediaCommand.PAUSE);
|
||||||
|
} else if (flags & _MediaCommand.SEEK) {
|
||||||
|
supportedMediaCommands.push(MediaCommand.SEEK);
|
||||||
|
} else if (flags & _MediaCommand.STREAM_VOLUME) {
|
||||||
|
supportedMediaCommands.push(MediaCommand.STREAM_VOLUME);
|
||||||
|
} else if (flags & _MediaCommand.STREAM_MUTE) {
|
||||||
|
supportedMediaCommands.push(MediaCommand.STREAM_MUTE);
|
||||||
|
} else if (flags & _MediaCommand.QUEUE_NEXT) {
|
||||||
|
supportedMediaCommands.push("queue_next");
|
||||||
|
} else if (flags & _MediaCommand.QUEUE_PREV) {
|
||||||
|
supportedMediaCommands.push("queue_prev");
|
||||||
|
}
|
||||||
|
|
||||||
|
return supportedMediaCommands;
|
||||||
|
}
|
||||||
|
|
||||||
interface GetEstimatedTimeOpts {
|
interface GetEstimatedTimeOpts {
|
||||||
currentTime: number;
|
currentTime: number;
|
||||||
lastUpdateTime: number;
|
lastUpdateTime: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user