diff --git a/dashboard-ui/scripts/chromecast.js b/dashboard-ui/scripts/chromecast.js index 792185d9be..2f75ed9e47 100644 --- a/dashboard-ui/scripts/chromecast.js +++ b/dashboard-ui/scripts/chromecast.js @@ -1010,6 +1010,7 @@ var self = this; var isPositionSliderActive = false; + var currentPlaylistIndex; self.name = PlayerName; @@ -1365,11 +1366,35 @@ } }; + self.beginPlayerUpdates = function () { + // Setup polling here + }; + + self.endPlayerUpdates = function () { + // Stop polling here + }; + self.volumeDown = function () { }; self.volumeUp = function () { }; + + self.getPlayerState = function () { + + var deferred = $.Deferred(); + + var result = self.getPlayerStateInternal(); + + deferred.resolveWith(null, [result]); + + return deferred.promise(); + }; + + self.getPlayerStateInternal = function () { + + return {}; + }; } MediaController.registerPlayer(new chromecastPlayer()); diff --git a/dashboard-ui/scripts/mediacontroller.js b/dashboard-ui/scripts/mediacontroller.js index 74672e59a0..da61ab2c46 100644 --- a/dashboard-ui/scripts/mediacontroller.js +++ b/dashboard-ui/scripts/mediacontroller.js @@ -43,12 +43,12 @@ $(self).trigger('playerchange'); }; - self.setDefaultPlayerActive = function() { + self.setDefaultPlayerActive = function () { self.setActivePlayer(self.getDefaultPlayer()); }; self.removeActivePlayer = function (name) { - + if (self.getPlayerInfo().name == name) { self.setDefaultPlayerActive(); } @@ -78,7 +78,7 @@ } - targets = targets.sort(function(a,b) { + targets = targets.sort(function (a, b) { var aVal = a.isLocalPlayer ? 0 : 1; var bVal = b.isLocalPlayer ? 0 : 1; @@ -241,14 +241,12 @@ function onWebSocketMessageReceived(e, msg) { - var localPlayer = msg.MessageType === "Play" || - msg.MessageType === "Playstate" || - msg.MessageType === "GeneralCommand" ? - MediaController.getLocalPlayer() : - null; + var localPlayer; if (msg.MessageType === "Play") { + localPlayer = MediaController.getLocalPlayer(); + if (msg.Data.PlayCommand == "PlayNext") { localPlayer.queueNext({ ids: msg.Data.ItemIds }); } @@ -268,6 +266,8 @@ } else if (msg.MessageType === "Playstate") { + localPlayer = MediaController.getLocalPlayer(); + if (msg.Data.Command === 'Stop') { localPlayer.stop(); } @@ -286,14 +286,13 @@ else if (msg.Data.Command === 'PreviousTrack') { localPlayer.previousTrack(); } - else if (msg.Data.Command === 'Fullscreen') { - localPlayer.remoteFullscreen(); - } } else if (msg.MessageType === "GeneralCommand") { var cmd = msg.Data; + localPlayer = MediaController.getLocalPlayer(); + if (cmd.Name === 'Mute') { localPlayer.mute(); } @@ -309,6 +308,12 @@ else if (cmd.Name === 'ToggleMute') { localPlayer.toggleMute(); } + else if (msg.Data.Command === 'Fullscreen') { + localPlayer.remoteFullscreen(); + } + else if (msg.Data.Command === 'SetVolume') { + localPlayer.setVolume(cmd.Arguments.Volume); + } } } diff --git a/dashboard-ui/scripts/mediaplayer.js b/dashboard-ui/scripts/mediaplayer.js index c583fad7fd..dafa956312 100644 --- a/dashboard-ui/scripts/mediaplayer.js +++ b/dashboard-ui/scripts/mediaplayer.js @@ -169,7 +169,7 @@ }); if (currentItem.MediaType == "Video") { - ApiClient.stopActiveEncodings().done(function() { + ApiClient.stopActiveEncodings().done(function () { self.startTimeTicksOffset = ticks; element.src = currentSrc; @@ -213,7 +213,7 @@ currentTimeElement.html(timeText); } - var state = self.getPlayerState(currentMediaElement, currentItem, currentMediaSource); + var state = self.getPlayerStateInternal(currentMediaElement, currentItem, currentMediaSource); $(self).trigger('positionchange', [state]); }; @@ -481,7 +481,7 @@ var mediaControls = $("#videoControls"); - var state = self.getPlayerState(currentMediaElement, item, currentMediaSource); + var state = self.getPlayerStateInternal(currentMediaElement, item, currentMediaSource); var url = ""; @@ -865,6 +865,8 @@ elem.pause(); + var isVideo = currentItem.MediaType == "Video"; + $(elem).off("ended.playnext").on("ended", function () { $(this).off(); @@ -875,10 +877,12 @@ elem.src = ""; currentMediaElement = null; + currentItem = null; + currentMediaSource = null; }).trigger("ended"); - if (currentItem.MediaType == "Video") { + if (isVideo) { if (self.isFullScreen()) { self.exitFullScreen(); } @@ -890,13 +894,46 @@ return currentMediaElement; }; - self.getPlayerState = function (playerElement, item, mediaSource) { + self.getPlayerState = function() { - var itemName = ''; - var itemSubName = ''; + var deferred = $.Deferred(); + + var result = self.getPlayerStateInternal(currentMediaElement, currentItem, currentMediaSource); + + deferred.resolveWith(null, [result]); + + return deferred.promise(); + }; + + self.getPlayerStateInternal = function (playerElement, item, mediaSource) { + + var state = {}; + + if (playerElement) { + + state.volumeLevel = playerElement.volume * 100; + state.isMuted = playerElement.volume == 0; + state.isPaused = playerElement.paused; + state.positionTicks = self.getCurrentTicks(playerElement); + } + + if (mediaSource) { + + state.mediaSourceId = mediaSource.Id; + state.runtimeTicks = mediaSource.RunTimeTicks; + + state.canSeek = mediaSource.RunTimeTicks && mediaSource.RunTimeTicks > 0; + } if (item) { + state.itemId = item.Id; + state.mediaType = item.MediaType; + state.itemType = item.Type; + + var itemName = ''; + var itemSubName = ''; + var name = item.Name; var seriesName = ''; @@ -928,46 +965,42 @@ if (!itemSubName && item.ProductionYear) { itemSubName = item.ProductionYear; } - } - var state = { - itemId: item.Id, - mediaSourceId: mediaSource.Id, - volumeLevel: playerElement.volume * 100, - isMuted: playerElement.volume == 0, - isPaused: playerElement.paused, - runtimeTicks: mediaSource.RunTimeTicks, - positionTicks: self.getCurrentTicks(playerElement), - canSeek: mediaSource.RunTimeTicks && mediaSource.RunTimeTicks > 0, - mediaType: item.MediaType, - itemName: itemName, - itemSubName: itemSubName, - itemType: item.Type - }; + var imageTags = item.ImageTags || {}; - var imageTags = item.ImageTags || {}; + if (imageTags.Primary) { - if (imageTags.Primary) { + state.primaryImageItemId = item.Id; + state.primaryImageTag = imageTags.Primary; + } - state.primaryImageItemId = item.Id; - state.primaryImageTag = imageTags.Primary; - } + if (item.BackdropImageTags && item.BackdropImageTags.length) { - if (item.BackdropImageTags && item.BackdropImageTags.length) { + state.backdropItemId = item.Id; + state.backdropImageTag = item.BackdropImageTags[0]; + } - state.backdropItemId = item.Id; - state.backdropImageTag = item.BackdropImageTags[0]; - } + if (imageTags.Thumb) { - if (imageTags.Thumb) { + state.thumbItemId = item.Id; + state.thumbImageTag = imageTags.Thumb; + } - state.thumbItemId = item.Id; - state.thumbImageTag = imageTags.Thumb; + state.itemName = itemName; + state.itemSubName = itemSubName; } return state; }; + self.beginPlayerUpdates = function () { + // Nothing to setup here + }; + + self.endPlayerUpdates = function () { + // Nothing to setup here + }; + self.onPlaybackStart = function (playerElement, item, mediaSource) { self.updateCanClientSeek(playerElement); @@ -976,7 +1009,7 @@ self.startProgressInterval(item.Id, mediaSource.Id); - var state = self.getPlayerState(playerElement, item, mediaSource); + var state = self.getPlayerStateInternal(playerElement, item, mediaSource); $(self).trigger('playbackstart', [state]); }; @@ -985,7 +1018,7 @@ self.saveVolume(playerElement.volume); - var state = self.getPlayerState(playerElement, currentItem, currentMediaSource); + var state = self.getPlayerStateInternal(playerElement, currentItem, currentMediaSource); $(self).trigger('volumechange', [state]); }; @@ -1017,14 +1050,14 @@ self.resetEnhancements(); } - var state = self.getPlayerState(playerElement, item, mediaSource); + var state = self.getPlayerStateInternal(playerElement, item, mediaSource); $(self).trigger('playbackstop', [state]); }; self.onPlaystateChange = function (playerElement) { - var state = self.getPlayerState(playerElement, currentItem, currentMediaSource); + var state = self.getPlayerStateInternal(playerElement, currentItem, currentMediaSource); $(self).trigger('playstatechange', [state]); }; diff --git a/dashboard-ui/scripts/nowplayingbar.js b/dashboard-ui/scripts/nowplayingbar.js index cb93b6aa1c..33422bafea 100644 --- a/dashboard-ui/scripts/nowplayingbar.js +++ b/dashboard-ui/scripts/nowplayingbar.js @@ -154,6 +154,13 @@ function updatePlayerState(state) { + if (state.itemName) { + showNowPlayingBar(); + } else { + hideNowPlayingBar(); + return; + } + lastPlayerState = state; if (!muteButton) { @@ -211,6 +218,8 @@ } currentTimeElement.html(timeText); + + updateNowPlayingInfo(state); } function updateNowPlayingInfo(state) { @@ -271,24 +280,13 @@ var player = this; - if (player.isDefaultPlayer && state.mediaType == 'Video') { - return; - } + player.beginPlayerUpdates(); - showNowPlayingBar(); - - updatePlayerState(state); - updateNowPlayingInfo(state); + onStateChanged.call(player, e, state); } - var nowPlayingBarTimeout; function showNowPlayingBar() { - if (nowPlayingBarTimeout) { - clearTimeout(nowPlayingBarTimeout); - nowPlayingBarTimeout = null; - } - var nowPlayingBar = getNowPlayingBar(); nowPlayingBar.show(); @@ -296,24 +294,21 @@ function hideNowPlayingBar() { - if (nowPlayingBarTimeout) { - clearTimeout(nowPlayingBarTimeout); - nowPlayingBarTimeout = null; - } - // Use a timeout to prevent the bar from hiding and showing quickly // in the event of a stop->play command - nowPlayingBarTimeout = setTimeout(function () { - getNowPlayingBar().hide(); - }, 500); + getNowPlayingBar().hide(); } function onPlaybackStopped(e, state) { + var player = this; + + player.endPlayerUpdates(); + hideNowPlayingBar(); } - function onVolumeChanged(e, state) { + function onStateChanged(e, state) { var player = this; @@ -329,7 +324,10 @@ if (currentPlayer) { $(currentPlayer).off('.nowplayingbar'); + currentPlayer.endPlayerUpdates(); currentPlayer = null; + + hideNowPlayingBar(); } } @@ -339,11 +337,20 @@ currentPlayer = player; + player.getPlayerState().done(function (state) { + + if (state.itemName) { + player.beginPlayerUpdates(); + } + + onStateChanged.call(player, null, state); + }); + $(player).on('playbackstart.nowplayingbar', onPlaybackStart) .on('playbackstop.nowplayingbar', onPlaybackStopped) - .on('volumechange.nowplayingbar', onVolumeChanged) - .on('playstatechange.nowplayingbar', onVolumeChanged) - .on('positionchange.nowplayingbar', onVolumeChanged); + .on('volumechange.nowplayingbar', onStateChanged) + .on('playstatechange.nowplayingbar', onStateChanged) + .on('positionchange.nowplayingbar', onStateChanged); } $(function () { diff --git a/dashboard-ui/scripts/remotecontrol.js b/dashboard-ui/scripts/remotecontrol.js index 35add5b195..a97c02f1ee 100644 --- a/dashboard-ui/scripts/remotecontrol.js +++ b/dashboard-ui/scripts/remotecontrol.js @@ -96,14 +96,14 @@ var id = $('#selectSession', popup).val(); - ApiClient.sendSystemCommand(id, 'GoHome'); + ApiClient.sendCommand(id, 'GoHome'); }); $('.btnGoToSettings', popup).on('click', function () { var id = $('#selectSession', popup).val(); - ApiClient.sendSystemCommand(id, 'GoToSettings'); + ApiClient.sendCommand(id, 'GoToSettings'); }); $('.btnSendMessage', popup).on('click', function () { @@ -129,21 +129,21 @@ var id = $('#selectSession', popup).val(); - ApiClient.sendSystemCommand(id, 'VolumeDown'); + ApiClient.sendCommand(id, 'VolumeDown'); }); $('.btnVolumeUp', popup).on('click', function () { var id = $('#selectSession', popup).val(); - ApiClient.sendSystemCommand(id, 'VolumeUp'); + ApiClient.sendCommand(id, 'VolumeUp'); }); $('.btnToggleMute', popup).on('click', function () { var id = $('#selectSession', popup).val(); - ApiClient.sendSystemCommand(id, 'ToggleMute'); + ApiClient.sendCommand(id, 'ToggleMute'); }); $('.btnStop', popup).on('click', function () { @@ -432,6 +432,20 @@ ApiClient.sendPlayCommand(sessionId, remoteOptions); } + function sendPlayStateCommand(command, options) { + + var sessionId = MediaController.getPlayerInfo().id; + + ApiClient.sendPlayStateCommand(sessionId, command, options); + } + + function sendCommand(command, options) { + + var sessionId = MediaController.getPlayerInfo().id; + + ApiClient.sendCommand(sessionId, command, options); + } + function remoteControlPlayer() { var self = this; @@ -469,19 +483,114 @@ }; self.stop = function () { + sendPlayStateCommand('stop'); + }; + self.nextTrack = function () { + sendPlayStateCommand('nextTrack'); + }; + + self.previousTrack = function () { + sendPlayStateCommand('previousTrack'); + }; + + self.seek = function (positionTicks) { + sendPlayStateCommand('seek', + { + SeekPositionTicks: positionTicks + }); + }; + + self.pause = function () { + sendPlayStateCommand('Pause'); + }; + + self.unpause = function () { + sendPlayStateCommand('Unpause'); }; self.mute = function () { - + sendCommand('Mute'); }; self.unMute = function () { - + sendCommand('Unmnute'); }; self.toggleMute = function () { + sendCommand('ToggleMute'); + }; + self.setVolume = function (vol) { + sendCommand('SetVolume', { + + Volume: vol + + }); + }; + + self.getPlayerState = function () { + + var deferred = $.Deferred(); + + ApiClient.getSessions().done(function (sessions) { + + var currentTargetId = MediaController.getPlayerInfo().id; + + // Update existing data + //updateSessionInfo(popup, msg.Data); + var session = sessions.filter(function (s) { + return s.Id == currentTargetId; + })[0]; + + if (session) { + session = getPlayerState(session); + } + + deferred.resolveWith(null, [session]); + }); + + return deferred.promise(); + }; + + function subscribeToPlayerUpdates() { + + if (ApiClient.isWebSocketOpen()) { + + ApiClient.sendWebSocketMessage("SessionsStart", "100,700"); + } + } + + function unsubscribeFromPlayerUpdates() { + + if (ApiClient.isWebSocketOpen()) { + + ApiClient.sendWebSocketMessage("SessionsStop"); + } + } + + var playerListenerCount = 0; + self.beginPlayerUpdates = function () { + + if (playerListenerCount <= 0) { + + playerListenerCount = 0; + + subscribeToPlayerUpdates(); + } + + playerListenerCount++; + }; + + self.endPlayerUpdates = function () { + + playerListenerCount--; + + if (playerListenerCount <= 0) { + + unsubscribeFromPlayerUpdates(); + playerListenerCount = 0; + } }; self.getTargets = function () { @@ -521,11 +630,67 @@ }; } - MediaController.registerPlayer(new remoteControlPlayer()); + var player = new remoteControlPlayer(); + + MediaController.registerPlayer(player); + + function getPlayerState(session) { + + var state = { + volumeLevel: session.VolumeLevel, + isMuted: session.IsMuted, + isPaused: session.IsPaused, + canSeek: session.CanSeek + }; + + var item = session.NowPlayingItem; + + if (item) { + + state.itemId = item.Id; + state.itemType = item.Type; + state.mediaType = item.MediaType; + state.runtimeTicks = item.RunTimeTicks; + state.mediaSource = item.MediaSourceId; + state.positionTicks = session.NowPlayingPositionTicks || 0; + + state.itemName = item.Name; + + state.primaryImageItemId = item.PrimaryImageItemId; + state.primaryImageTag = item.PrimaryImageTag; + + state.backdropItemId = item.BackdropItemId; + state.backdropImageTag = item.BackdropImageTag; + + state.thumbItemId = item.ThumbItemId; + state.thumbImageTag = item.ThumbImageTag; + } + + return state; + } + + function firePlaybackEvent(name, session) { + + $(player).trigger(name, [getPlayerState(session)]); + } function onWebSocketMessageReceived(e, msg) { - if (msg.MessageType === "SessionEnded") { + if (msg.MessageType === "Sessions") { + + var currentTargetId = MediaController.getPlayerInfo().id; + + // Update existing data + //updateSessionInfo(popup, msg.Data); + var session = msg.Data.filter(function (s) { + return s.Id == currentTargetId; + })[0]; + + if (session) { + firePlaybackEvent('playstatechange', session); + } + } + else if (msg.MessageType === "SessionEnded") { console.log("Server reports another session ended"); @@ -533,6 +698,12 @@ MediaController.setDefaultPlayerActive(); } } + else if (msg.MessageType === "PlaybackStart") { + firePlaybackEvent('playbackstart', msg.Data); + } + else if (msg.MessageType === "PlaybackStopped") { + firePlaybackEvent('playbackstop', msg.Data); + } } $(ApiClient).on("websocketmessage", onWebSocketMessageReceived); diff --git a/dashboard-ui/thirdparty/mediabrowser.apiclient.js b/dashboard-ui/thirdparty/mediabrowser.apiclient.js index a6c08509d5..71c59924a6 100644 --- a/dashboard-ui/thirdparty/mediabrowser.apiclient.js +++ b/dashboard-ui/thirdparty/mediabrowser.apiclient.js @@ -2197,13 +2197,13 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi dataType: "json" }); }; - + function normalizeImageOptions(options) { var ratio = window.devicePixelRatio; - + if (ratio) { - + if (options.width) { options.width = options.width * ratio; } @@ -2253,7 +2253,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi delete options.index; normalizeImageOptions(options); - + return self.getUrl(url, options); }; @@ -3794,7 +3794,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi }); }; - self.sendSystemCommand = function (sessionId, command) { + self.sendCommand = function (sessionId, command, options) { if (!sessionId) { throw new Error("null sessionId"); @@ -3804,12 +3804,22 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi throw new Error("null command"); } - var url = self.getUrl("Sessions/" + sessionId + "/System/" + command); + var url = self.getUrl("Sessions/" + sessionId + "/Command"); - return self.ajax({ + var ajaxOptions = { type: "POST", url: url - }); + }; + + options = { + Arguments: options || {}, + Name: command + }; + + ajaxOptions.data = JSON.stringify(options); + ajaxOptions.contentType = "application/json"; + + return self.ajax(ajaxOptions); }; self.sendMessageCommand = function (sessionId, options) {