Compare commits

...

6 Commits

Author SHA1 Message Date
JonnyWong16
e908c45cf2 v2.0.4-beta 2017-12-29 16:35:24 -08:00
JonnyWong16
c37e934f42 Check concurrent streams from the server instead of database 2017-12-29 16:29:20 -08:00
JonnyWong16
41f91956b8 Make sure current activity is loaded before refreshing 2017-12-29 16:26:50 -08:00
JonnyWong16
8b2bd5ce79 Add collections info pages 2017-12-29 15:52:08 -08:00
JonnyWong16
28e4151157 Remember tab when going back on library and user pages 2017-12-29 11:34:41 -08:00
JonnyWong16
66d45293e6 Add test Plex Web URL button 2017-12-29 10:47:31 -08:00
16 changed files with 969 additions and 547 deletions

View File

@@ -1,5 +1,17 @@
# Changelog
## v2.0.4-beta (2017-12-29)
* Monitoring:
* Fix: Current activity cards duplicating on the homepage.
* Notifications:
* Fix: Concurrent stream notifications being sent when there is an incorrect number of streams.
* UI:
* New: Info pages for collections.
* New: Button to test Plex Web URL override.
* Fix: Library and User pages return to the correct tab when pressing back.
## v2.0.3-beta (2017-12-25)
* Monitoring:

View File

@@ -1808,7 +1808,8 @@ a:hover .summary-poster-face-track .summary-poster-face-overlay span {
margin-left: 2px;
color: #999;
}
#children-list, #search-results-list {
.children-list,
.search-results-list {
position: relative;
z-index: 0;
}
@@ -1874,15 +1875,15 @@ a:hover .item-children-poster {
box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
z-index: -2;
}
.item-children-poster-face.season-poster {
.item-children-poster-face.poster-item {
width: 150px;
height: 225px;
}
.item-children-poster-face.episode-poster {
.item-children-poster-face.episode-item {
width: 250px;
height: 140px;
}
.item-children-poster-face.album-poster {
.item-children-poster-face.cover-item {
width: 150px;
height: 150px;
}
@@ -1915,15 +1916,13 @@ a:hover .item-children-poster {
margin-bottom: 20px;
clear: both;
}
.item-children-instance-text-wrapper.season-item {
.item-children-instance-text-wrapper.poster-item,
.item-children-instance-text-wrapper.cover-item {
width: 150px;
}
.item-children-instance-text-wrapper.episode-item {
width: 250px;
}
.item-children-instance-text-wrapper.album-item {
width: 150px;
}
.item-children-instance-text-wrapper h3 {
width: 100%;
padding: 5px 3px 0 3px;

View File

@@ -235,7 +235,12 @@
}
};
var create_instances = [];
var activity_ready = true;
function getCurrentActivity() {
activity_ready = false;
$.ajax({
url: 'get_activity',
type: 'GET',
@@ -298,6 +303,7 @@
// Create a new instance if it doesn't exist
if (!(instance.length)) {
create_instances.push(key);
getActivityInstance(key);
return;
}
@@ -501,6 +507,8 @@
$('#currentActivityHeader').text('');
$('#currentActivity').html('<div id="dashboard-no-activity" class="text-muted">Nothing is currently being played.</div>');
}
activity_ready = true;
}
});
}
@@ -520,13 +528,20 @@
$('#activity-instance-' + session_key + ' [data-toggle=tooltip]').tooltip({ container: 'body', placement: 'right', delay: 50 });
$('#terminate-button-' + session_key).tooltip('destroy').tooltip({ container: 'body', placement: 'left', delay: 50 });
lockScroll('#activity-instance-' + session_key + ' .dashboard-activity-info-scroller');
var index = create_instances.indexOf(session_key);
if (index > -1) {
create_instances.splice(index, 1);
}
}
});
}
getCurrentActivity();
setInterval(function () {
getCurrentActivity();
if (!(create_instances.length) && activity_ready) {
getCurrentActivity();
}
}, 2000);
setInterval(function(){

View File

@@ -80,7 +80,7 @@ DOCUMENTATION :: END
<div class="col-md-12">
<div class="summary-navbar-list">
<ul class="list-unstyled breadcrumb">
% if data['media_type'] == 'movie':
% if data['media_type'] in ('movie', 'collection'):
<li><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
<li class="active">${data['title']}</li>
% elif data['media_type'] == 'show':
@@ -116,9 +116,9 @@ DOCUMENTATION :: END
<div class="col-md-9">
<div class="summary-content-poster hidden-xs hidden-sm">
% if data['media_type'] == 'track':
<a href="${config['pms_web_url'] or 'https://app.plex.tv/desktop'}#!/server/${config['pms_identifier']}/details?key=%2Flibrary%2Fmetadata%2F${data['parent_rating_key']}" target="_blank" title="View in Plex Web">
<a href="${config['pms_web_url']}#!/server/${config['pms_identifier']}/details?key=%2Flibrary%2Fmetadata%2F${data['parent_rating_key']}" target="_blank" title="View in Plex Web">
% else:
<a href="${config['pms_web_url'] or 'https://app.plex.tv/desktop'}#!/server/${config['pms_identifier']}/details?key=%2Flibrary%2Fmetadata%2F${data['rating_key']}" target="_blank" title="View in Plex Web">
<a href="${config['pms_web_url']}#!/server/${config['pms_identifier']}/details?key=%2Flibrary%2Fmetadata%2F${data['rating_key']}" target="_blank" title="View in Plex Web">
% endif
% if data['media_type'] == 'episode':
<div class="summary-poster-face-episode" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=500&height=280&fallback=art);">
@@ -151,7 +151,7 @@ DOCUMENTATION :: END
</a>
</div>
<div class="summary-content-title">
% if data['media_type'] in ('movie', 'show', 'artist'):
% if data['media_type'] in ('movie', 'show', 'artist', 'collection'):
<h1>&nbsp;</h1><h1>${data['title']}</h1>
% elif data['media_type'] == 'season':
<h1>&nbsp;</h1><h1><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a></h1>
@@ -175,7 +175,7 @@ DOCUMENTATION :: END
<div class="col-md-9">
% if data['media_type'] == 'movie':
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 305px;">
% elif data['media_type'] == 'show' or data['media_type'] == 'season':
% elif data['media_type'] in ('show', 'season', 'collection'):
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 270px;">
% elif data['media_type'] == 'episode':
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 70px;">
@@ -235,6 +235,8 @@ DOCUMENTATION :: END
Aired <strong> <span id="airdate">${data['originally_available_at']}</span></strong>
% elif data['media_type'] == 'album' or data['media_type'] == 'track':
Released <strong> ${data['year']}</strong>
% elif data['media_type'] == 'collection':
Year <strong> ${data['min_year']} - ${data['max_year']}</strong>
% endif
</div>
<div class="summary-content-details-tag">
@@ -308,51 +310,65 @@ DOCUMENTATION :: END
</div>
% if data['media_type'] == 'show':
<div class="col-md-12">
<div class='table-card-header'>
<div class="table-card-header">
<div class="header-bar">
<span>Season List for <strong>${data['title']}</strong></span>
</div>
</div>
<div class='table-card-back'>
<div id="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading season list...</div>
<div class="table-card-back">
<div id="children-list" class="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading season list...</div>
</div>
</div>
% elif data['media_type'] == 'season':
<div class="col-md-12">
<div class='table-card-header'>
<div class="table-card-header">
<div class="header-bar">
<span>Episode List for <strong>${data['title']}</strong></span>
</div>
</div>
<div class='table-card-back'>
<div id="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading episode list...</div>
<div class="table-card-back">
<div id="children-list" class="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading episode list...</div>
</div>
</div>
% elif data['media_type'] == 'artist':
<div class="col-md-12">
<div class='table-card-header'>
<div class="table-card-header">
<div class="header-bar">
<span>Album List for <strong>${data['title']}</strong></span>
</div>
</div>
<div class='table-card-back'>
<div id="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading album list...</div>
<div class="table-card-back">
<div id="children-list" class="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading album list...</div>
</div>
</div>
% elif data['media_type'] == 'album':
<div class="col-md-12">
<div class='table-card-header'>
<div class="table-card-header">
<div class="header-bar">
<span>Track List for <strong>${data['title']}</strong></span>
</div>
</div>
<div class='table-card-back'>
<div id="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading track list...</div>
<div class="table-card-back">
<div id="children-list" class="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading track list...</div>
</div>
</div>
% endif
% elif data['media_type'] == 'collection':
<div class="col-md-12">
<div class='table-card-header'>
<div class="table-card-header">
<div class="header-bar">
<span>Movies in <strong>${data['title']}</strong> collection</span>
</div>
</div>
<div class="table-card-back">
<div id="children-list" class="children-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading movies list...</div>
</div>
</div>
<div id="collection-related-list-container" style="display: none;">
</div>
% endif
% if data['media_type'] != 'collection':
<div class="col-md-12">
<div class="table-card-header">
<div class="header-bar">
<span>Watch History for <strong>${data['title']}</strong></span>
</div>
@@ -420,6 +436,7 @@ DOCUMENTATION :: END
</table>
</div>
</div>
% endif
</div>
</div>
</div>
@@ -562,6 +579,7 @@ DOCUMENTATION :: END
}
</script>
% endif
% if data['media_type'] != 'collection':
<script>
$(document).ready(function () {
get_history();
@@ -638,7 +656,8 @@ DOCUMENTATION :: END
});
});
</script>
% if data['media_type'] in ('show', 'season', 'artist', 'album'):
% endif
% if data['media_type'] in ('show', 'season', 'artist', 'album', 'collection'):
<script>
$.ajax({
url: 'get_item_children',
@@ -646,7 +665,24 @@ DOCUMENTATION :: END
async: true,
data: { rating_key : "${data['rating_key']}" },
complete: function(xhr, status) {
$("#children-list").html(xhr.responseText); }
$("#children-list").html(xhr.responseText);
}
});
</script>
% endif
% if data['media_type'] == 'collection':
<script>
$.ajax({
url: 'get_item_children_related',
type: 'GET',
async: true,
data: {
rating_key : "${data['rating_key']}",
title: "${data['title']}"
},
complete: function(xhr, status) {
$("#collection-related-list-container").html(xhr.responseText).show();
}
});
</script>
% endif

