mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-11 10:09:59 +00:00
Initial commit
This commit is contained in:
16
ext/src/shim/cast/classes/ApiConfig.js
Executable file
16
ext/src/shim/cast/classes/ApiConfig.js
Executable file
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
|
||||
export default class ApiConfig {
|
||||
constructor (
|
||||
sessionRequest
|
||||
, sessionListener
|
||||
, receiverListener
|
||||
, opt_autoJoinPolicy
|
||||
, opt_defaultActionPolicy) {
|
||||
|
||||
this.autoJoinPolicy
|
||||
this.receiverListener = receiverListener;
|
||||
this.sessionListener = sessionListener;
|
||||
this.sessionRequest = sessionRequest;
|
||||
}
|
||||
};
|
||||
12
ext/src/shim/cast/classes/DialRequest.js
Executable file
12
ext/src/shim/cast/classes/DialRequest.js
Executable file
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
|
||||
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.DialRequest
|
||||
export default class DialRequest {
|
||||
constructor (
|
||||
appName
|
||||
, opt_launchParameter = null) {
|
||||
|
||||
this.appName = appName;
|
||||
this.launchParameter = opt_launchParameter;
|
||||
}
|
||||
};
|
||||
14
ext/src/shim/cast/classes/Error.js
Executable file
14
ext/src/shim/cast/classes/Error.js
Executable file
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.Error
|
||||
export default class Error {
|
||||
constructor (
|
||||
code
|
||||
, opt_description = null
|
||||
, opt_details = null) {
|
||||
|
||||
this.code = code;
|
||||
this.description = opt_description;
|
||||
this.details = opt_details;
|
||||
}
|
||||
};
|
||||
11
ext/src/shim/cast/classes/Image.js
Executable file
11
ext/src/shim/cast/classes/Image.js
Executable file
@@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
|
||||
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.Image
|
||||
export default class Image {
|
||||
width = null;
|
||||
height = null;
|
||||
|
||||
constructor (url) {
|
||||
this.url = url;
|
||||
}
|
||||
};
|
||||
24
ext/src/shim/cast/classes/Receiver.js
Executable file
24
ext/src/shim/cast/classes/Receiver.js
Executable file
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
|
||||
import { Capability
|
||||
, ReceiverType } from "../enums";
|
||||
|
||||
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.Receiver
|
||||
export default class Receiver {
|
||||
constructor (
|
||||
label
|
||||
, friendlyName
|
||||
, opt_capabilities = [
|
||||
Capability.VIDEO_OUT
|
||||
, Capability.AUDIO_OUT ]
|
||||
, opt_volume = null) {
|
||||
|
||||
this.capabilities = opt_capabilities;
|
||||
this.displayStatus = null;
|
||||
this.friendlyName = friendlyName;
|
||||
this.isActiveInput = null;
|
||||
this.label = label;
|
||||
this.receiverType = ReceiverType.CAST;
|
||||
this.volume = opt_volume;
|
||||
}
|
||||
};
|
||||
10
ext/src/shim/cast/classes/ReceiverDisplayStatus.js
Executable file
10
ext/src/shim/cast/classes/ReceiverDisplayStatus.js
Executable file
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.ReceiverDisplayStatus
|
||||
export default class ReceiverDisplayStatus {
|
||||
constructor (statusText, appImages) {
|
||||
this.appImages = appImages;
|
||||
this.showStop = null;
|
||||
this.statusText = statusText;
|
||||
}
|
||||
};
|
||||
10
ext/src/shim/cast/classes/SenderApplication.js
Executable file
10
ext/src/shim/cast/classes/SenderApplication.js
Executable file
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.SenderApplication
|
||||
export default class SenderApplication {
|
||||
constructor (platform) {
|
||||
this.packageId = null;
|
||||
this.platform = platform;
|
||||
this.url = null;
|
||||
}
|
||||
};
|
||||
319
ext/src/shim/cast/classes/Session.js
Executable file
319
ext/src/shim/cast/classes/Session.js
Executable file
@@ -0,0 +1,319 @@
|
||||
"use strict";
|
||||
|
||||
import _Error from "./Error";
|
||||
import Volume from "./Volume";
|
||||
import Media from "../../media/classes/Media";
|
||||
|
||||
import { SessionStatus
|
||||
, ErrorCode
|
||||
, VolumeControlType } from "../enums";
|
||||
|
||||
import { onMessage, sendMessage } from "../../messageBridge";
|
||||
|
||||
import uuid from "uuid/v1";
|
||||
|
||||
|
||||
export default class Session {
|
||||
constructor (
|
||||
sessionId
|
||||
, appId
|
||||
, displayName
|
||||
, appImages
|
||||
, receiver
|
||||
, successCallback) {
|
||||
|
||||
this._id = uuid();
|
||||
this._messageListeners = new Map();
|
||||
this._updateListeners = new Set();
|
||||
|
||||
this._sendMessageCallbacks = new Map();
|
||||
this._setReceiverMutedCallbacks = new Map();
|
||||
this._setReceiverVolumeLevelCallbacks = new Map();
|
||||
this._stopCallbacks = new Map();
|
||||
|
||||
this.sessionId = sessionId;
|
||||
this.appId = appId;
|
||||
this.appImages = appImages;
|
||||
this.displayName = displayName;
|
||||
this.receiver = receiver;
|
||||
|
||||
this.media = [];
|
||||
this.namespaces = [];
|
||||
this.senderApps = [];
|
||||
this.status = SessionStatus.DISCONNECTED;
|
||||
this.statusText = null;
|
||||
|
||||
this._sendMessage("bridge:bridgesession/initialize", {
|
||||
address: receiver._address
|
||||
, port: receiver._port
|
||||
, appId
|
||||
, sessionId
|
||||
});
|
||||
|
||||
|
||||
onMessage(message => {
|
||||
// Filter other session messages
|
||||
if (message._id && message._id !== this._id) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (message.subject) {
|
||||
case "shim:session/stopped":
|
||||
this.status = SessionStatus.STOPPED;
|
||||
this._updateListeners.forEach(listener => listener());
|
||||
break;
|
||||
|
||||
case "shim:session/connected":
|
||||
this.status = SessionStatus.CONNECTED;
|
||||
this.sessionId = message.data.sessionId;
|
||||
this.namespaces = message.data.namespaces;
|
||||
this.displayName = message.data.displayName;
|
||||
this.statusText = message.data.statusText;
|
||||
|
||||
if (successCallback) {
|
||||
successCallback(this);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "shim:session/updateStatus":
|
||||
if (message.data.volume) {
|
||||
if (!this.receiver.volume) {
|
||||
const receiverVolume = new Volume(
|
||||
message.data.volume.level
|
||||
, message.data.volume.muted);
|
||||
|
||||
receiverVolume.controlType = message.data.volume.controlType;
|
||||
receiverVolume.stepInterval = message.data.volume.stepInterval;
|
||||
|
||||
this.receiver.volume = receiverVolume;
|
||||
} else {
|
||||
this.receiver.volume.level = message.data.volume.level;
|
||||
this.receiver.volume.muted = message.data.volume.muted;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case "shim:session/impl_addMessageListener": {
|
||||
const { namespace, data } = message.data;
|
||||
this._messageListeners.get(namespace).forEach(
|
||||
listener => listener(namespace, data));
|
||||
break;
|
||||
}
|
||||
|
||||
case "shim:session/impl_sendMessage": {
|
||||
const { messageId, error } = message.data;
|
||||
const [ successCallback, errorCallback ]
|
||||
= this._sendMessageCallbacks.get(messageId)
|
||||
|
||||
if (error && errorCallback) {
|
||||
errorCallback(new _Error(ErrorCode.SESSION_ERROR));
|
||||
} else if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
this._sendMessageCallbacks.delete(messageId);
|
||||
break;
|
||||
}
|
||||
|
||||
case "shim:session/impl_setReceiverMuted": {
|
||||
const { volumeId, error } = message.data;
|
||||
const [ successCallback, errorCallback ]
|
||||
= this._setReceiverMutedCallbacks.get(volumeId);
|
||||
|
||||
if (error && errorCallback) {
|
||||
errorCallback(new _Error(ErrorCode.SESSION_ERROR));
|
||||
} else if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
break;
|
||||
this._setReceiverMutedCallbacks.delete(volumeId);
|
||||
}
|
||||
|
||||
case "shim:session/impl_setReceiverVolumeLevel": {
|
||||
const { volumeId, error } = message.data;
|
||||
const [ successCallback, errorCallback ]
|
||||
= this._setReceiverVolumeLevelCallbacks.get(volumeId);
|
||||
|
||||
if (error && errorCallback) {
|
||||
errorCallback(new _Error(ErrorCode.SESSION_ERROR));
|
||||
} else if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
this._setReceiverVolumeLevelCallbacks.delete(volumeId);
|
||||
break;
|
||||
}
|
||||
|
||||
case "shim:session/impl_stop": {
|
||||
const { stopId, error } = message.data;
|
||||
const [ successCallback, errorCallback ]
|
||||
= this._stopCallbacks.get(stopId);
|
||||
|
||||
if (error && errorCallback) {
|
||||
errorCallback(new _Error(ErrorCode.SESSION_ERROR));
|
||||
} else {
|
||||
this.status = SessionStatus.STOPPED;
|
||||
this._updateListeners.forEach(listener => listener());
|
||||
|
||||
if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
}
|
||||
this._stopCallbacks.delete(stopId);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_sendMessage (subject, data = {}) {
|
||||
sendMessage({
|
||||
subject
|
||||
, data
|
||||
, _id: this._id
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
addMediaListener (listener) {
|
||||
console.info("STUB :: Session#addMediaListener")
|
||||
}
|
||||
|
||||
addMessageListener (namespace, listener) {
|
||||
if (!this._messageListeners.has(namespace)) {
|
||||
this._messageListeners.set(namespace, new Set());
|
||||
}
|
||||
this._messageListeners.get(namespace).add(listener);
|
||||
this._sendMessage("bridge:bridgesession/impl_addMessageListener", {
|
||||
namespace
|
||||
});
|
||||
}
|
||||
|
||||
addUpdateListener (listener) {
|
||||
this._updateListeners.add(listener);
|
||||
}
|
||||
|
||||
leave (successCallback, errorCallback) {
|
||||
const id = uuid();
|
||||
|
||||
this._sendMessage("bridge:bridgesession/impl_leave", { id });
|
||||
|
||||
this._leaveCallbacks.set(id, [
|
||||
successCallback
|
||||
, errorCallback
|
||||
]);
|
||||
}
|
||||
|
||||
loadMedia (loadRequest, successCallback, errorCallback) {
|
||||
this.sendMediaMessage({
|
||||
type: "LOAD"
|
||||
, requestId: 0
|
||||
, media: loadRequest.media
|
||||
, activeTrackIds: loadRequest.activeTrackIds || []
|
||||
, autoplay: loadRequest.autoplay || false
|
||||
, currentTime: loadRequest.currentTime || 0
|
||||
, customData: loadRequest.customData || {}
|
||||
, repeatMode: "REPEAT_OFF"
|
||||
});
|
||||
|
||||
let hasResponded = false;
|
||||
|
||||
this.addMessageListener("urn:x-cast:com.google.cast.media"
|
||||
, (namespace, data) => {
|
||||
if (hasResponded) return;
|
||||
|
||||
const mediaObject = JSON.parse(data);
|
||||
|
||||
if (mediaObject.status && mediaObject.status.length > 0) {
|
||||
hasResponded = true;
|
||||
|
||||
const media = new Media(
|
||||
this.sessionId
|
||||
, mediaObject.status[0].mediaSessionId
|
||||
, this._id);
|
||||
|
||||
media.media = loadRequest.media;
|
||||
this.media = [ media ];
|
||||
|
||||
media.play();
|
||||
successCallback(media);
|
||||
} else {
|
||||
errorCallback(new _Error(ErrorCode.SESSION_ERROR));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
queueLoad () {
|
||||
console.info("STUB :: Session#queueLoad");
|
||||
}
|
||||
removeMediaListener (listener) {
|
||||
console.info("STUB :: Session#removeMediaListener");
|
||||
}
|
||||
removeMessageListener (namespace, listener) {
|
||||
this._messageListeners.get(namespace).delete(listener);
|
||||
}
|
||||
removeUpdateListener (namespace, listener) {
|
||||
this._updateListeners.delete(listener);
|
||||
}
|
||||
|
||||
sendMediaMessage (message) {
|
||||
this.sendMessage(
|
||||
"urn:x-cast:com.google.cast.media"
|
||||
, message
|
||||
, () => {}
|
||||
, () => {});
|
||||
}
|
||||
|
||||
sendMessage (namespace, message, successCallback, errorCallback) {
|
||||
const messageId = uuid();
|
||||
|
||||
this._sendMessage("bridge:bridgesession/impl_sendMessage", {
|
||||
namespace
|
||||
, message
|
||||
, messageId
|
||||
});
|
||||
|
||||
this._sendMessageCallbacks.set(messageId, [
|
||||
successCallback
|
||||
, errorCallback
|
||||
]);
|
||||
}
|
||||
|
||||
setReceiverMuted (muted, successCallback, errorCallback) {
|
||||
const volumeId = uuid();
|
||||
|
||||
this._sendMessage("bridge:bridgesession/impl_setReceiverMuted", {
|
||||
muted
|
||||
, volumeId
|
||||
});
|
||||
|
||||
this._setReceiverMutedCallbacks.set(volumeId, [
|
||||
successCallback
|
||||
, errorCallback
|
||||
]);
|
||||
}
|
||||
|
||||
setReceiverVolumeLevel (newLevel, successCallback, errorCallback) {
|
||||
const volumeId = uuid();
|
||||
this._sendMessage("bridge:bridgesession/impl_setReceiverVolumeLevel", {
|
||||
newLevel
|
||||
, volumeId
|
||||
});
|
||||
|
||||
this._setReceiverVolumeLevelCallbacks.set(volumeId, [
|
||||
successCallback
|
||||
, errorCallback
|
||||
]);
|
||||
}
|
||||
|
||||
stop (successCallback, errorCallback) {
|
||||
const stopId = uuid();
|
||||
this._sendMessage("bridge:bridgesession/impl_stop", { stopId });
|
||||
|
||||
this._stopCallbacks.set(stopId, [
|
||||
successCallback
|
||||
, errorCallback
|
||||
]);
|
||||
}
|
||||
}
|
||||
20
ext/src/shim/cast/classes/SessionRequest.js
Executable file
20
ext/src/shim/cast/classes/SessionRequest.js
Executable file
@@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
|
||||
import { Capability } from "../enums";
|
||||
import { requestSession as requestSessionTimeout } from "../../timeout.js";
|
||||
|
||||
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.SessionRequest
|
||||
export default class SessionRequest {
|
||||
constructor (
|
||||
appId
|
||||
, opt_capabilities = [
|
||||
Capability.VIDEO_OUT
|
||||
, Capability.AUDIO_OUT ]
|
||||
, opt_timeout = null) {
|
||||
|
||||
this.appId = appId;
|
||||
this.capabilities = opt_capabilities;
|
||||
this.language = null;
|
||||
this.requestSessionTimeout = requestSessionTimeout;
|
||||
}
|
||||
};
|
||||
10
ext/src/shim/cast/classes/Timeout.js
Executable file
10
ext/src/shim/cast/classes/Timeout.js
Executable file
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
import * as timeouts from "../../timeout.js";
|
||||
|
||||
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.Timeout
|
||||
export default class Timeout {
|
||||
constructor () {
|
||||
Object.assign(this, timeouts);
|
||||
}
|
||||
};
|
||||
13
ext/src/shim/cast/classes/Volume.js
Executable file
13
ext/src/shim/cast/classes/Volume.js
Executable file
@@ -0,0 +1,13 @@
|
||||
"use strict";
|
||||
|
||||
import { VolumeControlType } from "../enums";
|
||||
|
||||
// https://developers.google.com/cast/docs/reference/chrome/chrome.cast.Volume
|
||||
export default class Volume {
|
||||
constructor (
|
||||
opt_level = null
|
||||
, opt_muted = null) {
|
||||
this.level = opt_level;
|
||||
this.muted = opt_muted;
|
||||
}
|
||||
};
|
||||
28
ext/src/shim/cast/classes/index.js
Executable file
28
ext/src/shim/cast/classes/index.js
Executable file
@@ -0,0 +1,28 @@
|
||||
"use strict";
|
||||
|
||||
import ApiConfig from "./ApiConfig";
|
||||
import DialRequest from "./DialRequest";
|
||||
import Error_ from "./Error";
|
||||
import Image_ from "./Image";
|
||||
import Receiver from "./Receiver";
|
||||
import ReceiverDisplayStatus from "./ReceiverDisplayStatus";
|
||||
import SenderApplication from "./SenderApplication";
|
||||
import Session from "./Session";
|
||||
import SessionRequest from "./SessionRequest";
|
||||
import Timeout from "./Timeout";
|
||||
import Volume from "./Volume";
|
||||
|
||||
|
||||
export default {
|
||||
AutoJoinPolicy
|
||||
, Capability
|
||||
, DefaultActionPolicy
|
||||
, DialAppState
|
||||
, ErrorCode
|
||||
, ReceiverAction
|
||||
, ReceiverAvailability
|
||||
, ReceiverType
|
||||
, SenderPlatform
|
||||
, SessionStatus
|
||||
, VolumeControlType
|
||||
};
|
||||
74
ext/src/shim/cast/enums/index.js
Executable file
74
ext/src/shim/cast/enums/index.js
Executable file
@@ -0,0 +1,74 @@
|
||||
"use strict";
|
||||
|
||||
export const AutoJoinPolicy = {
|
||||
TAB_AND_ORIGIN_SCOPED: "tab_and_origin_scoped"
|
||||
, ORIGIN_SCOPED: "origin_scoped"
|
||||
, PAGE_SCOPED: "page_scoped"
|
||||
};
|
||||
|
||||
export const Capability = {
|
||||
VIDEO_OUT: "video_out"
|
||||
, AUDIO_OUT: "audio_out"
|
||||
, VIDEO_IN: "video_in"
|
||||
, AUDIO_IN: "audio_in"
|
||||
, MULTIZONE_GROUP: "multizone_group"
|
||||
};
|
||||
|
||||
export const DefaultActionPolicy = {
|
||||
CREATE_SESSION: "create_session"
|
||||
, CAST_THIS_TAB: "cast_this_tab"
|
||||
};
|
||||
|
||||
export const DialAppState = {
|
||||
RUNNING: "running"
|
||||
, STOPPED: "stopped"
|
||||
, ERROR: "error"
|
||||
};
|
||||
|
||||
export const ErrorCode = {
|
||||
CANCEL: "cancel"
|
||||
, TIMEOUT: "timeout"
|
||||
, API_NOT_INITIALIZED: "api_not_initialized"
|
||||
, INVALID_PARAMETER: "invalid_parameter"
|
||||
, EXTENSION_NOT_COMPATIBLE: "extension_not_compatible"
|
||||
, EXTENSION_MISSING: "extension_missing"
|
||||
, RECEIVER_UNAVAILABLE: "receiver_unavailable"
|
||||
, SESSION_ERROR: "session_error"
|
||||
, CHANNEL_ERROR: "channel_error"
|
||||
, LOAD_MEDIA_FAILED: "load_media_failed"
|
||||
};
|
||||
|
||||
export const ReceiverAction = {
|
||||
CAST: "cast"
|
||||
, STOP: "stop"
|
||||
};
|
||||
|
||||
export const ReceiverAvailability = {
|
||||
AVAILABLE: "available"
|
||||
, UNAVAILABLE: "unavailable"
|
||||
};
|
||||
|
||||
export const ReceiverType = {
|
||||
CAST: "cast"
|
||||
, DIAL: "dial"
|
||||
, HANGOUT: "hangout"
|
||||
, CUSTOM: "custom"
|
||||
};
|
||||
|
||||
export const SenderPlatform = {
|
||||
CHROME: "chrome"
|
||||
, IOS: "ios"
|
||||
, ANDROID: "android"
|
||||
};
|
||||
|
||||
export const SessionStatus = {
|
||||
CONNECTED: "connected"
|
||||
, DISCONNECTED: "disconnected"
|
||||
, STOPPED: "stopped"
|
||||
};
|
||||
|
||||
export const VolumeControlType = {
|
||||
ATTENUATION: "attenuation"
|
||||
, FIXED: "fixed"
|
||||
, MASTER: "master"
|
||||
};
|
||||
261
ext/src/shim/cast/index.js
Executable file
261
ext/src/shim/cast/index.js
Executable file
@@ -0,0 +1,261 @@
|
||||
"use strict";
|
||||
|
||||
import ApiConfig from "./classes/ApiConfig";
|
||||
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 { requestSession as requestSessionTimeout } from "../timeout";
|
||||
|
||||
import state from "../state";
|
||||
|
||||
import { onMessage, sendMessage } from "../messageBridge";
|
||||
|
||||
|
||||
const cast = {
|
||||
// Enums
|
||||
AutoJoinPolicy
|
||||
, Capability
|
||||
, DefaultActionPolicy
|
||||
, DialAppState
|
||||
, ErrorCode
|
||||
, ReceiverAction
|
||||
, ReceiverAvailability
|
||||
, ReceiverType
|
||||
, SenderPlatform
|
||||
, SessionStatus
|
||||
, VolumeControlType
|
||||
|
||||
// Classes
|
||||
, ApiConfig
|
||||
, DialRequest
|
||||
, Error: Error_
|
||||
, Image: Image_
|
||||
, Receiver
|
||||
, ReceiverDisplayStatus
|
||||
, SenderApplication
|
||||
, Session
|
||||
, SessionRequest
|
||||
, Timeout
|
||||
, Volume
|
||||
|
||||
, timeout: new Timeout()
|
||||
, isAvailable: true
|
||||
, VERSION: [ 1, 2 ]
|
||||
};
|
||||
|
||||
|
||||
const receiverListeners = new Set();
|
||||
|
||||
let sessionSuccessCallback;
|
||||
let sessionErrorCallback;
|
||||
|
||||
|
||||
cast.addReceiverActionListener = (listener) => {
|
||||
console.info("Caster (Debug): cast.addReceiverActionListener");
|
||||
receiverListeners.add(listener);
|
||||
};
|
||||
|
||||
cast.initialize = (
|
||||
apiConfig
|
||||
, successCallback
|
||||
, errorCallback) => {
|
||||
|
||||
console.info("Caster (Debug): cast.initialize");
|
||||
|
||||
// Already initialized
|
||||
if (state.apiConfig) {
|
||||
errorCallback(new Error_(ErrorCode.RECEIVER_UNAVAILABLE));
|
||||
return;
|
||||
}
|
||||
|
||||
state.apiConfig = apiConfig;
|
||||
|
||||
sendMessage({
|
||||
subject: "bridge:discover"
|
||||
});
|
||||
|
||||
apiConfig.receiverListener(state.receiverList.length
|
||||
? ReceiverAvailability.AVAILABLE
|
||||
: ReceiverAvailability.UNAVAILABLE);
|
||||
|
||||
successCallback();
|
||||
};
|
||||
|
||||
cast.logMessage = (message) => {
|
||||
console.log("CAST MSG:", message);
|
||||
};
|
||||
|
||||
cast.precache = (data) => {
|
||||
console.info("STUB :: cast.precache");
|
||||
};
|
||||
|
||||
cast.removeReceiverActionListener = (listener) => {
|
||||
receiverListeners.delete(listener);
|
||||
}
|
||||
|
||||
cast.requestSession = (
|
||||
successCallback
|
||||
, errorCallback
|
||||
, opt_sessionRequest = state.apiConfig.sessionRequest) => {
|
||||
|
||||
console.info("Caster (Debug): cast.requestSession");
|
||||
|
||||
// Called before initialization
|
||||
if (!state.apiConfig) {
|
||||
errorCallback(new Error_(ErrorCode.API_NOT_INITIALIZED));
|
||||
return;
|
||||
}
|
||||
|
||||
// No available receivers
|
||||
if (!state.receiverList.length) {
|
||||
errorCallback(new Error_(ErrorCode.RECEIVER_UNAVAILABLE));
|
||||
return;
|
||||
}
|
||||
|
||||
sessionSuccessCallback = successCallback;
|
||||
sessionErrorCallback = errorCallback;
|
||||
|
||||
// Open destination chooser
|
||||
sendMessage({
|
||||
subject: "main:openPopup"
|
||||
});
|
||||
};
|
||||
|
||||
cast.requestSessionById = (sessionId) => {
|
||||
console.info("STUB :: cast.requestSessionById");
|
||||
};
|
||||
|
||||
cast.setCustomReceivers = (receivers, successCallback, errorCallback) => {
|
||||
console.info("STUB :: cast.setCustomReceivers");
|
||||
};
|
||||
|
||||
cast.setPageContext = (win) => {
|
||||
console.info("STUB :: cast.setPageContext");
|
||||
};
|
||||
|
||||
cast.setReceiverDisplayStatus = (sessionId) => {
|
||||
console.info("STUB :: cast.setReceiverDisplayStatus");
|
||||
};
|
||||
|
||||
cast.unescape = (escaped) => unescape(escaped);
|
||||
|
||||
|
||||
onMessage(message => {
|
||||
switch (message.subject) {
|
||||
/**
|
||||
* Cast destination found (serviceUp). Set the API availability
|
||||
* property and call the page event function (__onGCastApiAvailable).
|
||||
*/
|
||||
case "shim:serviceUp":
|
||||
const receiver = new Receiver(
|
||||
message.data.id
|
||||
, message.data.friendlyName);
|
||||
|
||||
receiver._address = message.data.address;
|
||||
receiver._port = message.data.port;
|
||||
|
||||
if (state.receiverList.find(r => r.label === receiver.label)) {
|
||||
break;
|
||||
}
|
||||
|
||||
state.receiverList.push(receiver);
|
||||
|
||||
// Notify listeners of new cast destination
|
||||
state.apiConfig.receiverListener(ReceiverAvailability.AVAILABLE);
|
||||
receiverListeners.forEach(
|
||||
listener => listener(ReceiverAvailability.AVAILABLE));
|
||||
|
||||
break;
|
||||
|
||||
/**
|
||||
* Cast destination lost (serviceDown). Remove from the receiver list
|
||||
* and update availability state.
|
||||
*/
|
||||
case "shim:serviceDown":
|
||||
state.receiverList = state.receiverList.filter(
|
||||
receiver => receiver.label !== message.data.id);
|
||||
|
||||
if (state.receiverList.length === 0) {
|
||||
state.apiConfig.receiverListener(
|
||||
ReceiverAvailability.UNAVAILABLE);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "shim:selectReceiver":
|
||||
console.info("Caster (Debug): Selected receiver");
|
||||
const selectedReceiver = message.data;
|
||||
|
||||
const sessionConstructorArgs = [
|
||||
state.sessionList.length // sessionId
|
||||
, state.apiConfig.sessionRequest.appId // appId
|
||||
, selectedReceiver.friendlyName // displayName
|
||||
, [] // appImages
|
||||
, selectedReceiver // receiver
|
||||
, (session) => {
|
||||
sendMessage({
|
||||
subject: "popup:close"
|
||||
});
|
||||
|
||||
state.apiConfig.sessionListener(session);
|
||||
sessionSuccessCallback(session);
|
||||
}
|
||||
];
|
||||
|
||||
// If existing session active, stop it and start new one
|
||||
if (state.sessionList.length) {
|
||||
const lastSession
|
||||
= state.sessionList[state.sessionList.length - 1];
|
||||
|
||||
if (lastSession.status !== SessionStatus.STOPPED) {
|
||||
lastSession.stop(() => {
|
||||
state.sessionList.push(new Session(
|
||||
...sessionConstructorArgs));
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state.sessionList.push(new Session(...sessionConstructorArgs));
|
||||
|
||||
break;
|
||||
|
||||
/**
|
||||
* Popup is ready to receive data to populate the cast destination
|
||||
* chooser.
|
||||
*/
|
||||
case "shim:popupReady":
|
||||
sendMessage({
|
||||
subject: "popup:populate"
|
||||
, data: state.receiverList
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Trigger bridge mDNS discovery
|
||||
sendMessage({
|
||||
subject: "main:initialize"
|
||||
});
|
||||
|
||||
export default cast;
|
||||
Reference in New Issue
Block a user