mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-08 08:39:59 +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 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
|
||||
]);
|
||||
|
||||
@@ -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
|
||||
) {}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user