View File

@@ -37,13 +37,42 @@ DOCUMENTATION :: END
% else:
<li>
% endif
%if data['children_type'] == 'season':
% if data['children_type'] == 'movie':
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">
<div class="item-children-poster">
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
% if _session['user_group'] == 'admin':
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
% endif
</div>
</a>
<div class="item-children-instance-text-wrapper poster-item">
<h3>
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">${child['title']}</a>
</h3>
<h3 class="text-muted">${child['year']}</h3>
</div>
% elif data['children_type'] == 'show':
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">
<div class="item-children-poster">
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
% if _session['user_group'] == 'admin':
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
% endif
</div>
</a>
<div class="item-children-instance-text-wrapper poster-item">
<h3>
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">${child['title']}</a>
</h3>
</div>
% elif data['children_type'] == 'season':
<a href="info?rating_key=${child['rating_key']}" title="Season ${child['media_index']}">
<div class="item-children-poster">
% if child['thumb']:
<div class="item-children-poster-face season-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);">
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);">
% else:
<div class="item-children-poster-face season-poster" style="background-image: url(pms_image_proxy?img=${child['parent_thumb']}&width=300&height=450&fallback=poster);">
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['parent_thumb']}&width=300&height=450&fallback=poster);">
% endif
<div class="item-children-card-overlay">
<div class="item-children-overlay-text">
@@ -59,7 +88,7 @@ DOCUMENTATION :: END
% elif data['children_type'] == 'episode':
<a href="info?rating_key=${child['rating_key']}" title="Episode ${child['media_index']}">
<div class="item-children-poster">
<div class="item-children-poster-face episode-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=500&height=250&fallback=art);">
<div class="item-children-poster-face episode-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=500&height=250&fallback=art);">
<div class="item-children-card-overlay">
<div class="item-children-overlay-text">
Episode ${child['media_index']}
@@ -79,13 +108,13 @@ DOCUMENTATION :: END
% elif data['children_type'] == 'album':
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">
<div class="item-children-poster">
<div class="item-children-poster-face album-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=300&fallback=cover);"></div>
<div class="item-children-poster-face cover-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=300&fallback=cover);"></div>
% if _session['user_group'] == 'admin':
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
% endif
</div>
</a>
<div class="item-children-instance-text-wrapper album-item">
<div class="item-children-instance-text-wrapper cover-item">
<h3>
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">${child['title']}</a>
</h3>

View File

@@ -0,0 +1,99 @@
<%doc>
USAGE DOCUMENTATION :: PLEASE LEAVE THIS AT THE TOP OF THIS FILE
For Mako templating syntax documentation please visit: http://docs.makotemplates.org/en/latest/
Filename: info_collection_list.html
Version: 0.1
Variable names: data [list]
data :: Usable parameters
== Global keys ==
children_type Returns the type of children in the array.
children_count Returns the number of episodes in the array.
children_list Returns an array of episodes.
data['children_list'] :: Usable paramaters
== Global keys ==
rating_key Returns the unique identifier for the media item.
media_index Returns the episode number.
title Returns the name of the episode.
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
parent_thumb Returns the location of the item's parent thumbnail. Use with pms_image_proxy.
DOCUMENTATION :: END
</%doc>
% if data != None:
<%
types = ('movie', 'show', 'artist', 'album')
headers = {'movie': 'Movies',
'show': 'TV Shows',
'season': 'Seasons',
'episode': 'Episodes',
'artist': 'Artists',
'album': 'Albums',
'track': 'Tracks',
}
%>
% for media_type in types:
% if data['results_list'][media_type]:
<div class="col-md-12">
<div class="table-card-header">
<div class="header-bar">
<span>${headers[media_type]} in <strong>${title}</strong> collection</span>
</div>
</div>
<div class="table-card-back">
<div id="children-list" class="children-list">
<div class="item-children-wrapper">
<ul class="item-children-instance list-unstyled">
% for child in data['results_list'][media_type]:
<li>
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">
<div class="item-children-poster">
% if media_type in ('artist', 'album'):
<div class="item-children-poster-face cover-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=300&fallback=cover);"></div>
% else:
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
% endif
% if _session['user_group'] == 'admin':
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
% endif
</div>
</a>
% if media_type == 'artist':
<div class="item-children-instance-text-wrapper cover-item">
<h3>
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">${child['title']}</a>
</h3>
</div>
% elif media_type == 'album':
<div class="item-children-instance-text-wrapper cover-item">
<h3>
<a href="info?rating_key=${child['parent_rating_key']}" title="${child['parent_title']}">${child['parent_title']}</a>
</h3>
<h3>
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">${child['title']}</a>
</h3>
</div>
% else:
<div class="item-children-instance-text-wrapper poster-item">
<h3>
<a href="info?rating_key=${child['rating_key']}" title="${child['title']}">${child['title']}</a>
</h3>
<h3 class="text-muted">${child['year']}</h3>
</div>
% endif
</li>
% endfor
</ul>
</div>
</div>
</div>
</div>
% endif
% endfor
% endif

View File

@@ -54,6 +54,31 @@ DOCUMENTATION :: END
% if data != None:
% if data['results_count'] > 0:
% if 'collection' in data['results_list'] and data['results_list']['collection']:
<div class="item-children-wrapper">
<div class="item-children-section-title">
<h4>Collections</h4>
</div>
<ul class="item-children-instance list-unstyled">
% for child in data['results_list']['collection']:
<li>
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
<div class="item-children-poster">
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
% if _session['user_group'] == 'admin':
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
% endif
</div>
<div class="item-children-instance-text-wrapper poster-item">
<h3 title="${child['title']}">${child['title']}</h3>
<h3 class="text-muted">${child['min_year']} - ${child['max_year']}</h3>
</div>
</a>
</li>
% endfor
</ul>
</div>
% endif
% if 'movie' in data['results_list'] and data['results_list']['movie']:
<div class="item-children-wrapper">
<div class="item-children-section-title">
@@ -64,12 +89,12 @@ DOCUMENTATION :: END
<li>
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
<div class="item-children-poster">
<div class="item-children-poster-face season-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
% if _session['user_group'] == 'admin':
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
% endif
</div>
<div class="item-children-instance-text-wrapper season-item">
<div class="item-children-instance-text-wrapper poster-item">
<h3 title="${child['title']}">${child['title']}</h3>
<h3 class="text-muted">${child['year']}</h3>
</div>
@@ -89,12 +114,12 @@ DOCUMENTATION :: END
<li>
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
<div class="item-children-poster">
<div class="item-children-poster-face season-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
% if _session['user_group'] == 'admin':
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
% endif
</div>
<div class="item-children-instance-text-wrapper season-item">
<div class="item-children-instance-text-wrapper poster-item">
<h3 title="${child['title']}">${child['title']}</h3>
<h3 class="text-muted">${child['year']}</h3>
</div>
@@ -114,12 +139,12 @@ DOCUMENTATION :: END
<li>
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
<div class="item-children-poster">
<div class="item-children-poster-face season-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
<div class="item-children-poster-face poster-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450&fallback=poster);"></div>
% if _session['user_group'] == 'admin':
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
% endif
</div>
<div class="item-children-instance-text-wrapper season-item">
<div class="item-children-instance-text-wrapper poster-item">
<h3 title="${child['parent_title']}">${child['parent_title']}</h3>
<h3 class="text-muted">S${child['media_index']}</h3>
</div>
@@ -139,7 +164,7 @@ DOCUMENTATION :: END
<li>
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
<div class="item-children-poster">
<div class="item-children-poster-face episode-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=500&height=250&fallback=art);"></div>
<div class="item-children-poster-face episode-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=500&height=250&fallback=art);"></div>
% if _session['user_group'] == 'admin':
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
% endif
@@ -165,7 +190,7 @@ DOCUMENTATION :: END
<li>
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
<div class="item-children-poster">
<div class="item-children-poster-face album-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=300&fallback=cover);"></div>
<div class="item-children-poster-face cover-item style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=300&fallback=cover);"></div>
% if _session['user_group'] == 'admin':
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
% endif
@@ -189,7 +214,7 @@ DOCUMENTATION :: END
<li>
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
<div class="item-children-poster">
<div class="item-children-poster-face album-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=300&fallback=cover);"></div>
<div class="item-children-poster-face cover-item" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=300&fallback=cover);"></div>
% if _session['user_group'] == 'admin':
<span class="overlay-refresh-image" title="Refresh image"><i class="fa fa-refresh refresh_pms_image"></i></span>
% endif
@@ -214,7 +239,7 @@ DOCUMENTATION :: END
<li>
<a href="info?rating_key=${child['rating_key']}" id="${child['rating_key']}">
<div class="item-children-poster">
<div class="item-children-poster-face album-poster" style="background-image: url(pms_image_proxy?img=${child['parent_thumb']}&width=300&height=300&fallback=cover);">
<div class="item-children-poster-face cover-item" style="background-image: url(pms_image_proxy?img=${child['parent_thumb']}&width=300&height=300&fallback=cover);">
<div class="item-children-card-overlay">
<div class="item-children-overlay-text">
Track ${child['media_index']}

View File

@@ -71,11 +71,11 @@ DOCUMENTATION :: END
% endif
</div>
<div class="user-info-nav">
<ul class="user-info-nav">
<li class="active"><a href="#profile" data-toggle="tab">Profile</a></li>
<li><a id="history-tab-btn" href="#libraryHistory" data-toggle="tab">History</a></li>
<ul class="user-info-nav" role="tablist">
<li class="active"><a href="#tabs-profile" role="tab" data-toggle="tab">Profile</a></li>
<li><a id="history-tab-btn" href="#tabs-history" role="tab" data-toggle="tab">History</a></li>
% if _session['user_group'] == 'admin':
<li><a id="media-info-tab-btn" href="#libraryMediaInfo" data-toggle="tab">Media Info</a></li>
<li><a id="media-info-tab-btn" href="#tabs-mediainfo" role="tab" data-toggle="tab">Media Info</a></li>
% endif
</ul>
</div>
@@ -83,7 +83,7 @@ DOCUMENTATION :: END
</div>
</div>
<div class="tab-content">
<div class="tab-pane active" id="profile">
<div role="tabpanel" class="tab-pane active" id="tabs-profile">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
@@ -169,7 +169,7 @@ DOCUMENTATION :: END
</div>
</div>
</div>
<div class="tab-pane" id="libraryHistory">
<div role="tabpanel" class="tab-pane" id="tabs-history">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
@@ -221,7 +221,7 @@ DOCUMENTATION :: END
</div>
</div>
</div>
<div class="tab-pane" id="libraryMediaInfo">
<div role="tabpanel" class="tab-pane" id="tabs-mediainfo">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
@@ -366,8 +366,253 @@ DOCUMENTATION :: END
<script src="${http_root}js/tables/history_table.js${cache_param}"></script>
<script src="${http_root}js/tables/media_info_table.js${cache_param}"></script>
<script>
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
$.fn.dataTable.tables({ visible: true, api: true }).columns.adjust();
});
function loadHistoryTable() {
// Build watch history table
history_table_options.ajax = {
url: 'get_history',
type: 'post',
data: function ( d ) {
return {
json_data: JSON.stringify( d ),
section_id: section_id,
user_id: "${_session['user_id']}" == "None" ? null : "${_session['user_id']}"
};
}
}
history_table = $('#history_table-SID-${data["section_id"]}').DataTable(history_table_options);
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 11] });
$(colvis.button()).appendTo('#button-bar-history');
clearSearchButton('history_table-SID-${data["section_id"]}', history_table);
}
$('a[href="#tabs-history"]').on('shown.bs.tab', function() {
loadHistoryTable();
});
% if _session['user_group'] == 'admin':
function loadMediaInfoTable() {
// Build media info table
media_info_table_options.ajax = {
url: 'get_library_media_info',
type: 'post',
data: function ( d ) {
return {
json_data: JSON.stringify( d ),
section_id: section_id,
refresh: refresh_table
};
}
}
media_info_table = $('#media_info_table-SID-${data["section_id"]}').DataTable(media_info_table_options);
var colvis = new $.fn.dataTable.ColVis(media_info_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark' });
$(colvis.button()).appendTo('#button-bar-media-info');
clearSearchButton('media_info_table-SID-${data["section_id"]}', media_info_table);
}
$('a[href="#tabs-mediainfo"]').on('shown.bs.tab', function() {
loadMediaInfoTable();
});
$("#refresh-media-info-table").click(function () {
media_info_child_table = {};
refresh_table = true;
refresh_child_tables = true;
media_info_table.draw();
refresh_table = false;
});
$("#edit-library-tooltip").tooltip();
// Load edit library modal
$("#toggle-edit-library-modal").click(function() {
$("#edit-library-tooltip").tooltip('hide');
$.ajax({
url: 'edit_library_dialog',
data: { section_id: section_id },
cache: false,
async: true,
complete: function(xhr, status) {
$("#edit-library-modal").html(xhr.responseText);
}
});
});
$('#row-edit-mode').on('click', function() {
$('#row-edit-mode-alert').fadeIn(200);
if ($(this).hasClass('active')) {
if (history_to_delete.length > 0) {
$('#deleteCount').text(history_to_delete.length);
$('#confirm-modal-delete').modal();
$('#confirm-modal-delete').one('click', '#confirm-delete', function () {
history_to_delete.forEach(function(row, idx) {
$.ajax({
url: 'delete_history_rows',
type: 'POST',
data: { row_id: row },
async: true,
success: function (data) {
var msg = "History deleted";
showMsg(msg, false, true, 2000);
}
});
});
history_table.draw();
});
}
$('.delete-control').each(function () {
$(this).addClass('hidden');
$('#row-edit-mode-alert').fadeOut(200);
});
} else {
history_to_delete = [];
$('.delete-control').each(function() {
$(this).find('button.btn-danger').toggleClass('btn-warning').toggleClass('btn-danger');
$(this).removeClass('hidden');
});
}
});
% endif
$("#refresh-history-list").click(function () {
history_table.draw();
});
function recentlyWatched() {
// Populate recently watched
$.ajax({
url: 'library_recently_watched',
async: true,
data: {
section_id: section_id,
limit: 50
},
complete: function(xhr, status) {
$("#library-recently-watched").html(xhr.responseText);
highlightWatchedScrollerButton();
}
});
}
function recentlyAdded() {
// Populate recently added
$.ajax({
url: 'library_recently_added',
async: true,
data: {
section_id: section_id,
limit: 50
},
complete: function(xhr, status) {
$("#library-recently-added").html(xhr.responseText);
highlightAddedScrollerButton();
}
});
}
recentlyWatched();
recentlyAdded();
function highlightWatchedScrollerButton() {
var scroller = $("#recently-watched-row-scroller");
var numElems = scroller.find("li").length;
scroller.width(numElems * 175);
if (scroller.width() > $("#library-recently-watched").width()) {
$("#recently-watched-page-right").removeClass("disabled");
} else {
$("#recently-watched-page-right").addClass("disabled");
}
}
function highlightAddedScrollerButton() {
var scroller = $("#recently-added-row-scroller");
var numElems = scroller.find("li").length;
scroller.width(numElems * 175);
if (scroller.width() > $("#library-recently-added").width()) {
$("#recently-added-page-right").removeClass("disabled");
} else {
$("#recently-added-page-right").addClass("disabled");
}
}
$(window).resize(function() {
highlightWatchedScrollerButton();
highlightAddedScrollerButton();
});
$('div.art-face').animate({ opacity: 0.2 }, { duration: 1000 });
var leftTotalWatched = 0;
$(".paginate-watched").click(function (e) {
e.preventDefault();
var scroller = $("#recently-watched-row-scroller");
var containerWidth = $("#library-recently-watched").width();
var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175;
var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0);
leftTotalWatched = Math.max(Math.min(leftTotalWatched + scrollAmount, 0), leftMax);
scroller.animate({ left: leftTotalWatched }, 250);
if (leftTotalWatched == 0) {
$("#recently-watched-page-left").addClass("disabled").blur();
} else {
$("#recently-watched-page-left").removeClass("disabled");
}
if (leftTotalWatched == leftMax) {
$("#recently-watched-page-right").addClass("disabled").blur();
} else {
$("#recently-watched-page-right").removeClass("disabled");
}
});
var leftTotalAdded = 0;
$(".paginate-added").click(function (e) {
e.preventDefault();
var scroller = $("#recently-added-row-scroller");
var containerWidth = $("#library-recently-added").width();
var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175;
var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0);
leftTotalAdded = Math.max(Math.min(leftTotalAdded + scrollAmount, 0), leftMax);
scroller.animate({ left: leftTotalAdded }, 250);
if (leftTotalAdded == 0) {
$("#recently-added-page-left").addClass("disabled").blur();
} else {
$("#recently-added-page-left").removeClass("disabled");
}
if (leftTotalAdded == leftMax) {
$("#recently-added-page-right").addClass("disabled").blur();
} else {
$("#recently-added-page-right").removeClass("disabled");
}
});
$(document).ready(function () {
$("#edit-library-tooltip").tooltip();
// Javascript to enable link to tab
var hash = document.location.hash;
var prefix = "tab_";
if (hash) {
$('.user-info-nav a[href='+hash.replace(prefix,"")+']').tab('show').trigger('show.bs.tab');
}
// Change hash for page-reload
$('.user-info-nav a').on('shown.bs.tab', function (e) {
window.location.hash = e.target.hash.replace("#", "#" + prefix);
});
// Populate watch time stats
$.ajax({
@@ -389,237 +634,6 @@ DOCUMENTATION :: END
}
});
function loadHistoryTable() {
// Build watch history table
history_table_options.ajax = {
url: 'get_history',
type: 'post',
data: function ( d ) {
return {
json_data: JSON.stringify( d ),
section_id: section_id,
user_id: "${_session['user_id']}" == "None" ? null : "${_session['user_id']}"
};
}
}
history_table = $('#history_table-SID-${data["section_id"]}').DataTable(history_table_options);
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 11] });
$(colvis.button()).appendTo('#button-bar-history');
clearSearchButton('history_table-SID-${data["section_id"]}', history_table);
}
$( "#history-tab-btn" ).one( "click", function() {
loadHistoryTable();
});
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
$.fn.dataTable.tables({ visible: true, api: true }).columns.adjust();
});
% if _session['user_group'] == 'admin':
function loadMediaInfoTable() {
// Build media info table
media_info_table_options.ajax = {
url: 'get_library_media_info',
type: 'post',
data: function ( d ) {
return {
json_data: JSON.stringify( d ),
section_id: section_id,
refresh: refresh_table
};
}
}
media_info_table = $('#media_info_table-SID-${data["section_id"]}').DataTable(media_info_table_options);
var colvis = new $.fn.dataTable.ColVis(media_info_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark' });
$(colvis.button()).appendTo('#button-bar-media-info');
clearSearchButton('media_info_table-SID-${data["section_id"]}', media_info_table);
}
$( "#media-info-tab-btn" ).one( "click", function() {
loadMediaInfoTable();
});
$("#refresh-media-info-table").click(function () {
media_info_child_table = {};
refresh_table = true;
refresh_child_tables = true;
media_info_table.draw();
refresh_table = false;
});
// Load edit library modal
$("#toggle-edit-library-modal").click(function() {
$("#edit-library-tooltip").tooltip('hide');
$.ajax({
url: 'edit_library_dialog',
data: { section_id: section_id },
cache: false,
async: true,
complete: function(xhr, status) {
$("#edit-library-modal").html(xhr.responseText);
}
});
});
$('#row-edit-mode').on('click', function() {
$('#row-edit-mode-alert').fadeIn(200);
if ($(this).hasClass('active')) {
if (history_to_delete.length > 0) {
$('#deleteCount').text(history_to_delete.length);
$('#confirm-modal-delete').modal();
$('#confirm-modal-delete').one('click', '#confirm-delete', function () {
history_to_delete.forEach(function(row, idx) {
$.ajax({
url: 'delete_history_rows',
type: 'POST',
data: { row_id: row },
async: true,
success: function (data) {
var msg = "History deleted";
showMsg(msg, false, true, 2000);
}
});
});
history_table.draw();
});
}
$('.delete-control').each(function () {
$(this).addClass('hidden');
$('#row-edit-mode-alert').fadeOut(200);
});
} else {
history_to_delete = [];
$('.delete-control').each(function() {
$(this).find('button.btn-danger').toggleClass('btn-warning').toggleClass('btn-danger');
$(this).removeClass('hidden');
});
}
});
% endif
$("#refresh-history-list").click(function () {
history_table.draw();
});
function recentlyWatched() {
// Populate recently watched
$.ajax({
url: 'library_recently_watched',
async: true,
data: {
section_id: section_id,
limit: 50
},
complete: function(xhr, status) {
$("#library-recently-watched").html(xhr.responseText);
highlightWatchedScrollerButton();
}
});
}
function recentlyAdded() {
// Populate recently added
$.ajax({
url: 'library_recently_added',
async: true,
data: {
section_id: section_id,
limit: 50
},
complete: function(xhr, status) {
$("#library-recently-added").html(xhr.responseText);
highlightAddedScrollerButton();
}
});
}
recentlyWatched();
recentlyAdded();
function highlightWatchedScrollerButton() {
var scroller = $("#recently-watched-row-scroller");
var numElems = scroller.find("li").length;
scroller.width(numElems * 175);
if (scroller.width() > $("#library-recently-watched").width()) {
$("#recently-watched-page-right").removeClass("disabled");
} else {
$("#recently-watched-page-right").addClass("disabled");
}
}
function highlightAddedScrollerButton() {
var scroller = $("#recently-added-row-scroller");
var numElems = scroller.find("li").length;
scroller.width(numElems * 175);
if (scroller.width() > $("#library-recently-added").width()) {
$("#recently-added-page-right").removeClass("disabled");
} else {
$("#recently-added-page-right").addClass("disabled");
}
}
$(window).resize(function() {
highlightWatchedScrollerButton();
highlightAddedScrollerButton();
});
$('div.art-face').animate({ opacity: 0.2 }, { duration: 1000 });
var leftTotalWatched = 0;
$(".paginate-watched").click(function (e) {
e.preventDefault();
var scroller = $("#recently-watched-row-scroller");
var containerWidth = $("#library-recently-watched").width();
var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175;
var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0);
leftTotalWatched = Math.max(Math.min(leftTotalWatched + scrollAmount, 0), leftMax);
scroller.animate({ left: leftTotalWatched }, 250);
if (leftTotalWatched == 0) {
$("#recently-watched-page-left").addClass("disabled").blur();
} else {
$("#recently-watched-page-left").removeClass("disabled");
}
if (leftTotalWatched == leftMax) {
$("#recently-watched-page-right").addClass("disabled").blur();
} else {
$("#recently-watched-page-right").removeClass("disabled");
}
});
var leftTotalAdded = 0;
$(".paginate-added").click(function (e) {
e.preventDefault();
var scroller = $("#recently-added-row-scroller");
var containerWidth = $("#library-recently-added").width();
var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175;
var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0);
leftTotalAdded = Math.max(Math.min(leftTotalAdded + scrollAmount, 0), leftMax);
scroller.animate({ left: leftTotalAdded }, 250);
if (leftTotalAdded == 0) {
$("#recently-added-page-left").addClass("disabled").blur();
} else {
$("#recently-added-page-left").removeClass("disabled");
}
if (leftTotalAdded == leftMax) {
$("#recently-added-page-right").addClass("disabled").blur();
} else {
$("#recently-added-page-right").removeClass("disabled");
}
});
});
</script>
% endif

