mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
control remote players with now playing bar
This commit is contained in:
parent
85a08beb3e
commit
2835534c6d
6 changed files with 342 additions and 91 deletions
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 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 = '';
|
||||
|
||||
if (item) {
|
||||
|
||||
var name = item.Name;
|
||||
var seriesName = '';
|
||||
|
||||
|
@ -928,22 +965,6 @@
|
|||
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 || {};
|
||||
|
||||
|
@ -965,9 +986,21 @@
|
|||
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]);
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
||||
onStateChanged.call(player, e, state);
|
||||
}
|
||||
|
||||
showNowPlayingBar();
|
||||
|
||||
updatePlayerState(state);
|
||||
updateNowPlayingInfo(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);
|
||||
}
|
||||
|
||||
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 () {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue