mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-10 09:39:58 +00:00
426 lines
12 KiB
TypeScript
Executable File
426 lines
12 KiB
TypeScript
Executable File
"use strict";
|
|
|
|
import logger from "../../lib/logger";
|
|
|
|
import ApiConfig from "./classes/ApiConfig";
|
|
import CredentialsData from "./classes/CredentialsData";
|
|
import DialRequest from "./classes/DialRequest";
|
|
import Error_ from "./classes/Error";
|
|
import Image_ from "./classes/Image";
|
|
import Receiver_ from "./classes/Receiver";
|
|
import ReceiverDisplayStatus from "./classes/ReceiverDisplayStatus";
|
|
import SenderApplication from "./classes/SenderApplication";
|
|
import Session from "./classes/Session";
|
|
import SessionRequest from "./classes/SessionRequest";
|
|
import Timeout from "./classes/Timeout";
|
|
import Volume from "./classes/Volume";
|
|
|
|
import { AutoJoinPolicy
|
|
, Capability
|
|
, DefaultActionPolicy
|
|
, DialAppState
|
|
, ErrorCode
|
|
, ReceiverAction
|
|
, ReceiverAvailability
|
|
, ReceiverType
|
|
, SenderPlatform
|
|
, SessionStatus
|
|
, VolumeControlType } from "./enums";
|
|
|
|
import * as media from "./media";
|
|
|
|
import { Receiver } from "../../types";
|
|
import { onMessage, sendMessageResponse } from "../eventMessageChannel";
|
|
|
|
|
|
type ReceiverActionListener = (
|
|
receiver: Receiver_
|
|
, receiverAction: string) => void;
|
|
|
|
type RequestSessionSuccessCallback = (session: Session) => void;
|
|
|
|
type SuccessCallback = () => void;
|
|
type ErrorCallback = (err: Error_) => void;
|
|
|
|
|
|
let apiConfig: ApiConfig;
|
|
|
|
const receiverList: Array<{ id: string }> = [];
|
|
const sessionList: Session[] = [];
|
|
|
|
const receiverActionListeners = new Set<ReceiverActionListener>();
|
|
|
|
let sessionRequestInProgress = false;
|
|
let sessionSuccessCallback: RequestSessionSuccessCallback;
|
|
let sessionErrorCallback: ErrorCallback;
|
|
|
|
|
|
export {
|
|
// Enums
|
|
AutoJoinPolicy, Capability, DefaultActionPolicy, DialAppState, ErrorCode
|
|
, ReceiverAction, ReceiverAvailability, ReceiverType, SenderPlatform
|
|
, SessionStatus, VolumeControlType
|
|
|
|
// Classes
|
|
, ApiConfig, CredentialsData, DialRequest, ReceiverDisplayStatus
|
|
, SenderApplication, Session, SessionRequest, Timeout, Volume
|
|
|
|
, Error_ as Error
|
|
, Image_ as Image
|
|
, Receiver_ as Receiver
|
|
|
|
, media
|
|
};
|
|
|
|
export let isAvailable = false;
|
|
export const timeout = new Timeout();
|
|
export const VERSION = [1, 2];
|
|
|
|
export function addReceiverActionListener (
|
|
listener: ReceiverActionListener): void {
|
|
|
|
receiverActionListeners.add(listener);
|
|
}
|
|
|
|
export function initialize (
|
|
newApiConfig: ApiConfig
|
|
, successCallback?: SuccessCallback
|
|
, errorCallback?: ErrorCallback): void {
|
|
|
|
logger.info("cast.initialize");
|
|
|
|
// Already initialized
|
|
if (apiConfig) {
|
|
if (errorCallback) {
|
|
errorCallback(new Error_(ErrorCode.INVALID_PARAMETER));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
apiConfig = newApiConfig;
|
|
|
|
sendMessageResponse({
|
|
subject: "main:/shimInitialized"
|
|
, data: { appId: apiConfig.sessionRequest.appId }
|
|
});
|
|
|
|
if (successCallback) {
|
|
successCallback();
|
|
}
|
|
|
|
apiConfig.receiverListener(receiverList.length
|
|
? ReceiverAvailability.AVAILABLE
|
|
: ReceiverAvailability.UNAVAILABLE);
|
|
}
|
|
|
|
export function logMessage (message: string): void {
|
|
/* tslint:disable-next-line:no-console */
|
|
console.log("CAST MSG:", message);
|
|
}
|
|
|
|
export function precache (_data: string): void {
|
|
logger.info("STUB :: cast.precache");
|
|
}
|
|
|
|
export function removeReceiverActionListener (
|
|
listener: ReceiverActionListener): void {
|
|
|
|
receiverActionListeners.delete(listener);
|
|
}
|
|
|
|
export function requestSession (
|
|
successCallback: RequestSessionSuccessCallback
|
|
, errorCallback: ErrorCallback
|
|
, _sessionRequest: SessionRequest = apiConfig.sessionRequest): void {
|
|
|
|
logger.info("cast.requestSession");
|
|
|
|
// Called before initialization
|
|
if (!apiConfig) {
|
|
if (errorCallback) {
|
|
errorCallback(new Error_(ErrorCode.API_NOT_INITIALIZED));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Already requesting session
|
|
if (sessionRequestInProgress) {
|
|
if (errorCallback) {
|
|
errorCallback(new Error_(ErrorCode.INVALID_PARAMETER
|
|
, "Session request already in progress."));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// No available receivers
|
|
if (!receiverList.length) {
|
|
if (errorCallback) {
|
|
errorCallback(new Error_(ErrorCode.RECEIVER_UNAVAILABLE));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
sessionRequestInProgress = true;
|
|
|
|
sessionSuccessCallback = successCallback;
|
|
sessionErrorCallback = errorCallback;
|
|
|
|
// Open destination chooser
|
|
sendMessageResponse({
|
|
subject: "main:/selectReceiverBegin"
|
|
});
|
|
}
|
|
|
|
export function _requestSession (
|
|
_receiver: Receiver
|
|
, successCallback?: RequestSessionSuccessCallback
|
|
, errorCallback?: ErrorCallback): void {
|
|
|
|
logger.info("cast._requestSession");
|
|
|
|
if (!apiConfig) {
|
|
if (errorCallback) {
|
|
errorCallback(new Error_(ErrorCode.API_NOT_INITIALIZED));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (sessionRequestInProgress) {
|
|
if (errorCallback) {
|
|
errorCallback(new Error_(ErrorCode.INVALID_PARAMETER
|
|
, "Session request already in progress."));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (!receiverList.length) {
|
|
if (errorCallback) {
|
|
errorCallback(new Error_(ErrorCode.RECEIVER_UNAVAILABLE));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
sessionRequestInProgress = true;
|
|
|
|
|
|
const selectedReceiver = new Receiver_(
|
|
_receiver.id
|
|
, _receiver.friendlyName);
|
|
|
|
(selectedReceiver as any)._address = _receiver.host;
|
|
(selectedReceiver as any)._port = _receiver.port;
|
|
|
|
function createSession () {
|
|
sessionList.push(new Session(
|
|
sessionList.length.toString() // sessionId
|
|
, apiConfig.sessionRequest.appId // appId
|
|
, _receiver.friendlyName // displayName
|
|
, [] // appImages
|
|
, selectedReceiver // receiver
|
|
, (session: Session) => {
|
|
sendMessageResponse({
|
|
subject: "main:/sessionCreated"
|
|
});
|
|
|
|
sessionRequestInProgress = false;
|
|
|
|
if (successCallback) {
|
|
successCallback(session);
|
|
}
|
|
}));
|
|
}
|
|
|
|
// If an existing session is active, stop it and start new one
|
|
if (sessionList.length) {
|
|
const lastSession = sessionList[sessionList.length - 1];
|
|
|
|
if (lastSession.status !== SessionStatus.STOPPED) {
|
|
lastSession.stop(createSession);
|
|
}
|
|
} else {
|
|
createSession();
|
|
}
|
|
}
|
|
|
|
export function requestSessionById (_sessionId: string): void {
|
|
logger.info("STUB :: cast.requestSessionById");
|
|
}
|
|
|
|
export function setCustomReceivers (
|
|
_receivers: Receiver_[]
|
|
, _successCallback?: SuccessCallback
|
|
, _errorCallback?: ErrorCallback): void {
|
|
|
|
logger.info("STUB :: cast.setCustomReceivers");
|
|
}
|
|
|
|
export function setPageContext (_win: Window): void {
|
|
logger.info("STUB :: cast.setPageContext");
|
|
}
|
|
|
|
export function setReceiverDisplayStatus (_sessionId: string): void {
|
|
logger.info("STUB :: cast.setReceiverDisplayStatus");
|
|
}
|
|
|
|
export function unescape (escaped: string): string {
|
|
return decodeURI(escaped);
|
|
}
|
|
|
|
|
|
onMessage(async message => {
|
|
switch (message.subject) {
|
|
case "shim:/initialized": {
|
|
isAvailable = true;
|
|
break;
|
|
}
|
|
|
|
/**
|
|
* Cast destination found (serviceUp). Set the API availability
|
|
* property and call the page event function (__onGCastApiAvailable).
|
|
*/
|
|
case "shim:/serviceUp": {
|
|
const receiver = message.data;
|
|
|
|
if (receiverList.find(r => r.id === receiver.id)) {
|
|
break;
|
|
}
|
|
|
|
receiverList.push(receiver);
|
|
|
|
if (apiConfig) {
|
|
// Notify listeners of new cast destination
|
|
apiConfig.receiverListener(ReceiverAvailability.AVAILABLE);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/**
|
|
* Cast destination lost (serviceDown). Remove from the receiver list
|
|
* and update availability state.
|
|
*/
|
|
case "shim:/serviceDown": {
|
|
const receiverIndex = receiverList.findIndex(
|
|
receiver => receiver.id === message.data.id);
|
|
|
|
receiverList.splice(receiverIndex, 1);
|
|
|
|
if (receiverList.length === 0) {
|
|
if (apiConfig) {
|
|
apiConfig.receiverListener(
|
|
ReceiverAvailability.UNAVAILABLE);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case "shim:/selectReceiverEnd": {
|
|
logger.info("Selected receiver");
|
|
|
|
if (!sessionRequestInProgress) {
|
|
break;
|
|
}
|
|
|
|
const selectedReceiver = new Receiver_(
|
|
message.data.receiver.id
|
|
, message.data.receiver.friendlyName);
|
|
|
|
for (const listener of receiverActionListeners) {
|
|
logger.info("Calling receiver action listener (CAST)"
|
|
, message.data.receiver);
|
|
listener(selectedReceiver, ReceiverAction.CAST);
|
|
}
|
|
|
|
(selectedReceiver as any)._address = message.data.receiver.host;
|
|
(selectedReceiver as any)._port = message.data.receiver.port;
|
|
|
|
function createSession () {
|
|
sessionList.push(new Session(
|
|
sessionList.length.toString() // sessionId
|
|
, apiConfig.sessionRequest.appId // appId
|
|
, selectedReceiver.friendlyName // displayName
|
|
, [] // appImages
|
|
, selectedReceiver // receiver
|
|
, (session: Session) => {
|
|
sendMessageResponse({
|
|
subject: "main:/sessionCreated"
|
|
});
|
|
|
|
sessionRequestInProgress = false;
|
|
|
|
if (sessionSuccessCallback) {
|
|
sessionSuccessCallback(session);
|
|
}
|
|
}));
|
|
}
|
|
|
|
// If an existing session is active, stop it and start new one
|
|
if (sessionList.length) {
|
|
const lastSession = sessionList[sessionList.length - 1];
|
|
|
|
if (lastSession.status !== SessionStatus.STOPPED) {
|
|
lastSession.stop(createSession);
|
|
}
|
|
} else {
|
|
createSession();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case "shim:/selectReceiverStop": {
|
|
logger.info("Stopped receiver");
|
|
|
|
if (sessionRequestInProgress) {
|
|
sessionRequestInProgress = false;
|
|
|
|
for (const listener of receiverActionListeners) {
|
|
const castReceiver = new Receiver_(
|
|
message.data.receiver.id
|
|
, message.data.receiver.friendlyName);
|
|
|
|
logger.info("Calling receiver action listener (STOP)"
|
|
, message.data.receiver);
|
|
listener(castReceiver, ReceiverAction.STOP);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/**
|
|
* Popup closed before session established.
|
|
*/
|
|
case "shim:/selectReceiverCancelled": {
|
|
if (sessionRequestInProgress) {
|
|
sessionRequestInProgress = false;
|
|
|
|
if (sessionErrorCallback) {
|
|
sessionErrorCallback(new Error_(ErrorCode.CANCEL));
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case "shim:/launchApp": {
|
|
const receiver: Receiver = message.data.receiver;
|
|
_requestSession(receiver
|
|
, session => {
|
|
apiConfig.sessionListener(session);
|
|
});
|
|
|
|
break;
|
|
}
|
|
}
|
|
});
|