View File

@@ -18,7 +18,7 @@
</div>
</div>
<div class='table-card-back'>
<div id="search-results-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading search results...</div>
<div id="search-results-list" class="search-results-list"><i class="fa fa-refresh fa-spin"></i>&nbsp; Loading search results...</div>
</div>
</div>
</%def>

View File

@@ -559,7 +559,7 @@
<p class="help-block">Enable to have Tautulli check if remote access to the Plex Media Server goes down.</p>
</div>
<div class="form-group has-feedback" id="pms-ip-group">
<div class="form-group has-feedback" id="pms_ip_group">
<label for="pms_ip">Plex IP or Hostname</label>
<div class="row">
<div class="col-md-6">
@@ -569,7 +569,7 @@
<button class="btn btn-form" type="button" id="verify_server_button">Verify Server</button>
</span>
</div>
<span class="form-control-feedback" id="pms-verify" aria-hidden="true" style="display: none; right: 110px;"></span>
<span class="form-control-feedback" id="pms_verify" aria-hidden="true" style="display: none; right: 110px;"></span>
</div>
<div id="pms_ip_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
@@ -607,7 +607,12 @@
<label for="pms_logs_folder">Plex Web URL</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control" id="pms_web_url" name="pms_web_url" value="${config['pms_web_url']}" size="30" data-parsley-trigger="change" data-parsley-pattern="^https?:\/\/.+\/web\/index\.html$|^https:\/\/app.plex.tv\/desktop$" data-parsley-errors-container="#pms_web_url_error" data-parsley-error-message="Invalid Plex Web URL." required>
<div class="input-group">
<input type="text" class="form-control" id="pms_web_url" name="pms_web_url" value="${config['pms_web_url']}" size="30" data-parsley-trigger="change" data-parsley-pattern="^https?:\/\/\S+$|^https:\/\/app.plex.tv\/desktop$" data-parsley-errors-container="#pms_web_url_error" data-parsley-error-message="Invalid Plex Web URL.">
<span class="input-group-btn">
<button class="btn btn-form" type="button" id="test_pms_web_button">Test URL</button>
</span>
</div>
</div>
<div id="pms_web_url_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
@@ -1580,6 +1585,17 @@ $(document).ready(function() {
}
}
function preSaveChecks(_callback) {
if ($("#pms_identifier").val() == "") {
verifyServer();
}
verifyPMSWebURL();
if (_callback) {
_callback();
}
}
// Alert the user that their changes require a restart.
function postSaveChecks() {
if (serverChanged || authChanged || httpChanged || directoryChanged) {
@@ -1609,11 +1625,7 @@ $(document).ready(function() {
}
$('.save-button').click(function() {
if ($("#pms_identifier").val() == "") {
verifyServer(function () { saveSettings() });
} else {
saveSettings();
}
preSaveChecks(function () { saveSettings() });
});
initConfigCheckbox('#api_enabled');
@@ -1758,43 +1770,45 @@ $(document).ready(function() {
var pms_identifier = $("#pms_identifier").val();
var pms_ssl = $("#pms_ssl").is(':checked') ? 1 : 0;
var pms_is_remote = $("#pms_is_remote").is(':checked') ? 1 : 0;
if (($("#pms_ip").val() !== '') || ($("#pms_port").val() !== '')) {
$("#pms-verify").html('<i class="fa fa-refresh fa-spin"></i>');
$('#pms-verify').fadeIn('fast');
$("#pms_verify").html('<i class="fa fa-refresh fa-spin"></i>').fadeIn('fast');
$.ajax({
url: 'get_server_id',
data : { hostname: pms_ip, port: pms_port, identifier: pms_identifier, ssl: pms_ssl, remote: pms_is_remote },
data: {
hostname: pms_ip,
port: pms_port,
identifier: pms_identifier,
ssl: pms_ssl,
remote: pms_is_remote
},
cache: true,
async: true,
timeout: 10000,
error: function(jqXHR, textStatus, errorThrown) {
$("#pms-verify").html('<i class="fa fa-close"></i>');
$('#pms-verify').fadeIn('fast');
$("#pms-ip-group").addClass("has-error");
error: function (jqXHR, textStatus, errorThrown) {
$("#pms_verify").html('<i class="fa fa-close"></i>').fadeIn('fast');
$("#pms_ip_group").addClass("has-error");
},
success: function (json) {
var machine_identifier = json;
if (machine_identifier) {
$("#pms_identifier").val(machine_identifier);
$("#pms-verify").html('<i class="fa fa-check"></i>');
$('#pms-verify').fadeIn('fast');
$("#pms-ip-group").removeClass("has-error");
$("#pms_verify").html('<i class="fa fa-check"></i>').fadeIn('fast');
$("#pms_ip_group").removeClass("has-error");
if (_callback) {
_callback();
}
} else {
$("#pms-verify").html('<i class="fa fa-close"></i>');
$('#pms-verify').fadeIn('fast');
$("#pms-ip-group").addClass("has-error");
$("#pms_verify").html('<i class="fa fa-close"></i>').fadeIn('fast');
$("#pms_ip_group").addClass("has-error");
showMsg('<i class="fa fa-exclamation-circle"></i> Could not verify your server.', false, true, 5000, true)
}
}
});
} else {
$("#pms-verify").html('<i class="fa fa-close"></i>');
$('#pms-verify').fadeIn('fast');
$("#pms-ip-group").addClass("has-error");
$("#pms_verify").html('<i class="fa fa-close"></i>').fadeIn('fast');
$("#pms_ip_group").addClass("has-error");
showMsg('<i class="fa fa-exclamation-circle"></i> Could not verify your server.', false, true, 5000, true)
}
}
@@ -1804,11 +1818,21 @@ $(document).ready(function() {
verifyServer();
});
function verifyPMSWebURL() {
var pms_web_url = $.trim($("#pms_web_url").val());
$("#pms_web_url").val(pms_web_url || 'https://app.plex.tv/desktop');
}
$('#test_pms_web_button').on('click', function(){
var pms_web_url = $.trim($("#pms_web_url").val());
window.open(pms_web_url, '_blank');
});
// Plex.tv auth token fetch
$("#get-pms-auth-token").click(function() {
$("#pms-token-status").html('<i class="fa fa-refresh fa-spin"></i> Fetching token...');
var pms_username = $("#pms_username").val().trim();
var pms_password = $("#pms_password").val().trim();
var pms_username = $.trim($("#pms_username").val());
var pms_password = $.trim($("#pms_password").val());
if ((pms_username !== '') && (pms_password !== '')) {
$.ajax({
type: 'GET',

View File

@@ -61,19 +61,19 @@ DOCUMENTATION :: END
% endif
</div>
<div class="user-info-nav">
<ul class="user-info-nav">
<li class="active"><a href="#profile" data-toggle="tab">Profile</a></li>
<li><a id="history-tab-btn" href="#userHistory" data-toggle="tab">History</a></li>
<li><a id="sync-tab-btn" href="#userSyncItems" data-toggle="tab">Synced Items</a></li>
<li><a id="ip-tab-btn" href="#userAddresses" data-toggle="tab">IP Addresses</a></li>
<li><a id="login-tab-btn" href="#userLogins" data-toggle="tab">Tautulli Logins</a></li>
<ul class="user-info-nav" role="tablist">
<li class="active"><a href="#tabs-profile" role="tab" data-toggle="tab">Profile</a></li>
<li><a id="history-tab-btn" href="#tabs-history" role="tab" data-toggle="tab">History</a></li>
<li><a id="sync-tab-btn" href="#tabs-synceditems" role="tab" data-toggle="tab">Synced Items</a></li>
<li><a id="ip-tab-btn" href="#tabs-ipaddresses" role="tab" data-toggle="tab">IP Addresses</a></li>
<li><a id="login-tab-btn" href="#tabs-tautullilogins" role="tab" data-toggle="tab">Tautulli Logins</a></li>
</ul>
</div>
</div>
</div>
</div>
<div class="tab-content">
<div class="tab-pane active" id="profile">
<div role="tabpanel" class="tab-pane active" id="tabs-profile">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
@@ -134,7 +134,7 @@ DOCUMENTATION :: END
</div>
</div>
</div>
<div class="tab-pane" id="userHistory">
<div role="tabpanel" class="tab-pane" id="tabs-history">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
@@ -200,7 +200,7 @@ DOCUMENTATION :: END
</div>
</div>
</div>
<div class="tab-pane" id="userSyncItems">
<div role="tabpanel" class="tab-pane" id="tabs-synceditems">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
@@ -240,7 +240,7 @@ DOCUMENTATION :: END
</div>
</div>
</div>
<div class="tab-pane" id="userAddresses">
<div role="tabpanel" class="tab-pane" id="tabs-ipaddresses">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
@@ -271,7 +271,7 @@ DOCUMENTATION :: END
</div>
</div>
</div>
<div class="tab-pane" id="userLogins">
<div role="tabpanel" class="tab-pane" id="tabs-tautullilogins">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
@@ -369,23 +369,246 @@ DOCUMENTATION :: END
<script src="${http_root}js/dataTables.bootstrap.min.js"></script>
<script src="${http_root}js/dataTables.bootstrap.pagination.js"></script>
% if data:
<script>
% if str(data['user_id']).isdigit():
var user_id = ${data['user_id']};
% else:
var user_id = null;
% endif
var username = '${data['username'].replace("'", "\\'")}';
</script>
<script src="${http_root}js/moment-with-locale.js"></script>
<script src="${http_root}js/tables/history_table.js${cache_param}"></script>
<script src="${http_root}js/tables/user_ips.js${cache_param}"></script>
<script src="${http_root}js/tables/sync_table.js${cache_param}"></script>
<script src="${http_root}js/tables/login_logs.js${cache_param}"></script>
<script>
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
$.fn.dataTable.tables({ visible: true, api: true }).columns.adjust();
});
$('a[href="#tabs-profile"]').on('shown.bs.tab', function() {
var media_type = null;
loadHistoryTable(media_type);
});
function loadHistoryTable(media_type) {
// Build watch history table
history_table_options.ajax = {
url: 'get_history',
type: 'post',
data: function ( d ) {
return {
json_data: JSON.stringify( d ),
user_id: user_id,
media_type: media_type
};
}
}
history_table = $('#history_table-UID-${data["user_id"]}').DataTable(history_table_options);
history_table.column(2).visible(false);
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 11] });
$(colvis.button()).appendTo('#button-bar-history');
clearSearchButton('history_table-UID-${data["user_id"]}', history_table);
$('#media_type-selection').on('change', function () {
$('#media_type-selection > label').removeClass('active');
selected_filter = $('input[name=media_type-filter]:checked', '#media_type-selection');
$(selected_filter).closest('label').addClass('active');
media_type = $(selected_filter).val();
history_table.draw();
});
}
$('a[href="#tabs-history"]').on('shown.bs.tab', function() {
var media_type = null;
loadHistoryTable(media_type);
});
$('a[href="#tabs-synceditems"]').on('shown.bs.tab', function() {
// Build user sync table
sync_table_options.ajax = {
url: 'get_sync',
data: function(d) {
d.user_id = user_id;
}
}
sync_table = $('#sync_table-UID-${data["user_id"]}').DataTable(sync_table_options);
sync_table.column(1).visible(false);
var colvis_sync = new $.fn.dataTable.ColVis( sync_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark' } );
$( colvis_sync.button() ).appendTo('#button-bar-sync');
clearSearchButton('sync_table-UID-${data["user_id"]}', sync_table);
});
$('a[href="#tabs-ipaddresses"]').on('shown.bs.tab', function() {
// Build user IP table
user_ip_table_options.ajax = {
url: 'get_user_ips',
type: 'post',
data: function ( d ) {
return {
json_data: JSON.stringify( d ),
user_id: user_id
};
}
}
user_ip_table = $('#user_ip_table-UID-${data["user_id"]}').DataTable(user_ip_table_options);
clearSearchButton('user_ip_table-UID-${data["user_id"]}', user_ip_table);
});
$('a[href="#tabs-tautullilogins"]').on('shown.bs.tab', function() {
// Build user login table
login_log_table_options.ajax = {
url: 'get_user_logins',
data: function(d) {
d.user_id = user_id;
}
}
login_log_table = $('#login_log_table-UID-${data["user_id"]}').DataTable(login_log_table_options);
login_log_table.columns([1, 2]).visible(false);
var colvis_login = new $.fn.dataTable.ColVis( login_log_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark' } );
$( colvis_login.button() ).appendTo('#button-bar-login');
clearSearchButton('login_log_table-UID-${data["user_id"]}', login_log_table);
});
% if _session['user_group'] == 'admin':
$("#edit-user-tooltip").tooltip();
// Load edit user modal
$("#toggle-edit-user-modal").click(function() {
$("#edit-user-tooltip").tooltip('hide');
$.ajax({
url: 'edit_user_dialog',
data: { user_id: user_id },
cache: false,
async: true,
complete: function(xhr, status) {
$("#edit-user-modal").html(xhr.responseText);
}
});
});
$('#row-edit-mode').on('click', function() {
$('#row-edit-mode-alert').fadeIn(200);
if ($(this).hasClass('active')) {
if (history_to_delete.length > 0) {
$('#deleteCount').text(history_to_delete.length);
$('#confirm-modal-delete').modal();
$('#confirm-modal-delete').one('click', '#confirm-delete', function () {
history_to_delete.forEach(function(row, idx) {
$.ajax({
url: 'delete_history_rows',
type: 'POST',
data: { row_id: row },
async: true,
success: function (data) {
var msg = "History deleted";
showMsg(msg, false, true, 2000);
}
});
});
history_table.draw();
});
}
$('.delete-control').each(function () {
$(this).addClass('hidden');
$('#row-edit-mode-alert').fadeOut(200);
});
} else {
history_to_delete = [];
$('.delete-control').each(function() {
$(this).find('button.btn-danger').toggleClass('btn-warning').toggleClass('btn-danger');
$(this).removeClass('hidden');
});
}
});
% endif
$("#refresh-history-list").click(function () {
history_table.draw();
});
function recentlyWatched() {
// Populate recently watched
$.ajax({
url: 'get_user_recently_watched',
async: true,
data: {
user_id: user_id,
limit: 50
},
complete: function(xhr, status) {
$("#user-recently-watched").html(xhr.responseText);
highlightWatchedScrollerButton();
}
});
}
recentlyWatched();
function highlightWatchedScrollerButton() {
var scroller = $("#recently-watched-row-scroller");
var numElems = scroller.find("li").length;
scroller.width(numElems * 175);
if (scroller.width() > $("#user-recently-watched").width()) {
$("#recently-watched-page-right").removeClass("disabled");
} else {
$("#recently-watched-page-right").addClass("disabled");
}
}
$(window).resize(function() {
highlightWatchedScrollerButton();
});
var leftTotal = 0;
$(".paginate").click(function (e) {
e.preventDefault();
var scroller = $("#recently-watched-row-scroller");
var containerWidth = $("#user-recently-watched").width();
var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175;
var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0);
leftTotal = Math.max(Math.min(leftTotal + scrollAmount, 0), leftMax);
scroller.animate({ left: leftTotal }, 250);
if (leftTotal == 0) {
$("#recently-watched-page-left").addClass("disabled").blur();
} else {
$("#recently-watched-page-left").removeClass("disabled");
}
if (leftTotal == leftMax) {
$("#recently-watched-page-right").addClass("disabled").blur();
} else {
$("#recently-watched-page-right").removeClass("disabled");
}
});
$(document).ready(function () {
% if str(data['user_id']).isdigit():
var user_id = ${data['user_id']};
% else:
var user_id = null;
% endif
// Javascript to enable link to tab
var hash = document.location.hash;
var prefix = "tab_";
if (hash) {
$('.user-info-nav a[href='+hash.replace(prefix,"")+']').tab('show').trigger('show.bs.tab');
}
var username = '${data['username'].replace("'", "\\'")}';
$("#edit-user-tooltip").tooltip();
// Change hash for page-reload
$('.user-info-nav a').on('shown.bs.tab', function (e) {
window.location.hash = e.target.hash.replace("#", "#" + prefix);
});
// Populate watch time stats
$.ajax({
@@ -407,210 +630,6 @@ DOCUMENTATION :: END
}
});
function loadHistoryTable(media_type) {
// Build watch history table
history_table_options.ajax = {
url: 'get_history',
type: 'post',
data: function ( d ) {
return {
json_data: JSON.stringify( d ),
user_id: user_id,
media_type: media_type
};
}
}
history_table = $('#history_table-UID-${data["user_id"]}').DataTable(history_table_options);
history_table.column(2).visible(false);
var colvis = new $.fn.dataTable.ColVis(history_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark', exclude: [0, 11] });
$(colvis.button()).appendTo('#button-bar-history');
clearSearchButton('history_table-UID-${data["user_id"]}', history_table);
$('#media_type-selection').on('change', function () {
$('#media_type-selection > label').removeClass('active');
selected_filter = $('input[name=media_type-filter]:checked', '#media_type-selection');
$(selected_filter).closest('label').addClass('active');
media_type = $(selected_filter).val();
history_table.draw();
});
}
$( "#history-tab-btn" ).one( "click", function() {
var media_type = null;
loadHistoryTable(media_type);
});
$( "#ip-tab-btn" ).one( "click", function() {
// Build user IP table
user_ip_table_options.ajax = {
url: 'get_user_ips',
type: 'post',
data: function ( d ) {
return {
json_data: JSON.stringify( d ),
user_id: user_id
};
}
}
user_ip_table = $('#user_ip_table-UID-${data["user_id"]}').DataTable(user_ip_table_options);
clearSearchButton('user_ip_table-UID-${data["user_id"]}', user_ip_table);
});
$( "#sync-tab-btn" ).one( "click", function() {
// Build user sync table
sync_table_options.ajax = {
url: 'get_sync',
data: function(d) {
d.user_id = user_id;
}
}
sync_table = $('#sync_table-UID-${data["user_id"]}').DataTable(sync_table_options);
sync_table.column(1).visible(false);
var colvis_sync = new $.fn.dataTable.ColVis( sync_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark' } );
$( colvis_sync.button() ).appendTo('#button-bar-sync');
clearSearchButton('sync_table-UID-${data["user_id"]}', sync_table);
});
$( "#login-tab-btn" ).one( "click", function() {
// Build user login table
login_log_table_options.ajax = {
url: 'get_user_logins',
data: function(d) {
d.user_id = user_id;
}
}
login_log_table = $('#login_log_table-UID-${data["user_id"]}').DataTable(login_log_table_options);
login_log_table.columns([1, 2]).visible(false);
var colvis_login = new $.fn.dataTable.ColVis( login_log_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark' } );
$( colvis_login.button() ).appendTo('#button-bar-login');
clearSearchButton('login_log_table-UID-${data["user_id"]}', login_log_table);
});
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
$.fn.dataTable.tables({ visible: true, api: true }).columns.adjust();
});
% if _session['user_group'] == 'admin':
// Load edit user modal
$("#toggle-edit-user-modal").click(function() {
$("#edit-user-tooltip").tooltip('hide');
$.ajax({
url: 'edit_user_dialog',
data: { user_id: user_id },
cache: false,
async: true,
complete: function(xhr, status) {
$("#edit-user-modal").html(xhr.responseText);
}
});
});
$('#row-edit-mode').on('click', function() {
$('#row-edit-mode-alert').fadeIn(200);
if ($(this).hasClass('active')) {
if (history_to_delete.length > 0) {
$('#deleteCount').text(history_to_delete.length);
$('#confirm-modal-delete').modal();
$('#confirm-modal-delete').one('click', '#confirm-delete', function () {
history_to_delete.forEach(function(row, idx) {
$.ajax({
url: 'delete_history_rows',
type: 'POST',
data: { row_id: row },
async: true,
success: function (data) {
var msg = "History deleted";
showMsg(msg, false, true, 2000);
}
});
});
history_table.draw();
});
}
$('.delete-control').each(function () {
$(this).addClass('hidden');
$('#row-edit-mode-alert').fadeOut(200);
});
} else {
history_to_delete = [];
$('.delete-control').each(function() {
$(this).find('button.btn-danger').toggleClass('btn-warning').toggleClass('btn-danger');
$(this).removeClass('hidden');
});
}
});
% endif
$("#refresh-history-list").click(function () {
history_table.draw();
});
function recentlyWatched() {
// Populate recently watched
$.ajax({
url: 'get_user_recently_watched',
async: true,
data: {
user_id: user_id,
limit: 50
},
complete: function(xhr, status) {
$("#user-recently-watched").html(xhr.responseText);
highlightWatchedScrollerButton();
}
});
}
recentlyWatched();
function highlightWatchedScrollerButton() {
var scroller = $("#recently-watched-row-scroller");
var numElems = scroller.find("li").length;
scroller.width(numElems * 175);
if (scroller.width() > $("#user-recently-watched").width()) {
$("#recently-watched-page-right").removeClass("disabled");
} else {
$("#recently-watched-page-right").addClass("disabled");
}
}
$(window).resize(function() {
highlightWatchedScrollerButton();
});
var leftTotal = 0;
$(".paginate").click(function (e) {
e.preventDefault();
var scroller = $("#recently-watched-row-scroller");
var containerWidth = $("#user-recently-watched").width();
var scrollAmount = $(this).data("id") * parseInt(containerWidth / 175) * 175;
var leftMax = Math.min(-parseInt(scroller.width()) + Math.abs(scrollAmount), 0);
leftTotal = Math.max(Math.min(leftTotal + scrollAmount, 0), leftMax);
scroller.animate({ left: leftTotal }, 250);
if (leftTotal == 0) {
$("#recently-watched-page-left").addClass("disabled").blur();
} else {
$("#recently-watched-page-left").removeClass("disabled");
}
if (leftTotal == leftMax) {
$("#recently-watched-page-right").addClass("disabled").blur();
} else {
$("#recently-watched-page-right").removeClass("disabled");
}
});
});
</script>
% endif

View File

@@ -1104,7 +1104,7 @@ class DataFactory(object):
if str(rating_key).isdigit():
poster_key = rating_key
elif metadata:
if metadata['media_type'] in ('movie', 'show', 'artist'):
if metadata['media_type'] in ('movie', 'show', 'artist', 'collection'):
poster_key = metadata['rating_key']
elif metadata['media_type'] in ('season', 'album'):
poster_key = metadata['rating_key']

View File

@@ -16,6 +16,7 @@
import arrow
import bleach
from collections import Counter
from itertools import groupby
import json
from operator import itemgetter
@@ -145,10 +146,17 @@ def notify_conditions(notify_action=None, stream_data=None, timeline_data=None):
return False
if notify_action == 'on_concurrent':
ap = activity_processor.ActivityProcessor()
user_sessions = ap.get_sessions(user_id=stream_data['user_id'],
ip_address=plexpy.CONFIG.NOTIFY_CONCURRENT_BY_IP)
return len(user_sessions) >= plexpy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD
pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_current_activity()
user_sessions = []
if result:
user_sessions = [s for s in result['sessions'] if s['user_id'] == stream_data['user_id']]
if plexpy.CONFIG.NOTIFY_CONCURRENT_BY_IP:
return len(Counter(s['ip_address'] for s in user_sessions)) >= plexpy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD
else:
return len(user_sessions) >= plexpy.CONFIG.NOTIFY_CONCURRENT_THRESHOLD
elif notify_action == 'on_newdevice':
data_factory = datafactory.DataFactory()
@@ -504,7 +512,7 @@ def build_media_notify_params(notify_action=None, session=None, timeline=None, m
# Build Plex URL
metadata['plex_url'] = '{web_url}#!/server/{pms_identifier}/details?key=%2Flibrary%2Fmetadata%2F{rating_key}'.format(
web_url=plexpy.CONFIG.PMS_WEB_URL or 'https://app.plex.tv/desktop',
web_url=plexpy.CONFIG.PMS_WEB_URL,
pms_identifier=plexpy.CONFIG.PMS_IDENTIFIER,
rating_key=rating_key)

View File

@@ -257,6 +257,23 @@ class PmsConnect(object):
return request
def get_children_list_related(self, rating_key='', output_format=''):
"""
Return list of related children in requested collection item.
Parameters required: rating_key { ratingKey of parent }
Optional parameters: output_format { dict, json }
Output: array
"""
uri = '/hubs/metadata/' + rating_key + '/related'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
output_format=output_format)
return request
def get_childrens_list(self, rating_key='', output_format=''):
"""
Return list of children in requested library item.
@@ -414,7 +431,7 @@ class PmsConnect(object):
Output: array
"""
uri = '/hubs/search?query=' + urllib.quote(query.encode('utf8')) + '&limit=' + limit
uri = '/hubs/search?query=' + urllib.quote(query.encode('utf8')) + '&limit=' + limit + '&includeCollections=1'
request = self.request_handler.make_request(uri=uri,
proto=self.protocol,
request_type='GET',
@@ -607,7 +624,7 @@ class PmsConnect(object):
try:
xml_head = metadata.getElementsByTagName('MediaContainer')
except Exception as e:
logger.warn(u"Tautulli Pmsconnect :: Unable to parse XML for get_metadata: %s." % e)
logger.warn(u"Tautulli Pmsconnect :: Unable to parse XML for get_metadata_details: %s." % e)
return {}
metadata = {}
@@ -1039,6 +1056,50 @@ class PmsConnect(object):
helpers.get_xml_attr(metadata_main, 'title'))
}
elif metadata_type == 'collection':
metadata = {'media_type': metadata_type,
'sub_media_type': helpers.get_xml_attr(metadata_main, 'subtype'),
'section_id': section_id,
'library_name': library_name,
'rating_key': helpers.get_xml_attr(metadata_main, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(metadata_main, 'parentRatingKey'),
'grandparent_rating_key': helpers.get_xml_attr(metadata_main, 'grandparentRatingKey'),
'title': helpers.get_xml_attr(metadata_main, 'title'),
'parent_title': helpers.get_xml_attr(metadata_main, 'parentTitle'),
'grandparent_title': helpers.get_xml_attr(metadata_main, 'grandparentTitle'),
'sort_title': helpers.get_xml_attr(metadata_main, 'titleSort'),
'media_index': helpers.get_xml_attr(metadata_main, 'index'),
'parent_media_index': helpers.get_xml_attr(metadata_main, 'parentIndex'),
'studio': helpers.get_xml_attr(metadata_main, 'studio'),
'content_rating': helpers.get_xml_attr(metadata_main, 'contentRating'),
'summary': helpers.get_xml_attr(metadata_main, 'summary'),
'tagline': helpers.get_xml_attr(metadata_main, 'tagline'),
'rating': helpers.get_xml_attr(metadata_main, 'rating'),
'audience_rating': helpers.get_xml_attr(metadata_main, 'audienceRating'),
'user_rating': helpers.get_xml_attr(metadata_main, 'userRating'),
'duration': helpers.get_xml_attr(metadata_main, 'duration'),
'year': helpers.get_xml_attr(metadata_main, 'year'),
'min_year': helpers.get_xml_attr(metadata_main, 'minYear'),
'max_year': helpers.get_xml_attr(metadata_main, 'maxYear'),
'thumb': helpers.get_xml_attr(metadata_main, 'thumb').split('?')[0],
'parent_thumb': helpers.get_xml_attr(metadata_main, 'parentThumb'),
'grandparent_thumb': helpers.get_xml_attr(metadata_main, 'grandparentThumb'),
'art': helpers.get_xml_attr(metadata_main, 'art'),
'banner': helpers.get_xml_attr(metadata_main, 'banner'),
'originally_available_at': helpers.get_xml_attr(metadata_main, 'originallyAvailableAt'),
'added_at': helpers.get_xml_attr(metadata_main, 'addedAt'),
'updated_at': helpers.get_xml_attr(metadata_main, 'updatedAt'),
'last_viewed_at': helpers.get_xml_attr(metadata_main, 'lastViewedAt'),
'guid': helpers.get_xml_attr(metadata_main, 'guid'),
'child_count': helpers.get_xml_attr(metadata_main, 'childCount'),
'directors': directors,
'writers': writers,
'actors': actors,
'genres': genres,
'labels': labels,
'full_title': helpers.get_xml_attr(metadata_main, 'title')
}
elif metadata_type == 'clip':
metadata = {'media_type': metadata_type,
'section_id': section_id,
@@ -1795,7 +1856,6 @@ class PmsConnect(object):
else:
return False
def get_item_children(self, rating_key=''):
"""
Return processed and validated children list.
@@ -1836,8 +1896,11 @@ class PmsConnect(object):
for result in result_data:
children_output = {'section_id': section_id,
'rating_key': helpers.get_xml_attr(result, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(result, 'parentRatingKey'),
'media_index': helpers.get_xml_attr(result, 'index'),
'title': helpers.get_xml_attr(result, 'title'),
'parent_title': helpers.get_xml_attr(result, 'parentTitle'),
'year': helpers.get_xml_attr(result, 'year'),
'thumb': helpers.get_xml_attr(result, 'thumb'),
'parent_thumb': helpers.get_xml_attr(a, 'thumb'),
'duration': helpers.get_xml_attr(result, 'duration')
@@ -1852,6 +1915,72 @@ class PmsConnect(object):
return output
def get_item_children_related(self, rating_key=''):
"""
Return processed and validated children list.
Output: array
"""
children_data = self.get_children_list_related(rating_key, output_format='xml')
try:
xml_head = children_data.getElementsByTagName('MediaContainer')
except Exception as e:
logger.warn(u"Tautulli Pmsconnect :: Unable to parse XML for get_item_children_related: %s." % e)
return []
children_results_list = {'movie': [],
'show': [],
'season': [],
'episode': [],
'artist': [],
'album': [],
'track': [],
}
for a in xml_head:
section_id = helpers.get_xml_attr(a, 'librarySectionID')
hubs = a.getElementsByTagName('Hub')
for h in hubs:
size = helpers.get_xml_attr(h, 'size')
media_type = helpers.get_xml_attr(h, 'type')
title = helpers.get_xml_attr(h, 'title')
hub_identifier = helpers.get_xml_attr(h, 'hubIdentifier')
if size == '0' or not hub_identifier.startswith('collection.related') or \
media_type not in children_results_list.keys():
continue
result_data = []
if h.getElementsByTagName('Video'):
result_data = h.getElementsByTagName('Video')
if h.getElementsByTagName('Directory'):
result_data = h.getElementsByTagName('Directory')
if h.getElementsByTagName('Track'):
result_data = h.getElementsByTagName('Track')
for result in result_data:
children_output = {'section_id': section_id,
'rating_key': helpers.get_xml_attr(result, 'ratingKey'),
'parent_rating_key': helpers.get_xml_attr(result, 'parentRatingKey'),
'media_index': helpers.get_xml_attr(result, 'index'),
'title': helpers.get_xml_attr(result, 'title'),
'parent_title': helpers.get_xml_attr(result, 'parentTitle'),
'year': helpers.get_xml_attr(result, 'year'),
'thumb': helpers.get_xml_attr(result, 'thumb'),
'parent_thumb': helpers.get_xml_attr(a, 'thumb'),
'duration': helpers.get_xml_attr(result, 'duration')
}
children_results_list[media_type].append(children_output)
output = {'results_count': sum(len(s) for s in children_results_list.items()),
'results_list': children_results_list,
}
return output
def get_servers_info(self):
"""
Return the list of local servers.
@@ -2237,7 +2366,8 @@ class PmsConnect(object):
'episode': [],
'artist': [],
'album': [],
'track': []
'track': [],
'collection': []
}
for a in xml_head:

View File

@@ -1,2 +1,2 @@
PLEXPY_BRANCH = "beta"
PLEXPY_RELEASE_VERSION = "v2.0.3-beta"
PLEXPY_RELEASE_VERSION = "v2.0.4-beta"

View File

@@ -3588,7 +3588,7 @@ class WebInterface(object):
def get_item_children(self, rating_key='', **kwargs):
pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_item_children(rating_key)
result = pms_connect.get_item_children(rating_key=rating_key)
if result:
return serve_template(templatename="info_children_list.html", data=result, title="Children List")
@@ -3596,6 +3596,18 @@ class WebInterface(object):
logger.warn(u"Unable to retrieve data for get_item_children.")
return serve_template(templatename="info_children_list.html", data=None, title="Children List")
@cherrypy.expose
@requireAuth()
def get_item_children_related(self, rating_key='', title='', **kwargs):
pms_connect = pmsconnect.PmsConnect()
result = pms_connect.get_item_children_related(rating_key=rating_key)
if result:
return serve_template(templatename="info_collection_list.html", data=result, title=title)
else:
return serve_template(templatename="info_collection_list.html", data=None, title=title)
@cherrypy.expose
@cherrypy.tools.json_out()
@requireAuth()