diff --git a/dashboard-ui/scripts/chromecast.js b/dashboard-ui/scripts/chromecast.js index a56fe0f7ad..b0592593a3 100644 --- a/dashboard-ui/scripts/chromecast.js +++ b/dashboard-ui/scripts/chromecast.js @@ -5,17 +5,17 @@ /** * Constants of states for Chromecast device **/ - var DEVICE_STATE = { + var DEVICE_STATE = { 'IDLE': 0, 'ACTIVE': 1, 'WARNING': 2, - 'ERROR': 3, + 'ERROR': 3, }; /** * Constants of states for CastPlayer **/ - var PLAYER_STATE = { + var PLAYER_STATE = { 'IDLE': 'IDLE', 'LOADING': 'LOADING', 'LOADED': 'LOADED', @@ -23,11 +23,13 @@ 'PAUSED': 'PAUSED', 'STOPPED': 'STOPPED', 'SEEKING': 'SEEKING', - 'ERROR': 'ERROR' + 'ERROR': 'ERROR' }; var PlayerName = 'Chromecast'; - + var cPlayer = { + deviceState: DEVICE_STATE.IDLE + }; var CastPlayer = function () { /* device variables */ @@ -82,7 +84,7 @@ this.incrementMediaTimeHandler = this.incrementMediaTime.bind(this); this.mediaStatusUpdateHandler = this.onMediaStatusUpdate.bind(this); - this.initializeCastPlayer(); + this.initializeCastPlayer(); }; /** @@ -91,21 +93,23 @@ * invoked once the API has finished initialization. The sessionListener and * receiverListener may be invoked at any time afterwards, and possibly more than once. */ - CastPlayer.prototype.initializeCastPlayer = function () { + CastPlayer.prototype.initializeCastPlayer = function () { - if (!chrome) { - return; + if (!chrome) { + return; } - if (!chrome.cast || !chrome.cast.isAvailable) { + if (!chrome.cast || !chrome.cast.isAvailable) { setTimeout(this.initializeCastPlayer.bind(this), 1000); - return; + return; } // v1 Id AE4DA10A // v2 Id 472F0435 - var applicationID = '472F0435'; + // default receiver chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID + + var applicationID = chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID; // request session var sessionRequest = new chrome.cast.SessionRequest(applicationID); @@ -115,23 +119,23 @@ console.log('chromecast.initialize'); - chrome.cast.initialize(apiConfig, this.onInitSuccess.bind(this), this.errorHandler); + chrome.cast.initialize(apiConfig, this.onInitSuccess.bind(this), this.errorHandler); }; /** * Callback function for init success */ - CastPlayer.prototype.onInitSuccess = function () { + CastPlayer.prototype.onInitSuccess = function () { this.isInitialized = true; - console.log("chromecast init success"); + console.log("chromecast init success"); }; /** * Generic error callback function */ - CastPlayer.prototype.onError = function () { - console.log("chromecast error"); + CastPlayer.prototype.onError = function () { + console.log("chromecast error"); }; /** @@ -141,17 +145,17 @@ * join existing session and occur in Cast mode and media * status gets synced up with current media of the session */ - CastPlayer.prototype.sessionListener = function (e) { + CastPlayer.prototype.sessionListener = function (e) { this.session = e; - if (this.session) { + if (this.session) { this.deviceState = DEVICE_STATE.ACTIVE; MediaController.setActivePlayer(PlayerName); - if (this.session.media[0]) { - this.onMediaDiscovered('activeSession', this.session.media[0]); + if (this.session.media[0]) { + this.onMediaDiscovered('activeSession', this.session.media[0]); } - this.session.addUpdateListener(this.sessionUpdateListener.bind(this)); - } + this.session.addUpdateListener(this.sessionUpdateListener.bind(this)); + } }; /** @@ -159,31 +163,31 @@ * This indicates availability of receivers but * does not provide a list of device IDs */ - CastPlayer.prototype.receiverListener = function (e) { + CastPlayer.prototype.receiverListener = function (e) { - if (e === 'available') { + if (e === 'available') { console.log("chromecast receiver found"); - this.hasReceivers = true; + this.hasReceivers = true; } - else { + else { console.log("chromecast receiver list empty"); - this.hasReceivers = false; - } + this.hasReceivers = false; + } }; /** * session update listener */ - CastPlayer.prototype.sessionUpdateListener = function (isAlive) { - if (!isAlive) { + CastPlayer.prototype.sessionUpdateListener = function (isAlive) { + if (!isAlive) { this.session = null; this.deviceState = DEVICE_STATE.IDLE; this.castPlayerState = PLAYER_STATE.IDLE; this.currentMediaSession = null; clearInterval(this.timer); - MediaController.removeActivePlayer(PlayerName); - } + MediaController.removeActivePlayer(PlayerName); + } }; /** @@ -191,180 +195,185 @@ * passed to the API at initialization time is used; this may be overridden by passing a different * session request in opt_sessionRequest. */ - CastPlayer.prototype.launchApp = function () { + CastPlayer.prototype.launchApp = function () { console.log("chromecast launching app..."); chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this)); - if (this.timer) { - clearInterval(this.timer); - } + if (this.timer) { + clearInterval(this.timer); + } }; /** * Callback function for request session success * @param {Object} e A chrome.cast.Session object */ - CastPlayer.prototype.onRequestSessionSuccess = function (e) { + CastPlayer.prototype.onRequestSessionSuccess = function (e) { console.log("chromecast session success: " + e.sessionId); this.session = e; this.deviceState = DEVICE_STATE.ACTIVE; - this.session.addUpdateListener(this.sessionUpdateListener.bind(this)); + this.session.addUpdateListener(this.sessionUpdateListener.bind(this)); }; /** * Callback function for launch error */ - CastPlayer.prototype.onLaunchError = function () { + CastPlayer.prototype.onLaunchError = function () { console.log("chromecast launch error"); this.deviceState = DEVICE_STATE.ERROR; - Dashboard.alert({ + Dashboard.alert({ title: Globalize.translate("Error"), - message: Globalize.translate("ErrorLaunchingChromecast") + message: Globalize.translate("ErrorLaunchingChromecast") }); - MediaController.removeActivePlayer(PlayerName); + MediaController.removeActivePlayer(PlayerName); }; /** * Stops the running receiver application associated with the session. */ - CastPlayer.prototype.stopApp = function () { + CastPlayer.prototype.stopApp = function () { this.session.stop(this.onStopAppSuccess.bind(this, 'Session stopped'), - this.errorHandler); + this.errorHandler); }; /** * Callback function for stop app success */ - CastPlayer.prototype.onStopAppSuccess = function (message) { + CastPlayer.prototype.onStopAppSuccess = function (message) { console.log(message); this.deviceState = DEVICE_STATE.IDLE; this.castPlayerState = PLAYER_STATE.IDLE; this.currentMediaSession = null; - clearInterval(this.timer); + clearInterval(this.timer); }; /** * Loads media into a running receiver application * @param {Number} mediaIndex An index number to indicate current media content */ - CastPlayer.prototype.loadMedia = function (userId, options, command) { - - if (!this.session) { + CastPlayer.prototype.loadMedia = function (userId, options, command) { + var cPlayer = this; + if (!this.session) { console.log("no session"); - return; + return; } + startTimeTicks = 0; // TODO: update this + this.currentMediaOffset = startTimeTicks || 0; - //this.currentMediaOffset = startTimeTicks || 0; + ApiClient.getItem(userId, options.ids[0]).done(function (item) { + var maxBitrate = 12000000; + var mediaInfo = getMediaSourceInfo(userId, item, maxBitrate, item.MediaSources[0].Id, 0, 0); - //var maxBitrate = 12000000; - //var mediaInfo = getMediaSourceInfo(user, item, maxBitrate, mediaSourceId, audioStreamIndex, subtitleStreamIndex); + var streamUrl = getStreamUrl(item, mediaInfo, startTimeTicks, maxBitrate); - //var streamUrl = getStreamUrl(item, mediaInfo, startTimeTicks, maxBitrate); + var castMediaInfo = new chrome.cast.media.MediaInfo(streamUrl); - //var castMediaInfo = new chrome.cast.media.MediaInfo(streamUrl); + castMediaInfo.customData = getCustomData(item, mediaInfo.mediaSource.Id, startTimeTicks); + castMediaInfo.metadata = getMetadata(item); - //castMediaInfo.customData = getCustomData(item, mediaInfo.mediaSource.Id, startTimeTicks); - //castMediaInfo.metadata = getMetadata(item); + if (mediaInfo.streamContainer == 'm3u8') { + castMediaInfo.contentType = 'application/x-mpegURL'; + } else { + castMediaInfo.contentType = item.MediaType.toLowerCase() + '/' + mediaInfo.streamContainer.toLowerCase(); + } - //if (mediaInfo.streamContainer == 'm3u8') { - // castMediaInfo.contentType = 'application/x-mpegURL'; - //} else { - // castMediaInfo.contentType = item.MediaType.toLowerCase() + '/' + mediaInfo.streamContainer.toLowerCase(); - //} + castMediaInfo.streamType = mediaInfo.isStatic ? chrome.cast.media.StreamType.BUFFERED : chrome.cast.media.StreamType.LIVE; - //castMediaInfo.streamType = mediaInfo.isStatic ? chrome.cast.media.StreamType.BUFFERED : chrome.cast.media.StreamType.LIVE; + var request = new chrome.cast.media.LoadRequest(castMediaInfo); + request.autoplay = true; + request.currentTime = startTimeTicks ? startTimeTicks / 10000000 : 0; + + + cPlayer.castPlayerState = PLAYER_STATE.LOADING; + cPlayer.session.loadMedia(request, + cPlayer.onMediaDiscovered.bind(cPlayer, 'loadMedia'), + cPlayer.onLoadMediaError.bind(cPlayer)); + }); - //var request = new chrome.cast.media.LoadRequest(castMediaInfo); - //request.autoplay = true; - //request.currentTime = startTimeTicks ? startTimeTicks / 10000000 : 0; - //this.castPlayerState = PLAYER_STATE.LOADING; - //this.session.loadMedia(request, - // this.onMediaDiscovered.bind(this, 'loadMedia'), - // this.onLoadMediaError.bind(this)); }; /** * Callback function for loadMedia success * @param {Object} mediaSession A new media object. */ - CastPlayer.prototype.onMediaDiscovered = function (how, mediaSession) { + CastPlayer.prototype.onMediaDiscovered = function (how, mediaSession) { console.log("chromecast new media session ID:" + mediaSession.mediaSessionId + ' (' + how + ')'); this.currentMediaSession = mediaSession; this.currentMediaTime = mediaSession.currentTime; - if (how == 'loadMedia') { + if (how == 'loadMedia') { this.castPlayerState = PLAYER_STATE.PLAYING; clearInterval(this.timer); - this.startProgressTimer(); + this.startProgressTimer(); } - if (how == 'activeSession') { - this.castPlayerState = mediaSession.playerState; + if (how == 'activeSession') { + this.castPlayerState = mediaSession.playerState; } if (this.castPlayerState == PLAYER_STATE.PLAYING) { // start progress timer - this.startProgressTimer(); + this.startProgressTimer(); } this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler); - this.currentMediaDuration = mediaSession.media.duration * 10000000; + this.currentMediaDuration = mediaSession.media.duration * 10000000; }; /** * Callback function when media load returns error */ - CastPlayer.prototype.onLoadMediaError = function (e) { + CastPlayer.prototype.onLoadMediaError = function (e) { console.log("chromecast media error"); - this.castPlayerState = PLAYER_STATE.IDLE; + this.castPlayerState = PLAYER_STATE.IDLE; }; /** * Callback function for media status update from receiver * @param {!Boolean} e true/false */ - CastPlayer.prototype.onMediaStatusUpdate = function (e) { - if (e == false) { + CastPlayer.prototype.onMediaStatusUpdate = function (e) { + if (e == false) { this.currentMediaTime = 0; - this.castPlayerState = PLAYER_STATE.IDLE; + this.castPlayerState = PLAYER_STATE.IDLE; } console.log("chromecast updating media"); - this.updateProgressBarByTimer(); + this.updateProgressBarByTimer(); }; /** * Helper function * Increment media current position by 1 second */ - CastPlayer.prototype.incrementMediaTime = function () { - if (this.castPlayerState == PLAYER_STATE.PLAYING) { - if (this.currentMediaTime < this.currentMediaDuration) { + CastPlayer.prototype.incrementMediaTime = function () { + if (this.castPlayerState == PLAYER_STATE.PLAYING) { + if (this.currentMediaTime < this.currentMediaDuration) { this.currentMediaTime += 1; - this.updateProgressBarByTimer(); + this.updateProgressBarByTimer(); } - else { + else { this.currentMediaTime = 0; - clearInterval(this.timer); - } - } + clearInterval(this.timer); + } + } }; /** * Play media in Cast mode */ - CastPlayer.prototype.playMedia = function () { + CastPlayer.prototype.playMedia = function () { - if (!this.currentMediaSession) { - return; + if (!this.currentMediaSession) { + return; } - switch (this.castPlayerState) { + switch (this.castPlayerState) { case PLAYER_STATE.LOADED: case PLAYER_STATE.PAUSED: this.currentMediaSession.play(null, @@ -384,93 +393,93 @@ this.castPlayerState = PLAYER_STATE.PLAYING; break; default: - break; - } + break; + } }; /** * Pause media playback in Cast mode */ - CastPlayer.prototype.pauseMedia = function () { + CastPlayer.prototype.pauseMedia = function () { - if (!this.currentMediaSession) { - return; + if (!this.currentMediaSession) { + return; } - if (this.castPlayerState == PLAYER_STATE.PLAYING) { + if (this.castPlayerState == PLAYER_STATE.PLAYING) { this.castPlayerState = PLAYER_STATE.PAUSED; this.currentMediaSession.pause(null, this.mediaCommandSuccessCallback.bind(this, "paused " + this.currentMediaSession.sessionId), this.errorHandler); - clearInterval(this.timer); - } + clearInterval(this.timer); + } }; /** * Stop CC playback */ - CastPlayer.prototype.stopMedia = function () { + CastPlayer.prototype.stopMedia = function () { - if (!this.currentMediaSession) { - return; + if (!this.currentMediaSession) { + return; } this.currentMediaSession.stop(null, this.mediaCommandSuccessCallback.bind(this, "stopped " + this.currentMediaSession.sessionId), this.errorHandler); this.castPlayerState = PLAYER_STATE.STOPPED; - clearInterval(this.timer); + clearInterval(this.timer); }; /** * Set media volume in Cast mode * @param {Boolean} mute A boolean */ - CastPlayer.prototype.setReceiverVolume = function (mute, vol) { + CastPlayer.prototype.setReceiverVolume = function (mute, vol) { - if (!this.currentMediaSession) { - return; + if (!this.currentMediaSession) { + return; } - if (!mute) { + if (!mute) { this.currentVolume = vol || 1; this.session.setReceiverVolumeLevel(this.currentVolume, this.mediaCommandSuccessCallback.bind(this), - this.errorHandler); + this.errorHandler); } - else { + else { this.session.setReceiverMuted(true, this.mediaCommandSuccessCallback.bind(this), - this.errorHandler); - } + this.errorHandler); + } }; /** * Toggle mute CC */ - CastPlayer.prototype.toggleMute = function () { - if (this.audio == true) { - this.mute(); - } - else { - this.unMute(); + CastPlayer.prototype.toggleMute = function () { + if (this.audio == true) { + this.mute(); } + else { + this.unMute(); + } }; /** * Mute CC */ - CastPlayer.prototype.mute = function () { + CastPlayer.prototype.mute = function () { this.audio = false; - this.setReceiverVolume(true); + this.setReceiverVolume(true); }; /** * Unmute CC */ - CastPlayer.prototype.unMute = function () { + CastPlayer.prototype.unMute = function () { this.audio = true; - this.setReceiverVolume(false); + this.setReceiverVolume(false); }; @@ -478,13 +487,13 @@ * media seek function in either Cast or local mode * @param {Event} e An event object from seek */ - CastPlayer.prototype.seekMedia = function (event) { + CastPlayer.prototype.seekMedia = function (event) { var pos = parseInt(event); var curr = pos / 10000000; - if (this.castPlayerState != PLAYER_STATE.PLAYING && this.castPlayerState != PLAYER_STATE.PAUSED) { - return; + if (this.castPlayerState != PLAYER_STATE.PLAYING && this.castPlayerState != PLAYER_STATE.PAUSED) { + return; } this.currentMediaTime = curr; @@ -495,305 +504,306 @@ this.currentMediaSession.seek(request, this.onSeekSuccess.bind(this, 'media seek done'), this.errorHandler); - this.castPlayerState = PLAYER_STATE.SEEKING; + this.castPlayerState = PLAYER_STATE.SEEKING; }; /** * Callback function for seek success * @param {String} info A string that describe seek event */ - CastPlayer.prototype.onSeekSuccess = function (info) { + CastPlayer.prototype.onSeekSuccess = function (info) { console.log(info); - this.castPlayerState = PLAYER_STATE.PLAYING; + this.castPlayerState = PLAYER_STATE.PLAYING; }; /** * Callback function for media command success */ - CastPlayer.prototype.mediaCommandSuccessCallback = function (info, e) { - console.log(info); + CastPlayer.prototype.mediaCommandSuccessCallback = function (info, e) { + console.log(info); }; /** * Update progress bar when there is a media status update * @param {Object} e An media status update object */ - CastPlayer.prototype.updateProgressBar = function (e) { - if (e.idleReason == 'FINISHED' && e.playerState == 'IDLE') { + CastPlayer.prototype.updateProgressBar = function (e) { + if (e.idleReason == 'FINISHED' && e.playerState == 'IDLE') { clearInterval(this.timer); this.castPlayerState = PLAYER_STATE.STOPPED; - if (e.idleReason == 'FINISHED') { - $(this).trigger("/playback/complete", e); - } + if (e.idleReason == 'FINISHED') { + $(this).trigger("/playback/complete", e); + } } - else { + else { var p = Number(e.currentTime / this.currentMediaSession.media.duration + 1).toFixed(3); this.progressFlag = false; setTimeout(this.setProgressFlag.bind(this), 1000); // don't update progress in 1 second - } + } }; /** * Set progressFlag with a timeout of 1 second to avoid UI update * until a media status update from receiver */ - CastPlayer.prototype.setProgressFlag = function () { - this.progressFlag = true; + CastPlayer.prototype.setProgressFlag = function () { + this.progressFlag = true; }; /** * Update progress bar based on timer */ - CastPlayer.prototype.updateProgressBarByTimer = function () { - if (!this.currentMediaTime) { - this.currentMediaDuration = this.session.media[0].currentTime; + CastPlayer.prototype.updateProgressBarByTimer = function () { + if (!this.currentMediaTime) { + this.currentMediaDuration = this.session.media[0].currentTime; } - if (!this.currentMediaDuration) { - this.currentMediaDuration = this.session.media[0].media.customData.runTimeTicks; + if (!this.currentMediaDuration) { + this.currentMediaDuration = this.session.media[0].media.customData.runTimeTicks; } var pp = 0; - if (this.currentMediaDuration > 0) { - pp = Number(this.currentMediaTime / this.currentMediaDuration).toFixed(3); + if (this.currentMediaDuration > 0) { + pp = Number(this.currentMediaTime / this.currentMediaDuration).toFixed(3); } if (this.progressFlag) { // don't update progress if it's been updated on media status update event $(this).trigger("/playback/update", - [{ + [{ positionTicks: this.currentMediaTime * 10000000, - runtimeTicks: this.currentMediaDuration - }]); + runtimeTicks: this.currentMediaDuration + }]); } - if (pp > 100 || this.castPlayerState == PLAYER_STATE.IDLE) { + if (pp > 100 || this.castPlayerState == PLAYER_STATE.IDLE) { clearInterval(this.timer); this.deviceState = DEVICE_STATE.IDLE; this.castPlayerState = PLAYER_STATE.IDLE; - $(this).trigger("/playback/complete", true); - } + $(this).trigger("/playback/complete", true); + } }; /** * @param {function} A callback function for the fucntion to start timer */ - CastPlayer.prototype.startProgressTimer = function () { - if (this.timer) { + CastPlayer.prototype.startProgressTimer = function () { + if (this.timer) { clearInterval(this.timer); - this.timer = null; + this.timer = null; } // start progress timer - this.timer = setInterval(this.incrementMediaTimeHandler, this.timerStep); + this.timer = setInterval(this.incrementMediaTimeHandler, this.timerStep); }; + // Create Cast Player var castPlayer = new CastPlayer(); - function getCodecLimits() { + function getCodecLimits() { - return { + return { maxVideoAudioChannels: 6, maxAudioChannels: 2, maxVideoLevel: 41, maxWidth: 1920, maxHeight: 1080, - maxSampleRate: 44100 + maxSampleRate: 44100 - }; + }; } function canDirectStream(mediaType, mediaSource, maxBitrate) { // If bitrate is unknown don't direct stream - if (!mediaSource.Bitrate || mediaSource.Bitrate > maxBitrate) { - return false; + if (!mediaSource.Bitrate || mediaSource.Bitrate > maxBitrate) { + return false; } var codecLimits = getCodecLimits(); - if (mediaType == "Audio") { + if (mediaType == "Audio") { - return ['mp3', 'aac'].indexOf(mediaSource.Container || '') != -1; + return ['mp3', 'aac'].indexOf(mediaSource.Container || '') != -1; } - else if (mediaType == "Video") { + else if (mediaType == "Video") { - var videoStream = mediaSource.MediaStreams.filter(function (s) { + var videoStream = mediaSource.MediaStreams.filter(function (s) { - return s.Type == 'Video'; + return s.Type == 'Video'; })[0]; - if (!videoStream) { - return false; + if (!videoStream) { + return false; } - if (['high', 'main', 'baseline'].indexOf((videoStream.Profile || '').toLowerCase()) == -1) { - return false; + if (['high', 'main', 'baseline'].indexOf((videoStream.Profile || '').toLowerCase()) == -1) { + return false; } - if (!videoStream.Level || videoStream.Level > codecLimits.maxVideoLevel) { - return false; + if (!videoStream.Level || videoStream.Level > codecLimits.maxVideoLevel) { + return false; } - if (!videoStream.Width || videoStream.Width > codecLimits.maxWidth) { - return false; + if (!videoStream.Width || videoStream.Width > codecLimits.maxWidth) { + return false; } - if (!videoStream.Height || videoStream.Height > codecLimits.maxHeight) { - return false; + if (!videoStream.Height || videoStream.Height > codecLimits.maxHeight) { + return false; } - return ['mp4'].indexOf(mediaSource.Container || '') != -1; + return ['mp4'].indexOf(mediaSource.Container || '') != -1; } - throw new Error('Unrecognized MediaType'); + throw new Error('Unrecognized MediaType'); } - function canPlayAudioStreamDirect(audioStream, isVideo) { + function canPlayAudioStreamDirect(audioStream, isVideo) { var audioCodec = (audioStream.Codec || '').toLowerCase().replace('-', ''); if (audioCodec.indexOf('aac') == -1 && audioCodec.indexOf('mp3') == -1 && - audioCodec.indexOf('mpeg') == -1) { + audioCodec.indexOf('mpeg') == -1) { - return false; + return false; } var codecLimits = getCodecLimits(); var maxChannels = isVideo ? codecLimits.maxVideoAudioChannels : codecLimits.maxAudioChannels; - if (!audioStream.Channels || audioStream.Channels > maxChannels) { - return false; + if (!audioStream.Channels || audioStream.Channels > maxChannels) { + return false; } - if (!audioStream.SampleRate || audioStream.SampleRate > codecLimits.maxSampleRate) { - return false; + if (!audioStream.SampleRate || audioStream.SampleRate > codecLimits.maxSampleRate) { + return false; } - return true; + return true; } - function isSupportedCodec(mediaType, mediaSource) { + function isSupportedCodec(mediaType, mediaSource) { - if (mediaType == "Audio") { - return false; + if (mediaType == "Audio") { + return false; } - else if (mediaType == "Video") { + else if (mediaType == "Video") { - return mediaSource.MediaStreams.filter(function (m) { + return mediaSource.MediaStreams.filter(function (m) { - return m.Type == "Video" && (m.Codec || '').toLowerCase() == 'h264'; + return m.Type == "Video" && (m.Codec || '').toLowerCase() == 'h264'; - }).length > 0; + }).length > 0; } - throw new Error('Unrecognized MediaType'); + throw new Error('Unrecognized MediaType'); } - function getStreamByIndex(streams, type, index) { - return streams.filter(function (s) { + function getStreamByIndex(streams, type, index) { + return streams.filter(function (s) { - return s.Type == type && s.Index == index; + return s.Type == type && s.Index == index; - })[0]; + })[0]; } function getDefaultAudioStream(mediaStreams, user) { // Find all audio streams - var audioStreams = mediaStreams.filter(function (stream) { - return stream.Type == "Audio"; + var audioStreams = mediaStreams.filter(function (stream) { + return stream.Type == "Audio"; - }).sort(function (a, b) { + }).sort(function (a, b) { var av = a.IsDefault ? 0 : 1; var bv = b.IsDefault ? 0 : 1; - return av - bv; + return av - bv; }); - if (user.Configuration.AudioLanguagePreference) { + if (user.Configuration.AudioLanguagePreference) { - for (var i = 0, length = audioStreams.length; i < length; i++) { + for (var i = 0, length = audioStreams.length; i < length; i++) { var mediaStream = audioStreams[i]; - if (mediaStream.Language == user.Configuration.AudioLanguagePreference) { - return mediaStream.Index; - } + if (mediaStream.Language == user.Configuration.AudioLanguagePreference) { + return mediaStream.Index; + } - } + } } // Just use the first audio stream - return audioStreams[0]; + return audioStreams[0]; } - function getMediaSourceInfo(user, item, maxBitrate, mediaSourceId, audioStreamIndex, subtitleStreamIndex) { + function getMediaSourceInfo(user, item, maxBitrate, mediaSourceId, audioStreamIndex, subtitleStreamIndex) { var sources = item.MediaSources || []; // If a specific stream was requested, filter the list - if (mediaSourceId) { - sources = sources.filter(function (m) { + if (mediaSourceId) { + sources = sources.filter(function (m) { - return m.Id == mediaSourceId; + return m.Id == mediaSourceId; - }); + }); } // Find first one that can be direct streamed - var source = sources.filter(function (m) { + var source = sources.filter(function (m) { - var audioStreams = m.MediaStreams.filter(function (s) { - return s.Type == 'Audio'; + var audioStreams = m.MediaStreams.filter(function (s) { + return s.Type == 'Audio'; }); var audioStream = mediaSourceId == m.Id && audioStreamIndex != null ? getStreamByIndex(audioStreams, 'Audio', audioStreamIndex) : getDefaultAudioStream(audioStreams, user); - if (!audioStream || !canPlayAudioStreamDirect(audioStream, item.MediaType == 'Video')) { - return false; + if (!audioStream || !canPlayAudioStreamDirect(audioStream, item.MediaType == 'Video')) { + return false; } var subtitleStream = mediaSourceId == m.Id && subtitleStreamIndex != null ? getStreamByIndex(m.MediaStreams, 'Subtitle', subtitleStreamIndex) : null; - if (subtitleStream) { - return false; + if (subtitleStream) { + return false; } - return canDirectStream(item.MediaType, m, maxBitrate, audioStream); + return canDirectStream(item.MediaType, m, maxBitrate, audioStream); })[0]; - if (source) { - return { + if (source) { + return { mediaSource: source, isStatic: true, - streamContainer: source.Container - }; + streamContainer: source.Container + }; } // Find first one with supported codec - source = sources.filter(function (m) { + source = sources.filter(function (m) { - return isSupportedCodec(item.MediaType, m); + return isSupportedCodec(item.MediaType, m); })[0]; // Default to first one - return { + return { mediaSource: source || sources[0], isStatic: false, - streamContainer: item.MediaType == 'Audio' ? 'mp3' : 'm3u8' - }; + streamContainer: item.MediaType == 'Audio' ? 'mp3' : 'm3u8' + }; } - function getCustomData(item, mediaSourceId, startTimeTicks) { + function getCustomData(item, mediaSourceId, startTimeTicks) { - return { + return { serverAddress: ApiClient.serverAddress(), itemId: item.Id, @@ -801,154 +811,155 @@ deviceName: ApiClient.deviceName(), //deviceId: ApiClient.deviceId(), startTimeTicks: startTimeTicks || 0, - runTimeTicks: item.RunTimeTicks - }; + runTimeTicks: item.RunTimeTicks + }; } - function getMetadata(item) { + function getMetadata(item) { var metadata = {}; - if (item.Type == 'Episode') { + if (item.Type == 'Episode') { metadata = new chrome.cast.media.TvShowMediaMetadata(); metadata.type = chrome.cast.media.MetadataType.TV_SHOW; metadata.episodeTitle = item.Name; - if (item.PremiereDate) { - metadata.originalAirdate = parseISO8601Date(item.PremiereDate).toISOString(); + if (item.PremiereDate) { + metadata.originalAirdate = parseISO8601Date(item.PremiereDate).toISOString(); } metadata.seriesTitle = item.SeriesName; - if (item.IndexNumber != null) { - metadata.episode = metadata.episodeNumber = item.IndexNumber; + if (item.IndexNumber != null) { + metadata.episode = metadata.episodeNumber = item.IndexNumber; } - if (item.ParentIndexNumber != null) { - metadata.season = metadata.seasonNumber = item.ParentIndexNumber; - } + if (item.ParentIndexNumber != null) { + metadata.season = metadata.seasonNumber = item.ParentIndexNumber; + } } - else if (item.Type == 'Photo') { + else if (item.Type == 'Photo') { metadata = new chrome.cast.media.PhotoMediaMetadata(); metadata.type = chrome.cast.media.MetadataType.PHOTO; - if (item.PremiereDate) { - metadata.creationDateTime = parseISO8601Date(item.PremiereDate).toISOString(); - } + if (item.PremiereDate) { + metadata.creationDateTime = parseISO8601Date(item.PremiereDate).toISOString(); + } } - else if (item.MediaType == 'Audio') { + else if (item.MediaType == 'Audio') { metadata = new chrome.cast.media.MusicTrackMediaMetadata(); metadata.type = chrome.cast.media.MetadataType.MUSIC_TRACK; - if (item.ProductionYear) { - metadata.releaseYear = item.ProductionYear; + if (item.ProductionYear) { + metadata.releaseYear = item.ProductionYear; } - if (item.PremiereDate) { - metadata.releaseDate = parseISO8601Date(item.PremiereDate).toISOString(); + if (item.PremiereDate) { + metadata.releaseDate = parseISO8601Date(item.PremiereDate).toISOString(); } metadata.songName = item.Name; metadata.artist = item.Artists & item.Artists.length ? item.Artists[0] : ''; metadata.albumArtist = item.AlbumArtist; - if (item.IndexNumber != null) { - metadata.trackNumber = item.IndexNumber; + if (item.IndexNumber != null) { + metadata.trackNumber = item.IndexNumber; } - if (item.ParentIndexNumber != null) { - metadata.discNumber = item.ParentIndexNumber; + if (item.ParentIndexNumber != null) { + metadata.discNumber = item.ParentIndexNumber; } - var composer = (item.People || []).filter(function (p) { - return p.PersonType == 'Type'; + var composer = (item.People || []).filter(function (p) { + return p.PersonType == 'Type'; })[0]; - if (composer) { - metadata.composer = composer.Name; - } + if (composer) { + metadata.composer = composer.Name; + } } - else if (item.MediaType == 'Movie') { + else if (item.MediaType == 'Movie') { metadata = new chrome.cast.media.MovieMediaMetadata(); metadata.type = chrome.cast.media.MetadataType.MOVIE; - if (item.ProductionYear) { - metadata.releaseYear = item.ProductionYear; + if (item.ProductionYear) { + metadata.releaseYear = item.ProductionYear; } - if (item.PremiereDate) { - metadata.releaseDate = parseISO8601Date(item.PremiereDate).toISOString(); - } + if (item.PremiereDate) { + metadata.releaseDate = parseISO8601Date(item.PremiereDate).toISOString(); + } } - else { + else { metadata = new chrome.cast.media.GenericMediaMetadata(); metadata.type = chrome.cast.media.MetadataType.GENERIC; - if (item.ProductionYear) { - metadata.releaseYear = item.ProductionYear; + if (item.ProductionYear) { + metadata.releaseYear = item.ProductionYear; } - if (item.PremiereDate) { - metadata.releaseDate = parseISO8601Date(item.PremiereDate).toISOString(); - } + if (item.PremiereDate) { + metadata.releaseDate = parseISO8601Date(item.PremiereDate).toISOString(); + } } metadata.title = item.Name; - if (item.Studios && item.Studios.length) { - metadata.Studio = item.Studios[0]; + if (item.Studios && item.Studios.length) { + metadata.Studio = item.Studios[0]; } - return metadata; + return metadata; } - function getStreamUrl(item, mediaSourceInfo, startTimeTicks, maxBitrate) { + function getStreamUrl(item, mediaSourceInfo, startTimeTicks, maxBitrate) { var url; var codecLimits = getCodecLimits(); - if (item.MediaType == 'Audio') { + if (item.MediaType == 'Audio') { url = ApiClient.serverAddress() + '/mediabrowser/audio/' + item.Id + '/stream.' + mediaSourceInfo.streamContainer + '?'; + url += '&static=' + mediaSourceInfo.isStatic.toString(); url += '&maxaudiochannels=' + codecLimits.maxAudioChannels; - if (startTimeTicks) { - url += '&startTimeTicks=' + startTimeTicks.toString(); + if (startTimeTicks) { + url += '&startTimeTicks=' + startTimeTicks.toString(); } - if (maxBitrate) { - url += '&audiobitrate=' + Math.min(maxBitrate, 320000).toString(); + if (maxBitrate) { + url += '&audiobitrate=' + Math.min(maxBitrate, 320000).toString(); } url += '&audiosamplerate=' + codecLimits.maxSampleRate; url += '&mediasourceid=' + mediaSourceInfo.mediaSource.Id; - - return url; + url += '&deviceId=' + ApiClient.deviceId(); + return url; } - else if (item.MediaType == 'Video') { + else if (item.MediaType == 'Video') { url = ApiClient.serverAddress() + '/mediabrowser/videos/' + item.Id + '/stream.' + mediaSourceInfo.streamContainer + '?'; url += 'static=' + mediaSourceInfo.isStatic.toString(); url += '&maxaudiochannels=' + codecLimits.maxVideoAudioChannels; - if (startTimeTicks) { - url += '&startTimeTicks=' + startTimeTicks.toString(); + if (startTimeTicks) { + url += '&startTimeTicks=' + startTimeTicks.toString(); } - if (maxBitrate) { + if (maxBitrate) { var audioRate = 768000; url += '&audiobitrate=' + audioRate.toString(); - url += '&videobitrate=' + (maxBitrate - audioRate).toString(); + url += '&videobitrate=' + (maxBitrate - audioRate).toString(); } url += '&profile=high'; @@ -962,14 +973,14 @@ url += '&audiosamplerate=' + codecLimits.maxSampleRate; url += '&mediasourceid=' + mediaSourceInfo.mediaSource.Id; - - return url; + url += '&deviceId=' + ApiClient.deviceId(); + return url; } - throw new Error('Unrecognized MediaType'); + throw new Error('Unrecognized MediaType'); } - function chromecastPlayer() { + function chromecastPlayer() { var self = this; @@ -985,168 +996,168 @@ self.runtimeTicks = 0; - $(castPlayer).on("/playback/complete", function (e) { + $(castPlayer).on("/playback/complete", function (e) { var state = self.getPlayerStateInternal(); - $(self).trigger("playbackstop", [state]); + $(self).trigger("playbackstop", [state]); }); - $(castPlayer).on("/playback/update", function (e, data) { + $(castPlayer).on("/playback/update", function (e, data) { self.positionTicks = data.positionTicks; self.runtimeTicks = data.runtimeTicks; var state = self.getPlayerStateInternal(); - $(self).trigger("positionchange", [state]); + $(self).trigger("positionchange", [state]); }); - self.play = function (options) { - castPlayer.loadMedia(Dashboard.getCurrentUserId(), options, 'PlayNow'); + self.play = function (options) { + castPlayer.loadMedia(Dashboard.getCurrentUserId(), options, 'PlayNow'); }; - self.unpause = function () { + self.unpause = function () { self.isPaused = !self.isPaused; - castPlayer.playMedia(); + castPlayer.playMedia(); }; - self.pause = function () { + self.pause = function () { self.isPaused = true; - castPlayer.pauseMedia(); + castPlayer.pauseMedia(); }; - self.shuffle = function (id) { + self.shuffle = function (id) { var userId = Dashboard.getCurrentUserId(); - ApiClient.getItem(userId, id).done(function (item) { - var query = { + ApiClient.getItem(userId, id).done(function (item) { + var query = { UserId: userId, Fields: getItemFields, Limit: 50, Filters: "IsNotFolder", Recursive: true, - SortBy: "Random" + SortBy: "Random" }; - if (item.IsFolder) { - query.ParentId = id; + if (item.IsFolder) { + query.ParentId = id; } - else if (item.Type == "MusicArtist") { + else if (item.Type == "MusicArtist") { query.MediaTypes = "Audio"; - query.Artists = item.Name; + query.Artists = item.Name; } - else if (item.Type == "MusicGenre") { + else if (item.Type == "MusicGenre") { query.MediaTypes = "Audio"; - query.Genres = item.Name; - } else { - return; + query.Genres = item.Name; + } else { + return; } - self.getItemsForPlayback(query).done(function (result) { - self.play({ items: result.Items }); - }); - }); + self.getItemsForPlayback(query).done(function (result) { + self.play({ items: result.Items }); + }); + }); }; - self.instantMix = function (id) { + self.instantMix = function (id) { var userId = Dashboard.getCurrentUserId(); - ApiClient.getItem(userId, id).done(function (item) { + ApiClient.getItem(userId, id).done(function (item) { var promise; var mixLimit = 3; - if (item.Type == "MusicArtist") { - promise = ApiClient.getInstantMixFromArtist(name, { + if (item.Type == "MusicArtist") { + promise = ApiClient.getInstantMixFromArtist(name, { UserId: userId, Fields: getItemFields, - Limit: mixLimit - }); + Limit: mixLimit + }); } - else if (item.Type == "MusicGenre") { - promise = ApiClient.getInstantMixFromMusicGenre(name, { + else if (item.Type == "MusicGenre") { + promise = ApiClient.getInstantMixFromMusicGenre(name, { UserId: userId, Fields: getItemFields, - Limit: mixLimit - }); + Limit: mixLimit + }); } - else if (item.Type == "MusicAlbum") { - promise = ApiClient.getInstantMixFromAlbum(id, { + else if (item.Type == "MusicAlbum") { + promise = ApiClient.getInstantMixFromAlbum(id, { UserId: userId, Fields: getItemFields, - Limit: mixLimit - }); + Limit: mixLimit + }); } - else if (item.Type == "Audio") { - promise = ApiClient.getInstantMixFromSong(id, { + else if (item.Type == "Audio") { + promise = ApiClient.getInstantMixFromSong(id, { UserId: userId, Fields: getItemFields, - Limit: mixLimit - }); + Limit: mixLimit + }); } - else { - return; + else { + return; } - promise.done(function (result) { - self.play({ items: result.Items }); - }); - }); + promise.done(function (result) { + self.play({ items: result.Items }); + }); + }); }; - self.canQueueMediaType = function (mediaType) { - return mediaType == "Audio"; + self.canQueueMediaType = function (mediaType) { + return mediaType == "Audio"; }; - self.queue = function (options) { - castPlayer.loadMedia(Dashboard.getCurrentUserId(), options, 'PlayLast'); + self.queue = function (options) { + castPlayer.loadMedia(Dashboard.getCurrentUserId(), options, 'PlayLast'); }; - self.queueNext = function (options) { - castPlayer.loadMedia(Dashboard.getCurrentUserId(), options, 'PlayNext'); + self.queueNext = function (options) { + castPlayer.loadMedia(Dashboard.getCurrentUserId(), options, 'PlayNext'); }; - self.stop = function () { - castPlayer.stopMedia(); + self.stop = function () { + castPlayer.stopMedia(); }; - self.displayContent = function (options) { + self.displayContent = function (options) { }; - self.mute = function () { + self.mute = function () { self.isMuted = true; - castPlayer.mute(); + castPlayer.mute(); }; - self.unMute = function () { + self.unMute = function () { self.isMuted = false; - castPlayer.unMute(); + castPlayer.unMute(); }; - self.toggleMute = function () { - castPlayer.toggleMute(); + self.toggleMute = function () { + castPlayer.toggleMute(); }; - self.getTargets = function () { + self.getTargets = function () { var targets = []; - if (castPlayer.hasReceivers) { - targets.push(self.getCurrentTargetInfo()); + if (castPlayer.hasReceivers) { + targets.push(self.getCurrentTargetInfo()); } - return targets; + return targets; }; - self.getCurrentTargetInfo = function () { + self.getCurrentTargetInfo = function () { var appName = null; - if (castPlayer.session && castPlayer.session.receiver && castPlayer.session.receiver.friendlyName) { - appName = castPlayer.session.receiver.friendlyName; + if (castPlayer.session && castPlayer.session.receiver && castPlayer.session.receiver.friendlyName) { + appName = castPlayer.session.receiver.friendlyName; } - return { + return { name: PlayerName, id: PlayerName, playerName: self.name, // TODO: PlayerName == self.name, so do we need to use either/or? @@ -1159,18 +1170,18 @@ "Unmute", "ToggleMute", "SetVolume", - "DisplayContent"] - }; + "DisplayContent"] + }; }; - self.seek = function (position) { - castPlayer.seekMedia(position); + self.seek = function (position) { + castPlayer.seekMedia(position); }; - self.nextTrack = function () { + self.nextTrack = function () { }; - self.previousTrack = function () { + self.previousTrack = function () { }; self.beginPlayerUpdates = function () { @@ -1181,21 +1192,21 @@ // Stop polling here }; - self.volumeDown = function () { + self.volumeDown = function () { var vol = castPlayer.volumeLevel - 0.02; - castPlayer.setReceiverVolume(false, vol / 100); + castPlayer.setReceiverVolume(false, vol / 100); }; - self.volumeUp = function () { + self.volumeUp = function () { var vol = castPlayer.volumeLevel + 0.02; - castPlayer.setReceiverVolume(false, vol / 100); + castPlayer.setReceiverVolume(false, vol / 100); }; - self.setVolume = function (vol) { - castPlayer.setReceiverVolume(false, vol / 100); + self.setVolume = function (vol) { + castPlayer.setReceiverVolume(false, vol / 100); }; - self.getPlayerState = function () { + self.getPlayerState = function () { var deferred = $.Deferred(); @@ -1203,13 +1214,13 @@ deferred.resolveWith(null, [result]); - return deferred.promise(); + return deferred.promise(); }; - self.getPlayerStateInternal = function () { + self.getPlayerStateInternal = function () { - var state = { - PlayState: { + var state = { + PlayState: { CanSeek: self.runtimeTicks && self.positionTicks > 0, PositionTicks: self.positionTicks, @@ -1221,7 +1232,7 @@ // AudioStreamIndex: null, // SubtitleStreamIndex: null, // PlayMethod: 'DirectStream' or 'Transcode' - } + } }; // TODO: Implement @@ -1231,10 +1242,10 @@ //state.PlayState.MediaSourceId = 'xxx'; - state.NowPlayingItem = { + state.NowPlayingItem = { RunTimeTicks: self.runtimeTicks, - Name: 'Chromecast' + Name: 'Chromecast' }; var nowPlayingItem = state.NowPlayingItem; @@ -1256,20 +1267,19 @@ } - return state; - }; + return state; + }; } //MediaController.registerPlayer(new chromecastPlayer()); - //$(MediaController).on('playerchange', function () { + //$(MediaController).on('playerchange', function () { - // if (MediaController.getPlayerInfo().name == PlayerName) { - - // if (CastPlayer.deviceState != DEVICE_STATE.ACTIVE && CastPlayer.isInitialized) { - // CastPlayer.launchApp(); - // } - // } - //}); + // if (MediaController.getPlayerInfo().name == PlayerName) { + // if (castPlayer.deviceState != DEVICE_STATE.ACTIVE && castPlayer.isInitialized) { + // castPlayer.launchApp(); + // } + // } + //}); })(window, window.chrome, console); \ No newline at end of file