mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
added remote control menu
This commit is contained in:
parent
757e0a9046
commit
c018f44379
12 changed files with 254 additions and 15 deletions
BIN
dashboard-ui/css/images/remote.png
Normal file
BIN
dashboard-ui/css/images/remote.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -484,7 +484,7 @@ a.itemTag:hover {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.viewMenuImageLink {
|
.viewMenuImageLink:not(.remoteControlMenuLink) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,10 @@
|
||||||
height: 50px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nowPlaying .ui-slider-track {
|
||||||
|
margin-left: 15px!important;
|
||||||
|
}
|
||||||
|
|
||||||
@media all and (max-width: 550px) {
|
@media all and (max-width: 550px) {
|
||||||
.nowPlayingCell + .nowPlayingCell {
|
.nowPlayingCell + .nowPlayingCell {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -883,7 +883,7 @@
|
||||||
|
|
||||||
$('#btnRemote', page).on('click', function () {
|
$('#btnRemote', page).on('click', function () {
|
||||||
|
|
||||||
RemoteControl.showMenu({
|
RemoteControl.showMenuForItem({
|
||||||
|
|
||||||
item: currentItem,
|
item: currentItem,
|
||||||
context: getContext(currentItem),
|
context: getContext(currentItem),
|
||||||
|
|
|
@ -489,7 +489,7 @@
|
||||||
|
|
||||||
$('#btnRemote', page).on('click', function () {
|
$('#btnRemote', page).on('click', function () {
|
||||||
|
|
||||||
RemoteControl.showMenu({ item: currentItem, context: getParameterByName('context') || '' });
|
RemoteControl.showMenuForItem({ item: currentItem, context: getParameterByName('context') || '' });
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#btnEdit', page).on('click', function () {
|
$('#btnEdit', page).on('click', function () {
|
||||||
|
|
|
@ -1815,6 +1815,8 @@
|
||||||
html += '<a class="viewMenuLink viewMenuTextLink' + (view == 'games' ? selectedCssClass : '') + '" href="gamesrecommended.html">' + (view == 'games' ? selectedHtml : '') + '<span class="viewName">Games</span></a>';
|
html += '<a class="viewMenuLink viewMenuTextLink' + (view == 'games' ? selectedCssClass : '') + '" href="gamesrecommended.html">' + (view == 'games' ? selectedHtml : '') + '<span class="viewName">Games</span></a>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html += '<a class="viewMenuLink viewMenuImageLink remoteControlMenuLink" href="#" onclick="RemoteControl.showMenu();" title="Remote Control"><img src="css/images/remote.png" alt="Remote Control" /></a>';
|
||||||
|
|
||||||
html += '<div class="viewMenuSecondary">';
|
html += '<div class="viewMenuSecondary">';
|
||||||
|
|
||||||
html += Search.getSearchHtml();
|
html += Search.getSearchHtml();
|
||||||
|
|
|
@ -101,7 +101,7 @@
|
||||||
|
|
||||||
clearProgressInterval();
|
clearProgressInterval();
|
||||||
|
|
||||||
var intervalTime = ApiClient.isWebSocketOpen() ? 10000 : 30000;
|
var intervalTime = ApiClient.isWebSocketOpen() ? 5000 : 20000;
|
||||||
|
|
||||||
currentProgressInterval = setInterval(function () {
|
currentProgressInterval = setInterval(function () {
|
||||||
|
|
||||||
|
@ -831,6 +831,10 @@
|
||||||
|
|
||||||
self.playInternal = function (item, startPosition, user) {
|
self.playInternal = function (item, startPosition, user) {
|
||||||
|
|
||||||
|
if (item == null) {
|
||||||
|
throw new Error("item cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
if (self.isPlaying()) {
|
if (self.isPlaying()) {
|
||||||
self.stop();
|
self.stop();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
SortBy: "SortName",
|
SortBy: "SortName",
|
||||||
SortOrder: "Ascending",
|
SortOrder: "Ascending",
|
||||||
IncludeItemTypes: "Movie",
|
IncludeItemTypes: "Movie,Trailer",
|
||||||
Recursive: true,
|
Recursive: true,
|
||||||
Fields: "ItemCounts,DateCreated,UserData",
|
Fields: "ItemCounts,DateCreated,UserData",
|
||||||
StartIndex: 0
|
StartIndex: 0
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
SortBy: "SortName",
|
SortBy: "SortName",
|
||||||
SortOrder: "Ascending",
|
SortOrder: "Ascending",
|
||||||
IncludeItemTypes: "Movie",
|
IncludeItemTypes: "Movie,Trailer",
|
||||||
Recursive: true,
|
Recursive: true,
|
||||||
Fields: "ItemCounts,DateCreated,UserData",
|
Fields: "ItemCounts,DateCreated,UserData",
|
||||||
PersonTypes: "",
|
PersonTypes: "",
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
SortBy: "SortName",
|
SortBy: "SortName",
|
||||||
SortOrder: "Ascending",
|
SortOrder: "Ascending",
|
||||||
IncludeItemTypes: "Movie",
|
IncludeItemTypes: "Movie,Trailer",
|
||||||
Recursive: true,
|
Recursive: true,
|
||||||
Fields: "ItemCounts,DateCreated,UserData",
|
Fields: "ItemCounts,DateCreated,UserData",
|
||||||
StartIndex: 0
|
StartIndex: 0
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
SortBy: "SortName",
|
SortBy: "SortName",
|
||||||
SortOrder: "Ascending",
|
SortOrder: "Ascending",
|
||||||
IncludeItemTypes: "Audio",
|
IncludeItemTypes: "Audio,MusicVideo",
|
||||||
Recursive: true,
|
Recursive: true,
|
||||||
Fields: "ItemCounts,DateCreated,UserData",
|
Fields: "ItemCounts,DateCreated,UserData",
|
||||||
StartIndex: 0
|
StartIndex: 0
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMenu(options, sessionsPromise) {
|
function showMenuForItem(options, sessionsPromise) {
|
||||||
|
|
||||||
var playFromRendered;
|
var playFromRendered;
|
||||||
var trailersRendered;
|
var trailersRendered;
|
||||||
|
@ -204,11 +204,12 @@
|
||||||
|
|
||||||
var deviceId = ApiClient.deviceId();
|
var deviceId = ApiClient.deviceId();
|
||||||
|
|
||||||
|
// don't display the current session
|
||||||
sessions = sessions.filter(function (s) {
|
sessions = sessions.filter(function (s) {
|
||||||
return s.DeviceId != deviceId;
|
return s.DeviceId != deviceId;
|
||||||
});
|
});
|
||||||
|
|
||||||
renderSessions(sessions, options, elem);
|
renderSessionsInPlayMenu(sessions, options, elem);
|
||||||
|
|
||||||
if (ApiClient.isWebSocketOpen()) {
|
if (ApiClient.isWebSocketOpen()) {
|
||||||
ApiClient.sendWebSocketMessage("SessionsStart", "1500,1500");
|
ApiClient.sendWebSocketMessage("SessionsStart", "1500,1500");
|
||||||
|
@ -216,7 +217,7 @@
|
||||||
$(ApiClient).on("websocketmessage.remotecontrol", function (e, msg) {
|
$(ApiClient).on("websocketmessage.remotecontrol", function (e, msg) {
|
||||||
|
|
||||||
if (msg.MessageType === "Sessions") {
|
if (msg.MessageType === "Sessions") {
|
||||||
refreshSessions(msg.Data, elem);
|
updateSessionsInPlayMenu(msg.Data, elem);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -392,7 +393,7 @@
|
||||||
$('.chkSelectPlayTime:first', elem).checked(true);
|
$('.chkSelectPlayTime:first', elem).checked(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSessions(sessions, options, elem) {
|
function renderSessionsInPlayMenu(sessions, options, elem) {
|
||||||
|
|
||||||
if (!sessions.length) {
|
if (!sessions.length) {
|
||||||
elem.html('<p>There are currently no available media browser sessions to control.</p>');
|
elem.html('<p>There are currently no available media browser sessions to control.</p>');
|
||||||
|
@ -536,7 +537,7 @@
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshSessions(sessions, elem) {
|
function updateSessionsInPlayMenu(sessions, elem) {
|
||||||
|
|
||||||
for (var i = 0, length = sessions.length; i < length; i++) {
|
for (var i = 0, length = sessions.length; i < length; i++) {
|
||||||
|
|
||||||
|
@ -612,12 +613,240 @@
|
||||||
$('.chkSelectItem:first', elem).checked(true);
|
$('.chkSelectItem:first', elem).checked(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showMenu(sessions) {
|
||||||
|
|
||||||
|
var html = '<div data-role="popup" id="remoteControlFlyout">';
|
||||||
|
|
||||||
|
html += '<div class="ui-corner-top ui-bar-a" style="text-align:center;">';
|
||||||
|
html += '<div style="margin:.5em 0;">Remote Control</div>';
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
html += '<div data-role="content" class="ui-corner-bottom ui-content">';
|
||||||
|
|
||||||
|
html += '<div class="sessionsPopupContent">';
|
||||||
|
|
||||||
|
// Add controls here
|
||||||
|
html += '<div><label for="selectSession">Select session</label>';
|
||||||
|
html += '<select id="selectSession" name="selectSession" data-mini="true"></select></div>';
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
html += '<div class="nowPlayingInfo" style="margin:1em 0;">';
|
||||||
|
|
||||||
|
html += '<div class="nowPlaying" style="display:none;">';
|
||||||
|
|
||||||
|
html += getPlaybackHtml();
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
html += '<p class="nothingPlaying" style="display:none;">Nothing is currently playing.</p>';
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
html += '<p style="text-align:center;margin:.75em 0 0;">';
|
||||||
|
|
||||||
|
html += '<button type="button" data-icon="delete" onclick="$(\'#remoteControlFlyout\').popup(\'close\');" data-theme="a" data-mini="true" data-inline="true">Close</button>';
|
||||||
|
|
||||||
|
html += '</p>';
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
$(document.body).append(html);
|
||||||
|
|
||||||
|
var popup = $('#remoteControlFlyout').popup({ history: false, tolerance: 0, corners: false }).trigger('create').popup("open").on("popupafterclose", function () {
|
||||||
|
|
||||||
|
if (ApiClient.isWebSocketOpen()) {
|
||||||
|
ApiClient.sendWebSocketMessage("SessionsStop");
|
||||||
|
}
|
||||||
|
|
||||||
|
$(ApiClient).off("websocketmessage.remotecontrol");
|
||||||
|
|
||||||
|
$(this).off("popupafterclose").remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
renderSessionsInControlMenu(popup, sessions);
|
||||||
|
updateSessionInfo(popup, sessions);
|
||||||
|
|
||||||
|
if (ApiClient.isWebSocketOpen()) {
|
||||||
|
ApiClient.sendWebSocketMessage("SessionsStart", "1500,1500");
|
||||||
|
|
||||||
|
$(ApiClient).on("websocketmessage.remotecontrol", function (e, msg) {
|
||||||
|
|
||||||
|
if (msg.MessageType === "Sessions") {
|
||||||
|
|
||||||
|
// Update existing data
|
||||||
|
updateSessionInfo(popup, msg.Data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlaybackHtml() {
|
||||||
|
|
||||||
|
var html = '';
|
||||||
|
|
||||||
|
html += '<p class="nowPlayingTitle" style="text-align:center;margin-top:2em;"></p>';
|
||||||
|
|
||||||
|
html += '<p class="nowPlayingImage" style="text-align:center;"></p>';
|
||||||
|
|
||||||
|
html += '<div style="text-align:center;margin: 1em 0 1em;">';
|
||||||
|
|
||||||
|
html += '<div style="text-align:right;vertical-align:middle;padding-right:20px;font-weight: bold;">';
|
||||||
|
html += '<span class="nowPlayingTime"></span>';
|
||||||
|
html += '<span> / </span>';
|
||||||
|
html += '<span class="duration"></span>';
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
html += '<input type="range" name="positionSlider" id="positionSlider" min="0" max="100" value="50" style="display:none;" />';
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
html += '<div style="text-align:center; margin: 0 0 2em;">';
|
||||||
|
html += '<button class="btnPreviousTrack" type="button" data-icon="step-backward" data-inline="true" data-iconpos="notext">Previous track</button>';
|
||||||
|
html += '<span class="btnPauseParent"><button class="btnPause" type="button" data-icon="pause" data-inline="true" data-iconpos="notext">Pause</button></span>';
|
||||||
|
html += '<span class="btnPlayParent"><button class="btnPlay" type="button" data-icon="play" data-inline="true" data-iconpos="notext">Play</button></span>';
|
||||||
|
html += '<button class="btnStop" type="button" data-icon="stop" data-inline="true" data-iconpos="notext">Stop</button>';
|
||||||
|
html += '<button class="btnNextTrack" type="button" data-icon="step-forward" data-inline="true" data-iconpos="notext">Next track</button>';
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSessionInfo(popup, sessions) {
|
||||||
|
|
||||||
|
var id = $('#selectSession', popup).val();
|
||||||
|
|
||||||
|
// don't display the current session
|
||||||
|
var session = sessions.filter(function (s) {
|
||||||
|
return s.Id == id;
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
if (session && session.NowPlayingItem) {
|
||||||
|
|
||||||
|
$('.nothingPlaying', popup).hide();
|
||||||
|
|
||||||
|
var elem = $('.nowPlaying', popup).show();
|
||||||
|
|
||||||
|
updateNowPlaying(elem, session);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$('.nothingPlaying', popup).show();
|
||||||
|
$('.nowPlaying', popup).hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateNowPlaying(elem, session) {
|
||||||
|
|
||||||
|
var item = session.NowPlayingItem;
|
||||||
|
|
||||||
|
$('.nowPlayingTitle', elem).html(item.Name);
|
||||||
|
|
||||||
|
var imageContainer = $('.nowPlayingImage', elem);
|
||||||
|
|
||||||
|
if (item.PrimaryImageTag) {
|
||||||
|
imageContainer.show();
|
||||||
|
|
||||||
|
var img = $('img', imageContainer)[0];
|
||||||
|
|
||||||
|
var imgUrl = ApiClient.getImageUrl(item.Id, {
|
||||||
|
maxheight: 200,
|
||||||
|
type: 'Primary',
|
||||||
|
tag: item.PrimaryImageTag
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!img || img.src.toLowerCase().indexOf(imgUrl.toLowerCase()) == -1) {
|
||||||
|
imageContainer.html('<img style="max-height:100px;" src="' + imgUrl + '" />');
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
imageContainer.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
var time = session.NowPlayingPositionTicks || 0;
|
||||||
|
var duration = item.RunTimeTicks || 0;
|
||||||
|
|
||||||
|
var percent = duration ? 100 * time / duration : 0;
|
||||||
|
|
||||||
|
$('#positionSlider', elem).val(percent).slider('refresh');
|
||||||
|
|
||||||
|
$('.nowPlayingTime', elem).html(Dashboard.getDisplayTime(time));
|
||||||
|
$('.duration', elem).html(Dashboard.getDisplayTime(duration));
|
||||||
|
|
||||||
|
if (session.IsPaused) {
|
||||||
|
$('.btnPauseParent', elem).hide();
|
||||||
|
$('.btnPlayParent', elem).show();
|
||||||
|
} else {
|
||||||
|
$('.btnPauseParent', elem).show();
|
||||||
|
$('.btnPlayParent', elem).hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSessionsInControlMenu(popup, sessions) {
|
||||||
|
|
||||||
|
var deviceId = ApiClient.deviceId();
|
||||||
|
|
||||||
|
// don't display the current session
|
||||||
|
sessions = sessions.filter(function (s) {
|
||||||
|
return s.DeviceId != deviceId && s.SupportsRemoteControl;
|
||||||
|
});
|
||||||
|
|
||||||
|
var elem = $('#selectSession', popup);
|
||||||
|
|
||||||
|
var currentValue = elem.val();
|
||||||
|
|
||||||
|
if (currentValue) {
|
||||||
|
|
||||||
|
// Make sure the session is still active
|
||||||
|
var currentSession = sessions.filter(function (s) {
|
||||||
|
return s.Id == currentValue;
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
if (!currentSession) {
|
||||||
|
currentValue = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentValue && sessions.length) {
|
||||||
|
currentValue = sessions[0].Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = '';
|
||||||
|
|
||||||
|
for (var i = 0, length = sessions.length; i < length; i++) {
|
||||||
|
|
||||||
|
var session = sessions[i];
|
||||||
|
|
||||||
|
var text = session.Client + ' - ' + session.DeviceName;
|
||||||
|
|
||||||
|
if (session.UserName) {
|
||||||
|
text += ' - ' + session.UserName;
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '<option value="' + session.Id + '">' + text + '</option>';
|
||||||
|
}
|
||||||
|
|
||||||
|
elem.html(html).val(currentValue).selectmenu('refresh');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function remoteControl() {
|
function remoteControl() {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
self.showMenu = function (options) {
|
self.showMenuForItem = function (options) {
|
||||||
showMenu(options, ApiClient.getSessions({ SupportsRemoteControl: true }));
|
showMenuForItem(options, ApiClient.getSessions({ SupportsRemoteControl: true }));
|
||||||
|
};
|
||||||
|
|
||||||
|
self.showMenu = function () {
|
||||||
|
ApiClient.getSessions({ SupportsRemoteControl: true }).done(function (sessions) {
|
||||||
|
|
||||||
|
showMenu(sessions);
|
||||||
|
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue