diff --git a/dashboard-ui/css/librarybrowser.css b/dashboard-ui/css/librarybrowser.css index a93a84f3d7..3a17ea195f 100644 --- a/dashboard-ui/css/librarybrowser.css +++ b/dashboard-ui/css/librarybrowser.css @@ -1574,6 +1574,7 @@ span.itemCommunityRating:not(:empty) + .userDataIcons { .detailsMenuHeaderWithLogo h3 { padding: 0 30px; + line-height: .5; } @media all and (min-height:500px) { diff --git a/dashboard-ui/scripts/chromecast.js b/dashboard-ui/scripts/chromecast.js index 9718a909f4..e07cca4334 100644 --- a/dashboard-ui/scripts/chromecast.js +++ b/dashboard-ui/scripts/chromecast.js @@ -229,8 +229,6 @@ $(this).trigger('connect'); - MediaController.setActivePlayer(PlayerName); - this.sendMessage({ options: {}, command: 'Identify' @@ -548,6 +546,8 @@ $(castPlayer).on("connect", function (e) { + MediaController.setActivePlayer(PlayerName, self.getCurrentTargetInfo()); + console.log('cc: connect'); // Reset this so the next query doesn't make it appear like content is playing. self.lastPlayerData = {}; @@ -837,6 +837,13 @@ console.log(JSON.stringify(data)); return data; }; + + self.tryPair = function (target) { + + var deferred = $.Deferred(); + deferred.resolve(); + return deferred.promise(); + }; } function initializeChromecast() { diff --git a/dashboard-ui/scripts/librarylist.js b/dashboard-ui/scripts/librarylist.js index 47a78a8996..33456a0176 100644 --- a/dashboard-ui/scripts/librarylist.js +++ b/dashboard-ui/scripts/librarylist.js @@ -532,7 +532,7 @@ html += '
'; html += ''; - html += '

'; + html += '

'; html += ''; html += '
'; diff --git a/dashboard-ui/scripts/mediacontroller.js b/dashboard-ui/scripts/mediacontroller.js index 08e5c8f663..ac8143ef73 100644 --- a/dashboard-ui/scripts/mediacontroller.js +++ b/dashboard-ui/scripts/mediacontroller.js @@ -118,15 +118,42 @@ } currentPlayer = player; - currentTargetInfo = targetInfo || player.getCurrentTargetInfo(); + currentTargetInfo = targetInfo; console.log('Active player: ' + JSON.stringify(currentTargetInfo)); $(self).trigger('playerchange'); }; + self.trySetActivePlayer = function (player, targetInfo) { + + if (typeof (player) === 'string') { + player = players.filter(function (p) { + return p.name == player; + })[0]; + } + + if (!player) { + throw new Error('null player'); + } + + player.tryPair(targetInfo).done(function() { + + currentPlayer = player; + currentTargetInfo = targetInfo; + + console.log('Active player: ' + JSON.stringify(currentTargetInfo)); + + $(self).trigger('playerchange'); + }); + }; + self.setDefaultPlayerActive = function () { - self.setActivePlayer(self.getDefaultPlayer()); + + var player = self.getDefaultPlayer(); + var target = player.getTargets()[0]; + + self.setActivePlayer(player, target); }; self.removeActivePlayer = function (name) { @@ -597,7 +624,7 @@ var playableMediaTypes = this.getAttribute('data-mediatypes').split(','); var supportedCommands = this.getAttribute('data-commands').split(','); - MediaController.setActivePlayer(playerName, { + MediaController.trySetActivePlayer(playerName, { id: targetId, name: targetName, playableMediaTypes: playableMediaTypes, diff --git a/dashboard-ui/scripts/mediaplayer.js b/dashboard-ui/scripts/mediaplayer.js index c906e1638d..049ee50f0c 100644 --- a/dashboard-ui/scripts/mediaplayer.js +++ b/dashboard-ui/scripts/mediaplayer.js @@ -1731,15 +1731,18 @@ var getItemFields = "MediaSources,Chapters"; - self.getCurrentTargetInfo = function () { - return self.getTargets()[0]; + self.tryPair = function (target) { + + var deferred = $.Deferred(); + deferred.resolve(); + return deferred.promise(); }; } window.MediaPlayer = new mediaPlayer(); window.MediaController.registerPlayer(window.MediaPlayer); - window.MediaController.setActivePlayer(window.MediaPlayer); + window.MediaController.setActivePlayer(window.MediaPlayer, window.MediaPlayer.getTargets()[0]); })(document, setTimeout, clearTimeout, screen, window.store, $, setInterval, window); \ No newline at end of file diff --git a/dashboard-ui/scripts/remotecontrol.js b/dashboard-ui/scripts/remotecontrol.js index a8ae221878..a698f8c44f 100644 --- a/dashboard-ui/scripts/remotecontrol.js +++ b/dashboard-ui/scripts/remotecontrol.js @@ -276,6 +276,13 @@ return deferred.promise(); }; + + self.tryPair = function(target) { + + var deferred = $.Deferred(); + deferred.resolve(); + return deferred.promise(); + }; } var player = new remoteControlPlayer(); diff --git a/dashboard-ui/scripts/site.js b/dashboard-ui/scripts/site.js index 9a43b27fb0..5f10a5b428 100644 --- a/dashboard-ui/scripts/site.js +++ b/dashboard-ui/scripts/site.js @@ -1813,7 +1813,7 @@ var AppInfo = {}; }); if (Dashboard.isRunningInCordova()) { - requirejs(['thirdparty/cordova/chromecast']); + requirejs(['thirdparty/cordova/connectsdk']); } else { if ($.browser.chrome) { requirejs(['scripts/chromecast']); diff --git a/dashboard-ui/thirdparty/cordova/chromecast.js b/dashboard-ui/thirdparty/cordova/chromecast.js index 5f282702bb..fb2ff33e90 100644 --- a/dashboard-ui/thirdparty/cordova/chromecast.js +++ b/dashboard-ui/thirdparty/cordova/chromecast.js @@ -1 +1,348 @@ - \ No newline at end of file +(function () { + + var PlayerName = "Chromecast"; + var ApplicationID = "F4EB2E8E"; + var currentDevice; + + function chromecastPlayer() { + + var self = this; + + // MediaController needs this + self.name = PlayerName; + + self.getItemsForPlayback = function (query) { + + var userId = Dashboard.getCurrentUserId(); + + query.Limit = query.Limit || 100; + query.ExcludeLocationTypes = "Virtual"; + + return ApiClient.getItems(userId, query); + }; + + var castPlayer = {}; + + $(castPlayer).on("connect", function (e) { + + console.log('cc: connect'); + // Reset this so the next query doesn't make it appear like content is playing. + self.lastPlayerData = {}; + }); + + $(castPlayer).on("playbackstart", function (e, data) { + + console.log('cc: playbackstart'); + + castPlayer.initializeCastPlayer(); + + var state = self.getPlayerStateInternal(data); + $(self).trigger("playbackstart", [state]); + }); + + $(castPlayer).on("playbackstop", function (e, data) { + + console.log('cc: playbackstop'); + var state = self.getPlayerStateInternal(data); + + $(self).trigger("playbackstop", [state]); + + // Reset this so the next query doesn't make it appear like content is playing. + self.lastPlayerData = {}; + }); + + $(castPlayer).on("playbackprogress", function (e, data) { + + console.log('cc: positionchange'); + var state = self.getPlayerStateInternal(data); + + $(self).trigger("positionchange", [state]); + }); + + self.play = function (options) { + + Dashboard.getCurrentUser().done(function (user) { + + if (options.items) { + + self.playWithCommand(options, 'PlayNow'); + + } else { + + self.getItemsForPlayback({ + + Ids: options.ids.join(',') + + }).done(function (result) { + + options.items = result.Items; + self.playWithCommand(options, 'PlayNow'); + + }); + } + + }); + + }; + + self.playWithCommand = function (options, command) { + + if (!options.items) { + ApiClient.getItem(Dashboard.getCurrentUserId(), options.ids[0]).done(function (item) { + + options.items = [item]; + self.playWithCommand(options, command); + }); + + return; + } + + castPlayer.loadMedia(options, command); + }; + + self.unpause = function () { + castPlayer.playMedia(); + }; + + self.pause = function () { + castPlayer.pauseMedia(); + }; + + self.shuffle = function (id) { + + var userId = Dashboard.getCurrentUserId(); + + ApiClient.getItem(userId, id).done(function (item) { + + self.playWithCommand({ + + items: [item] + + }, 'Shuffle'); + + }); + + }; + + self.instantMix = function (id) { + + var userId = Dashboard.getCurrentUserId(); + + ApiClient.getItem(userId, id).done(function (item) { + + self.playWithCommand({ + + items: [item] + + }, 'InstantMix'); + + }); + + }; + + self.canQueueMediaType = function (mediaType) { + return mediaType == "Audio"; + }; + + self.queue = function (options) { + self.playWithCommnd(options, 'PlayLast'); + }; + + self.queueNext = function (options) { + self.playWithCommand(options, 'PlayNext'); + }; + + self.stop = function () { + castPlayer.stopMedia(); + }; + + self.displayContent = function (options) { + + castPlayer.sendMessage({ + options: options, + command: 'DisplayContent' + }); + }; + + self.mute = function () { + castPlayer.mute(); + }; + + self.unMute = function () { + self.setVolume(getCurrentVolume() + 2); + }; + + self.toggleMute = function () { + + var state = self.lastPlayerData || {}; + state = state.PlayState || {}; + + if (state.IsMuted) { + self.unMute(); + } else { + self.mute(); + } + }; + + function getBaseTargetInfo() { + var target = {}; + + target.playerName = PlayerName; + target.playableMediaTypes = ["Audio", "Video"]; + target.isLocalPlayer = false; + target.appName = PlayerName; + target.supportedCommands = [ + "VolumeUp", + "VolumeDown", + "Mute", + "Unmute", + "ToggleMute", + "SetVolume", + "SetAudioStreamIndex", + "SetSubtitleStreamIndex", + "DisplayContent" + ]; + + return target; + } + + function convertDeviceToTarget(device) { + + var target = getBaseTargetInfo(); + + target.name = target.deviceName = device.getFriendlyName(); + target.id = device.getId(); + + return target; + } + + self.getTargets = function () { + + var manager = ConnectSDK.discoveryManager; + + return manager.getDeviceList().map(convertDeviceToTarget); + }; + + self.seek = function (position) { + castPlayer.seekMedia(position); + }; + + self.setAudioStreamIndex = function (index) { + castPlayer.sendMessage({ + options: { + index: index + }, + command: 'SetAudioStreamIndex' + }); + }; + + self.setSubtitleStreamIndex = function (index) { + castPlayer.sendMessage({ + options: { + index: index + }, + command: 'SetSubtitleStreamIndex' + }); + }; + + self.nextTrack = function () { + castPlayer.sendMessage({ + options: {}, + command: 'NextTrack' + }); + }; + + self.previousTrack = function () { + castPlayer.sendMessage({ + options: {}, + command: 'PreviousTrack' + }); + }; + + self.beginPlayerUpdates = function () { + // Setup polling here + }; + + self.endPlayerUpdates = function () { + // Stop polling here + }; + + function getCurrentVolume() { + var state = self.lastPlayerData || {}; + state = state.PlayState || {}; + + return state.VolumeLevel == null ? 100 : state.VolumeLevel; + } + + self.volumeDown = function () { + + self.setVolume(getCurrentVolume() - 2); + }; + + self.volumeUp = function () { + + self.setVolume(getCurrentVolume() + 2); + }; + + self.setVolume = function (vol) { + + vol = Math.min(vol, 100); + vol = Math.max(vol, 0); + + castPlayer.setReceiverVolume(false, (vol / 100)); + }; + + self.getPlayerState = function () { + + var deferred = $.Deferred(); + + var result = self.getPlayerStateInternal(); + + deferred.resolveWith(null, [result]); + + return deferred.promise(); + }; + + self.lastPlayerData = {}; + + self.getPlayerStateInternal = function (data) { + + data = data || self.lastPlayerData; + self.lastPlayerData = data; + + console.log(JSON.stringify(data)); + return data; + }; + + self.tryPair = function (target) { + + var deferred = $.Deferred(); + deferred.resolve(); + return deferred.promise(); + }; + } + + function onDeviceLost() { + + } + + function initSdk() { + + var manager = ConnectSDK.discoveryManager; + + manager.addListener('devicelost', onDeviceLost); + + MediaController.registerPlayer(new chromecastPlayer()); + + $(MediaController).on('playerchange', function () { + + if (MediaController.getPlayerInfo().name == PlayerName) { + + // launch app if needed + } + }); + } + + initSdk(); + +})(); \ No newline at end of file diff --git a/dashboard-ui/thirdparty/cordova/connectsdk.js b/dashboard-ui/thirdparty/cordova/connectsdk.js new file mode 100644 index 0000000000..bd58636556 --- /dev/null +++ b/dashboard-ui/thirdparty/cordova/connectsdk.js @@ -0,0 +1,37 @@ +(function () { + + + function onDeviceFound() { + + } + + function onDeviceLost() { + + } + + function initSdk() { + + var manager = ConnectSDK.discoveryManager; + + manager.setPairingLevel(ConnectSDK.PairingLevel.OFF); + manager.setAirPlayServiceMode(ConnectSDK.AirPlayServiceMode.Media); + + // Show devices that support playing videos and pausing + manager.setCapabilityFilters([ + new ConnectSDK.CapabilityFilter(["MediaPlayer.Display.Video", "MediaControl.Pause"]) + ]); + + manager.addListener('devicefound', onDeviceFound); + manager.addListener('devicelost', onDeviceLost); + + requirejs(['thirdparty/cordova/chromecast']); + } + + document.addEventListener("deviceready", function () { + + initSdk(); + + }, false); + + +})(); \ No newline at end of file