define(["appSettings", "userSettings", "playbackManager", "connectionManager", "globalize", "events", "require", "castSenderApiLoader"], function(appSettings, userSettings, playbackManager, connectionManager, globalize, events, require, castSenderApiLoader) { "use strict"; function sendConnectionResult(isOk) { var resolve = currentResolve, reject = currentReject; currentResolve = null, currentReject = null, isOk ? resolve && resolve() : reject ? reject() : playbackManager.removeActivePlayer(PlayerName) } function alertText(text, title) { require(["alert"], function(alert) { alert({ text: text, title: title }) }) } function normalizeImages(state) { if (state && state.NowPlayingItem) { var item = state.NowPlayingItem; item.ImageTags && item.ImageTags.Primary || item.PrimaryImageTag && (item.ImageTags = item.ImageTags || {}, item.ImageTags.Primary = item.PrimaryImageTag), item.BackdropImageTag && item.BackdropItemId === item.Id && (item.BackdropImageTags = [item.BackdropImageTag]), item.BackdropImageTag && item.BackdropItemId !== item.Id && (item.ParentBackdropImageTags = [item.BackdropImageTag], item.ParentBackdropItemId = item.BackdropItemId) } } function getItemsForPlayback(apiClient, query) { var userId = apiClient.getCurrentUserId(); return query.Ids && 1 === query.Ids.split(",").length ? apiClient.getItem(userId, query.Ids.split(",")).then(function(item) { return { Items: [item], TotalRecordCount: 1 } }) : (query.Limit = query.Limit || 100, query.ExcludeLocationTypes = "Virtual", query.EnableTotalRecordCount = !1, apiClient.getItems(userId, query)) } function bindEventForRelay(instance, eventName) { events.on(instance._castPlayer, eventName, function(e, data) { var state = instance.getPlayerStateInternal(data); events.trigger(instance, eventName, [state]) }) } function initializeChromecast() { var instance = this; instance._castPlayer = new CastPlayer, document.dispatchEvent(new CustomEvent("chromecastloaded", { detail: { player: instance } })), events.on(instance._castPlayer, "connect", function(e) { currentResolve ? sendConnectionResult(!0) : playbackManager.setActivePlayer(PlayerName, instance.getCurrentTargetInfo()), console.log("cc: connect"), instance.lastPlayerData = null }), events.on(instance._castPlayer, "playbackstart", function(e, data) { console.log("cc: playbackstart"), instance._castPlayer.initializeCastPlayer(); var state = instance.getPlayerStateInternal(data); events.trigger(instance, "playbackstart", [state]) }), events.on(instance._castPlayer, "playbackstop", function(e, data) { console.log("cc: playbackstop"); var state = instance.getPlayerStateInternal(data); events.trigger(instance, "playbackstop", [state]), instance.lastPlayerData = {} }), events.on(instance._castPlayer, "playbackprogress", function(e, data) { var state = instance.getPlayerStateInternal(data); events.trigger(instance, "timeupdate", [state]) }), bindEventForRelay(instance, "timeupdate"), bindEventForRelay(instance, "pause"), bindEventForRelay(instance, "unpause"), bindEventForRelay(instance, "volumechange"), bindEventForRelay(instance, "repeatmodechange"), events.on(instance._castPlayer, "playstatechange", function(e, data) { var state = instance.getPlayerStateInternal(data); events.trigger(instance, "pause", [state]) }) } function ChromecastPlayer() { this.name = PlayerName, this.type = "mediaplayer", this.id = "chromecast", this.isLocalPlayer = !1, this.lastPlayerData = {}, castSenderApiLoader.load().then(initializeChromecast.bind(this)) } var currentResolve, currentReject, PlayerName = "Chromecast", DEVICE_STATE = { IDLE: 0, ACTIVE: 1, WARNING: 2, ERROR: 3 }, PLAYER_STATE = { IDLE: "IDLE", LOADING: "LOADING", LOADED: "LOADED", PLAYING: "PLAYING", PAUSED: "PAUSED", STOPPED: "STOPPED", SEEKING: "SEEKING", ERROR: "ERROR" }, CastPlayer = function() { this.deviceState = DEVICE_STATE.IDLE, this.currentMediaSession = null, this.session = null, this.castPlayerState = PLAYER_STATE.IDLE, this.hasReceivers = !1, this.errorHandler = this.onError.bind(this), this.mediaStatusUpdateHandler = this.onMediaStatusUpdate.bind(this), this.initializeCastPlayer() }; return CastPlayer.prototype.initializeCastPlayer = function() { var chrome = window.chrome; if (chrome) { if (!chrome.cast || !chrome.cast.isAvailable) return void setTimeout(this.initializeCastPlayer.bind(this), 1e3); var sessionRequest = new chrome.cast.SessionRequest("2D4B1DA3"), apiConfig = new chrome.cast.ApiConfig(sessionRequest, this.sessionListener.bind(this), this.receiverListener.bind(this), "origin_scoped"); console.log("chromecast.initialize"), chrome.cast.initialize(apiConfig, this.onInitSuccess.bind(this), this.errorHandler) } }, CastPlayer.prototype.onInitSuccess = function() { this.isInitialized = !0, console.log("chromecast init success") }, CastPlayer.prototype.onError = function() { console.log("chromecast error") }, CastPlayer.prototype.sessionListener = function(e) { this.session = e, this.session && (this.session.media[0] && this.onMediaDiscovered("activeSession", this.session.media[0]), this.onSessionConnected(e)) }, CastPlayer.prototype.messageListener = function(namespace, message) { if ("string" == typeof message && (message = JSON.parse(message)), "playbackerror" === message.type) { var errorCode = message.data; setTimeout(function() { alertText(globalize.translate("MessagePlaybackError" + errorCode), globalize.translate("HeaderPlaybackError")) }, 300) } else "connectionerror" === message.type ? setTimeout(function() { alertText(globalize.translate("MessageChromecastConnectionError"), globalize.translate("HeaderError")) }, 300) : message.type && events.trigger(this, message.type, [message.data]) }, CastPlayer.prototype.receiverListener = function(e) { this.hasReceivers = "available" === e }, CastPlayer.prototype.sessionUpdateListener = function(isAlive) { isAlive || (this.session = null, this.deviceState = DEVICE_STATE.IDLE, this.castPlayerState = PLAYER_STATE.IDLE, this.currentMediaSession = null, sendConnectionResult(!1)) }, CastPlayer.prototype.launchApp = function() { chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this)) }, CastPlayer.prototype.onRequestSessionSuccess = function(e) { this.onSessionConnected(e) }, CastPlayer.prototype.onSessionConnected = function(session) { this.session = session, this.deviceState = DEVICE_STATE.ACTIVE, this.session.addMessageListener("urn:x-cast:com.connectsdk", this.messageListener.bind(this)), this.session.addMediaListener(this.sessionMediaListener.bind(this)), this.session.addUpdateListener(this.sessionUpdateListener.bind(this)), events.trigger(this, "connect"), this.sendMessage({ options: {}, command: "Identify" }) }, CastPlayer.prototype.sessionMediaListener = function(e) { this.currentMediaSession = e, this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler) }, CastPlayer.prototype.onLaunchError = function() { this.deviceState = DEVICE_STATE.ERROR, sendConnectionResult(!1) }, CastPlayer.prototype.stopApp = function() { this.session && this.session.stop(this.onStopAppSuccess.bind(this, "Session stopped"), this.errorHandler) }, CastPlayer.prototype.onStopAppSuccess = function(message) { this.deviceState = DEVICE_STATE.IDLE, this.castPlayerState = PLAYER_STATE.IDLE, this.currentMediaSession = null }, CastPlayer.prototype.loadMedia = function(options, command) { return this.session ? (options.items = options.items.map(function(i) { return { Id: i.Id, ServerId: i.ServerId, Name: i.Name, Type: i.Type, MediaType: i.MediaType, IsFolder: i.IsFolder } }), this.sendMessage({ options: options, command: command })) : Promise.reject() }, CastPlayer.prototype.sendMessage = function(message) { var player = this, receiverName = null, session = player.session; session && session.receiver && session.receiver.friendlyName && (receiverName = session.receiver.friendlyName); var apiClient; apiClient = message.options && message.options.ServerId ? connectionManager.getApiClient(message.options.ServerId) : message.options && message.options.items && message.options.items.length ? connectionManager.getApiClient(message.options.items[0].ServerId) : connectionManager.currentApiClient(), message = Object.assign(message, { userId: apiClient.getCurrentUserId(), deviceId: apiClient.deviceId(), accessToken: apiClient.accessToken(), serverAddress: apiClient.serverAddress(), serverId: apiClient.serverId(), serverVersion: apiClient.serverVersion(), receiverName: receiverName }); var bitrateSetting = appSettings.maxChromecastBitrate(); return bitrateSetting && (message.maxBitrate = bitrateSetting), message.options && message.options.items && (message.subtitleAppearance = userSettings.getSubtitleAppearanceSettings(), message.subtitleBurnIn = appSettings.get("subtitleburnin") || ""), new Promise(function(resolve, reject) { require(["chromecastHelper"], function(chromecastHelper) { chromecastHelper.getServerAddress(apiClient).then(function(serverAddress) { message.serverAddress = serverAddress, player.sendMessageInternal(message).then(resolve, reject) }, reject) }) }) }, CastPlayer.prototype.sendMessageInternal = function(message) { return message = JSON.stringify(message), this.session.sendMessage("urn:x-cast:com.connectsdk", message, this.onPlayCommandSuccess.bind(this), this.errorHandler), Promise.resolve() }, CastPlayer.prototype.onPlayCommandSuccess = function() {}, CastPlayer.prototype.onMediaDiscovered = function(how, mediaSession) { this.currentMediaSession = mediaSession, "loadMedia" === how && (this.castPlayerState = PLAYER_STATE.PLAYING), "activeSession" === how && (this.castPlayerState = mediaSession.playerState), this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler) }, CastPlayer.prototype.onMediaStatusUpdate = function(e) { !1 === e && (this.castPlayerState = PLAYER_STATE.IDLE) }, CastPlayer.prototype.setReceiverVolume = function(mute, vol) { this.currentMediaSession && (mute ? this.session.setReceiverMuted(!0, this.mediaCommandSuccessCallback.bind(this), this.errorHandler) : this.session.setReceiverVolumeLevel(vol || 1, this.mediaCommandSuccessCallback.bind(this), this.errorHandler)) }, CastPlayer.prototype.mute = function() { this.setReceiverVolume(!0) }, CastPlayer.prototype.mediaCommandSuccessCallback = function(info, e) {}, ChromecastPlayer.prototype.tryPair = function(target) { var castPlayer = this._castPlayer; return castPlayer.deviceState !== DEVICE_STATE.ACTIVE && castPlayer.isInitialized ? new Promise(function(resolve, reject) { currentResolve = resolve, currentReject = reject, castPlayer.launchApp() }) : (currentResolve = null, currentReject = null, Promise.reject()) }, ChromecastPlayer.prototype.getTargets = function() { var targets = []; return this._castPlayer && this._castPlayer.hasReceivers && targets.push(this.getCurrentTargetInfo()), Promise.resolve(targets) }, ChromecastPlayer.prototype.getCurrentTargetInfo = function() { var appName = null, castPlayer = this._castPlayer; return castPlayer.session && castPlayer.session.receiver && castPlayer.session.receiver.friendlyName && (appName = castPlayer.session.receiver.friendlyName), { name: PlayerName, id: PlayerName, playerName: PlayerName, playableMediaTypes: ["Audio", "Video"], isLocalPlayer: !1, appName: PlayerName, deviceName: appName, supportedCommands: ["VolumeUp", "VolumeDown", "Mute", "Unmute", "ToggleMute", "SetVolume", "SetAudioStreamIndex", "SetSubtitleStreamIndex", "DisplayContent", "SetRepeatMode", "EndSession", "PlayMediaSource", "PlayTrailers"] } }, ChromecastPlayer.prototype.getPlayerStateInternal = function(data) { var triggerStateChange = !1; return data && !this.lastPlayerData && (triggerStateChange = !0), data = data || this.lastPlayerData, this.lastPlayerData = data, normalizeImages(data), triggerStateChange && events.trigger(this, "statechange", [data]), data }, ChromecastPlayer.prototype.playWithCommand = function(options, command) { if (!options.items) { var apiClient = connectionManager.getApiClient(options.serverId), instance = this; return apiClient.getItem(apiClient.getCurrentUserId(), options.ids[0]).then(function(item) { return options.items = [item], instance.playWithCommand(options, command) }) } return this._castPlayer.loadMedia(options, command) }, ChromecastPlayer.prototype.seek = function(position) { position = parseInt(position), position /= 1e7, this._castPlayer.sendMessage({ options: { position: position }, command: "Seek" }) }, ChromecastPlayer.prototype.setAudioStreamIndex = function(index) { this._castPlayer.sendMessage({ options: { index: index }, command: "SetAudioStreamIndex" }) }, ChromecastPlayer.prototype.setSubtitleStreamIndex = function(index) { this._castPlayer.sendMessage({ options: { index: index }, command: "SetSubtitleStreamIndex" }) }, ChromecastPlayer.prototype.setMaxStreamingBitrate = function(options) { this._castPlayer.sendMessage({ options: options, command: "SetMaxStreamingBitrate" }) }, ChromecastPlayer.prototype.isFullscreen = function() { var state = this.lastPlayerData || {}; return state = state.PlayState || {}, state.IsFullscreen }, ChromecastPlayer.prototype.nextTrack = function() { this._castPlayer.sendMessage({ options: {}, command: "NextTrack" }) }, ChromecastPlayer.prototype.previousTrack = function() { this._castPlayer.sendMessage({ options: {}, command: "PreviousTrack" }) }, ChromecastPlayer.prototype.volumeDown = function() { this._castPlayer.sendMessage({ options: {}, command: "VolumeDown" }) }, ChromecastPlayer.prototype.endSession = function() { var instance = this; this.stop().then(function() { setTimeout(function() { instance._castPlayer.stopApp() }, 1e3) }) }, ChromecastPlayer.prototype.volumeUp = function() { this._castPlayer.sendMessage({ options: {}, command: "VolumeUp" }) }, ChromecastPlayer.prototype.setVolume = function(vol) { vol = Math.min(vol, 100), vol = Math.max(vol, 0), this._castPlayer.sendMessage({ options: { volume: vol }, command: "SetVolume" }) }, ChromecastPlayer.prototype.unpause = function() { this._castPlayer.sendMessage({ options: {}, command: "Unpause" }) }, ChromecastPlayer.prototype.playPause = function() { this._castPlayer.sendMessage({ options: {}, command: "PlayPause" }) }, ChromecastPlayer.prototype.pause = function() { this._castPlayer.sendMessage({ options: {}, command: "Pause" }) }, ChromecastPlayer.prototype.stop = function() { return this._castPlayer.sendMessage({ options: {}, command: "Stop" }) }, ChromecastPlayer.prototype.displayContent = function(options) { this._castPlayer.sendMessage({ options: options, command: "DisplayContent" }) }, ChromecastPlayer.prototype.setMute = function(isMuted) { var castPlayer = this._castPlayer; isMuted ? castPlayer.sendMessage({ options: {}, command: "Mute" }) : castPlayer.sendMessage({ options: {}, command: "Unmute" }) }, ChromecastPlayer.prototype.getRepeatMode = function() { var state = this.lastPlayerData || {}; return state = state.PlayState || {}, state.RepeatMode }, ChromecastPlayer.prototype.playTrailers = function(item) { this._castPlayer.sendMessage({ options: { ItemId: item.Id, ServerId: item.ServerId }, command: "PlayTrailers" }) }, ChromecastPlayer.prototype.setRepeatMode = function(mode) { this._castPlayer.sendMessage({ options: { RepeatMode: mode }, command: "SetRepeatMode" }) }, ChromecastPlayer.prototype.toggleMute = function() { this._castPlayer.sendMessage({ options: {}, command: "ToggleMute" }) }, ChromecastPlayer.prototype.audioTracks = function() { var state = this.lastPlayerData || {}; return state = state.NowPlayingItem || {}, (state.MediaStreams || []).filter(function(s) { return "Audio" === s.Type }) }, ChromecastPlayer.prototype.getAudioStreamIndex = function() { var state = this.lastPlayerData || {}; return state = state.PlayState || {}, state.AudioStreamIndex }, ChromecastPlayer.prototype.subtitleTracks = function() { var state = this.lastPlayerData || {}; return state = state.NowPlayingItem || {}, (state.MediaStreams || []).filter(function(s) { return "Subtitle" === s.Type }) }, ChromecastPlayer.prototype.getSubtitleStreamIndex = function() { var state = this.lastPlayerData || {}; return state = state.PlayState || {}, state.SubtitleStreamIndex }, ChromecastPlayer.prototype.getMaxStreamingBitrate = function() { var state = this.lastPlayerData || {}; return state = state.PlayState || {}, state.MaxStreamingBitrate }, ChromecastPlayer.prototype.getVolume = function() { var state = this.lastPlayerData || {}; return state = state.PlayState || {}, null == state.VolumeLevel ? 100 : state.VolumeLevel }, ChromecastPlayer.prototype.isPlaying = function() { return null != (this.lastPlayerData || {}).NowPlayingItem }, ChromecastPlayer.prototype.isPlayingVideo = function() { var state = this.lastPlayerData || {}; return state = state.NowPlayingItem || {}, "Video" === state.MediaType }, ChromecastPlayer.prototype.isPlayingAudio = function() { var state = this.lastPlayerData || {}; return state = state.NowPlayingItem || {}, "Audio" === state.MediaType }, ChromecastPlayer.prototype.currentTime = function(val) { if (null != val) return this.seek(val); var state = this.lastPlayerData || {}; return state = state.PlayState || {}, state.PositionTicks }, ChromecastPlayer.prototype.duration = function() { var state = this.lastPlayerData || {}; return state = state.NowPlayingItem || {}, state.RunTimeTicks }, ChromecastPlayer.prototype.getBufferedRanges = function() { var state = this.lastPlayerData || {}; return state = state.PlayState || {}, state.BufferedRanges || [] }, ChromecastPlayer.prototype.paused = function() { var state = this.lastPlayerData || {}; return state = state.PlayState || {}, state.IsPaused }, ChromecastPlayer.prototype.isMuted = function() { var state = this.lastPlayerData || {}; return state = state.PlayState || {}, state.IsMuted }, ChromecastPlayer.prototype.shuffle = function(item) { var apiClient = connectionManager.getApiClient(item.ServerId), userId = apiClient.getCurrentUserId(), instance = this; apiClient.getItem(userId, item.Id).then(function(item) { instance.playWithCommand({ items: [item] }, "Shuffle") }) }, ChromecastPlayer.prototype.instantMix = function(item) { var apiClient = connectionManager.getApiClient(item.ServerId), userId = apiClient.getCurrentUserId(), instance = this; apiClient.getItem(userId, item.Id).then(function(item) { instance.playWithCommand({ items: [item] }, "InstantMix") }) }, ChromecastPlayer.prototype.canPlayMediaType = function(mediaType) { return "audio" === (mediaType = (mediaType || "").toLowerCase()) || "video" === mediaType }, ChromecastPlayer.prototype.canQueueMediaType = function(mediaType) { return this.canPlayMediaType(mediaType) }, ChromecastPlayer.prototype.queue = function(options) { this.playWithCommand(options, "PlayLast") }, ChromecastPlayer.prototype.queueNext = function(options) { this.playWithCommand(options, "PlayNext") }, ChromecastPlayer.prototype.play = function(options) { if (options.items) return this.playWithCommand(options, "PlayNow"); if (!options.serverId) throw new Error("serverId required!"); var instance = this; return getItemsForPlayback(connectionManager.getApiClient(options.serverId), { Ids: options.ids.join(",") }).then(function(result) { return options.items = result.Items, instance.playWithCommand(options, "PlayNow") }) }, ChromecastPlayer.prototype.toggleFullscreen = function() {}, ChromecastPlayer.prototype.beginPlayerUpdates = function() {}, ChromecastPlayer.prototype.endPlayerUpdates = function() {}, ChromecastPlayer.prototype.getPlaylist = function() { return Promise.resolve([]) }, ChromecastPlayer.prototype.getCurrentPlaylistItemId = function() {}, ChromecastPlayer.prototype.setCurrentPlaylistItem = function(playlistItemId) { return Promise.resolve() }, ChromecastPlayer.prototype.removeFromPlaylist = function(playlistItemIds) { return Promise.resolve() }, ChromecastPlayer.prototype.getPlayerState = function() { return this.getPlayerStateInternal() || {} }, ChromecastPlayer });