Hide Session/Media private data and preserve Media constructor signature

This commit is contained in:
hensm
2022-09-01 04:36:42 +01:00
committed by Matt Hensman
parent 7a35da2ba1
commit 6562294586
7 changed files with 237 additions and 257 deletions

View File

@@ -5,13 +5,13 @@ import { v4 as uuid } from "uuid";
import logger from "../../lib/logger";
import eventMessaging from "../pageMessenging";
import { convertSupportedMediaCommandsFlags } from "../utils";
import {
import type {
MediaStatus,
ReceiverMediaMessage,
SenderMediaMessage,
SenderMessage,
_MediaCommand
SenderMessage
} from "./types";
import { SessionStatus } from "./enums";
@@ -22,9 +22,13 @@ import type {
SenderApplication
} from "./classes";
import { MediaCommand } from "./media/enums";
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
@@ -32,7 +36,7 @@ import Media, { NS_MEDIA } from "./media/Media";
*/
function updateMedia(media: Media, status: MediaStatus) {
if (status.currentTime) {
media._lastUpdateTime = Date.now();
MediaLastUpdateTimes.set(media, Date.now());
}
// Copy basic props
@@ -46,23 +50,9 @@ function updateMedia(media: Media, status: MediaStatus) {
if (status.repeatMode) media.repeatMode = status.repeatMode;
if (status.volume) media.volume = status.volume;
// Convert supportedMediaCommands bitflags to string array
const supportedMediaCommands: string[] = [];
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;
media.supportedMediaCommands = convertSupportedMediaCommandsFlags(
status.supportedMediaCommands
);
// Update queue state
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 UpdateListener = (isAlive: boolean) => void;
type SendMessageCallback = [(() => void)?, ((err: CastError) => void)?];
export default class Session {
#loadMediaRequest?: LoadRequest;
#loadMediaSuccessCallback?: (media: Media) => void;
#loadMediaErrorCallback?: (err: CastError) => void;
_messageListeners = new Map<string, Set<MessageListener>>();
_updateListeners = new Set<UpdateListener>();
_sendMessageCallbacks = new Map<
string,
[(() => void)?, ((err: CastError) => void)?]
>();
get #messageListeners() {
const messageListeners = SessionMessageListeners.get(this);
if (!messageListeners)
throw logger.error("Missing session message listeners!");
return messageListeners;
}
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[] = [];
namespaces: Array<{ name: string }> = [];
@@ -129,6 +155,7 @@ export default class Session {
) {
this.transportId = sessionId || "";
SessionMessageListeners.set(this, new Map());
this.addMessageListener(NS_MEDIA, this.#mediaMessageListener);
}
@@ -147,9 +174,8 @@ export default class Session {
// Handle Media creation
if (!media) {
media = new Media(
this.sessionId,
mediaStatus.mediaSessionId,
media = createMedia(
[this.sessionId, mediaStatus.mediaSessionId],
this.#sendMediaMessage
);
@@ -158,8 +184,11 @@ export default class Session {
this.#loadMediaSuccessCallback?.(media);
} else {
updateMedia(media, mediaStatus);
for (const listener of media._updateListeners) {
listener(true);
const updateListeners = MediaUpdateListeners.get(media);
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) {
if (!this._messageListeners.has(namespace)) {
this._messageListeners.set(namespace, new Set());
if (!this.#messageListeners.has(namespace)) {
this.#messageListeners.set(namespace, new Set());
}
this._messageListeners.get(namespace)?.add(listener);
this.#messageListeners.get(namespace)?.add(listener);
}
removeMessageListener(namespace: string, listener: MessageListener) {
this._messageListeners.get(namespace)?.delete(listener);
this.#messageListeners.get(namespace)?.delete(listener);
}
addUpdateListener(listener: UpdateListener) {
this._updateListeners.add(listener);
this.#updateListeners.add(listener);
}
removeUpdateListener(listener: UpdateListener) {
this._updateListeners.delete(listener);
this.#updateListeners.delete(listener);
}
leave(
@@ -272,7 +301,7 @@ export default class Session {
}
});
this._sendMessageCallbacks.set(messageId, [
this.#sendMessageCallbacks.set(messageId, [
successCallback,
errorCallback
]);

View File

@@ -6,6 +6,7 @@ import {
AutoJoinPolicy,
Capability,
DefaultActionPolicy,
ErrorCode,
ReceiverAvailability,
ReceiverType,
VolumeControlType
@@ -35,7 +36,7 @@ export class DialRequest {
export class Error {
constructor(
public code: string,
public code: ErrorCode,
public description: Nullable<string> = null,
public details: unknown = null
) {}

View File

@@ -33,9 +33,14 @@ import {
Volume
} 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 = (
receiver: Receiver,
@@ -49,6 +54,7 @@ export default class {
#apiConfig?: ApiConfig;
#sessionRequest?: SessionRequest;
/** Current receiver availability. */
#receiverAvailability = ReceiverAvailability.UNAVAILABLE;
#initializeSuccessCallback?: () => void;
@@ -87,7 +93,7 @@ export default class {
Volume = Volume;
Session = Session;
media = media;
media = { ...media };
VERSION = [1, 2];
isAvailable = false;
@@ -101,10 +107,19 @@ export default class {
switch (message.subject) {
case "cast:initialized":
this.isAvailable = true;
this.#initializeSuccessCallback?.();
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;
/**
@@ -120,13 +135,13 @@ export default class {
status.appImages
);
const session = new Session(
const session = createSession([
status.sessionId,
status.appId,
status.displayName,
status.appImages,
status.receiver
);
]);
session.namespaces = status.namespaces;
session.senderApps = status.senderApps;
@@ -164,8 +179,11 @@ export default class {
session.namespaces = status.namespaces;
session.receiver.volume = status.volume;
for (const listener of session._updateListeners) {
listener(session.status !== SessionStatus.STOPPED);
const updateListeners = SessionUpdateListeners.get(session);
if (updateListeners) {
for (const listener of updateListeners) {
listener(session.status !== SessionStatus.STOPPED);
}
}
break;
@@ -176,8 +194,12 @@ export default class {
const session = this.#sessions.get(sessionId);
if (session) {
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 session = this.#sessions.get(sessionId);
if (session) {
const listeners = session._messageListeners.get(namespace);
const listeners =
SessionMessageListeners.get(session)?.get(namespace);
if (listeners) {
for (const listener of listeners) {
listener(namespace, messageData);
@@ -207,12 +230,16 @@ export default class {
break;
}
const callbacks = session._sendMessageCallbacks.get(messageId);
if (callbacks) {
const [successCallback, errorCallback] = callbacks;
const sendMessageCallback =
SessionSendMessageCallbacks.get(session)?.get(messageId);
if (sendMessageCallback) {
const [successCallback, errorCallback] =
sendMessageCallback;
if (error) {
errorCallback?.(new CastError(error));
errorCallback?.(
new CastError(ErrorCode.CHANNEL_ERROR, error)
);
return;
}
@@ -236,26 +263,11 @@ export default class {
break;
}
// Popup closed before session established
case "cast:sessionRequestCancelled": {
if (this.#sessionRequest) {
this.#sessionRequest = undefined;
this.#requestSessionErrorCallback?.(
new CastError(ErrorCode.CANCEL)
);
}
break;
}
case "cast:receiverAction": {
case "cast:receiverAction":
for (const actionListener of this.#receiverActionListeners) {
actionListener(message.data.receiver, message.data.action);
}
break;
}
}
}

View File

@@ -2,9 +2,15 @@
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 { ErrorCode } from "../enums";
import {
BreakStatus,
EditTracksInfoRequest,
@@ -26,23 +32,52 @@ import {
VideoInformation,
VolumeRequest
} from "./classes";
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";
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;
export default class Media {
#id = uuid();
// Timestamp of last status update
_lastUpdateTime = 0;
_updateListeners = new Set<UpdateListener>();
get #updateListeners() {
const updateListeners = MediaUpdateListeners.get(this);
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;
breakStatus?: BreakStatus;
@@ -65,19 +100,13 @@ export default class Media {
preloadedItemId: Nullable<number> = null;
queueData?: QueueData;
constructor(
public sessionId: string,
public mediaSessionId: number,
public _sendMediaMessage: (
message: DistributiveOmit<SenderMediaMessage, "requestId">
) => Promise<void>
) {}
constructor(public sessionId: string, public mediaSessionId: number) {}
addUpdateListener(listener: UpdateListener) {
this._updateListeners.add(listener);
this.#updateListeners?.add(listener);
}
removeUpdateListener(listener: UpdateListener) {
this._updateListeners.delete(listener);
this.#updateListeners?.delete(listener);
}
editTracksInfo(
@@ -85,7 +114,7 @@ export default class Media {
successCallback?: () => void,
errorCallback?: (err: CastError) => void
) {
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...editTracksInfoRequest,
type: "EDIT_TRACKS_INFO",
mediaSessionId: this.mediaSessionId
@@ -108,7 +137,7 @@ export default class Media {
return getEstimatedTime({
currentTime: this.breakStatus.currentBreakClipTime,
lastUpdateTime: this._lastUpdateTime,
lastUpdateTime: this.#lastUpdateTime,
duration: currentBreakClip.duration
});
}
@@ -127,7 +156,7 @@ export default class Media {
return getEstimatedTime({
currentTime: this.breakStatus.currentBreakTime,
lastUpdateTime: this._lastUpdateTime,
lastUpdateTime: this.#lastUpdateTime,
duration: currentBreak.duration
});
}
@@ -144,7 +173,7 @@ export default class Media {
if (this.playerState === PlayerState.PLAYING) {
return getEstimatedTime({
currentTime: this.currentTime,
lastUpdateTime: this._lastUpdateTime,
lastUpdateTime: this.#lastUpdateTime,
duration: this.media?.duration
});
}
@@ -161,7 +190,7 @@ export default class Media {
successCallback?: () => void,
errorCallback?: (err: CastError) => void
) {
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...getStatusRequest,
type: "MEDIA_GET_STATUS",
mediaSessionId: this.mediaSessionId
@@ -175,7 +204,7 @@ export default class Media {
successCallback?: () => void,
errorCallback?: (err: CastError) => void
) {
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...pauseRequest,
type: "PAUSE",
mediaSessionId: this.mediaSessionId
@@ -189,7 +218,7 @@ export default class Media {
successCallback?: () => void,
errorCallback?: (err: CastError) => void
) {
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...playRequest,
type: "PLAY",
mediaSessionId: this.mediaSessionId
@@ -203,7 +232,7 @@ export default class Media {
successCallback?: () => void,
errorCallback?: (err: CastError) => void
) {
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...new QueueInsertItemsRequest([item]),
type: "QUEUE_INSERT",
sessionId: this.sessionId,
@@ -218,7 +247,7 @@ export default class Media {
successCallback?: () => void,
errorCallback?: (err: CastError) => void
) {
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...queueInsertItemsRequest,
type: "QUEUE_INSERT",
sessionId: this.sessionId,
@@ -237,7 +266,7 @@ export default class Media {
const jumpRequest = new QueueJumpRequest();
jumpRequest.currentItemId = itemId;
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...jumpRequest,
type: "QUEUE_UPDATE",
sessionId: this.sessionId,
@@ -283,7 +312,7 @@ export default class Media {
reorderItemsRequest.insertBefore = existingItem.itemId;
}
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...reorderItemsRequest,
type: "QUEUE_REORDER",
sessionId: this.sessionId,
@@ -301,7 +330,7 @@ export default class Media {
const jumpRequest = new QueueJumpRequest();
jumpRequest.jump = 1;
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...jumpRequest,
type: "QUEUE_UPDATE",
sessionId: this.sessionId,
@@ -318,7 +347,7 @@ export default class Media {
const jumpRequest = new QueueJumpRequest();
jumpRequest.jump = -1;
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...jumpRequest,
type: "QUEUE_UPDATE",
sessionId: this.sessionId,
@@ -348,7 +377,7 @@ export default class Media {
successCallback?: () => void,
errorCallback?: (err: CastError) => void
) {
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...queueRemoveItemsRequest,
mediaSessionId: this.mediaSessionId,
@@ -364,7 +393,7 @@ export default class Media {
successCallback?: () => void,
errorCallback?: (err: CastError) => void
) {
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...queueReorderItemsRequest,
mediaSessionId: this.mediaSessionId,
@@ -383,7 +412,7 @@ export default class Media {
const setPropertiesRequest = new QueueSetPropertiesRequest();
setPropertiesRequest.repeatMode = repeatMode;
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...setPropertiesRequest,
type: "QUEUE_UPDATE",
sessionId: this.sessionId,
@@ -398,7 +427,7 @@ export default class Media {
successCallback?: () => void,
errorCallback?: (err: CastError) => void
) {
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...queueUpdateItemsRequest,
type: "QUEUE_UPDATE",
sessionId: this.sessionId,
@@ -413,7 +442,7 @@ export default class Media {
successCallback?: () => void,
errorCallback?: (err: CastError) => void
) {
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...seekRequest,
type: "SEEK",
mediaSessionId: this.mediaSessionId
@@ -427,7 +456,7 @@ export default class Media {
successCallback?: () => void,
errorCallback?: (err: CastError) => void
) {
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...volumeRequest,
type: "MEDIA_SET_VOLUME",
mediaSessionId: this.mediaSessionId
@@ -445,7 +474,7 @@ export default class Media {
stopRequest = new StopRequest();
}
this._sendMediaMessage({
this.#mediaMessageCallback?.({
...stopRequest,
type: "STOP",
mediaSessionId: this.mediaSessionId

View File

@@ -1,140 +1,20 @@
"use strict";
import Media from "./Media";
export * from "./enums";
export * from "./classes";
import {
ContainerType,
HdrType,
HlsSegmentFormat,
HlsVideoSegmentFormat,
IdleReason,
MediaCommand,
MetadataType,
PlayerState,
QueueType,
RepeatMode,
ResumeState,
StreamType,
TextTrackEdgeType,
TextTrackFontGenericFamily,
TextTrackFontStyle,
TextTrackType,
TextTrackWindowType,
TrackType,
UserAction
} from "./enums";
export { default as Media } from "./Media";
import {
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 const DEFAULT_MEDIA_RECEIVER_APP_ID = "CC1AD845";
export default {
DEFAULT_MEDIA_RECEIVER_APP_ID: "CC1AD845",
timeout: {
editTracksInfo: 0,
getStatus: 0,
load: 0,
pause: 0,
play: 0,
queue: 0,
seek: 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
export const timeout = {
editTracksInfo: 0,
getStatus: 0,
load: 0,
pause: 0,
play: 0,
queue: 0,
seek: 0,
setVolume: 0,
stop: 0
};

View File

@@ -14,7 +14,7 @@ const logger = new Logger("fx_cast [media sender]");
interface MediaSenderOpts {
mediaUrl: string;
contextTabId?: number;
targetElementId?: number;
mediaElement?: HTMLMediaElement;
}
export default class MediaSender {
@@ -23,6 +23,7 @@ export default class MediaSender {
private mediaUrl: string;
private contextTabId?: number;
/** Target media element if loaded as a content script. */
private mediaElement?: HTMLMediaElement;
private isLocalMedia = false;
@@ -34,12 +35,7 @@ export default class MediaSender {
constructor(opts: MediaSenderOpts) {
this.mediaUrl = opts.mediaUrl;
this.contextTabId = opts.contextTabId;
if (opts.targetElementId) {
this.mediaElement = browser.menus.getTargetElement(
opts.targetElementId
) as HTMLMediaElement;
}
this.mediaElement = opts.mediaElement;
this.init();
}
@@ -79,7 +75,7 @@ export default class MediaSender {
),
undefined,
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:") {
const window_ = window as any;
let mediaElement: Optional<HTMLMediaElement>;
if (window_.targetElementId) {
mediaElement = browser.menus.getTargetElement(
window_.targetElementId
) as HTMLMediaElement;
}
new MediaSender({
mediaUrl: window_.mediaUrl,
targetElementId: window_.targetElementId
mediaElement
});
}

View File

@@ -1,6 +1,8 @@
import { ReceiverDevice, ReceiverDeviceCapabilities } from "../types";
import { Receiver } from "./sdk/classes";
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
@@ -29,8 +31,8 @@ export function hasRequiredCapabilities(
});
}
/** Convert capabilities bitflags to string array. */
export function convertCapabilitiesFlags(flags: ReceiverDeviceCapabilities) {
// Convert capabilities bitflag to string array
const capabilities: Capability[] = [];
if (flags & ReceiverDeviceCapabilities.VIDEO_OUT)
capabilities.push(Capability.VIDEO_OUT);
@@ -47,6 +49,26 @@ export function convertCapabilitiesFlags(flags: ReceiverDeviceCapabilities) {
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 {
currentTime: number;
lastUpdateTime: number;