Merge pull request #263 from drzoidberg33/library-id-changes

Library id changes
This commit is contained in:
JonnyWong16 2016-01-17 16:43:46 -08:00
commit ecedd4d231
74 changed files with 7355 additions and 3748 deletions

View file

@ -179,6 +179,11 @@ from plexpy import version
% else:
<li><a href="home"><i class="fa fa-lg fa-home"></i></a></li>
% endif
% if title == "Libraries" or title == "Library" or title == "Info":
<li class="active"><a href="libraries">Libraries</a></li>
% else:
<li><a href="libraries">Libraries</a></li>
% endif
% if title == "Users" or title == "User":
<li class="active"><a href="users">Users</a></li>
% else:

View file

@ -498,6 +498,16 @@ textarea.form-control:focus {
-moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
}
.libraries-poster-face {
overflow: hidden;
float: left;
background-size: contain;
height: 40px;
width: 40px;
/*-webkit-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
-moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);*/
}
a .poster-face:hover,
a .cover-face:hover,
a .users-poster-face:hover {
@ -1195,6 +1205,7 @@ a:hover .summary-poster-face-track .summary-poster-face-overlay span {
width: 250px;
height: 1px;
margin: 0 40px 20px 25px;
position: relative;
}
.summary-content {
position: relative;
@ -1218,6 +1229,18 @@ a:hover .summary-poster-face-track .summary-poster-face-overlay span {
margin-left: 2px;
margin-right: 10px;
}
.summary-content-media-info-wrapper {
width: 100%;
position: absolute;
bottom: 0;
left: 0;
text-align: center;
}
.summary-content-media-info {
max-width: 75pt;
max-height: 20px;
margin: 0 10px;
}
.summary-content-summary {
overflow: hidden;
color: #fff;
@ -1654,7 +1677,6 @@ a:hover .item-children-poster {
}
.user-player-instance-box {
float: left;
width: 75px;
border-radius: 3px;
-webkit-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
-moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
@ -1676,6 +1698,7 @@ a:hover .item-children-poster {
font-weight: normal;
width: 140px;
margin-left: 10px;
margin-bottom: 10px;
}
.user-player-instance-playcount h3 {
font-size: 30px;
@ -1695,6 +1718,35 @@ a:hover .item-children-poster {
top: 15px;
left: 0px;
}
.library-info-poster-face {
float: left;
margin-top: 15px;
margin-right: 15px;
background-size: contain;
height: 80px;
width: 80px;
/*-webkit-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
-moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);*/
}
.library-user-instance-box {
float: left;
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
border-radius: 50%;
-webkit-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
-moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
background-size: contain;
position: relative;
height: 80px;
width: 80px;
}
.library-user-instance-box:hover {
-webkit-box-shadow: inset 0 0 0 2px #e9a049;
-moz-box-shadow: inset 0 0 0 2px #e9a049;
box-shadow: inset 0 0 0 2px #e9a049;
}
.home-platforms {
}
.home-platforms ul {
@ -2127,7 +2179,8 @@ a .home-platforms-instance-list-oval:hover,
float: right;
}
.colvis-button-bar,
.refresh-users-button {
.refresh-users-button,
.refresh-libraries-button {
float: right;
}
.nav-settings,
@ -2346,42 +2399,52 @@ a .home-platforms-instance-list-oval:hover,
background: #fff;
border: 0;
font-weight: bold;
border-radius: 2px;
}
.history-title .popover.right {
margin-left: 12px;
z-index: 5;
.history-thumbnail-popover {
z-index: 2;
padding: 0;
border: 0;
}
.history-title .popover.right .popover-content {
padding: 5px 8px;
.history-thumbnail-popover.popover.right {
margin-left: 15px;
}
.history-thumbnail-popover .popover-content {
color: #000;
padding: 0;
}
.history-thumbnail {
background-position: center;
background-size: cover;
width: 80px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
-webkit-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
-moz-box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
box-shadow: 0 0 4px rgba(0,0,0,.3),inset 0 0 0 1px rgba(255,255,255,.1);
}
.edit-user-toggles {
.edit-user-toggles,
.edit-library-toggles {
padding-right: 10px;
}
.edit-user-toggles > input[type='checkbox'] {
.edit-user-toggles > input[type='checkbox'],
.edit-library-toggles > input[type='checkbox'] {
display: none;
}
.edit-user-toggles > input[type='checkbox'] + label {
.edit-user-toggles > input[type='checkbox'] + label,
.edit-library-toggles > input[type='checkbox'] + label {
color: #444;
cursor: pointer;
}
.edit-user-toggles > input[type='checkbox']:checked + label {
.edit-user-toggles > input[type='checkbox']:checked + label,
.edit-library-toggles > input[type='checkbox']:checked + label {
color: #fff;
cursor: pointer;
}
.edit-user-name > input[type='text'] {
margin: 0;
}
.popover {
z-index: 2;
}
.popover .popover-content {
color: #000;
}
.noTransition
{
-moz-transition: none !important;
@ -2418,7 +2481,9 @@ a .home-platforms-instance-list-oval:hover,
left: 12px;
}
#users-to-delete > li,
#users-to-purge > li {
#users-to-purge > li,
#libraries-to-delete > li,
#libraries-to-purge > li {
color: #e9a049;
}
#updatebar {
@ -2468,8 +2533,7 @@ a .home-platforms-instance-list-oval:hover,
right: 0;
bottom: 0;
left: 0;
overflow-x: hidden;
overflow-y: scroll;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
::-webkit-scrollbar {
@ -2496,12 +2560,19 @@ a .home-platforms-instance-list-oval:hover,
width: 100%;
}
}
table.display,
table.display tr.shown + tr table[id^='history_child'],
table.display tr.shown + tr table[id^='media_info_child'],
table.display tr.shown + tr table[id^='media_info_child'] tr.shown + tr table[id^='media_info_child'] {
table-layout: auto;
}
table.display tr.shown + tr div.slider {
display: none;
}
table.display tr.shown + tr > td {
padding-top: 0;
padding-bottom: 0;
padding-left: 0;
}
table.display tr.shown + tr:hover {
background-color: rgba(255,255,255,0);
@ -2512,7 +2583,9 @@ table.display tr.shown + tr .pagination > .active > a,
table.display tr.shown + tr .pagination > .active > a:hover {
color: #fff;
}
table.display tr.shown + tr table[id^='history_child'] td:hover a {
table.display tr.shown + tr table[id^='history_child'] td:hover a,
table.display tr.shown + tr table[id^='media_info_child'] > tr > td:hover a,
table.display tr.shown + tr table[id^='media_info_child'] tr.shown + tr table[id^='media_info_child'] td:hover a {
color: #F9AA03;
}
table.display tr.shown + tr .pagination > .disabled > a {
@ -2523,14 +2596,22 @@ table.display tr.shown + tr .pagination > li > a:hover {
}
table[id^='history_child'] {
margin-top: 0;
margin-left: -4px;
opacity: .6;
}
table[id^='history_child'] thead th {
table[id^='media_info_child'] {
margin-top: 0;
}
table[id^='history_child'] thead th,
table[id^='media_info_child'] thead th {
line-height: 0;
height: 0 !important;
overflow: hidden;
}
table[id^='media_info_child'] table[id^='media_info_child'] thead th {
line-height: 25px;
height: 35px !important;
overflow: hidden;
}
#search_form {
width: 300px;
padding: 8px 15px;
@ -2564,7 +2645,6 @@ table[id^='history_child'] thead th {
-o-transition: background 0.3s;
transition: background 0.3s;
}
.notification-params {
margin-top: 10px;
background-color: #282828;
@ -2589,7 +2669,6 @@ table[id^='history_child'] thead th {
.notification-params tr:nth-child(even) td {
background-color: rgba(255,255,255,0.010);
}
#days-selection label {
margin-bottom: 0;
}
@ -2598,3 +2677,36 @@ table[id^='history_child'] thead th {
width: 75px;
height: 34px;
}
.card-sortable {
height: 36px;
padding: 0 20px 0 0;
line-height: 34px;
cursor: move;
cursor: -webkit-grab;
cursor: grab;
border-bottom: 1px solid #232323;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
.card {
position: relative;
background-color: #282828;
border-top: 1px solid #2d2d2d;
}
.card label {
font-weight: normal;
}
.card-handle {
display: inline-block;
width: 30px;
margin-right: 10px;
color: #444;
text-align: center;
background-color: #2f2f2f;
}
.selectize-input input[type='text'] {
height: 20px;
}

View file

@ -69,12 +69,12 @@ DOCUMENTATION :: END
% for a in data['sessions']:
<div class="dashboard-instance" id="instance-${a['session_key']}">
% if a['media_type'] == 'movie' or a['media_type'] == 'episode' or a['media_type'] == 'track':
<a href="info?item_id=${a['rating_key']}">
<a href="info?rating_key=${a['rating_key']}">
% endif
<div class="dashboard-activity-poster">
% if a['media_type'] == 'movie' and not a['indexes']:
% if (a['media_type'] == 'movie' and not a['indexes']) or (a['indexes'] and not a['view_offset']):
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280);"></div>
% elif a['media_type'] == 'episode' and not a['indexes']:
% elif (a['media_type'] == 'episode' and not a['indexes']) or (a['indexes'] and not a['view_offset']):
<div class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${a['art']}&width=500&height=280);"></div>
% elif a['indexes']:
<div class="dashboard-activity-poster-face bif" style="background-image: url(pms_image_proxy?img=${a['bif_thumb']}&width=500&height=280); display: none;"></div>
@ -227,13 +227,13 @@ DOCUMENTATION :: END
<i class="fa fa-spinner"></i>&nbsp;
% endif
% if a['media_type'] == 'episode':
<a href="info?item_id=${a['rating_key']}" title="${a['grandparent_title']} - ${a['title']}">${a['grandparent_title']} - ${a['title']}</a>
<a href="info?rating_key=${a['rating_key']}" title="${a['grandparent_title']} - ${a['title']}">${a['grandparent_title']} - ${a['title']}</a>
% elif a['media_type'] == 'movie':
<a href="info?item_id=${a['rating_key']}" title="${a['title']}">${a['title']}</a>
<a href="info?rating_key=${a['rating_key']}" title="${a['title']}">${a['title']}</a>
% elif a['media_type'] == 'clip':
<span title="${a['title']}">${a['title']}</span>
% elif a['media_type'] == 'track':
<a href="info?item_id=${a['rating_key']}" title="${a['grandparent_title']} - ${a['title']}">${a['grandparent_title']} - ${a['title']}</a>
<a href="info?rating_key=${a['rating_key']}" title="${a['grandparent_title']} - ${a['title']}">${a['grandparent_title']} - ${a['title']}</a>
% elif a['media_type'] == 'photo':
<span title="${a['parent_title']}">${a['parent_title']}</span>
% else:
@ -246,7 +246,7 @@ DOCUMENTATION :: END
% elif a['media_type'] == 'movie':
<span title="${a['year']}">${a['year']}</span>
% elif a['media_type'] == 'track':
<a href="info?item_id=${a['parent_rating_key']}" title="${a['parent_title']}">${a['parent_title']}</a>
<a href="info?rating_key=${a['parent_rating_key']}" title="${a['parent_title']}">${a['parent_title']}</a>
% elif a['media_type'] == 'photo':
<span title="${a['title']}">${a['title']}</span>
% else:

View file

@ -0,0 +1,179 @@
<%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: edit_library.html
Version: 0.1
Variable names: data [list]
data :: Usable parameters
== Global keys ==
section_id Returns the library id of the library.
section_name Returns the name of the library.
section_type Returns the type of the library.
library_thumb Returns the thumbnail for the library.
custom_thumb Returns the custom thumbnail for the library.
library_art Returns the artwork for the library.
count Returns the item count for the library.
parent_count Returns the parent item count for the library.
child_count Returns the child item count for the library.
do_notify Returns bool value for whether to send notifications for the library.
keep_history Returns bool value for whether to keep history for the library.
DOCUMENTATION :: END
</%doc>
<%!
from plexpy import helpers
%>
% if data != None:
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Edit library <strong>${data['section_name']}</strong></h4>
</div>
<div class="modal-body" id="modal-text">
<fieldset>
<div class="form-group">
<label for="profile_url">Library Picture URL</label>
<div class="row">
<div class="col-md-8">
<input type="text" class="form-control" id="custom_thumb_url" name="custom_thumb_url" value="${data['library_thumb']}">
</div>
</div>
<p class="help-block">Change the library's picture in PlexPy. To reset to default, leave this field empty and save.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="do_notify" name="do_notify" value="1" ${helpers.checked(data['do_notify'])}> Enable notifications
</label>
<p class="help-block">Uncheck this if you do not want to receive notifications for this library's activity.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="keep_history" name="keep_history" value="1" ${helpers.checked(data['keep_history'])}> Keep history
</label>
<p class="help-block">Uncheck this if you do not want this keep any history on this library's activity.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="do_notify_created" name="do_notify_created" value="1" ${helpers.checked(data['do_notify_created'])}> Enable recently added notifications
</label>
<p class="help-block">Uncheck this if you do not want to receive recently added notifications for this library.</p>
</div>
% if data['section_id']:
<div class="form-group">
<button class="btn btn-danger" id="delete-all-history">Purge</button>
<p class="help-block">DANGER ZONE! Click the purge button to remove all history logged for this library. This is permanent!</p>
</div>
% endif
</fieldset>
</div>
<div class="modal-footer">
<div>
<span id="edit-library-status-message"></span>
<input type="button" id="save_library" class="btn btn-bright" value="Save">
</div>
</div>
</div>
</div>
<div id="confirm-modal-purge" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="confirm-modal-purge">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title" id="myModalLabel">Confirm Purge</h4>
</div>
<div class="modal-body" style="text-align: center;">
<p>Are you REALLY sure you want to purge all history for this library?</p>
<p>This is permanent and cannot be undone!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-purge">Purge</button>
</div>
</div>
</div>
</div>
<script>
// Save library options
$("#save_library").on('click', function () {
var custom_thumb = $("#custom_thumb_url").val();
var do_notify = 0;
var do_notify_created = 0;
var keep_history = 0;
if ($("#do_notify").is(":checked")) {
do_notify = 1;
}
if ($("#do_notify_created").is(":checked")) {
do_notify_created = 1;
}
if ($("#keep_history").is(":checked")) {
keep_history = 1;
}
$.ajax({
url: 'edit_library',
data: {
section_id: '${data["section_id"]}',
custom_thumb: custom_thumb,
do_notify: do_notify,
do_notify_created: do_notify_created,
keep_history: keep_history
},
cache: false,
async: true,
success: function (data) {
location.reload();
}
});
});
$("#delete-all-history").on('click', function() {
$('#confirm-modal-purge').modal();
$('#confirm-modal-purge').one('click', '#confirm-purge', function () {
$.ajax({
url: 'delete_all_library_history',
data: { section_id: '${data["section_id"]}' },
cache: false,
async: true,
success: function(data) {
location.reload();
}
});
});
});
$(document).ready(function() {
// Move #confirm-modal to parent container
if (!($('#edit-library-modal').next().is('#confirm-modal-purge'))) {
$('#confirm-modal-purge').appendTo($('#edit-library-modal').parent());
}
$('#edit-library-modal > #confirm-modal-purge').remove();
$('#edit-library-modal').css('z-index', '1050');
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1049');
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
$('#confirm-modal-purge').on('show.bs.modal', function () {
// Fix position to match parent modal
var currentPadding = parseInt($('body').css('padding-right'));
$(this).children('.modal-dialog').css('left', -currentPadding/2);
$('#edit-library-modal').css('overflow-y', 'hidden');
});
$('#confirm-modal-purge').on('shown.bs.modal', function () {
$(this).css('z-index', '1060');
$('.modal-backdrop').not('.modal-backdrop-stack').css('z-index', '1059');
$('.modal-backdrop').not('.modal-backdrop-stack').addClass('modal-backdrop-stack');
});
$('#confirm-modal-purge').on('hidden.bs.modal', function () {
$('body').addClass('modal-open');
$('#edit-library-modal').css('overflow-y', 'auto');
});
});
</script>
% endif

View file

@ -10,21 +10,30 @@ Variable names: data [list]
data :: Usable parameters
== Global keys ==
user Return the real Plex username
user_id Return the Plex user_id
friendly_name Returns the friendly edited Plex username
do_notify Returns bool value for whether the user should trigger notifications
keep_history Returns bool value for whether the user's activity should be logged
user_id Returns the user id of the user.
username Returns the user's username.
friendly_name Returns the friendly name of the user.
email Returns the user's email address.
user_thumb Returns the thumbnail for the user.
is_home_user Returns bool value for whether the user is part of a Plex Home.
is_allow_sync Returns bool value for whether the user has sync rights.
is_restricted Returns bool value for whether the user account is restricted.
do_notify Returns bool value for whether to send notifications for the user.
keep_history Returns bool value for whether to keep history for the user.
DOCUMENTATION :: END
</%doc>
% if data is not None:
<%!
from plexpy import helpers
%>
% if data != None:
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title">Edit user <strong>${data['user']}</strong></h4>
<h4 class="modal-title">Edit user <strong>${data['username']}</strong></h4>
</div>
<div class="modal-body" id="modal-text">
<fieldset>
@ -41,20 +50,20 @@ DOCUMENTATION :: END
<label for="profile_url">Profile Picture URL</label>
<div class="row">
<div class="col-md-8">
<input type="text" class="form-control" id="profile_url" name="profile_url" value="${data['thumb']}">
<input type="text" class="form-control" id="custom_avatar_url" name="custom_avatar_url" value="${data['user_thumb']}">
</div>
</div>
<p class="help-block">Change the users profile picture in PlexPy. To reset to default, leave this field empty and save then perform a user refresh.</p>
<p class="help-block">Change the users profile picture in PlexPy. To reset to default, leave this field empty and save.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="do_notify" name="do_notify" value="1" ${data['do_notify']}> Enable notifications
<input type="checkbox" id="do_notify" name="do_notify" value="1" ${helpers.checked(data['do_notify'])}> Enable notifications
</label>
<p class="help-block">Uncheck this if you do not want to receive notifications for this user's activity.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="keep_history" name="keep_history" value="1" ${data['keep_history']}> Keep history
<input type="checkbox" id="keep_history" name="keep_history" value="1" ${helpers.checked(data['keep_history'])}> Keep history
</label>
<p class="help-block">Uncheck this if you do not want this keep any history on this user's activity.</p>
</div>
@ -69,7 +78,7 @@ DOCUMENTATION :: END
<div class="modal-footer">
<div>
<span id="edit-user-status-message"></span>
<input type="button" id="save_user_name" class="btn btn-bright" value="Save">
<input type="button" id="save_user" class="btn btn-bright" value="Save">
</div>
</div>
</div>
@ -93,10 +102,10 @@ DOCUMENTATION :: END
</div>
</div>
<script>
// Set new friendly name
$("#save_user_name").click(function() {
// Set user options
$("#save_user").on('click', function () {
var friendly_name = $("#friendly_name").val();
var thumb = $("#profile_url").val();
var custom_thumb = $("#custom_avatar_url").val();
var do_notify = 0;
var keep_history = 0;
if ($("#do_notify").is(":checked")) {
@ -106,35 +115,21 @@ DOCUMENTATION :: END
keep_history = 1;
}
% if data['user_id']:
$.ajax({
url: 'edit_user',
data: {user_id: '${data['user_id']}', friendly_name: friendly_name, do_notify: do_notify, keep_history: keep_history, thumb: thumb},
data: {
user_id: '${data["user_id"]}',
friendly_name: friendly_name,
custom_thumb: custom_thumb,
do_notify: do_notify,
keep_history: keep_history
},
cache: false,
async: true,
success: function(data) {
$("#edit-user-status-message").html(data);
if ($.trim(friendly_name) !== '') {
$('.set-username').html(document.createTextNode(friendly_name));
}
$("#user-profile-thumb").attr('src', thumb);
location.reload();
}
});
% else:
$.ajax({
url: 'edit_user',
data: {user: '${data['user']}', friendly_name: friendly_name, do_notify: do_notify, keep_history: keep_history, thumb: thumb},
cache: false,
async: true,
success: function(data) {
$("#edit-user-status-message").html(data);
if ($.trim(friendly_name) !== '') {
$(".set-username").html(friendly_name);
}
$("#user-profile-thumb").attr('src', thumb);
}
});
% endif
});
$("#delete-all-history").on('click', function() {
@ -142,7 +137,7 @@ DOCUMENTATION :: END
$('#confirm-modal').one('click', '#confirm-purge', function () {
$.ajax({
url: 'delete_all_user_history',
data: {user_id: '${data['user_id']}'},
data: { user_id: '${data["user_id"]}' },
cache: false,
async: true,
success: function(data) {
@ -155,7 +150,8 @@ DOCUMENTATION :: END
$(document).ready(function() {
// Move #confirm-modal to parent container
if(!($('#edit-user-modal').next().is('#confirm-modal'))) {
$('#confirm-modal').appendTo($('#edit-user-modal').parent()); }
$('#confirm-modal').appendTo($('#edit-user-modal').parent());
}
$('#edit-user-modal > #confirm-modal').remove();
$('#edit-user-modal').css('z-index', '1050');
@ -179,5 +175,4 @@ DOCUMENTATION :: END
});
});
</script>
% endif

View file

@ -25,18 +25,18 @@
<table class="display" id="history_table" width="100%">
<thead>
<tr>
<th align='left' id="delete_row">Delete</th>
<th align='left' id="time">Time</th>
<th align='left' id="friendly_name">User</th>
<th align='left' id="ip_address">IP Address</th>
<th align='left' id="platform">Platform</th>
<th align='left' id="device">Player</th>
<th align='left' id="title">Title</th>
<th align='left' id="started">Started</th>
<th align='left' id="paused_counter">Paused</th>
<th align='left' id="stopped">Stopped</th>
<th align='left' id="duration">Duration</th>
<th align='left' id="percent_complete"></th>
<th align="left" id="delete_row">Delete</th>
<th align="left" id="time">Time</th>
<th align="left" id="friendly_name">User</th>
<th align="left" id="ip_address">IP Address</th>
<th align="left" id="platform">Platform</th>
<th align="left" id="device">Player</th>
<th align="left" id="title">Title</th>
<th align="left" id="started">Started</th>
<th align="left" id="paused_counter">Paused</th>
<th align="left" id="stopped">Stopped</th>
<th align="left" id="duration">Duration</th>
<th align="left" id="percent_complete"></th>
</tr>
</thead>
<tbody>
@ -83,8 +83,8 @@
type: 'post',
data: function (d) {
return {
'json_data': JSON.stringify(d),
'media_type': media_type
json_data: JSON.stringify(d),
media_type: media_type
};
}
}

View file

@ -13,11 +13,11 @@
<table class="display" id="history_table" width="100%">
<thead>
<tr>
<th align='left' id="started">Started</th>
<th align='left' id="stopped">Stopped</th>
<th align='left' id="friendly_name">User</th>
<th align='left' id="player">Player</th>
<th align='left' id="title">Title</th>
<th align="left" id="started">Started</th>
<th align="left" id="stopped">Stopped</th>
<th align="left" id="friendly_name">User</th>
<th align="left" id="player">Player</th>
<th align="left" id="title">Title</th>
</tr>
</thead>
<tbody>
@ -34,12 +34,13 @@
$(document).ready(function() {
$('#date-header').html(moment('${data}','YYYY-MM-DD').format('ddd MMM Do YYYY'));
history_table_modal_options.ajax = {
"url": "get_history",
type: "post",
url: 'get_history',
type: 'post',
data: function ( d ) {
return { 'json_data': JSON.stringify( d ),
'grouping': false,
'start_date': '${data}'
return {
json_data: JSON.stringify(d),
grouping: false,
start_date: '${data}'
};
}
}

View file

@ -70,7 +70,6 @@ DOCUMENTATION :: END
%>
% if data:
% if data[0]['stat_id']:
<ul class="list-unstyled">
% if any(top_stat['rows'] for top_stat in data):
% for top_stat in data:
@ -83,7 +82,7 @@ DOCUMENTATION :: END
</div>
<div class="home-platforms-instance-playcount">
<h4>
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
${top_stat['rows'][0]['title']}
</a>
</h4>
@ -95,7 +94,7 @@ DOCUMENTATION :: END
% endif
</div>
</div>
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
% if top_stat['rows'][0]['grandparent_thumb']:
<div class="home-platforms-instance-poster">
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
@ -117,7 +116,7 @@ DOCUMENTATION :: END
<div class="home-platforms-instance-list-info">
<div class="home-platforms-instance-list-name">
<h5>
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
${top_stat['rows'][loop.index]['title']}
</a>
</h5>
@ -131,7 +130,7 @@ DOCUMENTATION :: END
% endif
</div>
</div>
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
% if top_stat['rows'][loop.index]['grandparent_thumb']:
<div class="home-platforms-instance-list-poster">
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
@ -163,7 +162,7 @@ DOCUMENTATION :: END
</div>
<div class="home-platforms-instance-playcount">
<h4>
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
${top_stat['rows'][0]['title']}
</a>
</h4>
@ -171,7 +170,7 @@ DOCUMENTATION :: END
<p> users</p>
</div>
</div>
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
% if top_stat['rows'][0]['grandparent_thumb'] != '':
<div class="home-platforms-instance-poster">
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
@ -193,7 +192,7 @@ DOCUMENTATION :: END
<div class="home-platforms-instance-list-info">
<div class="home-platforms-instance-list-name">
<h5>
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
${top_stat['rows'][loop.index]['title']}
</a>
</h5>
@ -203,7 +202,7 @@ DOCUMENTATION :: END
<p> users</p>
</div>
</div>
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
% if top_stat['rows'][loop.index]['grandparent_thumb']:
<div class="home-platforms-instance-list-poster">
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=450&fallback=poster);"></div>
@ -235,7 +234,7 @@ DOCUMENTATION :: END
</div>
<div class="home-platforms-instance-playcount">
<h4>
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
${top_stat['rows'][0]['title']}
</a>
</h4>
@ -247,7 +246,7 @@ DOCUMENTATION :: END
% endif
</div>
</div>
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
% if top_stat['rows'][0]['thumb']:
<div class="home-platforms-instance-poster">
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['thumb']}&width=300&height=450&fallback=poster);"></div>
@ -269,7 +268,7 @@ DOCUMENTATION :: END
<div class="home-platforms-instance-list-info">
<div class="home-platforms-instance-list-name">
<h5>
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
${top_stat['rows'][loop.index]['title']}
</a>
</h5>
@ -283,7 +282,7 @@ DOCUMENTATION :: END
% endif
</div>
</div>
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
% if top_stat['rows'][loop.index]['thumb']:
<div class="home-platforms-instance-list-poster">
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['thumb']}&width=300&height=450&fallback=poster);"></div>
@ -315,7 +314,7 @@ DOCUMENTATION :: END
</div>
<div class="home-platforms-instance-playcount">
<h4>
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
${top_stat['rows'][0]['title']}
</a>
</h4>
@ -323,7 +322,7 @@ DOCUMENTATION :: END
<p> users</p>
</div>
</div>
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
% if top_stat['rows'][0]['thumb']:
<div class="home-platforms-instance-poster">
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['thumb']}&width=300&height=450&fallback=poster);"></div>
@ -345,7 +344,7 @@ DOCUMENTATION :: END
<div class="home-platforms-instance-list-info">
<div class="home-platforms-instance-list-name">
<h5>
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
${top_stat['rows'][loop.index]['title']}
</a>
</h5>
@ -355,7 +354,7 @@ DOCUMENTATION :: END
<p> users</p>
</div>
</div>
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
% if top_stat['rows'][loop.index]['thumb']:
<div class="home-platforms-instance-list-poster">
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['thumb']}&width=300&height=450&fallback=poster);"></div>
@ -387,7 +386,7 @@ DOCUMENTATION :: END
</div>
<div class="home-platforms-instance-playcount">
<h4>
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
${top_stat['rows'][0]['title']}
</a>
</h4>
@ -399,7 +398,7 @@ DOCUMENTATION :: END
% endif
</div>
</div>
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
% if top_stat['rows'][0]['grandparent_thumb']:
<div class="home-platforms-instance-poster">
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=300&fallback=poster);"></div>
@ -421,7 +420,7 @@ DOCUMENTATION :: END
<div class="home-platforms-instance-list-info">
<div class="home-platforms-instance-list-name">
<h5>
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
${top_stat['rows'][loop.index]['title']}
</a>
</h5>
@ -435,7 +434,7 @@ DOCUMENTATION :: END
% endif
</div>
</div>
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
% if top_stat['rows'][loop.index]['grandparent_thumb']:
<div class="home-platforms-instance-list-poster">
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=300&fallback=poster);"></div>
@ -467,7 +466,7 @@ DOCUMENTATION :: END
</div>
<div class="home-platforms-instance-playcount">
<h4>
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
${top_stat['rows'][0]['title']}
</a>
</h4>
@ -475,7 +474,7 @@ DOCUMENTATION :: END
<p> users</p>
</div>
</div>
<a href="info?item_id=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
<a href="info?rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
% if top_stat['rows'][0]['grandparent_thumb'] != '':
<div class="home-platforms-instance-poster">
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['grandparent_thumb']}&width=300&height=300&fallback=poster);"></div>
@ -497,7 +496,7 @@ DOCUMENTATION :: END
<div class="home-platforms-instance-list-info">
<div class="home-platforms-instance-list-name">
<h5>
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
${top_stat['rows'][loop.index]['title']}
</a>
</h5>
@ -507,7 +506,7 @@ DOCUMENTATION :: END
<p> users</p>
</div>
</div>
<a href="info?item_id=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
<a href="info?rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
% if top_stat['rows'][loop.index]['grandparent_thumb']:
<div class="home-platforms-instance-list-poster">
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['grandparent_thumb']}&width=300&height=300&fallback=poster);"></div>
@ -697,7 +696,7 @@ DOCUMENTATION :: END
</div>
<div class="home-platforms-instance-last-user">
<h4>
<a href="info?source=history&item_id=${top_stat['rows'][0]['row_id']}" title="${top_stat['rows'][0]['title']}">
<a href="info?source=history&rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
${top_stat['rows'][0]['title']}
</a>
</h4>
@ -719,7 +718,7 @@ DOCUMENTATION :: END
</p>
</div>
</div>
<a href="info?source=history&item_id=${top_stat['rows'][0]['row_id']}" title="${top_stat['rows'][0]['title']}">
<a href="info?source=history&rating_key=${top_stat['rows'][0]['rating_key']}" title="${top_stat['rows'][0]['title']}">
% if top_stat['rows'][0]['thumb']:
<div class="home-platforms-instance-poster">
<div class="home-platforms-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][0]['thumb']}&width=300&height=450&fallback=poster);"></div>
@ -741,7 +740,7 @@ DOCUMENTATION :: END
<div class="home-platforms-instance-list-info">
<div class="home-platforms-instance-list-name">
<h5>
<a href="info?source=history&item_id=${top_stat['rows'][loop.index]['row_id']}" title="${top_stat['rows'][loop.index]['title']}">
<a href="info?source=history&rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
${top_stat['rows'][loop.index]['title']}
</a>
</h5>
@ -765,7 +764,7 @@ DOCUMENTATION :: END
</p>
</div>
</div>
<a href="info?source=history&item_id=${top_stat['rows'][loop.index]['row_id']}" title="${top_stat['rows'][loop.index]['title']}">
<a href="info?source=history&rating_key=${top_stat['rows'][loop.index]['rating_key']}" title="${top_stat['rows'][loop.index]['title']}">
% if top_stat['rows'][loop.index]['thumb']:
<div class="home-platforms-instance-list-poster">
<div class="home-platforms-list-poster-face" style="background-image: url(pms_image_proxy?img=${top_stat['rows'][loop.index]['thumb']}&width=300&height=450&fallback=poster);"></div>
@ -873,7 +872,3 @@ DOCUMENTATION :: END
% else:
<div class="text-muted">No stats to show for the selected period.</div><br>
% endif
% else:
<div class="text-muted">Unable to retrieve data from database. Please check your <a href="settings">settings</a>.
</div><br>
% endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 819 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

View file

@ -16,7 +16,7 @@
</div>
</div>
</div>
% if config['home_stats_cards'] > 'watch_statistics':
% if config['home_stats_cards']:
<div class="row">
<div class="col-md-12">
<div class="padded-header">
@ -29,7 +29,7 @@
</div>
</div>
% endif
% if config['home_library_cards'] > 'library_statistics':
% if config['home_library_cards']:
<div class="row">
<div class="col-md-12">
<div class="padded-header" id="library-statistics-header">

View file

@ -18,8 +18,8 @@ duration Returns the standard runtime of the media.
content_rating Returns the age rating for the media.
summary Returns a brief description of the media plot.
grandparent_title Returns the name of the show, or artist.
parent_index Returns the index number of the season.
index Returns the index number of the episode, or track.
parent_media_index Returns the index number of the season.
media_index Returns the index number of the episode, or track.
parent_thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
writers Returns an array of writers.
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
@ -32,19 +32,6 @@ directors Returns an array of directors.
studio Returns the name of the studio.
originally_available_at Returns the air date of the item.
query :: Usable parameters
== Global keys ==
query_string Returns the string used for the search query.
title Returns the name of the movie, episode, or track.
parent_title Returns the name of the album.
grandparent_title Returns the name of the show, or artist.
media_index Returns the index number of the episode, or track.
parent_media_index Returns the index number of the season.
year Returns the release year of the movie, or show.
media_type Returns the type of media. Either 'movie', 'show', 'season', 'episode', 'artist', 'album', or 'track'.
rating_key Returns the unique identifier for the media item.
DOCUMENTATION :: END
</%doc>
@ -60,61 +47,50 @@ DOCUMENTATION :: END
% if data:
<div class="container-fluid">
<div class="row">
% if data['media_type'] != 'library':
<div class="art-face" style="background-image:url(pms_image_proxy?img=${data['art']}&width=1920&height=1080)"></div>
% endif
<div class="summary-container">
<div class="summary-navbar">
<div class="col-md-12">
<div class="summary-navbar-list">
<ul class="list-unstyled breadcrumb">
% if data['media_type'] == 'library':
% if data['library'] == 'movie':
<li class="active">Movies</li>
% elif data['library'] == 'show':
<li class="active">TV Shows</li>
% elif data['library'] == 'artist':
<li class="active">Music</li>
% endif
% elif data['media_type'] == 'movie':
<li><a href="info?item_id=movie">Movies</a></li>
% if data['media_type'] == 'movie':
<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':
<li><a href="info?item_id=show">TV Shows</a></li>
<li><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
<li class="active">${data['title']}</li>
% elif data['media_type'] == 'season':
<li class="hidden-xs hidden-sm"><a href="info?item_id=show">TV Shows</a></li>
<li><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></li>
<li class="active">Season ${data['index']}</li>
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
<li><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a></li>
<li class="active">Season ${data['media_index']}</li>
% elif data['media_type'] == 'episode':
<li class="hidden-xs hidden-sm"><a href="info?item_id=show">TV Shows</a></li>
<li class="hidden-xs hidden-sm"><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></li>
<li><a href="info?item_id=${data['parent_rating_key']}">Season ${data['parent_index']}</a></li>
<li class="active">Episode ${data['index']} - ${data['title']}</li>
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
<li class="hidden-xs hidden-sm"><a href="info?rating_key=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></li>
<li><a href="info?rating_key=${data['parent_rating_key']}">Season ${data['parent_media_index']}</a></li>
<li class="active">Episode ${data['media_index']} - ${data['title']}</li>
% elif data['media_type'] == 'artist':
<li><a href="info?item_id=artist">Music</a></li>
<li><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
<li class="active">${data['title']}</li>
% elif data['media_type'] == 'album':
<li class="hidden-xs hidden-sm"><a href="info?item_id=artist">Music</a></li>
<li><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></li>
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
<li><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a></li>
<li class="active">${data['title']}</li>
% elif data['media_type'] == 'track':
<li class="hidden-xs hidden-sm"><a href="info?item_id=artist">Music</a></li>
<li class="hidden-xs hidden-sm"><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></li>
<li><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></li>
<li class="active">Track ${data['index']} - ${data['title']}</li>
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['section_id']}">${data['library_name']}</a></li>
<li class="hidden-xs hidden-sm"><a href="info?rating_key=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></li>
<li><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a></li>
<li class="active">Track ${data['media_index']} - ${data['title']}</li>
% endif
</ul>
</div>
</div>
</div>
% if data['media_type'] != 'library':
<div class="summary-content-title-wrapper">
<div class="col-md-9">
<div class="summary-content-poster hidden-xs hidden-sm">
% if data['media_type'] == 'track':
<a href="http://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details/%2Flibrary%2Fmetadata%2F${data['parent_rating_key']}" target="Plex/Web" title="View in Plex/Web">
% elif data['media_type'] != 'library':
% else:
<a href="http://app.plex.tv/web/app#!/server/${config['pms_identifier']}/details/%2Flibrary%2Fmetadata%2F${data['rating_key']}" target="Plex/Web" title="View in Plex/Web">
% endif
% if data['media_type'] == 'episode':
@ -129,7 +105,7 @@ DOCUMENTATION :: END
<span></span>
</div>
</div>
% elif data['media_type'] != 'library':
% else:
<div class="summary-poster-face" style="background-image: url(pms_image_proxy?img=${data['thumb']}&width=300&height=450&fallback=poster);">
<div class="summary-poster-face-overlay">
<span></span>
@ -142,33 +118,71 @@ DOCUMENTATION :: END
% if data['media_type'] == 'movie' or data['media_type'] == 'show' or data['media_type'] == 'artist':
<h1>&nbsp;</h1><h1>${data['title']}</h1>
% elif data['media_type'] == 'season':
<h1>&nbsp;</h1><h1><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></h1>
<h3 class="hidden-xs">S${data['index']}</h3>
<h1>&nbsp;</h1><h1><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a></h1>
<h3 class="hidden-xs">S${data['media_index']}</h3>
% elif data['media_type'] == 'episode':
<h1><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></h1>
<h1><a href="info?rating_key=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></h1>
<h2>${data['title']}</h2>
<h3 class="hidden-xs">S${data['parent_index']} &middot; E${data['index']}</h3>
<h3 class="hidden-xs">S${data['parent_media_index']} &middot; E${data['media_index']}</h3>
% elif data['media_type'] == 'album':
<h1><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a></h1>
<h1><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a></h1>
<h2>${data['title']}</h2>
% elif data['media_type'] == 'track':
<h1><a href="info?item_id=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></h1>
<h2><a href="info?item_id=${data['parent_rating_key']}">${data['parent_title']}</a> - ${data['title']}</h2>
<h3 class="hidden-xs">T${data['index']}</h3>
<h1><a href="info?rating_key=${data['grandparent_rating_key']}">${data['grandparent_title']}</a></h1>
<h2><a href="info?rating_key=${data['parent_rating_key']}">${data['parent_title']}</a> - ${data['title']}</h2>
<h3 class="hidden-xs">T${data['media_index']}</h3>
% endif
</div>
</div>
</div>
% endif
<div class="summary-content-wrapper">
% if data['media_type'] != 'library':
<div class="col-md-9">
% if data['media_type'] == 'movie' or data['media_type'] == 'show' or data['media_type'] == 'season':
% if data['media_type'] == 'movie':
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 310px;">
<div class="summary-content-media-info-wrapper">
% if data['video_resolution']:
<img class="summary-content-media-info" src="interfaces/default/images/media_info/${data['video_resolution']}.png" />
% endif
% if data['audio_codec']:
<img class="summary-content-media-info" src="interfaces/default/images/media_info/${data['audio_codec']}.png" />
% endif
% if data['audio_channels']:
<img class="summary-content-media-info" src="interfaces/default/images/media_info/${data['audio_channels']}ch.png" />
% endif
</div>
</div>
% elif data['media_type'] == 'show' or data['media_type'] == 'season':
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 275px;"></div>
% elif data['media_type'] == 'episode':
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 40px;"></div>
% elif data['media_type'] == 'artist' or data['media_type'] == 'album' or data['media_type'] == 'track':
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 75px;">
<div class="summary-content-media-info-wrapper">
% if data['video_resolution']:
<img class="summary-content-media-info" src="interfaces/default/images/media_info/${data['video_resolution']}.png" />
% endif
% if data['audio_codec']:
<img class="summary-content-media-info" src="interfaces/default/images/media_info/${data['audio_codec']}.png" />
% endif
% if data['audio_channels']:
<img class="summary-content-media-info" src="interfaces/default/images/media_info/${data['audio_channels']}ch.png" />
% endif
</div>
</div>
% elif data['media_type'] == 'artist' or data['media_type'] == 'album':
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 150px;"></div>
% elif data['media_type'] == 'track':
<div class="summary-content-padding hidden-xs hidden-sm" style="height: 185px;">
<div class="summary-content-media-info-wrapper">
% if data['video_resolution']:
<img class="summary-content-media-info" src="interfaces/default/images/media_info/${data['video_resolution']}.png" />
% endif
% if data['audio_codec']:
<img class="summary-content-media-info" src="interfaces/default/images/media_info/${data['audio_codec']}.png" />
% endif
% if data['audio_channels']:
<img class="summary-content-media-info" src="interfaces/default/images/media_info/${data['audio_channels']}ch.png" />
% endif
</div>
</div>
% else:
<div class="summary-content-padding hidden-xs hidden-sm"></div>
% endif
@ -313,7 +327,6 @@ DOCUMENTATION :: END
</div>
</div>
% endif
% endif
<div class='col-md-12'>
<div class='table-card-header'>
<div class="header-bar">
@ -375,117 +388,6 @@ DOCUMENTATION :: END
</div>
</div>
</div>
% else:
<div class="container-fluid">
<div class="row">
<div class="summary-container">
<div class="summary-navbar">
<div class="col-md-12">
<div class="summary-navbar-list">
<ul class="list-unstyled breadcrumb">
% if query:
% if query['media_type'] == 'movie':
<li><a href="info?item_id=movie">Movies</a></li>
<li class="active">${query['title']}</li>
% elif query['media_type'] == 'show':
<li><a href="info?item_id=show">TV Shows</a></li>
<li class="active">${query['grandparent_title']}</li>
% elif query['media_type'] == 'season':
<li class="hidden-xs hidden-sm"><a href="info?item_id=show">TV Shows</a></li>
<li class="hidden-xs hidden-sm">${query['grandparent_title']}</li>
<li class="active">Season ${query['parent_media_index']}</li>
% elif query['media_type'] == 'episode':
<li class="hidden-xs hidden-sm"><a href="info?item_id=show">TV Shows</a></li>
<li class="hidden-xs hidden-sm">${query['grandparent_title']}</li>
<li>Season ${query['parent_media_index']}</li>
<li class="active">Episode ${query['media_index']} - ${query['title']}</li>
% elif query['media_type'] == 'artist':
<li><a href="info?item_id=artist">Music</a></li>
<li class="active">${query['grandparent_title']}</li>
% elif query['media_type'] == 'album':
<li class="hidden-xs hidden-sm"><a href="info?item_id=artist">Music</a></li>
<li>${query['grandparent_title']}</li>
<li class="active">${query['parent_title']}</li>
% elif query['media_type'] == 'track':
<li class="hidden-xs hidden-sm"><a href="info?item_id=artist">Music</a></li>
<li class="hidden-xs hidden-sm">${query['grandparent_title']}</li>
<li>${query['parent_title']}</li>
<li class="active">Track ${query['media_index']} - ${query['title']}</li>
% endif
% endif
</ul>
</div>
</div>
</div>
<div class="summary-content-title-wrapper">
<div class="col-md-12">
<h4 style="text-align: center; margin-bottom: 20px;">
Error retrieving item metadata. This media item is not available in the Plex Media Server library.
</h4>
% if query:
<h4 style="text-align: center; margin-bottom: 20px;">
If the item has been moved, please select the correct match below to update the PlexPy database.
</h4>
% endif
</div>
</div>
<div class="summary-content-wrapper">
<div class='col-md-12'>
% if query:
<div class='table-card-header'>
<div class="header-bar">
<span>Search Results for <strong>${query['query_string']}</strong></span>
</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>
<div class="modal fade" id="confirm-modal" tabindex="-1" role="dialog" aria-labelledby="confirm-modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title" id="myModalLabel">Confirm Update</h4>
</div>
<div class="modal-body" style="text-align: center;">
<p>Are you REALLY sure you want to replace
<p><strong>
% if query['media_type'] == 'movie':
${query['title']}<br />${query['year']}
% elif query['media_type'] == 'show':
${query['grandparent_title']}
% elif query['media_type'] == 'season':
${query['grandparent_title']}<br />S${query['parent_media_index']}
% elif query['media_type'] == 'episode':
${query['grandparent_title']}<br />${query['title']}<br />S${query['parent_media_index']} &middot; E${query['media_index']}
% elif query['media_type'] == 'artist':
${query['grandparent_title']}
% elif query['media_type'] == 'album':
${query['grandparent_title']}<br />${query['parent_title']}
% elif query['media_type'] == 'track':
${query['grandparent_title']}<br />${query['title']}<br />${query['parent_title']}
% endif
</strong></p>
<p> with </p>
<p><span id="new_title"></span></p>
% if query['media_type'] != 'movie':
<p>All items for <strong>${query['grandparent_title']}</strong> will also be updated.</p>
% endif
<p>This is permanent and cannot be undone!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-update">Update</button>
</div>
</div>
</div>
</div>
% endif
</div>
</div>
</div>
</div>
</div>
% endif
</%def>
@ -499,28 +401,18 @@ DOCUMENTATION :: END
% if data:
<script src="interfaces/default/js/tables/history_table.js"></script>
% if data['media_type'] == 'library':
<!-- Need to find a place to put this -->
% if data['media_type'] == 'show' or data['media_type'] == 'artist':
<script>
function get_history() {
history_table_options.ajax = {
url: 'get_history',
type: 'post',
data: function ( d ) {
return { 'json_data': JSON.stringify( d ),
'media_type': "${data['media_type_filter']}" };
}
}
}
</script>
% elif data['media_type'] == 'show' or data['media_type'] == 'artist':
<script>
function get_history() {
history_table_options.ajax = {
url: 'get_history',
type: 'post',
data: function ( d ) {
return { 'json_data': JSON.stringify( d ),
'grandparent_rating_key': "${data['rating_key']}" };
return {
json_data: JSON.stringify( d ),
grandparent_rating_key: "${data['rating_key']}"
};
}
}
}
@ -532,8 +424,10 @@ DOCUMENTATION :: END
url: 'get_history',
type: 'post',
data: function ( d ) {
return { 'json_data': JSON.stringify( d ),
'parent_rating_key': "${data['rating_key']}" };
return {
json_data: JSON.stringify( d ),
parent_rating_key: "${data['rating_key']}"
};
}
}
}
@ -545,8 +439,10 @@ DOCUMENTATION :: END
url: 'get_history',
type: 'post',
data: function ( d ) {
return { 'json_data': JSON.stringify( d ),
'rating_key': "${data['rating_key']}" };
return {
json_data: JSON.stringify( d ),
rating_key: "${data['rating_key']}"
};
}
}
}
@ -611,16 +507,7 @@ DOCUMENTATION :: END
});
</script>
% endif
% if data['media_type'] != 'library':
<!--
<script>
$('#row-edit-mode').after('<a href="info?item_id=${data['rating_key']}" class="btn btn-danger btn-edit" id="fix-metadata" \
data-toggle="tooltip" data-placement="left" title="Fix metadata if the item was moved in Plex"><i class="fa fa-wrench"></i> Fix Metadata</a>');
$('#fix-metadata').tooltip();
</script>
-->
% endif
% if data['media_type'] != 'library' and data['rating']:
% if data['rating']:
<script>
// Convert rating to 5 star rating type
var starRating = Math.round(${data['rating']} / 2);
@ -628,49 +515,11 @@ DOCUMENTATION :: END
</script>
% endif
<script>
$('#row-edit-mode').after('<a href="update_metadata?rating_key=${data['rating_key']}" class="btn btn-danger btn-edit" id="fix-metadata"> \
<i class="fa fa-wrench"></i> Fix Metadata</a>');
$("#airdate").html(moment($("#airdate").text()).format('MMM DD, YYYY'));
$("#runtime").html(millisecondsToMinutes($("#runtime").text(), true));
$('div.art-face').animate({ opacity: 0.2 }, { duration: 1000 });
</script>
% elif query:
<script>
$.ajax({
url: 'get_search_results_children',
type: 'GET',
async: true,
data: {'query': "${query['query_string'].replace('"','\\"')}",
'media_type': "${query['media_type']}",
'season_index': "${query['parent_media_index']}"
},
complete: function(xhr, status) {
$("#search-results-list").html(xhr.responseText); }
});
$(document).on('click', '#search-results-list a', function (e) {
e.preventDefault();
var new_rating_key = $(this).attr("id");
var new_href = $(this).attr("href");
$('#new_title').html($(this).find('.item-children-instance-text-wrapper').html());
$('#confirm-modal').modal();
$('#confirm-modal').one('click', '#confirm-update', function () {
$(this).prop('disabled', true);
var msg = "<i class='fa fa-refresh fa-spin'></i>&nbspUpdating database..."
showMsg(msg, false, false, 0)
$.ajax({
url: 'update_history_rating_key',
data: { old_rating_key: "${query['rating_key']}",
new_rating_key: new_rating_key,
media_type: "${query['media_type']}"
},
async: true,
success: function (data) {
window.location.href = new_href;
}
});
});
});
</script>
% endif
</%def>

View file

@ -18,7 +18,7 @@ data['children_list'] :: Usable paramaters
== Global keys ==
rating_key Returns the unique identifier for the media item.
index Returns the episode number.
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.
@ -37,7 +37,7 @@ DOCUMENTATION :: END
% else:
<li>
% endif
<a href="info?item_id=${child['rating_key']}">
<a href="info?rating_key=${child['rating_key']}">
%if data['children_type'] == 'season':
<div class="item-children-poster">
% if child['thumb']:
@ -47,7 +47,7 @@ DOCUMENTATION :: END
% endif
<div class="item-children-card-overlay">
<div class="item-children-overlay-text">
Season ${child['index']}
Season ${child['media_index']}
</div>
</div>
</div>
@ -57,7 +57,7 @@ DOCUMENTATION :: END
<div class="item-children-poster-face episode-poster" style="background-image: url(pms_image_proxy?img=${child['thumb']}&width=300&height=450);">
<div class="item-children-card-overlay">
<div class="item-children-overlay-text">
Episode ${child['index']}
Episode ${child['media_index']}
</div>
</div>
</div>
@ -75,7 +75,7 @@ DOCUMENTATION :: END
% elif data['children_type'] == 'track':
% if loop.index % 2 == 0:
<div class="item-children-list-item-even">
<span class="item-children-list-item-index">${child['index']}</span>
<span class="item-children-list-item-index">${child['media_index']}</span>
<span class="item-children-list-item-title">${child['title']}</span>
<span class="item-children-list-item-duration" id="item-children-list-item-duration-${loop.index + 1}">
<script>$('#item-children-list-item-duration-${loop.index + 1}').text(moment.utc(${child['duration']}).format("m:ss"));</script>
@ -83,7 +83,7 @@ DOCUMENTATION :: END
</div>
% else:
<div class="item-children-list-item-odd">
<span class="item-children-list-item-index">${child['index']}</span>
<span class="item-children-list-item-index">${child['media_index']}</span>
<span class="item-children-list-item-title">${child['title']}</span>
<span class="item-children-list-item-duration" id="item-children-list-item-duration-${loop.index + 1}">
<script>$('#item-children-list-item-duration-${loop.index + 1}').text(moment.utc(${child['duration']}).format("m:ss"));</script>

View file

@ -35,8 +35,8 @@ duration Returns the standard runtime of the media.
content_rating Returns the age rating for the media.
summary Returns a brief description of the media plot.
grandparent_title Returns the name of the show, or artist.
parent_index Returns the index number of the season.
index Returns the index number of the episode, or track.
parent_media_index Returns the index number of the season.
media_index Returns the index number of the episode, or track.
parent_thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
writers Returns an array of writers.
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
@ -62,7 +62,7 @@ DOCUMENTATION :: END
<ul class="item-children-instance list-unstyled">
% for child in data['results_list']['movie']:
<li>
<a href="info?item_id=${child['rating_key']}" id="${child['rating_key']}">
<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);"></div>
</div>
@ -84,7 +84,7 @@ DOCUMENTATION :: END
<ul class="item-children-instance list-unstyled">
% for child in data['results_list']['show']:
<li>
<a href="info?item_id=${child['rating_key']}" id="${child['rating_key']}">
<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);"></div>
</div>
@ -106,13 +106,13 @@ DOCUMENTATION :: END
<ul class="item-children-instance list-unstyled">
% for child in data['results_list']['season']:
<li>
<a href="info?item_id=${child['rating_key']}" id="${child['rating_key']}">
<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);"></div>
</div>
<div class="item-children-instance-text-wrapper season-item">
<h3 title="${child['parent_title']}">${child['parent_title']}</h3>
<h3 class="text-muted">S${child['index']}</h3>
<h3 class="text-muted">S${child['media_index']}</h3>
</div>
</a>
</li>
@ -128,14 +128,14 @@ DOCUMENTATION :: END
<ul class="item-children-instance list-unstyled">
% for child in data['results_list']['episode']:
<li>
<a href="info?item_id=${child['rating_key']}" id="${child['rating_key']}">
<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=300&height=450);"></div>
</div>
<div class="item-children-instance-text-wrapper episode-item">
<h3 title="${child['grandparent_title']}">${child['grandparent_title']}</h3>
<h3 title="${child['title']}">${child['title']}</h3>
<h3 class="text-muted">S${child['parent_index']} &middot; E${child['index']}</h3>
<h3 class="text-muted">S${child['parent_media_index']} &middot; E${child['media_index']}</h3>
</div>
</a>
</li>
@ -151,7 +151,7 @@ DOCUMENTATION :: END
<ul class="item-children-instance list-unstyled">
% for child in data['results_list']['artist']:
<li>
<a href="info?item_id=${child['rating_key']}" id="${child['rating_key']}">
<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);"></div>
</div>
@ -172,7 +172,7 @@ DOCUMENTATION :: END
<ul class="item-children-instance list-unstyled">
% for child in data['results_list']['album']:
<li>
<a href="info?item_id=${child['rating_key']}" id="${child['rating_key']}">
<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);"></div>
</div>
@ -194,12 +194,12 @@ DOCUMENTATION :: END
<ul class="item-children-instance list-unstyled">
% for child in data['results_list']['track']:
<li>
<a href="info?item_id=${child['rating_key']}" id="${child['rating_key']}">
<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);">
<div class="item-children-card-overlay">
<div class="item-children-overlay-text">
Track ${child['index']}
Track ${child['media_index']}
</div>
</div>
</div>

File diff suppressed because one or more lines are too long

View file

@ -56,7 +56,7 @@ history_table_options = {
}
},
"searchable": false,
"width": "8%",
"width": "7%",
"className": "no-wrap expand-history"
},
{
@ -73,7 +73,7 @@ history_table_options = {
$(td).html(cellData);
}
},
"width": "8%",
"width": "9%",
"className": "no-wrap hidden-xs"
},
{
@ -106,7 +106,7 @@ history_table_options = {
$(td).html(cellData);
}
},
"width": "8%",
"width": "10%",
"className": "no-wrap hidden-md hidden-sm hidden-xs modal-control"
},
{
@ -137,23 +137,23 @@ history_table_options = {
var thumb_popover = '';
if (rowData['media_type'] === 'movie') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120">' + cellData + ' (' + rowData['year'] + ')</span>'
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + ' (' + rowData['year'] + ')</span>'
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'episode') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120">' + cellData + ' \
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + ' \
(S' + rowData['parent_media_index'] + '&middot; E' + rowData['media_index'] + ')</span>'
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;" >' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;" >' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'track') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=poster" data-height="80">' + cellData + ' (' + rowData['parent_title'] + ')</span>'
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=poster" data-height="80" data-width="80">' + cellData + ' (' + rowData['parent_title'] + ')</span>'
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else {
$(td).html('<a href="info?item_id=' + rowData['id'] + '">' + cellData + '</a>');
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
}
}
},
"width": "35%"
"width": "33%"
},
{
"targets": [7],
@ -226,7 +226,7 @@ history_table_options = {
"searchable": false,
"orderable": false,
"className": "no-wrap hidden-md hidden-sm hidden-xs",
"width": "1%"
"width": "2%"
},
],
"drawCallback": function (settings) {
@ -245,8 +245,9 @@ history_table_options = {
container: 'body',
trigger: 'hover',
placement: 'right',
template: '<div class="popover history-thumbnail-popover" role="tooltip"><div class="arrow" style="top: 50%;"></div><div class="popover-content"></div></div>',
content: function () {
return '<div class="history-thumbnail" style="background-image: url(' + $(this).data('img') + '); height: ' + $(this).data('height') + 'px;" />';
return '<div class="history-thumbnail" style="background-image: url(' + $(this).data('img') + '); height: ' + $(this).data('height') + 'px; width: ' + $(this).data('width') + 'px;" />';
}
});
@ -260,7 +261,7 @@ history_table_options = {
var rowData = this.data();
if (rowData['group_count'] != 1 && rowData['reference_id'] in history_child_table) {
// if grouped row and a child table was already created
$(this.node()).find('i.fa').toggleClass('fa-plus-circle').toggleClass('fa-minus-circle');
$(this.node()).find('i.fa.fa-plus-circle').toggleClass('fa-plus-circle').toggleClass('fa-minus-circle');
this.child(childTableFormat(rowData)).show();
createChildTable(this, rowData)
}
@ -426,13 +427,13 @@ function childTableOptions(rowData) {
history_child_options.pageLength = 10;
history_child_options.bStateSave = false;
history_child_options.ajax = {
"url": "get_history",
type: "post",
url: 'get_history',
type: 'post',
data: function (d) {
return {
'json_data': JSON.stringify(d),
'grouping': false,
'reference_id': rowData['reference_id']
json_data: JSON.stringify(d),
grouping: false,
reference_id: rowData['reference_id']
};
}
}
@ -447,8 +448,10 @@ function childTableOptions(rowData) {
$('.watched-tooltip').tooltip();
$('.thumb-tooltip').popover({
html: true,
container: 'body',
trigger: 'hover',
placement: 'right',
template: '<div class="popover history-thumbnail-popover" role="tooltip"><div class="arrow" style="top: 50%;"></div><div class="popover-content"></div></div>',
content: function () {
return '<div class="history-thumbnail" style="background-image: url(' + $(this).data('img') + '); height: ' + $(this).data('height') + 'px;" />';
}
@ -469,7 +472,7 @@ function childTableOptions(rowData) {
// Format the detailed history child table
function childTableFormat(rowData) {
return '<div class="slider">' +
'<table id="history_child-' + rowData['reference_id'] + '">' +
'<table id="history_child-' + rowData['reference_id'] + '" width="100%">' +
'<thead>' +
'<tr>' +
'<th align="left" id="delete_row">Delete</th>' +

View file

@ -25,6 +25,7 @@ history_table_modal_options = {
"serverSide": true,
"pageLength": 10,
"lengthChange": false,
"autoWidth": false,
"order": [ 0, 'desc'],
"columnDefs": [
{
@ -37,9 +38,9 @@ history_table_modal_options = {
$(td).html(moment(cellData,"X").format(time_format));
}
},
"searchable": false,
"width": "10%",
"className": "no-wrap",
"width": "5%"
"searchable": false
},
{
"targets": [1],
@ -51,9 +52,9 @@ history_table_modal_options = {
$(td).html(moment(cellData,"X").format(time_format));
}
},
"searchable": false,
"width": "10%",
"className": "no-wrap",
"width": "5%"
"searchable": false
},
{
"targets": [2],
@ -69,6 +70,7 @@ history_table_modal_options = {
$(td).html(cellData);
}
},
"width": "15%",
"className": "no-wrap hidden-xs"
},
{
@ -87,6 +89,7 @@ history_table_modal_options = {
$(td).html('<div><a href="#" data-target="#info-modal" data-toggle="modal"><div style="float: left;">' + transcode_dec + '&nbsp' + cellData + '</div></a></div>');
}
},
"width": "25%",
"className": "no-wrap hidden-sm hidden-xs modal-control"
},
{
@ -98,22 +101,23 @@ history_table_modal_options = {
var thumb_popover = '';
if (rowData['media_type'] === 'movie') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120">' + cellData + ' (' + rowData['year'] + ')</span>'
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + ' (' + rowData['year'] + ')</span>'
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'episode') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120">' + cellData + ' \
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + ' \
(S' + rowData['parent_media_index'] + '&middot; E' + rowData['media_index'] + ')</span>'
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;" >' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;" >' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'track') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=poster" data-height="80">' + cellData + ' (' + rowData['parent_title'] + ')</span>'
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=poster" data-height="80" data-width="80">' + cellData + ' (' + rowData['parent_title'] + ')</span>'
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
} else {
$(td).html('<a href="info?item_id=' + rowData['id'] + '">' + cellData + '</a>');
}
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
}
}
},
"width": "40%"
}
],
"drawCallback": function (settings) {
@ -129,8 +133,9 @@ history_table_modal_options = {
container: '#history-modal',
trigger: 'hover',
placement: 'right',
template: '<div class="popover history-thumbnail-popover" role="tooltip"><div class="arrow" style="top: 50%;"></div><div class="popover-content"></div></div>',
content: function () {
return '<div class="history-thumbnail" style="background-image: url(' + $(this).data('img') + '); height: ' + $(this).data('height') + 'px;" />';
return '<div class="history-thumbnail" style="background-image: url(' + $(this).data('img') + '); height: ' + $(this).data('height') + 'px; width: ' + $(this).data('width') + 'px;" />';
}
});
},

View file

@ -0,0 +1,293 @@
var libraries_to_delete = [];
var libraries_to_purge = [];
libraries_list_table_options = {
"language": {
"search": "Search: ",
"lengthMenu":"Show _MENU_ entries per page",
"info":"Showing _START_ to _END_ of _TOTAL_ active libraries",
"infoEmpty":"Showing 0 to 0 of 0 entries",
"infoFiltered":"",
"emptyTable": "No data in table",
},
"destroy": true,
"processing": false,
"serverSide": true,
"pageLength": 10,
"order": [ 2, 'asc'],
"autoWidth": true,
"stateSave": true,
"pagingType": "bootstrap",
"columnDefs": [
{
"targets": [0],
"data": null,
"createdCell": function (td, cellData, rowData, row, col) {
$(td).html('<div class="edit-library-toggles">' +
'<button class="btn btn-xs btn-warning delete-library" data-id="' + rowData['section_id'] + '" data-toggle="button"><i class="fa fa-trash-o fa-fw"></i> Delete</button>&nbsp' +
'<button class="btn btn-xs btn-warning purge-library" data-id="' + rowData['section_id'] + '" data-toggle="button"><i class="fa fa-eraser fa-fw"></i> Purge</button>&nbsp&nbsp&nbsp' +
'<input type="checkbox" id="do_notify-' + rowData['section_id'] + '" name="do_notify" value="1" ' + rowData['do_notify'] + '><label class="edit-tooltip" for="do_notify-' + rowData['section_id'] + '" data-toggle="tooltip" title="Toggle Notifications"><i class="fa fa-bell fa-lg fa-fw"></i></label>&nbsp' +
'<input type="checkbox" id="keep_history-' + rowData['section_id'] + '" name="keep_history" value="1" ' + rowData['keep_history'] + '><label class="edit-tooltip" for="keep_history-' + rowData['section_id'] + '" data-toggle="tooltip" title="Toggle History"><i class="fa fa-history fa-lg fa-fw"></i></label>&nbsp' +
'<input type="checkbox" id="do_notify_created-' + rowData['section_id'] + '" name="do_notify_created" value="1" ' + rowData['do_notify_created'] + '><label class="edit-tooltip" for="do_notify_created-' + rowData['section_id'] + '" data-toggle="tooltip" title="Toggle Recently Added"><i class="fa fa-download fa-lg fa-fw"></i></label>&nbsp' +
'</div>');
},
"width": "7%",
"className": "edit-control no-wrap hidden",
"searchable": false,
"orderable": false
},
{
"targets": [1],
"data": "library_thumb",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
if (rowData['library_thumb'].substring(0, 4) == "http") {
$(td).html('<a href="library?section_id=' + rowData['section_id'] + '"><div class="libraries-poster-face" style="background-image: url(' + rowData['library_thumb'] + ');"></div></a>');
} else {
$(td).html('<a href="library?section_id=' + rowData['section_id'] + '"><div class="libraries-poster-face" style="background-image: url(pms_image_proxy?img=' + rowData['library_thumb'] + '&width=80&height=80&fallback=poster);"></div></a>');
}
} else {
$(td).html('<a href="library?section_id=' + rowData['section_id'] + '"><div class="libraries-poster-face" style="background-image: url(interfaces/default/images/cover.png);"></div></a>');
}
},
"orderable": false,
"searchable": false,
"width": "5%",
"className": "libraries-thumbs"
},
{
"targets": [2],
"data": "section_name",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html('<div data-id="' + rowData['section_id'] + '">' +
'<a href="library?section_id=' + rowData['section_id'] + '">' + cellData + '</a>' +
'</div>');
} else {
$(td).html('n/a');
}
},
"width": "10%",
"className": "no-wrap"
},
{
"targets": [3],
"data": "section_type",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "10%",
"className": "no-wrap hidden-xs"
},
{
"targets": [4],
"data": "count",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "10%",
"className": "no-wrap hidden-xs"
},
{
"targets": [5],
"data": "parent_count",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "10%",
"className": "no-wrap hidden-xs"
},
{
"targets": [6],
"data": "child_count",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "10%",
"className": "no-wrap hidden-xs"
},
{
"targets": [7],
"data": "last_accessed",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(moment(cellData, "X").fromNow());
} else {
$(td).html("never");
}
},
"searchable": false,
"width": "10%",
"className": "no-wrap hidden-xs"
},
{
"targets": [8],
"data":"last_watched",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
var media_type = '';
var thumb_popover = ''
if (rowData['media_type'] === 'movie') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=120&fallback=poster" data-height="120" data-width="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'episode') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=120&fallback=poster" data-height="120" data-width="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;" >' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'track') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=80&fallback=poster" data-height="80" data-width="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type']) {
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
}
} else {
$(td).html('n/a');
}
},
"width": "25%",
"className": "hidden-sm hidden-xs"
},
{
"targets": [9],
"data": "plays",
"searchable": false,
"width": "10%"
}
],
"drawCallback": function (settings) {
// Jump to top of page
//$('html,body').scrollTop(0);
$('#ajaxMsg').fadeOut();
// Create the tooltips.
$('.purge-tooltip').tooltip();
$('.edit-tooltip').tooltip();
$('.transcode-tooltip').tooltip();
$('.media-type-tooltip').tooltip();
$('.thumb-tooltip').popover({
html: true,
container: 'body',
trigger: 'hover',
placement: 'right',
template: '<div class="popover history-thumbnail-popover" role="tooltip"><div class="arrow" style="top: 50%;"></div><div class="popover-content"></div></div>',
content: function () {
return '<div class="history-thumbnail" style="background-image: url(' + $(this).data('img') + '); height: ' + $(this).data('height') + 'px; width: ' + $(this).data('width') + 'px;" />';
}
});
if ($('#row-edit-mode').hasClass('active')) {
$('.edit-control').each(function () {
$(this).removeClass('hidden');
});
}
},
"preDrawCallback": function(settings) {
var msg = "<i class='fa fa-refresh fa-spin'></i>&nbspFetching rows...";
showMsg(msg, false, false, 0)
},
"rowCallback": function (row, rowData) {
if ($.inArray(rowData['section_id'], libraries_to_delete) !== -1) {
$(row).find('button.delete-library[data-id="' + rowData['section_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
}
if ($.inArray(rowData['section_id'], libraries_to_purge) !== -1) {
$(row).find('button.purge-library[data-id="' + rowData['section_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
}
}
}
$('#libraries_list_table').on('change', 'td.edit-control > .edit-library-toggles > input', function () {
var tr = $(this).parents('tr');
var row = libraries_list_table.row(tr);
var rowData = row.data();
var do_notify = 0;
var do_notify_created = 0;
var keep_history = 0;
if ($('#do_notify-' + rowData['section_id']).is(':checked')) {
do_notify = 1;
}
if ($('#do_notify_created-' + rowData['section_id']).is(':checked')) {
do_notify_created = 1;
}
if ($('#keep_history-' + rowData['section_id']).is(':checked')) {
keep_history = 1;
}
if (rowData['custom_thumb']) {
custom_thumb = rowData['custom_thumb']
} else {
custom_thumb = rowData['library_thumb']
}
$.ajax({
url: 'edit_library',
data: {
section_id: rowData['section_id'],
do_notify: do_notify,
do_notify_created: do_notify_created,
keep_history: keep_history,
custom_thumb: custom_thumb
},
cache: false,
async: true,
success: function (data) {
var msg = "Library updated";
showMsg(msg, false, true, 2000);
}
});
});
$('#libraries_list_table').on('click', 'td.edit-control > .edit-library-toggles > button.delete-library', function () {
var tr = $(this).parents('tr');
var row = libraries_list_table.row(tr);
var rowData = row.data();
var index_delete = $.inArray(rowData['section_id'], libraries_to_delete);
var index_purge = $.inArray(rowData['section_id'], libraries_to_purge);
if (index_delete === -1) {
libraries_to_delete.push(rowData['section_id']);
if (index_purge === -1) {
tr.find('button.purge-library').click();
}
} else {
libraries_to_delete.splice(index_delete, 1);
if (index_purge != -1) {
tr.find('button.purge-library').click();
}
}
$(this).toggleClass('btn-warning').toggleClass('btn-danger');
});
$('#libraries_list_table').on('click', 'td.edit-control > .edit-library-toggles > button.purge-library', function () {
var tr = $(this).parents('tr');
var row = libraries_list_table.row(tr);
var rowData = row.data();
var index_delete = $.inArray(rowData['section_id'], libraries_to_delete);
var index_purge = $.inArray(rowData['section_id'], libraries_to_purge);
if (index_purge === -1) {
libraries_to_purge.push(rowData['section_id']);
} else {
libraries_to_purge.splice(index_purge, 1);
if (index_delete != -1) {
tr.find('button.delete-library').click();
}
}
$(this).toggleClass('btn-warning').toggleClass('btn-danger');
});

View file

@ -0,0 +1,479 @@
var date_format = 'YYYY-MM-DD';
var time_format = 'hh:mm a';
$.ajax({
url: 'get_date_formats',
type: 'GET',
success: function(data) {
date_format = data.date_format;
time_format = data.time_format;
}
});
var refresh_child_tables = false;
media_info_table_options = {
"destroy": true,
"language": {
"search": "Search: ",
"lengthMenu":"Show _MENU_ entries per page",
"info":"Showing _START_ to _END_ of _TOTAL_ library items",
"infoEmpty":"Showing 0 to 0 of 0 entries",
"infoFiltered":"<span class='hidden-md hidden-sm hidden-xs'>(filtered from _MAX_ total entries)</span>",
"emptyTable": "No data in table"
},
"pagingType": "bootstrap",
"stateSave": false,
"processing": false,
"serverSide": true,
"pageLength": 25,
"order": [ 1, 'asc'],
"autoWidth": false,
"columnDefs": [
{
"targets": [0],
"data": "added_at",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
var expand_details = '';
var date = moment(cellData, "X").format(date_format);
if (rowData['media_type'] === 'show') {
expand_details = '<span class="expand-media-info-tooltip" data-toggle="tooltip" title="Show Seasons"><i class="fa fa-plus-circle fa-fw"></i></span>';
$(td).html('<div><a href="#"><div style="float: left;">' + expand_details + '&nbsp;' + date + '</div></a></div>');
} else if (rowData['media_type'] === 'season') {
expand_details = '<span class="expand-media-info-tooltip" data-toggle="tooltip" title="Show Episodes"><i class="fa fa-plus-circle fa-fw"></i></span>';
$(td).html('<div><a href="#"><div style="float: left;">' + expand_details + '&nbsp;' + date + '</div></a></div>');
} else if (rowData['media_type'] === 'artist') {
expand_details = '<span class="expand-media-info-tooltip" data-toggle="tooltip" title="Show Albumns"><i class="fa fa-plus-circle fa-fw"></i></span>';
$(td).html('<div><a href="#"><div style="float: left;">' + expand_details + '&nbsp;' + date + '</div></a></div>');
} else if (rowData['media_type'] === 'album') {
expand_details = '<span class="expand-media-info-tooltip" data-toggle="tooltip" title="Show Tracks"><i class="fa fa-plus-circle fa-fw"></i></span>';
$(td).html('<div><a href="#"><div style="float: left;">' + expand_details + '&nbsp;' + date + '</div></a></div>');
} else if (rowData['media_type'] === 'photo' && rowData['parent_rating_key'] == '') {
expand_details = '<span class="expand-media-info-tooltip" data-toggle="tooltip" title="Show Photos"><i class="fa fa-plus-circle fa-fw"></i></span>';
$(td).html('<div><a href="#"><div style="float: left;">' + expand_details + '&nbsp;' + date + '</div></a></div>');
} else {
$(td).html('<div style="float: left;"><i class="fa fa-fw"></i>&nbsp;' + date + '</div>');
}
}
},
"width": "7%",
"className": "no-wrap expand-media-info",
"searchable": false
},
{
"targets": [1],
"data": "title",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
var media_type = '';
var thumb_popover = '';
if (rowData['media_type'] === 'movie') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + ' (' + rowData['year'] + ')</span>'
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'show') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="TV Show"><i class="fa fa-television fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'season') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Season"><i class="fa fa-television fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="120" data-width="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left; padding-left: 15px;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'episode') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=450&fallback=poster" data-height="80" data-width="140">E' + rowData['media_index'] + ' - ' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left; padding-left: 30px;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'artist') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Artist"><i class="fa fa-music fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=poster" data-height="80" data-width="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'album') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Album"><i class="fa fa-music fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=poster" data-height="80" data-width="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left; padding-left: 15px;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'track') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=300&height=300&fallback=poster" data-height="80" data-width="80">T' + rowData['media_index'] + ' - ' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?rating_key=' + rowData['rating_key'] + '"><div style="float: left; padding-left: 30px;">' + media_type + '&nbsp;' + thumb_popover + '</div></a></div>');
} else {
$(td).html(cellData);
}
}
},
"width": "20%"
},
{
"targets": [2],
"data": "container",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "6%",
"className": "no-wrap hidden-sm hidden-xs"
},
{
"targets": [3],
"data": "bitrate",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData + ' kbps');
}
},
"width": "6%",
"className": "no-wrap hidden-md hidden-sm hidden-xs",
"searchable": false
},
{
"targets": [4],
"data": "video_codec",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "8%",
"className": "no-wrap hidden-sm hidden-xs"
},
{
"targets": [5],
"data": "video_resolution",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "8%",
"className": "no-wrap hidden-md hidden-sm hidden-xs"
},
{
"targets": [6],
"data": "video_framerate",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "8%",
"className": "no-wrap hidden-md hidden-sm hidden-xs"
},
{
"targets": [7],
"data": "audio_codec",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "8%",
"className": "no-wrap hidden-sm hidden-xs"
},
{
"targets": [8],
"data": "audio_channels",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData + ' ch');
}
},
"width": "8%",
"className": "no-wrap hidden-md hidden-sm hidden-xs"
},
{
"targets": [9],
"data": "file_size",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(Math.round(cellData / Math.pow(1024, 2)).toString() + ' MiB');
} else {
if (rowData['section_type'] != 'photo' && get_file_sizes != null) {
get_file_sizes = true;
}
}
},
"width": "7%",
"className": "no-wrap hidden-md hidden-sm hidden-xs",
"searchable": false
},
{
"targets": [10],
"data": "last_watched",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
date = moment(cellData, "X").format(date_format);
$(td).html(date);
}
},
"width": "7%",
"className": "no-wrap hidden-xs",
"searchable": false
},
{
"targets": [11],
"data": "play_count",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
}
},
"width": "5%",
"className": "no-wrap hidden-xs",
"searchable": false
}
],
"drawCallback": function (settings) {
// Jump to top of page
// $('html,body').scrollTop(0);
$('#ajaxMsg').fadeOut();
// Create the tooltips.
$('.expand-media-info-tooltip').tooltip({ container: 'body' });
$('.media-type-tooltip').tooltip({ container: 'body' });
$('.thumb-tooltip').popover({
html: true,
container: 'body',
trigger: 'hover',
placement: 'right',
template: '<div class="popover history-thumbnail-popover" role="tooltip"><div class="arrow" style="top: 50%;"></div><div class="popover-content"></div></div>',
content: function () {
return '<div class="history-thumbnail" style="background-image: url(' + $(this).data('img') + '); height: ' + $(this).data('height') + 'px; width: ' + $(this).data('width') + 'px;" />';
}
});
media_info_table.rows().every(function () {
var rowData = this.data();
if (rowData['rating_key'] in media_info_child_table) {
// if a child table was already created
$(this.node()).find('i.fa.fa-plus-circle').toggleClass('fa-plus-circle').toggleClass('fa-minus-circle');
this.child(childTableFormatMedia(rowData)).show();
createChildTableMedia(this, rowData)
}
});
if (get_file_sizes) {
$('#get_file_sizes_message').show();
$('#refresh-media-info-table').prop('disabled', true);
$.ajax({
url: 'get_media_info_file_sizes',
async: true,
data: { section_id: section_id },
complete: function (xhr, status) {
response = JSON.parse(xhr.responseText)
if (response.success == true) {
$('#get_file_sizes_message').hide();
$('#refresh-media-info-table').prop('disabled', false);
media_info_table.draw();
}
}
});
get_file_sizes = false;
}
$("#media_info_table_info").append('<span class="hidden-md hidden-sm hidden-xs"> with a total file size of ' +
Math.round(settings.json.filtered_file_size / Math.pow(1024, 3)).toString() + ' GiB' +
' (filtered from ' + Math.round(settings.json.total_file_size / Math.pow(1024, 3)).toString() + ' GiB)</span>');
},
"preDrawCallback": function(settings) {
var msg = "<i class='fa fa-refresh fa-spin'></i>&nbspFetching rows...";
showMsg(msg, false, false, 0)
},
"rowCallback": function (row, rowData, rowIndex) {
if (rowData['rating_key'] in media_info_child_table) {
// if a child table was already created
$(row).addClass('shown')
media_info_table.row(row).child(childTableFormatMedia(rowData)).show();
}
}
}
// Parent table expand detailed media info
$('#media_info_table').on('click', '> tbody > tr > td.expand-media-info a', function () {
var tr = $(this).closest('tr');
var row = media_info_table.row(tr);
var rowData = row.data();
$(this).find('i.fa').toggleClass('fa-plus-circle').toggleClass('fa-minus-circle');
if (row.child.isShown()) {
$('div.slider', row.child()).slideUp(function () {
row.child.hide();
tr.removeClass('shown');
delete media_info_child_table[rowData['rating_key']];
});
} else {
tr.addClass('shown');
row.child(childTableFormatMedia(rowData)).show();
createChildTableMedia(row, rowData);
}
});
// Initialize the detailed media info child table options using the parent table options
function childTableOptionsMedia(rowData) {
switch (rowData['media_type']) {
case 'show':
section_type = 'season';
break;
case 'season':
section_type = 'episode';
break;
case 'artist':
section_type = 'album';
break;
case 'album':
section_type = 'track';
break;
case 'photo':
section_type = 'picture';
break;
}
media_info_table_options = media_info_table_options;
// Remove settings that are not necessary
media_info_table_options.searching = false;
media_info_table_options.lengthChange = false;
media_info_table_options.info = false;
media_info_table_options.pageLength = 10;
media_info_table_options.bStateSave = false;
media_info_table_options.ajax = {
url: 'get_library_media_info',
type: 'post',
data: function (d) {
return {
json_data: JSON.stringify(d),
section_id: rowData['section_id'],
section_type: section_type,
rating_key: rowData['rating_key'],
refresh: refresh_child_tables
};
}
}
media_info_table_options.fnDrawCallback = function (settings) {
$('#ajaxMsg').fadeOut();
// Create the tooltips.
$('.expand-media-info-tooltip').tooltip({ container: 'body' });
$('.media-type-tooltip').tooltip();
$('.thumb-tooltip').popover({
html: true,
container: 'body',
trigger: 'hover',
placement: 'right',
template: '<div class="popover history-thumbnail-popover" role="tooltip"><div class="arrow" style="top: 50%;"></div><div class="popover-content"></div></div>',
content: function () {
return '<div class="history-thumbnail" style="background-image: url(' + $(this).data('img') + '); height: ' + $(this).data('height') + 'px; width: ' + $(this).data('width') + 'px;" />';
}
});
if (rowData['rating_key'] in media_info_child_table) {
media_info_child_table[rowData['rating_key']].rows().every(function () {
var childrowData = this.data();
if (childrowData['rating_key'] in media_info_child_table) {
// if a child table was already created
$(this.node()).find('i.fa.fa-plus-circle').toggleClass('fa-plus-circle').toggleClass('fa-minus-circle');
this.child(childTableFormatMedia(childrowData)).show();
createChildTableMedia(this, childrowData)
}
});
}
if (get_file_sizes) {
$('#refresh-media-info-table').prop('disabled', true);
$.ajax({
url: 'get_media_info_file_sizes',
async: true,
data: {
section_id: section_id,
rating_key: rowData['rating_key']
},
complete: function (xhr, status) {
response = JSON.parse(xhr.responseText)
if (response.success == true) {
$('#refresh-media-info-table').prop('disabled', false);
media_info_child_table[rowData['rating_key']].draw();
}
}
});
get_file_sizes = false;
}
$(this).closest('div.slider').slideDown();
}
media_info_table_options.fnRowCallback = function (row, rowData, rowIndex) {
if (rowData['rating_key'] in media_info_child_table) {
// if a child table was already created
$(row).addClass('shown')
media_info_table.row(row).child(childTableFormatMedia(rowData)).show();
}
}
return media_info_table_options;
}
// Format the detailed media info child table
function childTableFormatMedia(rowData) {
return '<div class="slider">' +
'<table id="media_info_child-' + rowData['rating_key'] + '" data-id="' + rowData['rating_key'] + '" width="100%">' +
'<thead>' +
'<tr>' +
'<th align="left" id="added_at">Added At</th>' +
'<th align="left" id="title">Title</th>' +
'<th align="left" id="container">Container</th>' +
'<th align="left" id="bitrate">Bitrate</th>' +
'<th align="left" id="video_codec">Video Codec</th>' +
'<th align="left" id="video_resolution">Video Resolution</th>' +
'<th align="left" id="video_resolution">Video Framerate</th>' +
'<th align="left" id="audio_codec">Audio Codec</th>' +
'<th align="left" id="audio_channels">Audio Channels</th>' +
'<th align="left" id="file_size">File Size</th>' +
'<th align="left" id="last_watched">Last Watched</th>' +
'<th align="left" id="total_plays">Total Plays</th>' +
'</tr>' +
'</thead>' +
'<tbody>' +
'</tbody>' +
'</table>' +
'</div>';
}
// Create the detailed media info child table
media_info_child_table = {};
function createChildTableMedia(row, rowData) {
media_info_table_options = childTableOptionsMedia(rowData);
// initialize the child table
media_info_child_table[rowData['rating_key']] = $('#media_info_child-' + rowData['rating_key']).DataTable(media_info_table_options);
// Set child table column visibility to match parent table
var visibility = media_info_table.columns().visible();
for (var i = 0; i < visibility.length; i++) {
if (!(visibility[i])) { media_info_child_table[rowData['rating_key']].column(i).visible(visibility[i]); }
}
media_info_table.on('column-visibility', function (e, settings, colIdx, visibility) {
if (row.child.isShown()) {
media_info_child_table[rowData['rating_key']].column(colIdx).visible(visibility);
}
});
// Child table expand detailed media info
$('table[id^=media_info_child-' + rowData['rating_key'] + ']').on('click', '> tbody > tr > td.expand-media-info a', function () {
var table_id = $(this).closest('table').data('id');
var tr = $(this).closest('tr');
var row = media_info_child_table[table_id].row(tr);
var rowData = row.data();
$(this).find('i.fa').toggleClass('fa-plus-circle').toggleClass('fa-minus-circle');
if (row.child.isShown()) {
$('div.slider', row.child()).slideUp(function () {
row.child.hide();
tr.removeClass('shown');
delete media_info_child_table[rowData['rating_key']];
});
} else {
tr.addClass('shown');
row.child(childTableFormatMedia(rowData)).show();
createChildTableMedia(row, rowData);
}
});
}

View file

@ -4,7 +4,7 @@ var plex_log_table_options = {
"serverSide": false,
"pagingType": "bootstrap",
"order": [ 0, 'desc'],
"pageLength": 10,
"pageLength": 50,
"stateSave": true,
"language": {
"search":"Search: ",

View file

@ -50,7 +50,7 @@ sync_table_options = {
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
if (rowData['metadata_type'] !== '') {
$(td).html('<a href="info?item_id=' + rowData['rating_key'] + '">' + cellData + '</a>');
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
} else {
$(td).html(cellData);
}

View file

@ -88,18 +88,18 @@ user_ip_table_options = {
var thumb_popover = ''
if (rowData['media_type'] === 'movie') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=120&fallback=poster" data-height="120">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=120&fallback=poster" data-height="120" data-width="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'episode') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=120&fallback=poster" data-height="120">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;" >' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=120&fallback=poster" data-height="120" data-width="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;" >' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'track') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=80&fallback=poster" data-height="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=80&fallback=poster" data-height="80" data-width="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type']) {
$(td).html('<a href="info?item_id=' + rowData['id'] + '">' + cellData + '</a>');
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
} else {
$(td).html('n/a');
}
@ -126,10 +126,12 @@ user_ip_table_options = {
$('.watched-tooltip').tooltip();
$('.thumb-tooltip').popover({
html: true,
container: 'body',
trigger: 'hover',
placement: 'right',
template: '<div class="popover history-thumbnail-popover" role="tooltip"><div class="arrow" style="top: 50%;"></div><div class="popover-content"></div></div>',
content: function () {
return '<div style="background-image: url(' + $(this).data('img') + '); width: 80px; height: ' + $(this).data('height') + 'px;" />';
return '<div class="history-thumbnail" style="background-image: url(' + $(this).data('img') + '); height: ' + $(this).data('height') + 'px;" />';
}
});

View file

@ -23,12 +23,12 @@ users_list_table_options = {
"targets": [0],
"data": null,
"createdCell": function (td, cellData, rowData, row, col) {
$(td).html('<div class="edit-user-toggles"><button class="btn btn-xs btn-warning delete-user" data-id="' + rowData['user_id'] + '" data-toggle="button"><i class="fa fa-trash-o fa-fw"></i> Delete</button>&nbsp' +
$(td).html('<div class="edit-user-toggles">' +
'<button class="btn btn-xs btn-warning delete-user" data-id="' + rowData['user_id'] + '" data-toggle="button"><i class="fa fa-trash-o fa-fw"></i> Delete</button>&nbsp' +
'<button class="btn btn-xs btn-warning purge-user" data-id="' + rowData['user_id'] + '" data-toggle="button"><i class="fa fa-eraser fa-fw"></i> Purge</button>&nbsp&nbsp&nbsp' +
'<input type="checkbox" id="do_notify-' + rowData['user_id'] + '" name="do_notify" value="1" ' + rowData['do_notify'] + '><label class="edit-tooltip" for="do_notify-' + rowData['user_id'] + '" data-toggle="tooltip" title="Toggle Notifications"><i class="fa fa-bell fa-lg fa-fw"></i></label>&nbsp' +
'<input type="checkbox" id="keep_history-' + rowData['user_id'] + '" name="keep_history" value="1" ' + rowData['keep_history'] + '><label class="edit-tooltip" for="keep_history-' + rowData['user_id'] + '" data-toggle="tooltip" title="Toggle History"><i class="fa fa-history fa-lg fa-fw"></i></label>&nbsp');
// Show/hide user currently doesn't work
//'<input type="checkbox" id="show_hide-' + rowData['user_id'] + '" name="show_hide" value="1" checked><label class="edit-tooltip" for="show_hide-' + rowData['user_id'] + '" data-toggle="tooltip" title="Show/Hide User"><i class="fa fa-eye fa-lg fa-fw"></i></label>');
'<input type="checkbox" id="keep_history-' + rowData['user_id'] + '" name="keep_history" value="1" ' + rowData['keep_history'] + '><label class="edit-tooltip" for="keep_history-' + rowData['user_id'] + '" data-toggle="tooltip" title="Toggle History"><i class="fa fa-history fa-lg fa-fw"></i></label>&nbsp' +
'</div>');
},
"width": "7%",
"className": "edit-control no-wrap hidden",
@ -42,7 +42,7 @@ users_list_table_options = {
if (cellData === '') {
$(td).html('<a href="user?user_id=' + rowData['user_id'] + '"><div class="users-poster-face" style="background-image: url(interfaces/default/images/gravatar-default-80x80.png);"></div></a>');
} else {
$(td).html('<a href="user?user_id=' + rowData['user_id'] + '"><div class="users-poster-face" style="background-image: url(' + cellData + ');"></div></a>');
$(td).html('<a href="user?user_id=' + rowData['user_id'] + '"><div class="users-poster-face" style="background-image: url(' + rowData['user_thumb'] + ');"></div></a>');
}
},
"orderable": false,
@ -54,16 +54,13 @@ users_list_table_options = {
"targets": [2],
"data": "friendly_name",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
if (rowData['user_id'] > 0) {
$(td).html('<div class="edit-user-name" data-id="' + rowData['user_id'] + '"><a href="user?user_id=' + rowData['user_id'] + '">' + cellData + '</a>' +
'<input type="text" class="hidden" value="' + cellData + '"></div>');
if (cellData !== null && cellData !== '') {
$(td).html('<div class="edit-user-name" data-id="' + rowData['user_id'] + '">' +
'<a href="user?user_id=' + rowData['user_id'] + '">' + cellData + '</a>' +
'<input type="text" class="hidden" value="' + cellData + '">' +
'</div>');
} else {
$(td).html('<div class="edit-user-name" data-id="' + rowData['user_id'] + '"><a href="user?user=' + rowData['user'] + '">' + cellData + '</a>' +
'<input type="text" class="hidden" value="' + cellData + '"></div>');
}
} else {
$(td).html(cellData);
$(td).html('n/a');
}
},
"width": "10%",
@ -72,11 +69,11 @@ users_list_table_options = {
{
"targets": [3],
"data": "last_seen",
"render": function ( data, type, full ) {
if (data) {
return moment(data, "X").fromNow();
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== null && cellData !== '') {
$(td).html(moment(cellData, "X").fromNow());
} else {
return "never";
$(td).html("never");
}
},
"searchable": false,
@ -108,7 +105,7 @@ users_list_table_options = {
"targets": [5],
"data": "platform",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
if (cellData !== null && cellData !== '') {
$(td).html(cellData);
} else {
$(td).html('n/a');
@ -121,7 +118,7 @@ users_list_table_options = {
"targets": [6],
"data":"player",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData) {
if (cellData !== null && cellData !== '') {
var transcode_dec = '';
if (rowData['video_decision'] === 'transcode') {
transcode_dec = '<span class="transcode-tooltip" data-toggle="tooltip" title="Transcode"><i class="fa fa-server fa-fw"></i></span>';
@ -142,27 +139,27 @@ users_list_table_options = {
"targets": [7],
"data":"last_watched",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
if (cellData !== null && cellData !== '') {
var media_type = '';
var thumb_popover = ''
if (rowData['media_type'] === 'movie') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Movie"><i class="fa fa-film fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=120&fallback=poster" data-height="120">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=120&fallback=poster" data-height="120" data-width="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'episode') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Episode"><i class="fa fa-television fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=120&fallback=poster" data-height="120">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;" >' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=120&fallback=poster" data-height="120" data-width="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;" >' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type'] === 'track') {
media_type = '<span class="media-type-tooltip" data-toggle="tooltip" title="Track"><i class="fa fa-music fa-fw"></i></span>';
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=80&fallback=poster" data-height="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?source=history&item_id=' + rowData['id'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
thumb_popover = '<span class="thumb-tooltip" data-toggle="popover" data-img="pms_image_proxy?img=' + rowData['thumb'] + '&width=80&height=80&fallback=poster" data-height="80" data-width="80">' + cellData + '</span>'
$(td).html('<div class="history-title"><a href="info?source=history&rating_key=' + rowData['rating_key'] + '"><div style="float: left;">' + media_type + '&nbsp' + thumb_popover + '</div></a></div>');
} else if (rowData['media_type']) {
$(td).html('<a href="info?item_id=' + rowData['id'] + '">' + cellData + '</a>');
$(td).html('<a href="info?rating_key=' + rowData['rating_key'] + '">' + cellData + '</a>');
}
} else {
$(td).html('n/a');
}
}
},
"width": "30%",
"className": "hidden-sm hidden-xs"
@ -190,8 +187,9 @@ users_list_table_options = {
html: true,
trigger: 'hover',
placement: 'right',
template: '<div class="popover history-thumbnail-popover" role="tooltip"><div class="arrow" style="top: 50%;"></div><div class="popover-content"></div></div>',
content: function () {
return '<div style="background-image: url(' + $(this).data('img') + '); width: 80px; height: ' + $(this).data('height') + 'px;" />';
return '<div class="history-thumbnail" style="background-image: url(' + $(this).data('img') + '); height: ' + $(this).data('height') + 'px; width: ' + $(this).data('width') + 'px;" />';
}
});
@ -206,8 +204,11 @@ users_list_table_options = {
showMsg(msg, false, false, 0)
},
"rowCallback": function (row, rowData) {
if ($.inArray(rowData['user_id'], users_to_delete) !== -1) {
$(row).find('button.delete-user[data-id="' + rowData['user_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
}
if ($.inArray(rowData['user_id'], users_to_purge) !== -1) {
$(row).find('button[data-id="' + rowData['user_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
$(row).find('button.purge-user[data-id="' + rowData['user_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
}
}
}

View file

@ -0,0 +1,202 @@
<%inherit file="base.html"/>
<%def name="headIncludes()">
<link rel="stylesheet" href="interfaces/default/css/dataTables.bootstrap.css">
<link rel="stylesheet" href="interfaces/default/css/plexpy-dataTables.css">
</%def>
<%def name="body()">
<div class='container-fluid'>
% if config['update_section_ids'] == 1:
<div id="update_section_ids_message" style="text-align: center; margin-top: 20px;">
<i class="fa fa-exclamation-triangle"></i> PlexPy needs to update the Library IDs in your databse. Click the "<strong>Refresh libraries</strong>" button below to begin the update.
</div>
% elif config['update_section_ids'] == -1:
<div id="update_section_ids_message" style="text-align: center; margin-top: 20px;">
<i class="fa fa-refresh fa-spin"></i> PlexPy is updating library IDs in the database. This could take a few minutes depending on the size of your database.
<br />
You may leave this page and come back later.
</div>
% endif
<div class='table-card-header'>
<div class="header-bar">
<span><i class="fa fa-book"></i> All Libraries</span>
</div>
<div class="button-bar">
% if config['update_section_ids'] == -1:
<button class="btn btn-dark refresh-libraries-button" id="refresh-libraries-list" disabled><i class="fa fa-refresh"></i> Refresh libraries</button>
% else:
<button class="btn btn-dark refresh-libraries-button" id="refresh-libraries-list"><i class="fa fa-refresh"></i> Refresh libraries</button>
% endif
<button class="btn btn-danger btn-edit" data-toggle="button" aria-pressed="false" autocomplete="off" id="row-edit-mode">
<i class="fa fa-pencil"></i> Edit mode
</button>&nbsp
<div class="alert alert-danger alert-edit" role="alert" id="row-edit-mode-alert"><i class="fa fa-exclamation-triangle"></i>&nbspSelect libraries to delete/purge. Data is deleted/purged upon exiting edit mode.</div>
</div>
</div>
<div class='table-card-back'>
<table id="libraries_list_table" class="display" width="100%">
<thead>
<tr>
<th align="left" id="edit_row">Edit</th>
<th align="right" id="library_thumb"></th>
<th align="left" id="section_name">Library Name</th>
<th align="left" id="section_type">Library Type</th>
<th align="left" id="count">Total Movies / TV Shows / Artists</th>
<th align="left" id="parent_count">Total Seasons / Albums</th>
<th align="left" id="child_count">Total Episodes / Tracks</th>
<th align="left" id="last_accessed">Last Accessed</th>
<th align="left" id="last_watched">Last Watched</th>
<th align="left" id="total_plays">Total Plays</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="modal fade" id="confirm-modal" tabindex="-1" role="dialog" aria-labelledby="confirm-modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title" id="myModalLabel">Confirm Delete/Purge</h4>
</div>
<div class="modal-body" style="text-align: center;">
<ul id="libraries-to-delete" class="list-unstyled"></ul>
<ul id="libraries-to-purge" class="list-unstyled"></ul>
<p>This is permanent and cannot be undone!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-delete">Confirm</button>
</div>
</div>
</div>
</div>
</div>
</div>
</%def>
<%def name="javascriptIncludes()">
<script src="interfaces/default/js/jquery.dataTables.min.js"></script>
<script src="interfaces/default/js/dataTables.bootstrap.min.js"></script>
<script src="interfaces/default/js/dataTables.bootstrap.pagination.js"></script>
<script src="interfaces/default/js/moment-with-locale.js"></script>
<script src="interfaces/default/js/tables/libraries.js"></script>
<script>
$(document).ready(function () {
libraries_list_table_options.ajax = {
url: 'get_library_list',
type: 'POST',
data: function ( d ) {
return {
json_data: JSON.stringify(d)
};
}
}
libraries_list_table = $('#libraries_list_table').DataTable(libraries_list_table_options);
clearSearchButton('libraries_list_table', libraries_list_table);
$('#row-edit-mode').on('click', function () {
$('#row-edit-mode-alert').fadeIn(200);
$('#libraries-to-delete').html('');
$('#libraries-to-purge').html('');
if ($(this).hasClass('active')) {
if (libraries_to_purge.length > 0) {
$('.edit-control').each(function () {
$(this).find('button.btn-danger').toggleClass('btn-warning').toggleClass('btn-danger');
});
libraries_to_purge = $.grep(libraries_to_purge, function (value) {
return $.inArray(value, libraries_to_delete) < 0;
});
if (libraries_to_delete.length > 0) {
$('#libraries-to-delete').prepend('<p>Are you REALLY sure you want to delete the following libraries:</p>')
for (var i = 0; i < libraries_to_delete.length; i++) {
$('#libraries-to-delete').append('<li>' + $('div[data-id=' + libraries_to_delete[i] + ']').text() + '</li>');
}
}
if (libraries_to_purge.length > 0) {
$('#libraries-to-purge').prepend('<p>Are you REALLY sure you want to purge all history for the following libraries:</p>')
for (var i = 0; i < libraries_to_purge.length; i++) {
$('#libraries-to-purge').append('<li>' + $('div[data-id=' + libraries_to_purge[i] + ']').text() + '</li>');
}
}
$('#confirm-modal').modal();
$('#confirm-modal').one('click', '#confirm-delete', function () {
for (var i = 0; i < libraries_to_delete.length; i++) {
$.ajax({
url: 'delete_library',
data: { section_id: libraries_to_delete[i] },
cache: false,
async: true,
success: function (data) {
var msg = "Library deleted";
showMsg(msg, false, true, 2000);
}
});
}
for (var i = 0; i < libraries_to_purge.length; i++) {
$.ajax({
url: 'delete_all_library_history',
data: { section_id: libraries_to_purge[i] },
cache: false,
async: true,
success: function (data) {
var msg = "Library history purged";
showMsg(msg, false, true, 2000);
}
});
}
libraries_list_table.draw();
});
}
$('.edit-control').each(function () {
$(this).addClass('hidden');
$('#row-edit-mode-alert').fadeOut(200);
});
} else {
libraries_to_delete = [];
libraries_to_purge = [];
$('.edit-control').each(function () {
$(this).find('button.btn-danger').toggleClass('btn-warning').toggleClass('btn-danger');
$(this).removeClass('hidden');
});
}
});
});
$("#refresh-libraries-list").click(function () {
if ("${config['update_section_ids']}" == "1") {
$('#update_section_ids_message').html(
'<i class="fa fa-refresh fa-spin"></i> PlexPy is updating library IDs in the database. This could take a few minutes depending on the size of your database.' +
'<br />' +
'You may leave this page and come back later.');
$(this).prop('disabled', true);
}
$.ajax({
url: 'refresh_libraries_list',
cache: false,
async: true,
success: function (data) {
showMsg('<i class="fa fa-refresh"></i>&nbspLibraries list refresh started...', false, true, 2000, false);
},
complete: function (data) {
showMsg('<i class="fa fa-check"></i>&nbspLibraries list refreshed.', false, true, 2000, false);
},
error: function (jqXHR, textStatus, errorThrown) {
showMsg('<i class="fa fa-exclamation-circle"></i>&nbspUnable to refresh libraries list.', false, true, 2000, true);
}
});
});
</script>
</%def>

View file

@ -0,0 +1,519 @@
<%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: library.html
Version: 0.1
Variable names: data [list]
data :: Usable parameters
section_id Returns the library id of the library.
section_name Returns the name of the library.
section_type Returns the type of the library.
library_thumb Returns the thumbnail for the library.
custom_thumb Returns the custom thumbnail for the library.
library_art Returns the artwork for the library.
count Returns the item count for the library.
parent_count Returns the parent item count for the library.
child_count Returns the child item count for the library.
do_notify Returns bool value for whether to send notifications for the library.
keep_history Returns bool value for whether to keep history for the library.
DOCUMENTATION :: END
</%doc>
<%inherit file="base.html"/>
<%def name="headIncludes()">
<link rel="stylesheet" href="interfaces/default/css/dataTables.bootstrap.css">
<link rel="stylesheet" href="interfaces/default/css/dataTables.colVis.css">
<link rel="stylesheet" href="interfaces/default/css/plexpy-dataTables.css">
</%def>
<%def name="body()">
% if data:
<div class="container-fluid">
<div class="row">
<div class="art-face" style="background-image:url(pms_image_proxy?img=${data['library_art']}&width=1920&height=1080)"></div>
<div class="summary-container">
<div class="summary-navbar">
<div class="col-md-12">
<div class="summary-navbar-list">
<ul class="list-unstyled breadcrumb">
<li class="active">${data['section_name']}</li>
</ul>
</div>
</div>
</div>
<div class="summary-content-wrapper">
<div class="col-md-12">
<div class="table-card-back">
<div class="user-info-wrapper">
% if data['library_thumb'][:4] == 'http':
<div class="library-info-poster-face" style="background-image: url(${data['library_thumb']});"></div>
% else:
<div class="library-info-poster-face" style="background-image: url(pms_image_proxy?img=${data['library_thumb']}&width=80&height=80&fallback=cover);"></div>
% endif
<div class="user-info-username">
<span class="set-username">${data['section_name']}</span>
<span id="edit-library-tooltip" data-target="tooltip" title="Edit library details">
<a href="#" data-toggle="modal" data-target="#edit-library-modal" id="toggle-edit-library-modal"><i class="fa fa-pencil"></i></a>
</span>
</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>
<li><a id="media-info-tab-btn" href="#libraryMediaInfo" data-toggle="tab">Media Info</a></li>
</ul>
</div>
</div>
</div>
</div>
<div id="edit-library-modal" class="modal fade" tabindex="-1" role="dialog"
aria-labelledby="edit-library-modal">
</div>
<div class="tab-content">
<div class="tab-pane active" id="profile">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<div class="table-card-header">
<div class="header-bar">
<span><i class="fa fa-line-chart"></i> Global Stats</span>
</div>
</div>
<div class="table-card-back">
<div id="library-time-stats" class="user-overview-stats-wrapper">
<div class='muted'><i class="fa fa-refresh fa-spin"></i> Loading data...</div>
<br>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<div class="table-card-header">
<div class="header-bar">
<span><i class="fa fa-group"></i> User Stats</span>
</div>
</div>
<div class="table-card-back">
<div id="library-user-stats" class="user-player">
<div class='muted'><i class="fa fa-refresh fa-spin"></i> Loading data...</div>
<br>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<div class="table-card-header">
<div class="header-bar">
<span><i class="fa fa-history"></i> Recently Watched</span>
</div>
</div>
<div class="table-card-back">
<div id="library-recently-watched">
<div class='muted'><i class="fa fa-refresh fa-spin"></i> Loading data...</div>
<br>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<div class="table-card-header">
<div class="header-bar">
<span><i class="fa fa-history"></i> Recently Added</span>
</div>
</div>
<div class="table-card-back">
<div id="library-recently-added">
<div class='muted'><i class="fa fa-refresh fa-spin"></i> Loading data...</div>
<br>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane" id="libraryHistory">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<div class='table-card-header'>
<div class="header-bar">
<span>
<i class="fa fa-history"></i> Watch History for <strong>
<span class="set-username">${data['section_name']}</span>
</strong>
</span>
</div>
<div class="button-bar">
<div class="colvis-button-bar hidden-xs" id="button-bar-history"></div>
<button class="btn btn-danger btn-edit" data-toggle="button" aria-pressed="false" autocomplete="off" id="row-edit-mode">
<i class="fa fa-trash-o"></i> Delete mode
</button>
<div class="alert alert-danger alert-edit" role="alert" id="row-edit-mode-alert"><i class="fa fa-exclamation-triangle"></i>&nbspSelect rows to delete. Data is deleted upon exiting delete mode.</div>
</div>
</div>
<div class="table-card-back">
<table class="display" id="history_table" width="100%">
<thead>
<tr>
<th align="left" id="delete">Delete</th>
<th align="left" id="time">Time</th>
<th align="left" id="friendly_name">User</th>
<th align="left" id="ip_address">IP Address</th>
<th align="left" id="platform">Platform</th>
<th align="left" id="player">Player</th>
<th align="left" id="title">Title</th>
<th align="left" id="started">Started</th>
<th align="left" id="paused_counter">Paused</th>
<th align="left" id="stopped">Stopped</th>
<th align="left" id="duration">Duration</th>
<th align="left" id="percent_complete"></th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane" id="libraryMediaInfo">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
% if config['get_file_sizes'] and data['section_id'] in config['get_file_sizes_hold']['section_ids']:
<div id="get_file_sizes_message" style="text-align: center; margin-top: 20px;">
% else:
<div id="get_file_sizes_message" style="text-align: center; margin-top: 20px; display: none;">
% endif
<i class="fa fa-refresh fa-spin"></i> PlexPy is calculating the file sizes for the library's media info. This could take a few minutes depending on the size of your library.
<br />
You may leave this page and come back later.
</div>
<div class='table-card-header'>
<div class="header-bar">
<span>
<i class="fa fa-history"></i> All Media Info for <strong>
<span class="set-username">${data['section_name']}</span>
</strong>
</span>
</div>
<div class="button-bar">
% if config['get_file_sizes'] and data['section_id'] in config['get_file_sizes_hold']['section_ids']:
<button class="btn btn-dark refresh-media-info-table-button" id="refresh-media-info-table" style="margin-right: 5px;" disabled>
<i class="fa fa-refresh"></i> Refresh media info
</button>
% else:
<button class="btn btn-dark refresh-media-info-table-button" id="refresh-media-info-table" style="margin-right: 5px;">
<i class="fa fa-refresh"></i> Refresh media info
</button>
% endif
<div class="colvis-button-bar hidden-xs" id="button-bar-media-info"></div>
</div>
</div>
<div class="table-card-back">
<table class="display" id="media_info_table" width="100%">
<thead>
<tr>
<th align="left" id="added_at">Added At</th>
<th align="left" id="title">Title</th>
<th align="left" id="container">Container</th>
<th align="left" id="bitrate">Bitrate</th>
<th align="left" id="video_codec">Video Codec</th>
<th align="left" id="video_resolution">Video Resolution</th>
<th align="left" id="video_resolution">Video Framerate</th>
<th align="left" id="audio_codec">Audio Codec</th>
<th align="left" id="audio_channels">Audio Channels</th>
<th align="left" id="file_size">File Size</th>
<th align="left" id="last_watched">Last Watched</th>
<th align="left" id="total_plays">Total Plays</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="info-modal" tabindex="-1" role="dialog" aria-labelledby="info-modal">
</div>
<div class="modal fade" id="ip-info-modal" tabindex="-1" role="dialog" aria-labelledby="ip-info-modal">
</div>
<div class="modal fade" id="confirm-modal" tabindex="-1" role="dialog" aria-labelledby="confirm-modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title" id="myModalLabel">Confirm Delete</h4>
</div>
<div class="modal-body" style="text-align: center;">
<p>Are you REALLY sure you want to delete <strong><span id="deleteCount"></span></strong> history item(s)?</p>
<p>This is permanent and cannot be undone!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-delete">Delete</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<footer></footer>
% else:
<div class="container-fluid">
<div class="row">
<div class="summary-container">
<div class="summary-navbar">
<div class="col-md-12">
<div class="summary-navbar-list">
</div>
</div>
</div>
<div class="summary-content-wrapper">
<div class='col-md-12'>
<div style="text-align: center; margin-top: 20px;">
<i class="fa fa-exclamation-triangle"></i> Error retrieving library information. Please see the logs for more details.
</div>
</div>
</div>
</div>
</div>
</div>
% endif
</%def>
<%def name="javascriptIncludes()">
<script src="interfaces/default/js/jquery.dataTables.min.js"></script>
<script src="interfaces/default/js/dataTables.colVis.js"></script>
<script src="interfaces/default/js/dataTables.bootstrap.min.js"></script>
<script src="interfaces/default/js/dataTables.bootstrap.pagination.js"></script>
% if data:
<script>
% if str(data['section_id']).isdigit():
var section_id = ${data['section_id']};
% else:
var section_id = null;
% endif
var section_name = '${data['section_name'].replace("'", "\\'")}';
var refresh_table = false;
% if config['get_file_sizes']:
var get_file_sizes = false;
% else:
var get_file_sizes = null;
% endif
</script>
<script src="interfaces/default/js/moment-with-locale.js"></script>
<script src="interfaces/default/js/tables/history_table.js"></script>
<script src="interfaces/default/js/tables/media_info_table.js"></script>
<script>
$(document).ready(function () {
$("#edit-library-tooltip").tooltip();
// Populate watch time stats
$.ajax({
url: 'get_library_watch_time_stats',
async: true,
data: { section_id: section_id },
complete: function(xhr, status) {
$("#library-time-stats").html(xhr.responseText);
}
});
// Populate user stats
$.ajax({
url: 'get_library_user_stats',
async: true,
data: { section_id: section_id },
complete: function(xhr, status) {
$("#library-user-stats").html(xhr.responseText);
}
});
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
};
}
}
history_table = $('#history_table').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', history_table);
}
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').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', media_info_table);
}
$( "#history-tab-btn" ).one( "click", function() {
loadHistoryTable();
});
$( "#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').modal();
$('#confirm-modal').one('click', '#confirm-delete', function () {
for (var i = 0; i < history_to_delete.length; i++) {
$.ajax({
url: 'delete_history_rows',
data: { row_id: history_to_delete[i] },
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');
});
}
});
function recentlyWatched() {
var widthVal = $("#library-recently-watched").width();
var tmp = (widthVal-25) / 175;
if (tmp > 0) {
var containerSize = parseInt(tmp);
} else {
var containerSize = 1;
}
// Populate recently watched
$.ajax({
url: 'get_library_recently_watched',
async: true,
data: {
section_id: section_id,
limit: containerSize
},
complete: function(xhr, status) {
$("#library-recently-watched").html(xhr.responseText);
}
});
}
function recentlyAdded() {
var widthVal = $("#library-recently-added").width();
var tmp = (widthVal-25) / 175;
if (tmp > 0) {
var containerSize = parseInt(tmp);
} else {
var containerSize = 1;
}
// Populate recently added
$.ajax({
url: 'get_library_recently_added',
async: true,
data: {
section_id: section_id,
limit: containerSize
},
complete: function(xhr, status) {
$("#library-recently-added").html(xhr.responseText);
}
});
}
recentlyWatched();
recentlyAdded();
$(window).resize(function() {
recentlyWatched();
recentlyAdded();
});
$('div.art-face').animate({ opacity: 0.2 }, { duration: 1000 });
});
</script>
% endif
</%def>

View file

@ -0,0 +1,96 @@
<%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: library_recently_added.html
Version: 0.1
Variable names: data [array]
data[array_index] :: Usable parameters
== Global keys ==
media_type Returns the type of media. Either 'movie' or 'episode' or 'album'.
rating_key Returns the unique identifier for the media item.
parent_rating_key Returns the unique identifier for the season or artist.
grandparent_rating_key Returns the unique identifier for the show.
title Returns the name of the movie, episode, album.
parent_title Returns the name of the artist.
grandparent_title Returns the name of the show.
media_index Returns the index number of the episode.
parent_media_index Returns the index number of the season.
section_id Returns the library section number of the media item.
library_name Returns the library section name of the media item.
year Returns the release year of the movie, episode, or album.
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
parent_thumb Returns the location of the artist's thumbnail. Use with pms_image_proxy.
grandparent_thumb Returns the location of the show's thumbnail. Use with pms_image_proxy.
added_at Returns the time when the media was added to the library.
DOCUMENTATION :: END
</%doc>
% if data:
<div class="dashboard-recent-media-row">
<ul class="dashboard-recent-media list-unstyled">
% for item in data:
<li>
% if item['media_type'] == 'episode' or item['media_type'] == 'movie':
<a href="info?rating_key=${item['rating_key']}">
<div class="dashboard-recent-media-poster">
% if item['media_type'] == 'episode':
% if item['parent_thumb']:
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['parent_thumb']}&width=300&height=450&fallback=poster);">
% else:
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['grandparent_thumb']}&width=300&height=450&fallback=poster);">
% endif
% elif item['media_type'] == 'movie':
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=450&fallback=poster);">
% endif
<div class="dashboard-recent-media-overlay">
<div class="dashboard-recent-media-overlay-text" id="added_at-${item['rating_key']}">
<script>
$('#added_at-${item['rating_key']}').text('Added ' + moment(${item['added_at']}, "X").fromNow())
</script>
</div>
</div>
</div>
</div>
<div class="dashboard-recent-media-metacontainer">
% if item['media_type'] == 'episode':
<h3 title="${item['grandparent_title']}">${item['grandparent_title']}</h3>
<h3 title="${item['title']}">${item['title']}</h3>
<h3 class="text-muted">S${item['parent_media_index']} &middot; E${item['media_index']}</h3>
% elif item['media_type'] == 'movie':
<h3 title="${item['title']}">${item['title']}</h3>
<h3 class="text-muted">${item['year']}</h3>
% endif
</div>
</a>
% elif item['media_type'] == 'album':
<a href="info?rating_key=${item['rating_key']}">
<div class="dashboard-recent-media-cover">
<div class="dashboard-recent-media-cover-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=300&fallback=cover);">
<div class="dashboard-recent-media-overlay">
<div class="dashboard-recent-media-overlay-text" id="added_at-${item['rating_key']}">
<script>
$('#added_at-${item['rating_key']}').text('Added ' + moment(${item['added_at']}, "X").fromNow())
</script>
</div>
</div>
</div>
</div>
<div class="dashboard-recent-media-metacontainer">
<h3 title="${item['parent_title']}">${item['parent_title']}</h3>
<h3 class="text-muted">${item['title']}</h3>
</div>
</a>
% endif
</li>
% endfor
</ul>
</div>
% else:
<div class="text-muted">Unable to retrieve data from database.
</div><br>
% endif

View file

@ -9,23 +9,16 @@ Variable names: data [array]
data[array_index] :: Usable parameters
data['type'] Returns the type of the library. Either 'movie', 'show', 'photo', or 'artist'.
data['rows'] Returns an array containing stat data
data Returns an array containing stat data
data[array_index]['rows'] :: Usable parameters
data[array_index] :: Usable parameters
title Returns the title of the library.
section_name Returns the title of the library.
section_type Returns the type of the library.
thumb Returns the thumb of the library.
count Returns the number of items in the library.
count_type Returns the sorting type for the library
== Only if 'type' is 'show'
episode_count Return the number of episodes in the library.
episode_count_type Return the sorting type for the episodes.
== Only if 'type' is 'artist'
album_count Return the number of episodes in the library.
album_count_type Return the sorting type for the episodes.
count Returns the number of top level items in the library.
parent_count Returns the number of parent items in the library.
child_count Returns the number of child items in the library.
DOCUMENTATION :: END
</%doc>
@ -37,37 +30,54 @@ DOCUMENTATION :: END
<li>
<div class="home-platforms-instance-info">
<div class="home-platforms-instance-name">
% if library['type'] != 'photo':
<h4>
<a href="info?item_id=${library['type']}" title="${library['rows']['title']}">${library['rows']['title']}</a>
<a href="library?section_id=${library['section_id']}" title="${library['section_name']}">${library['section_name']}</a>
</h4>
% else:
<h4>${library['rows']['title']}</h4>
% endif
</div>
% if library['section_type'] == 'movie':
<div class="home-platforms-instance-playcount">
<h5>${library['rows']['count_type']}</h5>
<h3>${library['rows']['count']}</h3>
<h5>All Movies</h5>
<h3>${library['count']}</h3>
<p> items</p>
</div>
% if library['type'] == 'show':
<div class="home-platforms-instance-playcount" style="padding-left: 10px;">
<h5>${library['rows']['episode_count_type']}</h5>
<h3>${library['rows']['episode_count']}</h3>
% elif library['section_type'] == 'show':
<div class="home-platforms-instance-playcount">
<h5>All Shows</h5>
<h3>${library['count']}</h3>
<p> items</p>
</div>
% endif
% if library['type'] == 'artist':
<div class="home-platforms-instance-playcount" style="padding-left: 10px;">
<h5>${library['rows']['album_count_type']}</h5>
<h3>${library['rows']['album_count']}</h3>
<h5>All Episodes</h5>
<h3>${library['child_count']}</h3>
<p> items</p>
</div>
% elif library['section_type'] == 'artist':
<div class="home-platforms-instance-playcount">
<h5>All Artists</h5>
<h3>${library['count']}</h3>
<p> items</p>
</div>
<div class="home-platforms-instance-playcount" style="padding-left: 10px;">
<h5>All Albums</h5>
<h3>${library['parent_count']}</h3>
<p> items</p>
</div>
% elif library['section_type'] == 'photo':
<div class="home-platforms-instance-playcount">
<h5>All Albums</h5>
<h3>${library['count']}</h3>
<p> items</p>
</div>
<div class="home-platforms-instance-playcount" style="padding-left: 10px;">
<h5>All Photos</h5>
<h3>${library['child_count']}</h3>
<p> items</p>
</div>
% endif
</div>
% if library['rows']['thumb']:
% if library['thumb']:
<div class="home-platforms-instance-poster">
<div class="home-platforms-library-thumb" style="background-image: url(pms_image_proxy?img=${library['rows']['thumb']}&width=300&height=300&fallback=poster);"></div>
<div class="home-platforms-library-thumb" style="background-image: url(pms_image_proxy?img=${library['thumb']}&width=300&height=300&fallback=poster);"></div>
</div>
% else:
<div class="home-platforms-instance-poster">
@ -79,6 +89,5 @@ DOCUMENTATION :: END
% endfor
</ul>
% else:
<div class="text-muted">Unable to retrieve data from server. Please check your <a href="settings">settings</a>.
</div><br>
<div class="text-muted">No stats to show.</div><br>
% endif

View file

@ -0,0 +1,42 @@
<%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: library_user_stats.html
Version: 0.1
Variable names: data [array]
data[array_index] :: Usable parameters
== Global keys ==
user Returns the name of the user.
user_id Returns the user id of the user.
thumb Returns the avatar of the user.
total_plays Returns the play count for the user.
DOCUMENTATION :: END
</%doc>
% if data:
% for a in data:
<ul class="list-unstyled">
<div class="user-player-instance">
<li>
<a href="user?user_id=${a['user_id']}" title="${a['user']}">
<div class="library-user-instance-box" style="background-image: url(${a['thumb']});"></div>
</a>
<div class=" user-player-instance-name">
<a href="user?user_id=${a['user_id']}" title="${a['user']}">${a['user']}</a>
</div>
<div class="user-player-instance-playcount">
<h3>${a['total_plays']}</h3>
<p> plays</p>
</div>
</li>
</div>
</ul>
% endfor
% else:
<div class="text-muted">No stats to show.</div><br>
% endif

View file

@ -18,7 +18,7 @@ from plexpy import helpers
<div class='container-fluid'>
<div class='table-card-header'>
<div class="header-bar">
<span><i class="fa fa-book"></i> Logs</span>
<span><i class="fa fa-list-alt"></i> Logs</span>
</div>
<div class="button-bar">
<button class="btn btn-dark" id="clear-logs"><i class="fa fa-trash-o"></i> Clear log</button>

View file

@ -10,15 +10,22 @@ Variable names: data [array]
data[array_index] :: Usable parameters
== Global keys ==
media_type Returns the type of media. Either 'movie' or 'episode' or 'album'.
rating_key Returns the unique identifier for the media item.
media_type Returns the media type of media. Either 'movie' or 'season' or 'album'.
parent_rating_key Returns the unique identifier for the season or artist.
grandparent_rating_key Returns the unique identifier for the show.
title Returns the name of the movie, episode, album.
parent_title Returns the name of the artist.
grandparent_title Returns the name of the show.
media_index Returns the index number of the episode.
parent_media_index Returns the index number of the season.
section_id Returns the library section number of the media item.
library_name Returns the library section name of the media item.
year Returns the release year of the movie, episode, or album.
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
parent_thumb Returns the location of the artist's thumbnail. Use with pms_image_proxy.
grandparent_thumb Returns the location of the show's thumbnail. Use with pms_image_proxy.
added_at Returns the time when the media was added to the library.
title Returns the name of the movie or season.
parent_title Returns the name of the TV Show a season belongs too.
== Only if 'media_type' is 'movie' ==
year Returns the movie release year.
DOCUMENTATION :: END
</%doc>
@ -30,7 +37,7 @@ DOCUMENTATION :: END
<div class="dashboard-recent-media-instance">
<li>
% if item['media_type'] == 'season' or item['media_type'] == 'movie':
<a href="info?item_id=${item['rating_key']}">
<a href="info?rating_key=${item['rating_key']}">
<div class="dashboard-recent-media-poster">
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=450&fallback=poster);">
<div class="dashboard-recent-media-overlay">
@ -53,7 +60,7 @@ DOCUMENTATION :: END
</div>
</a>
% elif item['media_type'] == 'album':
<a href="info?item_id=${item['rating_key']}">
<a href="info?rating_key=${item['rating_key']}">
<div class="dashboard-recent-media-cover">
<div class="dashboard-recent-media-cover-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=300&fallback=cover);">
<div class="dashboard-recent-media-overlay">

View file

@ -98,25 +98,78 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
</div>
<div class="form-group">
<label for="home_stats_cards">Cards</label>
<label for="sortable_home_stats_cards">Cards</label>
<p class="help-block">
Select the cards to show in the watch statistics on the home page. Select none to disable.<br>
Drag the items below to reorder your homepage content.
</p>
<div class="row">
<div class="col-md-6">
<select multiple class="form-control" id="home_stats_cards" name="home_stats_cards" data-parsley-trigger="change">
<option id="card-watch_statistics" value="watch_statistics" class="hidden" selected>Watch Statistics</option>
<option id="card-top_tv" value="top_tv">Most Watched TV</option>
<option id="card-popular_tv" value="popular_tv">Most Popular TV</option>
<option id="card-top_movies" value="top_movies">Most Watched Movie</option>
<option id="card-popular_movies" value="popular_movies">Most Popular Movie</option>
<option id="card-top_music" value="top_music">Most Listened Music</option>
<option id="card-popular_music" value="popular_music">Most Popular Music</option>
<option id="card-last_watched" value="last_watched">Last Watched</option>
<option id="card-top_users" value="top_users">Most Active User</option>
<option id="card-top_platforms" value="top_platforms">Most Active Platform</option>
<option id="card-most_concurrent" value="most_concurrent">Most Concurrent Streams</option>
</select>
<ul class="list-unstyled" id="sortable_home_stats_cards" data-parsley-trigger="change">
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-top_tv" name="hscard-top_tv" value="top_tv"> Most Watched TV
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-popular_tv" name="hscard-popular_tv" value="popular_tv"> Most Popular TV
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-top_movies" name="hscard-top_movies" value="top_movies"> Most Watched Movie
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-popular_movies" name="hscard-popular_movies" value="popular_movies"> Most Popular Movie
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-top_music" name="hscard-top_music" value="top_music"> Most Listened Music
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-popular_music" name="hscard-popular_music" value="popular_music"> Most Popular Music
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-last_watched" name="hscard-last_watched" value="last_watched"> Last Watched
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-top_users" name="hscard-top_users" value="top_users"> Most Active User
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-top_platforms" name="hscard-top_platforms" value="top_platforms"> Most Active Platform
</label>
</li>
<li class="card card-sortable">
<div class="card-handle"><i class="fa fa-bars"></i></div>
<label>
<input type="checkbox" id="hscard-most_concurrent" name="hscard-most_concurrent" value="most_concurrent"> Most Concurrent Streams
</label>
</li>
</ul>
<input type="text" id="home_stats_cards" name="home_stats_cards" style="display: none;" />
</div>
</div>
<p class="help-block">Select the cards to show in the watch statistics on the home page. Select none to disable.</p>
</div>
<div class="form-group">
<label for="home_stats_length">Time Frame</label>
@ -151,14 +204,17 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
<div class="form-group">
<label for="home_library_cards">Cards</label>
<p class="help-block">
Select the cards to show in the library statistics on the home page. Select none to disable.<br>
Drag the items below to reorder your homepage content.
</p>
<div class="row">
<div class="col-md-6">
<select multiple class="form-control" id="home_library_cards" name="home_library_cards" data-parsley-trigger="change">
<option id="card-library_statistics" value="library_statistics" class="hidden" selected>Library Statistics</option>
</select>
<ul class="list-unstyled" id="sortable_home_library_cards" data-parsley-trigger="change">
</ul>
<input type="text" id="home_library_cards" name="home_library_cards" style="display: none;" />
</div>
</div>
<p class="help-block">Select the cards to show in the library statistics on the home page. Select none to disable.</p>
</div>
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
@ -348,7 +404,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
<h3>Friends List</h3>
</div>
<div class="form-group">
<label for="refresh_users_interval">User List Refresh Interval</label>
<label for="refresh_users_interval">Users List Refresh Interval</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="refresh_users_interval" name="refresh_users_interval" value="${config['refresh_users_interval']}" size="5" data-parsley-range="[1,24]" data-parsley-trigger="change" data-parsley-errors-container="#refresh_users_interval_error" required>
@ -359,9 +415,29 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="refresh_users_on_startup" name="refresh_users_on_startup" value="1" ${config['refresh_users_on_startup']}> Refresh User List on Startup
<input type="checkbox" id="refresh_users_on_startup" name="refresh_users_on_startup" value="1" ${config['refresh_users_on_startup']}> Refresh Users List on Startup
</label>
<p class="help-block">Refresh the user list when PlexPy starts.</p>
<p class="help-block">Refresh the users list when PlexPy starts.</p>
</div>
<div class="padded-header">
<h3>Libraries List</h3>
</div>
<div class="form-group">
<label for="refresh_libraries_interval">Libraries List Refresh Interval</label>
<div class="row">
<div class="col-md-2">
<input type="text" class="form-control" data-parsley-type="integer" id="refresh_libraries_interval" name="refresh_libraries_interval" value="${config['refresh_libraries_interval']}" size="5" data-parsley-range="[1,24]" data-parsley-trigger="change" data-parsley-errors-container="#refresh_libraries_interval_error" required>
</div>
<div id="refresh_libraries_interval_error" class="alert alert-danger settings-alert" role="alert"></div>
</div>
<p class="help-block">The interval (in hours) PlexPy will request an updated libraries list from your Plex Media Server. 1 minimum, 24 maximum.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="refresh_libraries_on_startup" name="refresh_libraries_on_startup" value="1" ${config['refresh_libraries_on_startup']}> Refresh Libraries List on Startup
</label>
<p class="help-block">Refresh the libraries list when PlexPy starts.</p>
</div>
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
@ -377,6 +453,13 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
</label>
<p class="help-block">If you have media indexing enabled on your server, use these on the activity pane.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="get_file_sizes" name="get_file_sizes" value="1" ${config['get_file_sizes']}> Calculate Total File Sizes [experimental]
</label>
<p class="help-block">Enable if you want PlexPy to calculate the total file size for TV Shows/Seasons and Artists/Albums on the media info tables.<br />
This is currently experimental.</p>
</div>
<div class="padded-header">
<h3>PlexWatch Import Tool</h3>
@ -402,9 +485,10 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
</div>
<div class="checkbox">
<label>
<input type="checkbox" class="monitor-settings" id="monitoring_use_websocket" name="monitoring_use_websocket" value="1" ${config['monitoring_use_websocket']}> Use Websocket (requires restart)
<input type="checkbox" class="monitor-settings" id="monitoring_use_websocket" name="monitoring_use_websocket" value="1" ${config['monitoring_use_websocket']}> Use Websocket (requires restart) [experimental]
</label>
<p class="help-block">Instead of polling the server at regular intervals let the server tell us when something happens. This is currently experimental. Encrypted websocket is not currently supported.</p>
<p class="help-block">Instead of polling the server at regular intervals let the server tell us when something happens.<br />
This is currently experimental. Encrypted websocket is not currently supported.</p>
</div>
<div class="checkbox">
<label>
@ -761,11 +845,9 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
<input class="form-control" type="text" id="notify_scripts_args_text" name="notify_scripts_args_text" value="${config['notify_scripts_args_text']}" data-parsley-trigger="change">
<p class="help-block">Set custom arguments passed to the scripts.</p>
</div>
</li>
</ul>
</li>
</ul>
<p><input type="button" class="btn btn-bright save-button" value="Save" data-success="Changes saved successfully"></p>
</div>
@ -782,7 +864,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
% for agent in available_notification_agents:
<li>
<span>
% if agent['on_play'] or agent['on_stop'] or agent['on_pause'] or agent['on_resume'] or agent['on_buffer'] or agent['on_watched'] or agent['on_created'] or agent['on_extdown'] or agent['on_intdown']:
% if any(k[:2] == 'on' and v == 1 for k, v in agent.iteritems()):
<a href="javascript:void(0)" data-target="#notification-triggers-modal" data-id="${agent['id']}" class="toggle-notification-triggers-modal toggle-left active" data-toggle="modal"><i class="fa fa-lg fa-bell"></i></a>
% else:
<a href="javascript:void(0)" data-target="#notification-triggers-modal" data-id="${agent['id']}" class="toggle-notification-triggers-modal toggle-left" data-toggle="modal"><i class="fa fa-lg fa-bell"></i></a>
@ -1149,6 +1231,10 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
<td><strong>{title}</strong></td>
<td>The full title of the item being played.</td>
</tr>
<tr>
<td><strong>{library_name}</strong></td>
<td>The library title of the item being played.</td>
</tr>
<tr>
<td><strong>{show_name}</strong></td>
<td>The title of the TV series being played.</td>
@ -1313,6 +1399,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
<%def name="javascriptIncludes()">
<script src="interfaces/default/js/parsley.min.js"></script>
<script src="interfaces/default/js/Sortable.min.js"></script>
<script>
$(document).ready(function() {
@ -1599,46 +1686,80 @@ $(document).ready(function() {
var accordion_timeline = new Accordion($('#accordion-timeline'), false);
var accordion_scripts = new Accordion($('#accordion-scripts'), false);
var cards = "${config['home_stats_cards']}".split(/[\s,]+/);
cards.forEach(function (item) {
$('#card-'+item).prop('selected', !$(this).prop('selected'));
// Sortable home_stats_cards
function set_home_stats_cards() {
var home_stats_cards = [];
var hscards = $('[id^=hscard-]').serializeArray();
$.each(hscards, function(i, card) {
home_stats_cards.push(card.value);
});
$('#home_stats_cards').on('mousedown', function(e) {
e.preventDefault();
var scroll = this.scrollTop;
e.target.selected = !e.target.selected;
this.scrollTop = scroll;
}).on('mousemove', function(e) {
e.preventDefault()
$('#home_stats_cards').val(home_stats_cards);
};
var config_cards = ${config['home_stats_cards'] | n};
config_cards.reverse().forEach(function (item) {
$('#hscard-' + item).prop('checked', !$(this).prop('checked'))
$('#hscard-' + item).closest('li.card').prependTo('#sortable_home_stats_cards');
});
Sortable.create(sortable_home_stats_cards, {
animation: 250,
onSort: function(elem, ui) {
set_home_stats_cards();
}
});
$('[id^=hscard-]').change(function() { set_home_stats_cards(); });
set_home_stats_cards()
// Sortable home_library_cards
function set_home_library_cards() {
var home_library_cards = [];
var hlcards = $('[id^=hlcard-]').serializeArray();
$.each(hlcards, function(i, card) {
home_library_cards.push(card.value);
});
$('#home_library_cards').val(home_library_cards);
};
$.ajax({
url: 'get_server_children',
url: 'get_library_sections',
data: { },
async: true,
complete: function (xhr, status) {
server_children_info = $.parseJSON(xhr.responseText);
libraries_list = server_children_info.libraries_list;
complete: function (data) {
libraries_list = $.parseJSON(data.responseText);
for (var i in libraries_list) {
title = libraries_list[i].title;
key = libraries_list[i].key;
$('#home_library_cards').append('<option id="card-' + key + '" value="' + key + '">' + title + '</option>')
var title = libraries_list[i].section_name;
var key = libraries_list[i].section_id;
$('#sortable_home_library_cards').append(
'<li class="card card-sortable">' +
'<div class="card-handle"><i class="fa fa-bars"></i></div>' +
'<label>' +
'<input type="checkbox" id="hlcard-' + key + '" name="hlcard-' + key + '" value="' + key + '"> ' + title +
'</label>' +
'</li>'
);
}
var cards = "${config['home_library_cards']}".split(/[\s,]+/);
cards.forEach(function (item) {
$('#card-'+item).prop('selected', !$(this).prop('selected'));
var config_cards = ${config['home_library_cards'] | n};
config_cards.reverse().forEach(function (item) {
$('#hlcard-' + item).prop('checked', !$(this).prop('checked'));
$('#hlcard-' + item).closest('li.card').prependTo('#sortable_home_library_cards');
});
$('[id^=hlcard-]').change(function() { set_home_library_cards(); });
set_home_library_cards()
}
});
$('#home_library_cards').on('mousedown', function(e) {
e.preventDefault();
var scroll = this.scrollTop;
e.target.selected = !e.target.selected;
this.scrollTop = scroll;
}).on('mousemove', function(e) {
e.preventDefault()
Sortable.create(sortable_home_library_cards, {
animation: 250,
onSort: function(elem, ui) {
set_home_library_cards();
}
});
// auto resizing textarea for custom notification message body
$('textarea[data-autoresize]').each(function() {
var offset = this.offsetHeight - this.clientHeight;

View file

@ -54,7 +54,7 @@
<script>
$(document).ready(function() {
sync_table_options.ajax = {
"url": "get_sync"
url: 'get_sync'
}
sync_table = $('#sync_table').DataTable(sync_table_options);
var colvis = new $.fn.dataTable.ColVis( sync_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark' } );

View file

@ -0,0 +1,200 @@
<%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: update_metadata.html
Version: 0.1
Variable names: query [list]
query :: Usable parameters
== Global keys ==
query_string Returns the string used for the search query.
title Returns the name of the movie, episode, or track.
parent_title Returns the name of the album.
grandparent_title Returns the name of the show, or artist.
media_index Returns the index number of the episode, or track.
parent_media_index Returns the index number of the season.
year Returns the release year of the movie, or show.
media_type Returns the type of media. Either 'movie', 'show', 'season', 'episode', 'artist', 'album', or 'track'.
rating_key Returns the unique identifier for the media item.
DOCUMENTATION :: END
</%doc>
<%inherit file="base.html"/>
<%def name="headIncludes()">
</%def>
<%def name="body()">
% if query:
<div class="container-fluid">
<div class="row">
<div class="summary-container">
<div class="summary-navbar">
<div class="col-md-12">
<div class="summary-navbar-list">
<ul class="list-unstyled breadcrumb">
% if query['media_type'] == 'movie':
<li>Movies</li>
<li class="active">${query['title']}</li>
% elif query['media_type'] == 'show':
<li>TV Shows</li>
<li class="active">${query['grandparent_title']}</li>
% elif query['media_type'] == 'season':
<li class="hidden-xs hidden-sm">TV Shows</li>
<li class="hidden-xs hidden-sm">${query['grandparent_title']}</li>
<li class="active">Season ${query['parent_media_index']}</li>
% elif query['media_type'] == 'episode':
<li class="hidden-xs hidden-sm">TV Shows</li>
<li class="hidden-xs hidden-sm">${query['grandparent_title']}</li>
<li>Season ${query['parent_media_index']}</li>
<li class="active">Episode ${query['media_index']} - ${query['title']}</li>
% elif query['media_type'] == 'artist':
<li><Music</li>
<li class="active">${query['grandparent_title']}</li>
% elif query['media_type'] == 'album':
<li class="hidden-xs hidden-sm">Music</li>
<li>${query['grandparent_title']}</li>
<li class="active">${query['parent_title']}</li>
% elif query['media_type'] == 'track':
<li class="hidden-xs hidden-sm">Music</li>
<li class="hidden-xs hidden-sm">${query['grandparent_title']}</li>
<li>${query['parent_title']}</li>
<li class="active">Track ${query['media_index']} - ${query['title']}</li>
% endif
</ul>
</div>
</div>
</div>
<div class="summary-content-wrapper">
<div class='col-md-12'>
<div style="text-align: center; margin-top: 20px;">
<i class="fa fa-exclamation-triangle"></i> Error retrieving item metadata. This media item is not available in the Plex Media Server library.
</div>
<div style="text-align: center; margin-top: 20px;">
Please select the correct match below to update the PlexPy database.
</div>
<div class='table-card-header'>
<div class="header-bar">
<span>Search Results for <strong>${query['query_string']}</strong></span>
</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>
<div class="modal fade" id="confirm-modal" tabindex="-1" role="dialog" aria-labelledby="confirm-modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
<h4 class="modal-title" id="myModalLabel">Confirm Update</h4>
</div>
<div class="modal-body" style="text-align: center;">
<p>Are you REALLY sure you want to replace
<p><strong>
% if query['media_type'] == 'movie':
${query['title']}<br />${query['year']}
% elif query['media_type'] == 'show':
${query['grandparent_title']}
% elif query['media_type'] == 'season':
${query['grandparent_title']}<br />S${query['parent_media_index']}
% elif query['media_type'] == 'episode':
${query['grandparent_title']}<br />${query['title']}<br />S${query['parent_media_index']} &middot; E${query['media_index']}
% elif query['media_type'] == 'artist':
${query['grandparent_title']}
% elif query['media_type'] == 'album':
${query['grandparent_title']}<br />${query['parent_title']}
% elif query['media_type'] == 'track':
${query['grandparent_title']}<br />${query['title']}<br />${query['parent_title']}
% endif
</strong></p>
<p> with </p>
<p><span id="new_title"></span></p>
% if query['media_type'] != 'movie':
<p>All items for <strong>${query['grandparent_title']}</strong> will also be updated.</p>
% endif
<p>This is permanent and cannot be undone!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-dark" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-update">Update</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
% else:
<div class="container-fluid">
<div class="row">
<div class="summary-container">
<div class="summary-navbar">
<div class="col-md-12">
<div class="summary-navbar-list">
</div>
</div>
</div>
<div class="summary-content-wrapper">
<div class='col-md-12'>
<div style="text-align: center; margin-top: 20px;">
<i class="fa fa-exclamation-triangle"></i> Error retrieving item metadata. This media item is not available in the Plex Media Server library.
</div>
</div>
</div>
</div>
</div>
</div>
% endif
</%def>
<%def name="javascriptIncludes()">
% if query:
<script>
$.ajax({
url: 'get_search_results_children',
type: 'GET',
async: true,
data: {'query': "${query['query_string']}",
'media_type': "${query['media_type']}",
'season_index': "${query['parent_media_index']}"
},
complete: function(xhr, status) {
$("#search-results-list").html(xhr.responseText); }
});
$(document).on('click', '#search-results-list a', function (e) {
e.preventDefault();
var new_rating_key = $(this).attr("id");
var new_href = $(this).attr("href");
$('#new_title').html($(this).find('.item-children-instance-text-wrapper').html());
$('#confirm-modal').modal();
$('#confirm-modal').one('click', '#confirm-update', function () {
$(this).prop('disabled', true);
var msg = "<i class='fa fa-refresh fa-spin'></i>&nbspUpdating database..."
showMsg(msg, false, false, 0)
$.ajax({
url: 'update_metadata_details',
data: {
old_rating_key: "${query['rating_key']}",
new_rating_key: new_rating_key,
media_type: "${query['media_type']}"
},
async: true,
success: function (data) {
window.location.href = new_href;
}
});
});
});
</script>
% endif
</%def>

View file

@ -13,10 +13,12 @@ user_id Returns the user id of the user.
username Returns the user's username.
friendly_name Returns the friendly name of the user.
email Returns the user's email address.
thumb Returns the thumbnail for the user.
user_thumb Returns the thumbnail for the user.
is_home_user Returns bool value for whether the user is part of a Plex Home.
is_allow_sync Returns bool value for whether the user has sync rights.
is_restricted Returns bool value for whether the user account is restricted.
do_notify Returns bool value for whether to send notifications for the user.
keep_history Returns bool value for whether to keep history for the user.
DOCUMENTATION :: END
@ -33,17 +35,28 @@ from plexpy import helpers
<link rel="stylesheet" href="interfaces/default/css/plexpy-dataTables.css">
</%def>
% if user != None:
<%def name="body()">
% if data:
<div class="container-fluid">
<div class="row">
<div class="summary-container">
<div class="summary-navbar">
<div class="col-md-12">
<div class="summary-navbar-list">
<ul class="list-unstyled breadcrumb"></ul>
</div>
</div>
</div>
<div class="summary-content-wrapper">
<div class="col-md-12">
<div class="table-card-back">
<div class="user-info-wrapper">
<div class="user-info-poster-face" id="user-gravatar" style="background-image: url(${data['thumb']});">
</div>
<div class="user-info-poster-face" style="background-image: url(${data['user_thumb']});"></div>
<div class="user-info-username">
<span class="set-username">${data['friendly_name']}</span> <span id="edit-user-tooltip" data-target="tooltip" title="Edit user details"><a href="#" data-toggle="modal" data-target="#edit-user-modal" id="toggle-edit-user-modal"><i class="fa fa-pencil"></i></a></span>
<span class="set-username">${data['friendly_name']}</span>
<span id="edit-user-tooltip" data-target="tooltip" title="Edit user details">
<a href="#" data-toggle="modal" data-target="#edit-user-modal" id="toggle-edit-user-modal"><i class="fa fa-pencil"></i></a>
</span>
</div>
<div class="user-info-nav">
<ul class="user-info-nav">
@ -56,11 +69,9 @@ from plexpy import helpers
</div>
</div>
</div>
</div>
<div id="edit-user-modal" class="modal fade" tabindex="-1" role="dialog"
aria-labelledby="edit-user-modal">
</div>
</div>
<div class="tab-content">
<div class="tab-pane active" id="profile">
<div class="container-fluid">
@ -121,9 +132,11 @@ from plexpy import helpers
<div class="col-md-12">
<div class="table-card-header">
<div class="header-bar">
<span><i class="fa fa-map-marker"></i> IP Addresses for <strong>
<span>
<i class="fa fa-map-marker"></i> IP Addresses for <strong>
<span class="set-username">${data['friendly_name']}</span>
</strong></span>
</strong>
</span>
</div>
</div>
<div class="table-card-back">
@ -150,9 +163,11 @@ from plexpy import helpers
<div class="col-md-12">
<div class='table-card-header'>
<div class="header-bar">
<span><i class="fa fa-history"></i> Watch History for <strong>
<span>
<i class="fa fa-history"></i> Watch History for <strong>
<span class="set-username">${data['friendly_name']}</span>
</strong></span>
</strong>
</span>
</div>
<div class="button-bar">
<div class="colvis-button-bar hidden-xs" id="button-bar-history"></div>
@ -166,22 +181,21 @@ from plexpy import helpers
<table class="display" id="history_table" width="100%">
<thead>
<tr>
<th align='left' id="delete">Delete</th>
<th align='left' id="time">Time</th>
<th align='left' id="friendly_name">User</th>
<th align='left' id="ip_address">IP Address</th>
<th align='left' id="platform">Platform</th>
<th align='left' id="player">Player</th>
<th align='left' id="title">Title</th>
<th align='left' id="started">Started</th>
<th align='left' id="paused_counter">Paused</th>
<th align='left' id="stopped">Stopped</th>
<th align='left' id="duration">Duration</th>
<th align='left' id="percent_complete"></th>
<th align="left" id="delete">Delete</th>
<th align="left" id="time">Time</th>
<th align="left" id="friendly_name">User</th>
<th align="left" id="ip_address">IP Address</th>
<th align="left" id="platform">Platform</th>
<th align="left" id="player">Player</th>
<th align="left" id="title">Title</th>
<th align="left" id="started">Started</th>
<th align="left" id="paused_counter">Paused</th>
<th align="left" id="stopped">Stopped</th>
<th align="left" id="duration">Duration</th>
<th align="left" id="percent_complete"></th>
</tr>
</thead>
<tbody>
</tbody>
<tbody></tbody>
</table>
</div>
</div>
@ -194,9 +208,11 @@ from plexpy import helpers
<div class="col-md-12">
<div class='table-card-header'>
<div class="header-bar">
<span><i class="fa fa-cloud-download"></i> Synced Items for <strong>
<span>
<i class="fa fa-cloud-download"></i> Synced Items for <strong>
<span class="set-username">${data['friendly_name']}</span>
</strong></span>
</strong>
</span>
</div>
<div class="colvis-button-bar hidden-xs" id="button-bar-sync">
</div>
@ -205,21 +221,20 @@ from plexpy import helpers
<table class="display" id="sync_table" width="100%">
<thead>
<tr>
<th align='left' id="state">State</th>
<th align='left' id="username">Username</th>
<th align='left' id="sync_title">Title</th>
<th align='left' id="type">Type</th>
<th align='left' id="sync_platform">Platform</th>
<th align='left' id="device">Device</th>
<th align='left' id="size">Total Size</th>
<th align='left' id="items">Total Items</th>
<th align='left' id="converted">Converted</th>
<th align='left' id="downloaded">Downloaded</th>
<th align='left' id="sync_percent_complete">Complete</th>
<th align="left" id="state">State</th>
<th align="left" id="username">Username</th>
<th align="left" id="sync_title">Title</th>
<th align="left" id="type">Type</th>
<th align="left" id="sync_platform">Platform</th>
<th align="left" id="device">Device</th>
<th align="left" id="size">Total Size</th>
<th align="left" id="items">Total Items</th>
<th align="left" id="converted">Converted</th>
<th align="left" id="downloaded">Downloaded</th>
<th align="left" id="sync_percent_complete">Complete</th>
</tr>
</thead>
<tbody>
</tbody>
<tbody></tbody>
</table>
</div>
</div>
@ -249,7 +264,32 @@ from plexpy import helpers
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<footer></footer>
% else:
<div class="container-fluid">
<div class="row">
<div class="summary-container">
<div class="summary-navbar">
<div class="col-md-12">
<div class="summary-navbar-list">
</div>
</div>
</div>
<div class="summary-content-wrapper">
<div class='col-md-12'>
<div style="text-align: center; margin-top: 20px;">
<i class="fa fa-exclamation-triangle"></i> Error retrieving user information. Please see the logs for more details.
</div>
</div>
</div>
</div>
</div>
</div>
% endif
</%def>
<%def name="javascriptIncludes()">
@ -257,6 +297,7 @@ from plexpy import helpers
<script src="interfaces/default/js/dataTables.colVis.js"></script>
<script src="interfaces/default/js/dataTables.bootstrap.min.js"></script>
<script src="interfaces/default/js/dataTables.bootstrap.pagination.js"></script>
% if data:
<script src="interfaces/default/js/moment-with-locale.js"></script>
<script src="interfaces/default/js/tables/history_table.js"></script>
<script src="interfaces/default/js/tables/user_ips.js"></script>
@ -264,7 +305,7 @@ from plexpy import helpers
<script>
$(document).ready(function () {
% if data['user_id']:
% if str(data['user_id']).isdigit():
var user_id = ${data['user_id']};
% else:
var user_id = null;
@ -301,10 +342,9 @@ from plexpy import helpers
type: 'post',
data: function ( d ) {
return {
'json_data': JSON.stringify( d ),
'user_id': user_id,
'user': username,
'media_type': media_type
json_data: JSON.stringify( d ),
user_id: user_id,
media_type: media_type
};
}
}
@ -348,12 +388,12 @@ from plexpy import helpers
$( "#ip-tab-btn" ).one( "click", function() {
// Build user IP table
user_ip_table_options.ajax = {
"url": "get_user_ips",
url: 'get_user_ips',
type: 'post',
data: function ( d ) {
return { 'json_data': JSON.stringify( d ),
'user_id': user_id,
'user': username
return {
json_data: JSON.stringify( d ),
user_id: user_id
};
}
}
@ -365,10 +405,9 @@ from plexpy import helpers
$( "#sync-tab-btn" ).one( "click", function() {
// Build user sync table
sync_table_options.ajax = {
"url": "get_sync",
"data": function(d) {
url: 'get_sync',
data: function(d) {
d.user_id = user_id;
d.user = username;
}
}
sync_table = $('#sync_table').DataTable(sync_table_options);
@ -385,7 +424,7 @@ from plexpy import helpers
$("#edit-user-tooltip").tooltip('hide');
$.ajax({
url: 'edit_user_dialog',
data: { user_id: user_id, user: username },
data: { user_id: user_id },
cache: false,
async: true,
complete: function(xhr, status) {
@ -432,26 +471,23 @@ from plexpy import helpers
});
function recentlyWatched() {
var widthVal = $('body').find("#user-recently-watched").width();
var tmp = (widthVal-32) / 180;
var widthVal = $("#user-recently-watched").width();
var tmp = (widthVal-25) / 175;
if (tmp > 0) {
containerSize = parseInt(tmp);
var containerSize = parseInt(tmp);
} else {
containerSize = 1;
var containerSize = 1;
}
% if data['user_id']:
var user_id = ${data['user_id']};
% else:
var user_id = null;
% endif
// Populate recently watched
$.ajax({
url: 'get_user_recently_watched',
async: true,
data: { user_id: user_id, user: username, limit: containerSize },
data: {
user_id: user_id,
limit: containerSize
},
complete: function(xhr, status) {
$("#user-recently-watched").html(xhr.responseText);
}
@ -464,14 +500,5 @@ from plexpy import helpers
});
});
</script>
</%def>
% else:
<div class="clear"></div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span10 offset1">
<h3>Error retrieving user information. Please see the logs for more details.</h3>
</div>
</div>
</div>
% endif
</%def>

View file

@ -18,7 +18,7 @@ total_plays Returns the play count for the player.
DOCUMENTATION :: END
</%doc>
% if data != None:
% if data:
% for a in data:
<ul class="list-unstyled">
<div class="user-player-instance">
@ -39,6 +39,5 @@ DOCUMENTATION :: END
</script>
% endfor
% else:
<div class="text-muted">Unable to retrieve data from database. Please check your <a href="settings">settings</a>.
</div><br>
<div class="text-muted">No stats to show.</div><br>
% endif

View file

@ -12,29 +12,27 @@ data[array_index] :: Usable parameters
== Global keys ==
rating_key Returns the unique identifier for the media item.
row_id Returns the unique row id for the media item in the database.
type Returns the type of media. Either 'movie' or 'episode'.
media_type Returns the type of media. Either 'movie' or 'episode' or 'album'.
thumb Returns the location of the item's thumbnail. Use with pms_image_proxy.
time Returns the last watched time of the media.
title Returns the name of the movie or episode.
== Only if 'type' is 'episode ==
parent_title Returns the name of the TV Show a season belongs too.
parent_index Returns the season number.
index Returns the episode number.
== Only if 'type' is 'movie' ==
year Returns the movie release year.
user Returns the name of the user that watched the item.
title Returns the name of the movie, episode, album.
parent_title Returns the name of the artist.
grandparent_title Returns the name of the show.
media_index Returns the index number of the episode.
parent_media_index Returns the index number of the season.
year Returns the release year of the movie, episode, or album.
DOCUMENTATION :: END
</%doc>
% if data != None:
% if data:
<div class="dashboard-recent-media-row">
<ul class="dashboard-recent-media list-unstyled">
% for item in data:
<li>
% if item['type'] == 'episode' or item['type'] == 'movie':
<a href="info?source=history&item_id=${item['row_id']}">
% if item['media_type'] == 'episode' or item['media_type'] == 'movie':
<a href="info?source=history&rating_key=${item['rating_key']}">
<div class="dashboard-recent-media-poster">
<div class="dashboard-recent-media-poster-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=450&fallback=poster);">
<div class="dashboard-recent-media-overlay">
@ -47,20 +45,18 @@ DOCUMENTATION :: END
</div>
</div>
<div class="dashboard-recent-media-metacontainer">
% if item['type'] == 'episode':
% if item['media_type'] == 'episode':
<h3 title="${item['grandparent_title']}">${item['grandparent_title']}</h3>
<h3 title="${item['title']}">${item['title']}</h3>
<h3 class="text-muted">S${item['parent_index']} &middot; E${item['index']}</h3>
% elif item['type'] == 'movie':
<h3 class="text-muted">S${item['parent_media_index']} &middot; E${item['media_index']}</h3>
% elif item['media_type'] == 'movie':
<h3 title="${item['title']}">${item['title']}</h3>
<h3 class="text-muted">${item['year']}</h3>
% endif
<div class="text-muted" id="time-${item['time']}">
</div>
</div>
</a>
% elif item['type'] == 'track':
<a href="info?source=history&item_id=${item['row_id']}">
% elif item['media_type'] == 'track':
<a href="info?source=history&rating_key=${item['rating_key']}">
<div class="dashboard-recent-media-cover">
<div class="dashboard-recent-media-cover-face" style="background-image: url(pms_image_proxy?img=${item['thumb']}&width=300&height=300&fallback=cover);">
<div class="dashboard-recent-media-overlay">
@ -84,6 +80,5 @@ DOCUMENTATION :: END
</ul>
</div>
% else:
<div class="text-muted">Unable to retrieve data from database. Please check your <a href="settings">settings</a>.
</div><br>
<div class="text-muted">No stats to show.</div><br>
% endif

View file

@ -16,7 +16,7 @@ total_plays Returns the play count for the watch stat period..
DOCUMENTATION :: END
</%doc>
% if data != None:
% if data:
<ul class="list-unstyled">
% for a in data:
<div class='user-overview-stats-instance'>
@ -43,6 +43,6 @@ DOCUMENTATION :: END
% endfor
</ul>
% else:
<div class="text-muted">Unable to retrieve data from database. Please check your <a href="settings">settings</a>.
<div class="text-muted">Unable to retrieve data from database.
</div><br>
% endif

View file

@ -77,7 +77,9 @@
url: 'get_user_list',
type: 'POST',
data: function ( d ) {
return { 'json_data': JSON.stringify( d ) };
return {
json_data: JSON.stringify(d)
};
}
}
@ -177,10 +179,13 @@
cache: false,
async: true,
success: function(data) {
showMsg('<i class="fa fa-check"></i>&nbspUser list refresh started...',false,true,2000,false)
showMsg('<i class="fa fa-check"></i>&nbspUsers list refresh started...', false, true, 2000, false);
},
complete: function (data) {
showMsg('<i class="fa fa-check"></i>&nbspUsers list refreshed.', false, true, 2000, false);
},
error: function (jqXHR, textStatus, errorThrown) {
showMsg('<i class="fa fa-exclamation-circle"></i>&nbspUnable to refresh user list.',false,true,2000,true)
showMsg('<i class="fa fa-exclamation-circle"></i>&nbspUnable to refresh users list.', false, true, 2000, true);
}
});
});

View file

@ -168,8 +168,11 @@ from plexpy import common
<div style="display: none;">
<input type="checkbox" name="launch_browser" id="launch_browser" value="1" ${config['launch_browser']}>
<input type="checkbox" name="refresh_users_on_startup" id="refresh_users_on_startup" value="1" ${config['refresh_users_on_startup']}>
<input type="checkbox" name="refresh_libraries_on_startup" id="refresh_libraries_on_startup" value="1" ${config['refresh_libraries_on_startup']}>
<input type="checkbox" name="first_run_complete" id="first_run_complete" value="1" checked>
<input type="checkbox" name="check_github" id="check_github" value="1" checked>
<input type="text" name="home_stats_cards" id="home_stats_cards" value="first_run_wizard">
<input type="text" name="home_library_cards" id="home_library_cards" value="first_run_wizard">
</div>
</div>
<!-- Required fields but hidden -->
@ -370,12 +373,13 @@ from plexpy import common
var authenticated = false;
$("#verify-plex-server").click(function () {
var pms_ip = $("#pms_ip").val()
var pms_port = $("#pms_port").val()
var pms_identifier = $("#pms_identifier").val()
var pms_ssl = $("#pms_ssl").val()
var pms_is_remote = $("#pms_is_remote").val()
if (($("#pms_ip").val() !== '') || ($("#pms_port").val() !== '')) {
if (!(pms_verified)) {
var pms_ip = $("#pms_ip").val().trim();
var pms_port = $("#pms_port").val().trim();
var pms_identifier = $("#pms_identifier").val();
var pms_ssl = $("#pms_ssl").val();
var pms_is_remote = $("#pms_is_remote").val();
if ((pms_ip !== '') || (pms_port !== '')) {
$("#pms-verify-status").html('<i class="fa fa-refresh fa-spin"></i> Validating server...');
$('#pms-verify-status').fadeIn('fast');
$.ajax({
@ -405,6 +409,7 @@ from plexpy import common
$("#pms-verify-status").html('<i class="fa fa-exclamation-circle"></i> Please enter both fields.');
$('#pms-verify-status').fadeIn('fast');
}
}
});
$( ".pms-settings" ).change(function() {
@ -423,20 +428,23 @@ from plexpy import common
$("#pms-authenticate").click(function() {
$("#pms-token-status").html('<i class="fa fa-refresh fa-spin"></i> Fetching token...');
$('#pms-token-status').fadeIn('fast');
if (($("#pms_username").val() !== '') || ($("#pms_password").val() !== '')) {
var pms_username = $("#pms_username").val().trim();
var pms_password = $("#pms_password").val().trim();
if ((pms_username !== '') || (pms_password !== '')) {
$.ajax({
type: "post",
url: "https://plex.tv/users/sign_in.xml",
dataType: 'xml',
async: true,
headers: {'Content-Type': 'application/xml; charset=utf-8',
headers: {
'Content-Type': 'application/xml; charset=utf-8',
'X-Plex-Device-Name': 'PlexPy',
'X-Plex-Product': 'PlexPy',
'X-Plex-Version': '${common.VERSION_NUMBER}',
'X-Plex-Platform': '${common.PLATFORM}',
'X-Plex-Platform-Version': '${common.PLATFORM_VERSION}',
'X-Plex-Client-Identifier': '${config['pms_uuid']}',
'Authorization': 'Basic ' + btoa($("#pms_username").val() + ':' + $("#pms_password").val())
'X-Plex-Client-Identifier': '${config["pms_uuid"]}',
'Authorization': 'Basic ' + btoa(pms_username + ':' + pms_password)
},
error: function(jqXHR, textStatus, errorThrown) {
$("#pms-token-status").html('<i class="fa fa-exclamation-circle"></i> Authentation failed!');

View file

@ -175,6 +175,10 @@ def initialize(config_file):
if CONFIG.PMS_TOKEN and CONFIG.REFRESH_USERS_ON_STARTUP:
plextv.refresh_users()
# Refresh the libraries list on startup
if CONFIG.PMS_TOKEN and CONFIG.REFRESH_LIBRARIES_ON_STARTUP:
pmsconnect.refresh_libraries()
# Store the original umask
UMASK = os.umask(0)
os.umask(UMASK)
@ -256,8 +260,6 @@ def initialize_scheduler():
Start the scheduled background tasks. Re-schedule if interval settings changed.
"""
with SCHED_LOCK:
# Check if scheduler should be started
@ -280,7 +282,7 @@ def initialize_scheduler():
else:
seconds = 0
if CONFIG.PMS_IP and CONFIG.PMS_TOKEN:
if CONFIG.PMS_IP and CONFIG.PMS_TOKEN and CONFIG.UPDATE_SECTION_IDS != -1:
schedule_job(plextv.get_real_pms_url, 'Refresh Plex Server URLs',
hours=12, minutes=0, seconds=0)
schedule_job(pmsconnect.get_server_friendly_name, 'Refresh Plex Server Name',
@ -311,8 +313,14 @@ def initialize_scheduler():
else:
hours = 0
if CONFIG.PMS_TOKEN:
schedule_job(plextv.refresh_users, 'Refresh users list', hours=hours, minutes=0, seconds=0)
schedule_job(plextv.refresh_users, 'Refresh users list',
hours=hours, minutes=0, seconds=0)
if CONFIG.PMS_IP and CONFIG.PMS_TOKEN:
schedule_job(pmsconnect.refresh_libraries, 'Refresh libraries list',
hours=hours, minutes=0, seconds=0)
# Start scheduler
if start_jobs and len(SCHED.get_jobs()):
@ -369,7 +377,7 @@ def dbcheck():
# sessions table :: This is a temp table that logs currently active sessions
c_db.execute(
'CREATE TABLE IF NOT EXISTS sessions (id INTEGER PRIMARY KEY AUTOINCREMENT, '
'session_key INTEGER, rating_key INTEGER, media_type TEXT, started INTEGER, '
'session_key INTEGER, rating_key INTEGER, section_id INTEGER, media_type TEXT, started INTEGER, '
'paused_counter INTEGER DEFAULT 0, state TEXT, user_id INTEGER, user TEXT, friendly_name TEXT, '
'ip_address TEXT, machine_id TEXT, player TEXT, platform TEXT, title TEXT, parent_title TEXT, '
'grandparent_title TEXT, parent_rating_key INTEGER, grandparent_rating_key INTEGER, '
@ -405,11 +413,10 @@ def dbcheck():
'CREATE TABLE IF NOT EXISTS session_history_metadata (id INTEGER PRIMARY KEY, '
'rating_key INTEGER, parent_rating_key INTEGER, grandparent_rating_key INTEGER, '
'title TEXT, parent_title TEXT, grandparent_title TEXT, full_title TEXT, media_index INTEGER, '
'parent_media_index INTEGER, thumb TEXT, parent_thumb TEXT, grandparent_thumb TEXT, art TEXT, media_type TEXT, '
'year INTEGER, originally_available_at TEXT, added_at INTEGER, updated_at INTEGER, last_viewed_at INTEGER, '
'content_rating TEXT, summary TEXT, tagline TEXT, rating TEXT, duration INTEGER DEFAULT 0, guid TEXT, '
'directors TEXT, writers TEXT, actors TEXT, genres TEXT, studio TEXT)'
''
'parent_media_index INTEGER, section_id INTEGER, thumb TEXT, parent_thumb TEXT, grandparent_thumb TEXT, '
'art TEXT, media_type TEXT, year INTEGER, originally_available_at TEXT, added_at INTEGER, updated_at INTEGER, '
'last_viewed_at INTEGER, content_rating TEXT, summary TEXT, tagline TEXT, rating TEXT, '
'duration INTEGER DEFAULT 0, guid TEXT, directors TEXT, writers TEXT, actors TEXT, genres TEXT, studio TEXT)'
)
# users table :: This table keeps record of the friends list
@ -421,6 +428,23 @@ def dbcheck():
'keep_history INTEGER DEFAULT 1, deleted_user INTEGER DEFAULT 0)'
)
# notify_log table :: This is a table which logs notifications sent
c_db.execute(
'CREATE TABLE IF NOT EXISTS notify_log (id INTEGER PRIMARY KEY AUTOINCREMENT, '
'session_key INTEGER, rating_key INTEGER, user_id INTEGER, user TEXT, '
'agent_id INTEGER, agent_name TEXT, on_play INTEGER, on_stop INTEGER, on_watched INTEGER, '
'on_pause INTEGER, on_resume INTEGER, on_buffer INTEGER, on_created INTEGER)'
)
# library_sections table :: This table keeps record of the servers library sections
c_db.execute(
'CREATE TABLE IF NOT EXISTS library_sections (id INTEGER PRIMARY KEY AUTOINCREMENT, '
'server_id TEXT, section_id INTEGER UNIQUE, section_name TEXT, section_type TEXT, '
'thumb TEXT, custom_thumb_url TEXT, art TEXT, count INTEGER, parent_count INTEGER, child_count INTEGER, '
'do_notify INTEGER DEFAULT 1, do_notify_created INTEGER DEFAULT 1, keep_history INTEGER DEFAULT 1, '
'deleted_section INTEGER DEFAULT 0)'
)
# Upgrade sessions table from earlier versions
try:
c_db.execute('SELECT started from sessions')
@ -547,6 +571,59 @@ def dbcheck():
'ALTER TABLE sessions ADD COLUMN transcode_height INTEGER'
)
# Upgrade sessions table from earlier versions
try:
c_db.execute('SELECT buffer_count from sessions')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table sessions.")
c_db.execute(
'ALTER TABLE sessions ADD COLUMN buffer_count INTEGER DEFAULT 0'
)
c_db.execute(
'ALTER TABLE sessions ADD COLUMN buffer_last_triggered INTEGER'
)
# Upgrade sessions table from earlier versions
try:
c_db.execute('SELECT last_paused from sessions')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table sessions.")
c_db.execute(
'ALTER TABLE sessions ADD COLUMN last_paused INTEGER'
)
# Upgrade sessions table from earlier versions
try:
c_db.execute('SELECT section_id from sessions')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table sessions.")
c_db.execute(
'ALTER TABLE sessions ADD COLUMN section_id INTEGER'
)
# Upgrade session_history table from earlier versions
try:
c_db.execute('SELECT reference_id from session_history')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table session_history.")
c_db.execute(
'ALTER TABLE session_history ADD COLUMN reference_id INTEGER DEFAULT 0'
)
# Set reference_id to the first row where (user_id = previous row, rating_key != previous row) and user_id = user_id
c_db.execute(
'UPDATE session_history ' \
'SET reference_id = (SELECT (CASE \
WHEN (SELECT MIN(id) FROM session_history WHERE id > ( \
SELECT MAX(id) FROM session_history \
WHERE (user_id = t1.user_id AND rating_key <> t1.rating_key AND id < t1.id)) AND user_id = t1.user_id) IS NULL \
THEN (SELECT MIN(id) FROM session_history WHERE (user_id = t1.user_id)) \
ELSE (SELECT MIN(id) FROM session_history WHERE id > ( \
SELECT MAX(id) FROM session_history \
WHERE (user_id = t1.user_id AND rating_key <> t1.rating_key AND id < t1.id)) AND user_id = t1.user_id) END) ' \
'FROM session_history AS t1 ' \
'WHERE t1.id = session_history.id) '
)
# Upgrade session_history_metadata table from earlier versions
try:
c_db.execute('SELECT full_title from session_history_metadata')
@ -565,12 +642,13 @@ def dbcheck():
'ALTER TABLE session_history_metadata ADD COLUMN tagline TEXT'
)
# notify_log table :: This is a table which logs notifications sent
# Upgrade session_history_metadata table from earlier versions
try:
c_db.execute('SELECT section_id from session_history_metadata')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table session_history_metadata.")
c_db.execute(
'CREATE TABLE IF NOT EXISTS notify_log (id INTEGER PRIMARY KEY AUTOINCREMENT, '
'session_key INTEGER, rating_key INTEGER, user_id INTEGER, user TEXT, '
'agent_id INTEGER, agent_name TEXT, on_play INTEGER, on_stop INTEGER, on_watched INTEGER, '
'on_pause INTEGER, on_resume INTEGER, on_buffer INTEGER, on_created INTEGER)'
'ALTER TABLE session_history_metadata ADD COLUMN section_id INTEGER'
)
# Upgrade users table from earlier versions
@ -591,6 +669,24 @@ def dbcheck():
'ALTER TABLE users ADD COLUMN keep_history INTEGER DEFAULT 1'
)
# Upgrade users table from earlier versions
try:
c_db.execute('SELECT custom_avatar_url from users')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table users.")
c_db.execute(
'ALTER TABLE users ADD COLUMN custom_avatar_url TEXT'
)
# Upgrade users table from earlier versions
try:
c_db.execute('SELECT deleted_user from users')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table users.")
c_db.execute(
'ALTER TABLE users ADD COLUMN deleted_user INTEGER DEFAULT 0'
)
# Upgrade notify_log table from earlier versions
try:
c_db.execute('SELECT on_pause from notify_log')
@ -615,74 +711,12 @@ def dbcheck():
'ALTER TABLE notify_log ADD COLUMN on_created INTEGER'
)
# Upgrade sessions table from earlier versions
try:
c_db.execute('SELECT buffer_count from sessions')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table sessions.")
c_db.execute(
'ALTER TABLE sessions ADD COLUMN buffer_count INTEGER DEFAULT 0'
)
c_db.execute(
'ALTER TABLE sessions ADD COLUMN buffer_last_triggered INTEGER'
)
# Upgrade users table from earlier versions
try:
c_db.execute('SELECT custom_avatar_url from users')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table users.")
c_db.execute(
'ALTER TABLE users ADD COLUMN custom_avatar_url TEXT'
)
# Upgrade sessions table from earlier versions
try:
c_db.execute('SELECT last_paused from sessions')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table sessions.")
c_db.execute(
'ALTER TABLE sessions ADD COLUMN last_paused INTEGER'
)
# Add "Local" user to database as default unauthenticated user.
result = c_db.execute('SELECT id FROM users WHERE username = "Local"')
if not result.fetchone():
logger.debug(u'User "Local" does not exist. Adding user.')
c_db.execute('INSERT INTO users (user_id, username) VALUES (0, "Local")')
# Upgrade session_history table from earlier versions
try:
c_db.execute('SELECT reference_id from session_history')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table session_history.")
c_db.execute(
'ALTER TABLE session_history ADD COLUMN reference_id INTEGER DEFAULT 0'
)
# Set reference_id to the first row where (user_id = previous row, rating_key != previous row) and user_id = user_id
c_db.execute(
'UPDATE session_history ' \
'SET reference_id = (SELECT (CASE \
WHEN (SELECT MIN(id) FROM session_history WHERE id > ( \
SELECT MAX(id) FROM session_history \
WHERE (user_id = t1.user_id AND rating_key <> t1.rating_key AND id < t1.id)) AND user_id = t1.user_id) IS NULL \
THEN (SELECT MIN(id) FROM session_history WHERE (user_id = t1.user_id)) \
ELSE (SELECT MIN(id) FROM session_history WHERE id > ( \
SELECT MAX(id) FROM session_history \
WHERE (user_id = t1.user_id AND rating_key <> t1.rating_key AND id < t1.id)) AND user_id = t1.user_id) END) ' \
'FROM session_history AS t1 ' \
'WHERE t1.id = session_history.id) '
)
# Upgrade users table from earlier versions
try:
c_db.execute('SELECT deleted_user from users')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table users.")
c_db.execute(
'ALTER TABLE users ADD COLUMN deleted_user INTEGER DEFAULT 0'
)
conn_db.commit()
c_db.close()

View file

@ -16,7 +16,7 @@
import time
import plexpy
from plexpy import logger, pmsconnect, activity_processor, threading, notification_handler
from plexpy import logger, pmsconnect, activity_processor, threading, notification_handler, helpers
class ActivityHandler(object):
@ -165,8 +165,6 @@ class ActivityHandler(object):
# This function receives events from our websocket connection
def process(self):
if self.is_valid_session():
from plexpy import helpers
ap = activity_processor.ActivityProcessor()
db_session = ap.get_session_by_key(session_key=self.get_session_key())

View file

@ -13,7 +13,7 @@
# You should have received a copy of the GNU General Public License
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
from plexpy import logger, pmsconnect, plextv, notification_handler, database, helpers, activity_processor
from plexpy import logger, pmsconnect, plextv, notification_handler, database, helpers, activity_processor, libraries
import threading
import plexpy
@ -46,16 +46,7 @@ def check_active_sessions(ws_request=False):
media_container = session_list['sessions']
# Check our temp table for what we must do with the new streams
db_streams = monitor_db.select('SELECT started, session_key, rating_key, media_type, title, parent_title, '
'grandparent_title, user_id, user, friendly_name, ip_address, player, '
'platform, machine_id, parent_rating_key, grandparent_rating_key, state, '
'view_offset, duration, video_decision, audio_decision, width, height, '
'container, video_codec, audio_codec, bitrate, video_resolution, '
'video_framerate, aspect_ratio, audio_channels, transcode_protocol, '
'transcode_container, transcode_video_codec, transcode_audio_codec, '
'transcode_audio_channels, transcode_width, transcode_height, '
'paused_counter, last_paused '
'FROM sessions')
db_streams = monitor_db.select('SELECT * FROM sessions')
for stream in db_streams:
if any(d['session_key'] == str(stream['session_key']) and d['rating_key'] == str(stream['rating_key'])
for d in media_container):
@ -196,10 +187,16 @@ def check_recently_added():
pms_connect = pmsconnect.PmsConnect()
recently_added_list = pms_connect.get_recently_added_details(count='10')
library_data = libraries.Libraries()
if recently_added_list:
recently_added = recently_added_list['recently_added']
for item in recently_added:
library_details = library_data.get_details(section_id=item['section_id'])
if not library_details['do_notify_created']:
continue
metadata = []
if 0 < time_threshold - int(item['added_at']) <= time_interval:
@ -220,8 +217,12 @@ def check_recently_added():
% str(item['rating_key']))
if metadata:
if not plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT:
for item in metadata:
library_details = library_data.get_details(section_id=item['section_id'])
if 0 < time_threshold - int(item['added_at']) <= time_interval:
logger.debug(u"PlexPy Monitor :: Library item %s has been added to Plex." % str(item['rating_key']))
# Fire off notifications

View file

@ -29,6 +29,7 @@ class ActivityProcessor(object):
def write_session(self, session=None, notify=True):
if session:
values = {'session_key': session['session_key'],
'section_id': session['section_id'],
'rating_key': session['rating_key'],
'media_type': session['media_type'],
'state': session['state'],
@ -97,10 +98,13 @@ class ActivityProcessor(object):
self.db.upsert('sessions', ip_address, keys)
def write_session_history(self, session=None, import_metadata=None, is_import=False, import_ignore_interval=0):
from plexpy import users
from plexpy import users, libraries
user_data = users.Users()
user_details = user_data.get_user_friendly_name(user=session['user'])
user_details = user_data.get_details(user_id=session['user_id'])
library_data = libraries.Libraries()
library_details = library_data.get_details(section_id=session['section_id'])
if session:
logging_enabled = False
@ -155,7 +159,10 @@ class ActivityProcessor(object):
if not user_details['keep_history'] and not is_import:
logging_enabled = False
logger.debug(u"PlexPy ActivityProcessor :: History logging for user '%s' is disabled." % session['user'])
logger.debug(u"PlexPy ActivityProcessor :: History logging for user '%s' is disabled." % user_details['username'])
elif not library_details['keep_history'] and not is_import:
logging_enabled = False
logger.debug(u"PlexPy ActivityProcessor :: History logging for library '%s' is disabled." % library_details['section_name'])
if logging_enabled:
# logger.debug(u"PlexPy ActivityProcessor :: Attempting to write to session_history table...")
@ -250,19 +257,20 @@ class ActivityProcessor(object):
# logger.debug(u"PlexPy ActivityProcessor :: Attempting to write to session_history_metadata table...")
query = 'INSERT INTO session_history_metadata (id, rating_key, parent_rating_key, ' \
'grandparent_rating_key, title, parent_title, grandparent_title, full_title, media_index, ' \
'parent_media_index, thumb, parent_thumb, grandparent_thumb, art, media_type, year, ' \
'originally_available_at, added_at, updated_at, last_viewed_at, content_rating, summary, ' \
'tagline, rating, duration, guid, directors, writers, actors, genres, studio) VALUES ' \
'parent_media_index, section_id, thumb, parent_thumb, grandparent_thumb, art, media_type, ' \
'year, originally_available_at, added_at, updated_at, last_viewed_at, content_rating, ' \
'summary, tagline, rating, duration, guid, directors, writers, actors, genres, studio) VALUES ' \
'(last_insert_rowid(), ' \
'?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
'?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
args = [session['rating_key'], session['parent_rating_key'], session['grandparent_rating_key'],
session['title'], session['parent_title'], session['grandparent_title'], full_title,
metadata['index'], metadata['parent_index'], metadata['thumb'], metadata['parent_thumb'],
metadata['grandparent_thumb'], metadata['art'], session['media_type'], metadata['year'],
metadata['originally_available_at'], metadata['added_at'], metadata['updated_at'],
metadata['media_index'], metadata['parent_media_index'], metadata['section_id'], metadata['thumb'],
metadata['parent_thumb'], metadata['grandparent_thumb'], metadata['art'], session['media_type'],
metadata['year'], metadata['originally_available_at'], metadata['added_at'], metadata['updated_at'],
metadata['last_viewed_at'], metadata['content_rating'], metadata['summary'], metadata['tagline'],
metadata['rating'], metadata['duration'], metadata['guid'], directors, writers, actors, genres, metadata['studio']]
metadata['rating'], metadata['duration'], metadata['guid'], directors, writers, actors, genres,
metadata['studio']]
# logger.debug(u"PlexPy ActivityProcessor :: Writing session_history_metadata transaction...")
self.db.action(query=query, args=args)
@ -330,15 +338,7 @@ class ActivityProcessor(object):
def get_session_by_key(self, session_key=None):
if str(session_key).isdigit():
result = self.db.select('SELECT started, session_key, rating_key, media_type, title, parent_title, '
'grandparent_title, user_id, user, friendly_name, ip_address, player, '
'platform, machine_id, parent_rating_key, grandparent_rating_key, state, '
'view_offset, duration, video_decision, audio_decision, width, height, '
'container, video_codec, audio_codec, bitrate, video_resolution, '
'video_framerate, aspect_ratio, audio_channels, transcode_protocol, '
'transcode_container, transcode_video_codec, transcode_audio_codec, '
'transcode_audio_channels, transcode_width, transcode_height, '
'paused_counter, last_paused '
result = self.db.select('SELECT * '
'FROM sessions WHERE session_key = ? LIMIT 1', args=[session_key])
for session in result:
if session:

View file

@ -323,7 +323,7 @@ class Api(object):
custom_where = [['strftime("%Y-%m-%d", datetime(date, "unixepoch", "localtime"))', start_date]]
data_factory = datafactory.DataFactory()
history = data_factory.get_history(kwargs=kwargs, custom_where=custom_where)
history = data_factory.get_datatables_history(kwargs=kwargs, custom_where=custom_where)
self.data = history
return self.data

View file

@ -97,6 +97,8 @@ _CONFIG_DEFINITIONS = {
'FACEBOOK_ON_INTUP': (int, 'Facebook', 0),
'FIRST_RUN_COMPLETE': (int, 'General', 0),
'FREEZE_DB': (int, 'General', 0),
'GET_FILE_SIZES': (int, 'General', 0),
'GET_FILE_SIZES_HOLD': (dict, 'General', {'section_ids': [], 'rating_keys': []}),
'GIT_BRANCH': (str, 'General', 'master'),
'GIT_PATH': (str, 'General', ''),
'GIT_USER': (str, 'General', 'drzoidberg33'),
@ -118,12 +120,12 @@ _CONFIG_DEFINITIONS = {
'GROWL_ON_INTDOWN': (int, 'Growl', 0),
'GROWL_ON_EXTUP': (int, 'Growl', 0),
'GROWL_ON_INTUP': (int, 'Growl', 0),
'HOME_LIBRARY_CARDS': (str, 'General', 'library_statistics_first'),
'HOME_LIBRARY_CARDS': (list, 'General', ['first_run']),
'HOME_STATS_LENGTH': (int, 'General', 30),
'HOME_STATS_TYPE': (int, 'General', 0),
'HOME_STATS_COUNT': (int, 'General', 5),
'HOME_STATS_CARDS': (str, 'General', 'watch_statistics, top_tv, popular_tv, top_movies, popular_movies, ' \
'top_music, popular_music, last_watched, top_users, top_platforms, most_concurrent'),
'HOME_STATS_CARDS': (list, 'General', ['top_tv', 'popular_tv', 'top_movies', 'popular_movies', 'top_music', \
'popular_music', 'last_watched', 'top_users', 'top_platforms', 'most_concurrent']),
'HTTPS_CERT': (str, 'General', ''),
'HTTPS_KEY': (str, 'General', ''),
'HTTP_HOST': (str, 'General', '0.0.0.0'),
@ -293,6 +295,8 @@ _CONFIG_DEFINITIONS = {
'PUSHOVER_ON_INTDOWN': (int, 'Pushover', 0),
'PUSHOVER_ON_EXTUP': (int, 'Pushover', 0),
'PUSHOVER_ON_INTUP': (int, 'Pushover', 0),
'REFRESH_LIBRARIES_INTERVAL': (int, 'Monitoring', 12),
'REFRESH_LIBRARIES_ON_STARTUP': (int, 'Monitoring', 1),
'REFRESH_USERS_INTERVAL': (int, 'Monitoring', 12),
'REFRESH_USERS_ON_STARTUP': (int, 'Monitoring', 1),
'SLACK_ENABLED': (int, 'Slack', 0),
@ -370,6 +374,7 @@ _CONFIG_DEFINITIONS = {
'TWITTER_ON_EXTUP': (int, 'Twitter', 0),
'TWITTER_ON_INTUP': (int, 'Twitter', 0),
'UPDATE_DB_INTERVAL': (int, 'General', 24),
'UPDATE_SECTION_IDS': (int, 'General', 1),
'VERIFY_SSL_CERT': (bool_int, 'Advanced', 1),
'VIDEO_LOGGING_ENABLE': (int, 'Monitoring', 1),
'XBMC_ENABLED': (int, 'XBMC', 0),
@ -505,3 +510,17 @@ class Config(object):
self.MOVIE_LOGGING_ENABLE = 0
self.TV_LOGGING_ENABLE = 0
self.CONFIG_VERSION = '1'
if self.CONFIG_VERSION == '1':
# Change home_stats_cards to list
if self.HOME_STATS_CARDS:
home_stats_cards = ''.join(self.HOME_STATS_CARDS).split(', ')
if 'watch_statistics' in home_stats_cards:
home_stats_cards.remove('watch_statistics')
self.HOME_STATS_CARDS = home_stats_cards
# Change home_library_cards to list
if self.HOME_LIBRARY_CARDS:
home_library_cards = ''.join(self.HOME_LIBRARY_CARDS).split(', ')
if 'library_statistics' in home_library_cards:
home_library_cards.remove('library_statistics')
self.HOME_LIBRARY_CARDS = home_library_cards
self.CONFIG_VERSION = '2'

View file

@ -26,7 +26,7 @@ class DataFactory(object):
def __init__(self):
pass
def get_history(self, kwargs=None, custom_where=None, grouping=0, watched_percent=85):
def get_datatables_history(self, kwargs=None, custom_where=None, grouping=0, watched_percent=85):
data_tables = datatables.DataTables()
group_by = ['session_history.reference_id'] if grouping else ['session_history.id']
@ -81,8 +81,8 @@ class DataFactory(object):
['session_history.id', 'session_history_metadata.id'],
['session_history.id', 'session_history_media_info.id']],
kwargs=kwargs)
except:
logger.warn("Unable to execute database query for get_history.")
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_history: %s." % e)
return {'recordsFiltered': 0,
'recordsTotal': 0,
'draw': 0,
@ -98,12 +98,12 @@ class DataFactory(object):
for item in history:
filter_duration += int(item['duration'])
if item["media_type"] == 'episode' and item["parent_thumb"]:
thumb = item["parent_thumb"]
elif item["media_type"] == 'episode':
thumb = item["grandparent_thumb"]
if item['media_type'] == 'episode' and item['parent_thumb']:
thumb = item['parent_thumb']
elif item['media_type'] == 'episode':
thumb = item['grandparent_thumb']
else:
thumb = item["thumb"]
thumb = item['thumb']
if item['percent_complete'] >= watched_percent:
watched_status = 1
@ -113,37 +113,37 @@ class DataFactory(object):
watched_status = 0
# Rename Mystery platform names
platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])
platform = common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform'])
row = {"reference_id": item["reference_id"],
"id": item["id"],
"date": item["date"],
"started": item["started"],
"stopped": item["stopped"],
"duration": item["duration"],
"paused_counter": item["paused_counter"],
"user_id": item["user_id"],
"user": item["user"],
"friendly_name": item["friendly_name"],
"platform": platform,
"player": item['player'],
"ip_address": item["ip_address"],
"media_type": item["media_type"],
"rating_key": item["rating_key"],
"parent_rating_key": item["parent_rating_key"],
"grandparent_rating_key": item["grandparent_rating_key"],
"full_title": item["full_title"],
"parent_title": item["parent_title"],
"year": item["year"],
"media_index": item["media_index"],
"parent_media_index": item["parent_media_index"],
"thumb": thumb,
"video_decision": item["video_decision"],
"audio_decision": item["audio_decision"],
"percent_complete": int(round(item['percent_complete'])),
"watched_status": watched_status,
"group_count": item["group_count"],
"group_ids": item["group_ids"]
row = {'reference_id': item['reference_id'],
'id': item['id'],
'date': item['date'],
'started': item['started'],
'stopped': item['stopped'],
'duration': item['duration'],
'paused_counter': item['paused_counter'],
'user_id': item['user_id'],
'user': item['user'],
'friendly_name': item['friendly_name'],
'platform': platform,
'player': item['player'],
'ip_address': item['ip_address'],
'media_type': item['media_type'],
'rating_key': item['rating_key'],
'parent_rating_key': item['parent_rating_key'],
'grandparent_rating_key': item['grandparent_rating_key'],
'full_title': item['full_title'],
'parent_title': item['parent_title'],
'year': item['year'],
'media_index': item['media_index'],
'parent_media_index': item['parent_media_index'],
'thumb': thumb,
'video_decision': item['video_decision'],
'audio_decision': item['audio_decision'],
'percent_complete': int(round(item['percent_complete'])),
'watched_status': watched_status,
'group_count': item['group_count'],
'group_ids': item['group_ids']
}
rows.append(row)
@ -158,7 +158,7 @@ class DataFactory(object):
return dict
def get_home_stats(self, grouping=0, time_range='30', stats_type=0, stats_count='5', stats_cards='', notify_watched_percent='85'):
def get_home_stats(self, grouping=0, time_range='30', stats_type=0, stats_count='5', stats_cards=[], notify_watched_percent='85'):
monitor_db = database.MonitorDatabase()
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
@ -185,8 +185,8 @@ class DataFactory(object):
'ORDER BY %s DESC ' \
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
result = monitor_db.select(query)
except:
logger.warn("Unable to execute database query for get_home_stats: top_tv.")
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_home_stats: top_tv: %s." % e)
return None
for item in result:
@ -229,8 +229,8 @@ class DataFactory(object):
'ORDER BY users_watched DESC, %s DESC ' \
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
result = monitor_db.select(query)
except:
logger.warn("Unable to execute database query for get_home_stats: popular_tv.")
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_home_stats: popular_tv: %s." % e)
return None
for item in result:
@ -270,8 +270,8 @@ class DataFactory(object):
'ORDER BY %s DESC ' \
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
result = monitor_db.select(query)
except:
logger.warn("Unable to execute database query for get_home_stats: top_movies.")
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_home_stats: top_movies: %s." % e)
return None
for item in result:
@ -313,8 +313,8 @@ class DataFactory(object):
'ORDER BY users_watched DESC, %s DESC ' \
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
result = monitor_db.select(query)
except:
logger.warn("Unable to execute database query for get_home_stats: popular_movies.")
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_home_stats: popular_movies: %s." % e)
return None
for item in result:
@ -354,8 +354,8 @@ class DataFactory(object):
'ORDER BY %s DESC ' \
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
result = monitor_db.select(query)
except:
logger.warn("Unable to execute database query for get_home_stats: top_music.")
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_home_stats: top_music: %s." % e)
return None
for item in result:
@ -398,8 +398,8 @@ class DataFactory(object):
'ORDER BY users_watched DESC, %s DESC ' \
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
result = monitor_db.select(query)
except:
logger.warn("Unable to execute database query for get_home_stats: popular_music.")
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_home_stats: popular_music: %s." % e)
return None
for item in result:
@ -424,13 +424,13 @@ class DataFactory(object):
elif stat == 'top_users':
top_users = []
try:
query = 'SELECT t.user, t.user_id, t.custom_avatar_url as thumb, ' \
query = 'SELECT t.user, t.user_id, t.user_thumb, t.custom_thumb, ' \
'(CASE WHEN t.friendly_name IS NULL THEN t.username ELSE t.friendly_name END) ' \
' AS friendly_name, ' \
'MAX(t.started) AS last_watch, COUNT(t.id) AS total_plays, SUM(t.d) AS total_duration ' \
'FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - ' \
' (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) ' \
' AS d ' \
' AS d, users.thumb AS user_thumb, users.custom_avatar_url AS custom_thumb ' \
' FROM session_history ' \
' JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
' LEFT OUTER JOIN users ON session_history.user_id = users.user_id ' \
@ -441,15 +441,17 @@ class DataFactory(object):
'ORDER BY %s DESC ' \
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
result = monitor_db.select(query)
except:
logger.warn("Unable to execute database query for get_home_stats: top_users.")
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_home_stats: top_users: %s." % e)
return None
for item in result:
if not item['thumb'] or item['thumb'] == '':
user_thumb = common.DEFAULT_USER_THUMB
if item['custom_thumb'] and item['custom_thumb'] != item['user_thumb']:
user_thumb = item['custom_thumb']
elif item['user_thumb']:
user_thumb = item['user_thumb']
else:
user_thumb = item['thumb']
user_thumb = common.DEFAULT_USER_THUMB
row = {'user': item['user'],
'user_id': item['user_id'],
@ -490,8 +492,8 @@ class DataFactory(object):
'ORDER BY %s DESC ' \
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
result = monitor_db.select(query)
except:
logger.warn("Unable to execute database query for get_home_stats: top_platforms.")
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_home_stats: top_platforms: %s." % e)
return None
for item in result:
@ -542,8 +544,8 @@ class DataFactory(object):
'ORDER BY last_watch DESC ' \
'LIMIT %s' % (time_range, group_by, notify_watched_percent, stats_count)
result = monitor_db.select(query)
except:
logger.warn("Unable to execute database query for get_home_stats: last_watched.")
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_home_stats: last_watched: %s." % e)
return None
for item in result:
@ -645,8 +647,8 @@ class DataFactory(object):
result = monitor_db.select(query)
if result:
most_concurrent.append(calc_most_concurrent(title, result))
except:
logger.warn("Unable to execute database query for get_home_stats: most_concurrent.")
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_home_stats: most_concurrent: %s." % e)
return None
home_stats.append({'stat_id': stat,
@ -654,6 +656,35 @@ class DataFactory(object):
return home_stats
def get_library_stats(self, library_cards=[]):
monitor_db = database.MonitorDatabase()
library_stats = []
for id in library_cards:
if id.isdigit():
try:
query = 'SELECT section_id, section_name, section_type, thumb, count, parent_count, child_count ' \
'FROM library_sections ' \
'WHERE section_id = %s ' % id
result = monitor_db.select(query)
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_library_stats: %s." % e)
return None
for item in result:
library = {'section_id': item['section_id'],
'section_name': item['section_name'],
'section_type': item['section_type'],
'thumb': item['thumb'],
'count': item['count'],
'parent_count': item['parent_count'],
'child_count': item['child_count']
}
library_stats.append(library)
return library_stats
def get_stream_details(self, row_id=None):
monitor_db = database.MonitorDatabase()
@ -696,86 +727,33 @@ class DataFactory(object):
return stream_output
def get_recently_watched(self, user=None, user_id=None, limit='10'):
monitor_db = database.MonitorDatabase()
recently_watched = []
if not limit.isdigit():
limit = '10'
try:
if user_id:
query = 'SELECT session_history.id, session_history.media_type, session_history.rating_key, session_history.parent_rating_key, ' \
'title, parent_title, grandparent_title, thumb, parent_thumb, grandparent_thumb, media_index, parent_media_index, ' \
'year, started, user ' \
'FROM session_history_metadata ' \
'JOIN session_history ON session_history_metadata.id = session_history.id ' \
'WHERE user_id = ? ' \
'GROUP BY (CASE WHEN session_history.media_type = "track" THEN session_history.parent_rating_key ' \
' ELSE session_history.rating_key END) ' \
'ORDER BY started DESC LIMIT ?'
result = monitor_db.select(query, args=[user_id, limit])
elif user:
query = 'SELECT session_history.id, session_history.media_type, session_history.rating_key, session_history.parent_rating_key, ' \
'title, parent_title, grandparent_title, thumb, parent_thumb, grandparent_thumb, media_index, parent_media_index, ' \
'year, started, user ' \
'FROM session_history_metadata ' \
'JOIN session_history ON session_history_metadata.id = session_history.id ' \
'WHERE user = ? ' \
'GROUP BY (CASE WHEN session_history.media_type = "track" THEN session_history.parent_rating_key ' \
' ELSE session_history.rating_key END) ' \
'ORDER BY started DESC LIMIT ?'
result = monitor_db.select(query, args=[user, limit])
else:
query = 'SELECT session_history.id, session_history.media_type, session_history.rating_key, session_history.parent_rating_key, ' \
'title, parent_title, grandparent_title, thumb, parent_thumb, grandparent_thumb, media_index, parent_media_index, ' \
'year, started, user ' \
'FROM session_history_metadata ' \
'JOIN session_history ON session_history_metadata.id = session_history.id ' \
'GROUP BY (CASE WHEN session_history.media_type = "track" THEN session_history.parent_rating_key ' \
' ELSE session_history.rating_key END) ' \
'ORDER BY started DESC LIMIT ?'
result = monitor_db.select(query, args=[limit])
except:
logger.warn("Unable to execute database query for get_recently_watched.")
return None
for row in result:
if row['media_type'] == 'episode' and row['parent_thumb']:
thumb = row['parent_thumb']
elif row['media_type'] == 'episode':
thumb = row['grandparent_thumb']
else:
thumb = row['thumb']
recent_output = {'row_id': row['id'],
'type': row['media_type'],
'rating_key': row['rating_key'],
'title': row['title'],
'parent_title': row['parent_title'],
'grandparent_title': row['grandparent_title'],
'thumb': thumb,
'index': row['media_index'],
'parent_index': row['parent_media_index'],
'year': row['year'],
'time': row['started'],
'user': row['user']
}
recently_watched.append(recent_output)
return recently_watched
def get_metadata_details(self, row_id):
def get_metadata_details(self, rating_key):
monitor_db = database.MonitorDatabase()
if row_id:
query = 'SELECT rating_key, parent_rating_key, grandparent_rating_key, title, parent_title, grandparent_title, ' \
'full_title, media_index, parent_media_index, thumb, parent_thumb, grandparent_thumb, art, media_type, ' \
'year, originally_available_at, added_at, updated_at, last_viewed_at, content_rating, summary, tagline, ' \
'rating, duration, guid, directors, writers, actors, genres, studio ' \
if rating_key:
query = 'SELECT session_history_metadata.rating_key, session_history_metadata.parent_rating_key, ' \
'session_history_metadata.grandparent_rating_key, session_history_metadata.title, ' \
'session_history_metadata.parent_title, session_history_metadata.grandparent_title, ' \
'session_history_metadata.full_title, library_sections.section_name, ' \
'session_history_metadata.media_index, session_history_metadata.parent_media_index, ' \
'session_history_metadata.section_id, session_history_metadata.thumb, ' \
'session_history_metadata.parent_thumb, session_history_metadata.grandparent_thumb, ' \
'session_history_metadata.art, session_history_metadata.media_type, session_history_metadata.year, ' \
'session_history_metadata.originally_available_at, session_history_metadata.added_at, ' \
'session_history_metadata.updated_at, session_history_metadata.last_viewed_at, ' \
'session_history_metadata.content_rating, session_history_metadata.summary, ' \
'session_history_metadata.tagline, session_history_metadata.rating, session_history_metadata.duration, ' \
'session_history_metadata.guid, session_history_metadata.directors, session_history_metadata.writers, ' \
'session_history_metadata.actors, session_history_metadata.genres, session_history_metadata.studio, ' \
'session_history_media_info.container, session_history_media_info.bitrate, ' \
'session_history_media_info.video_codec, session_history_media_info.video_resolution, ' \
'session_history_media_info.video_framerate, session_history_media_info.audio_codec, ' \
'session_history_media_info.audio_channels ' \
'FROM session_history_metadata ' \
'WHERE id = ?'
result = monitor_db.select(query=query, args=[row_id])
'JOIN library_sections ON session_history_metadata.section_id = library_sections.section_id ' \
'JOIN session_history_media_info ON session_history_metadata.id = session_history_media_info.id ' \
'WHERE session_history_metadata.rating_key = ?'
result = monitor_db.select(query=query, args=[rating_key])
else:
result = []
@ -791,9 +769,9 @@ class DataFactory(object):
'parent_rating_key': item['parent_rating_key'],
'grandparent_rating_key': item['grandparent_rating_key'],
'grandparent_title': item['grandparent_title'],
'parent_index': item['parent_media_index'],
'parent_media_index': item['parent_media_index'],
'parent_title': item['parent_title'],
'index': item['media_index'],
'media_index': item['media_index'],
'studio': item['studio'],
'title': item['title'],
'content_rating': item['content_rating'],
@ -814,88 +792,61 @@ class DataFactory(object):
'writers': writers,
'directors': directors,
'genres': genres,
'actors': actors
'actors': actors,
'library_name': item['section_name'],
'section_id': item['section_id'],
'container': item['container'],
'bitrate': item['bitrate'],
'video_codec': item['video_codec'],
'video_resolution': item['video_resolution'],
'video_framerate': item['video_framerate'],
'audio_codec': item['audio_codec'],
'audio_channels': item['audio_channels']
}
return metadata
def delete_session_history_rows(self, row_id=None):
def get_total_duration(self, custom_where=None):
monitor_db = database.MonitorDatabase()
if row_id.isdigit():
logger.info(u"PlexPy DataFactory :: Deleting row id %s from the session history database." % row_id)
session_history_del = \
monitor_db.action('DELETE FROM session_history WHERE id = ?', [row_id])
session_history_media_info_del = \
monitor_db.action('DELETE FROM session_history_media_info WHERE id = ?', [row_id])
session_history_metadata_del = \
monitor_db.action('DELETE FROM session_history_metadata WHERE id = ?', [row_id])
return 'Deleted rows %s.' % row_id
# Split up custom wheres
if custom_where:
where = 'WHERE ' + ' AND '.join([w[0] + ' = "' + w[1] + '"' for w in custom_where])
else:
return 'Unable to delete rows. Input row not valid.'
where = ''
def delete_all_user_history(self, user_id=None):
try:
query = 'SELECT SUM(CASE WHEN stopped > 0 THEN (stopped - started) ELSE 0 END) - ' \
'SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS total_duration ' \
'FROM session_history ' \
'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
'%s ' % where
result = monitor_db.select(query)
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_total_duration: %s." % e)
return None
total_duration = 0
for item in result:
total_duration = item['total_duration']
return total_duration
def get_session_ip(self, session_key=''):
monitor_db = database.MonitorDatabase()
if user_id.isdigit():
logger.info(u"PlexPy DataFactory :: Deleting all history for user id %s from database." % user_id)
session_history_media_info_del = \
monitor_db.action('DELETE FROM '
'session_history_media_info '
'WHERE session_history_media_info.id IN (SELECT session_history_media_info.id '
'FROM session_history_media_info '
'JOIN session_history ON session_history_media_info.id = session_history.id '
'WHERE session_history.user_id = ?)', [user_id])
session_history_metadata_del = \
monitor_db.action('DELETE FROM '
'session_history_metadata '
'WHERE session_history_metadata.id IN (SELECT session_history_metadata.id '
'FROM session_history_metadata '
'JOIN session_history ON session_history_metadata.id = session_history.id '
'WHERE session_history.user_id = ?)', [user_id])
session_history_del = \
monitor_db.action('DELETE FROM '
'session_history '
'WHERE session_history.user_id = ?', [user_id])
return 'Deleted all items for user_id %s.' % user_id
if session_key:
query = 'SELECT ip_address FROM sessions WHERE session_key = %d' % int(session_key)
result = monitor_db.select(query)
else:
return 'Unable to delete items. Input user_id not valid.'
return None
def delete_user(self, user_id=None):
monitor_db = database.MonitorDatabase()
ip_address = 'N/A'
if user_id.isdigit():
self.delete_all_user_history(user_id)
logger.info(u"PlexPy DataFactory :: Deleting user with id %s from database." % user_id)
monitor_db.action('UPDATE users SET deleted_user = 1 WHERE user_id = ?', [user_id])
monitor_db.action('UPDATE users SET keep_history = 0 WHERE user_id = ?', [user_id])
monitor_db.action('UPDATE users SET do_notify = 0 WHERE user_id = ?', [user_id])
for item in result:
ip_address = item['ip_address']
return 'Deleted user with id %s.' % user_id
else:
return 'Unable to delete user. Input user_id not valid.'
def undelete_user(self, user_id=None, username=None):
monitor_db = database.MonitorDatabase()
if user_id and user_id.isdigit():
logger.info(u"PlexPy DataFactory :: Re-adding user with id %s to database." % user_id)
monitor_db.action('UPDATE users SET deleted_user = 0 WHERE user_id = ?', [user_id])
monitor_db.action('UPDATE users SET keep_history = 1 WHERE user_id = ?', [user_id])
monitor_db.action('UPDATE users SET do_notify = 1 WHERE user_id = ?', [user_id])
return 'Re-added user with id %s.' % user_id
elif username:
logger.info(u"PlexPy DataFactory :: Re-adding user with username %s to database." % username)
monitor_db.action('UPDATE users SET deleted_user = 0 WHERE username = ?', [username])
monitor_db.action('UPDATE users SET keep_history = 1 WHERE username = ?', [username])
monitor_db.action('UPDATE users SET do_notify = 1 WHERE username = ?', [username])
return 'Re-added user with username %s.' % username
else:
return 'Unable to re-add user. Input user_id or username not valid.'
return ip_address
def get_search_query(self, rating_key=''):
monitor_db = database.MonitorDatabase()
@ -984,8 +935,8 @@ class DataFactory(object):
grandparent_rating_key = result[0]['grandparent_rating_key']
except:
logger.warn("Unable to execute database query for get_rating_keys_list.")
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for get_rating_keys_list: %s." % e)
return {}
query = 'SELECT rating_key, parent_rating_key, grandparent_rating_key, title, parent_title, grandparent_title, ' \
@ -1028,7 +979,25 @@ class DataFactory(object):
return key_list
def update_rating_key(self, old_key_list='', new_key_list='', media_type=''):
def delete_session_history_rows(self, row_id=None):
monitor_db = database.MonitorDatabase()
if row_id.isdigit():
logger.info(u"PlexPy DataFactory :: Deleting row id %s from the session history database." % row_id)
session_history_del = \
monitor_db.action('DELETE FROM session_history WHERE id = ?', [row_id])
session_history_media_info_del = \
monitor_db.action('DELETE FROM session_history_media_info WHERE id = ?', [row_id])
session_history_metadata_del = \
monitor_db.action('DELETE FROM session_history_metadata WHERE id = ?', [row_id])
return 'Deleted rows %s.' % row_id
else:
return 'Unable to delete rows. Input row not valid.'
def update_metadata(self, old_key_list='', new_key_list='', media_type=''):
from plexpy import pmsconnect
pms_connect = pmsconnect.PmsConnect()
monitor_db = database.MonitorDatabase()
# function to map rating keys pairs
@ -1036,7 +1005,6 @@ class DataFactory(object):
pairs = {}
for k, v in old.iteritems():
if k in new:
if v['rating_key'] != new[k]['rating_key']:
pairs.update({v['rating_key']: new[k]['rating_key']})
if 'children' in old[k]:
pairs.update(get_pairs(old[k]['children'], new[k]['children']))
@ -1049,89 +1017,73 @@ class DataFactory(object):
mapping = get_pairs(old_key_list, new_key_list)
if mapping:
logger.info(u"PlexPy DataFactory :: Updating rating keys in the database.")
logger.info(u"PlexPy DataFactory :: Updating metadata in the database.")
for old_key, new_key in mapping.iteritems():
# check rating_key (3 tables)
monitor_db.action('UPDATE session_history SET rating_key = ? WHERE rating_key = ?',
[new_key, old_key])
monitor_db.action('UPDATE session_history_media_info SET rating_key = ? WHERE rating_key = ?',
[new_key, old_key])
monitor_db.action('UPDATE session_history_metadata SET rating_key = ? WHERE rating_key = ?',
[new_key, old_key])
# check parent_rating_key (2 tables)
monitor_db.action('UPDATE session_history SET parent_rating_key = ? WHERE parent_rating_key = ?',
[new_key, old_key])
monitor_db.action('UPDATE session_history_metadata SET parent_rating_key = ? WHERE parent_rating_key = ?',
[new_key, old_key])
result = pms_connect.get_metadata_details(new_key)
if result:
metadata = result['metadata']
if metadata['media_type'] == 'show' or metadata['media_type'] == 'artist':
# check grandparent_rating_key (2 tables)
monitor_db.action('UPDATE session_history SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?',
[new_key, old_key])
monitor_db.action('UPDATE session_history_metadata SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?',
[new_key, old_key])
# check thumb (1 table)
monitor_db.action('UPDATE session_history_metadata SET thumb = replace(thumb, ?, ?) \
WHERE thumb LIKE "/library/metadata/%s/thumb/%%"' % old_key,
[old_key, new_key])
# check parent_thumb (1 table)
monitor_db.action('UPDATE session_history_metadata SET parent_thumb = replace(parent_thumb, ?, ?) \
WHERE parent_thumb LIKE "/library/metadata/%s/thumb/%%"' % old_key,
[old_key, new_key])
# check grandparent_thumb (1 table)
monitor_db.action('UPDATE session_history_metadata SET grandparent_thumb = replace(grandparent_thumb, ?, ?) \
WHERE grandparent_thumb LIKE "/library/metadata/%s/thumb/%%"' % old_key,
[old_key, new_key])
# check art (1 table)
monitor_db.action('UPDATE session_history_metadata SET art = replace(art, ?, ?) \
WHERE art LIKE "/library/metadata/%s/art/%%"' % old_key,
[old_key, new_key])
return 'Updated rating key in database.'
elif metadata['media_type'] == 'season' or metadata['media_type'] == 'album':
# check parent_rating_key (2 tables)
monitor_db.action('UPDATE session_history SET parent_rating_key = ? WHERE parent_rating_key = ?',
[new_key, old_key])
monitor_db.action('UPDATE session_history_metadata SET parent_rating_key = ? WHERE parent_rating_key = ?',
[new_key, old_key])
else:
return 'No updated rating key needed in database. No changes were made.'
# for debugging
#return mapping
# check rating_key (2 tables)
monitor_db.action('UPDATE session_history SET rating_key = ? WHERE rating_key = ?',
[new_key, old_key])
monitor_db.action('UPDATE session_history_media_info SET rating_key = ? WHERE rating_key = ?',
[new_key, old_key])
def get_session_ip(self, session_key=''):
# update session_history_metadata table
self.update_metadata_details(old_key, new_key, metadata)
return 'Updated metadata in database.'
else:
return 'Unable to update metadata in database. No changes were made.'
def update_metadata_details(self, old_rating_key='', new_rating_key='', metadata=None):
if metadata:
# Create full_title
if metadata['media_type'] == 'episode' or metadata['media_type'] == 'track':
full_title = '%s - %s' % (metadata['grandparent_title'], metadata['title'])
else:
full_title = metadata['title']
directors = ";".join(metadata['directors'])
writers = ";".join(metadata['writers'])
actors = ";".join(metadata['actors'])
genres = ";".join(metadata['genres'])
#logger.info(u"PlexPy DataFactory :: Updating metadata in the database for rating key: %s." % new_rating_key)
monitor_db = database.MonitorDatabase()
if session_key:
query = 'SELECT ip_address FROM sessions WHERE session_key = %d' % int(session_key)
result = monitor_db.select(query)
else:
return None
# Update the session_history_metadata table
query = 'UPDATE session_history_metadata SET rating_key = ?, parent_rating_key = ?, ' \
'grandparent_rating_key = ?, title = ?, parent_title = ?, grandparent_title = ?, full_title = ?, ' \
'media_index = ?, parent_media_index = ?, section_id = ?, thumb = ?, parent_thumb = ?, ' \
'grandparent_thumb = ?, art = ?, media_type = ?, year = ?, originally_available_at = ?, ' \
'added_at = ?, updated_at = ?, last_viewed_at = ?, content_rating = ?, summary = ?, ' \
'tagline = ?, rating = ?, duration = ?, guid = ?, directors = ?, writers = ?, actors = ?, ' \
'genres = ?, studio = ? ' \
'WHERE rating_key = ?'
ip_address = 'N/A'
args = [metadata['rating_key'], metadata['parent_rating_key'], metadata['grandparent_rating_key'],
metadata['title'], metadata['parent_title'], metadata['grandparent_title'], full_title,
metadata['media_index'], metadata['parent_media_index'], metadata['section_id'], metadata['thumb'],
metadata['parent_thumb'], metadata['grandparent_thumb'], metadata['art'], metadata['media_type'],
metadata['year'], metadata['originally_available_at'], metadata['added_at'], metadata['updated_at'],
metadata['last_viewed_at'], metadata['content_rating'], metadata['summary'], metadata['tagline'],
metadata['rating'], metadata['duration'], metadata['guid'], directors, writers, actors, genres,
metadata['studio'],
old_rating_key]
for item in result:
ip_address = item['ip_address']
return ip_address
def get_total_duration(self, custom_where=None):
monitor_db = database.MonitorDatabase()
# Split up custom wheres
if custom_where:
where = 'WHERE ' + ' AND '.join([w[0] + ' = "' + w[1] + '"' for w in custom_where])
else:
where = ''
try:
query = 'SELECT SUM(CASE WHEN stopped > 0 THEN (stopped - started) ELSE 0 END) - ' \
'SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS total_duration ' \
'FROM session_history %s ' % where
result = monitor_db.select(query)
except:
logger.warn("Unable to execute database query for get_total_duration.")
return None
for item in result:
total_duration = item['total_duration']
return total_duration
monitor_db.action(query=query, args=args)

View file

@ -124,8 +124,9 @@ class DataTables(object):
order += ', '
order = order.rstrip(', ')
if order:
order = 'ORDER BY ' + order.rstrip(', ')
order = 'ORDER BY ' + order
# Build where parameters
if parameters['search']['value']:

View file

@ -32,10 +32,10 @@ class Graphs(object):
try:
if y_axis == 'plays':
query = 'SELECT date(started, "unixepoch", "localtime") as date_played, ' \
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count, ' \
'SUM(case when media_type = "track" then 1 else 0 end) as music_count ' \
query = 'SELECT date(started, "unixepoch", "localtime") AS date_played, ' \
'SUM(CASE WHEN media_type = "episode" THEN 1 ELSE 0 END) AS tv_count, ' \
'SUM(CASE WHEN media_type = "movie" THEN 1 ELSE 0 END) AS movie_count, ' \
'SUM(CASE WHEN media_type = "track" THEN 1 ELSE 0 END) AS music_count ' \
'FROM session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
'GROUP BY date_played ' \
@ -43,21 +43,21 @@ class Graphs(object):
result = monitor_db.select(query)
else:
query = 'SELECT date(started, "unixepoch", "localtime") as date_played, ' \
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_count, ' \
'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_count ' \
query = 'SELECT date(started, "unixepoch", "localtime") AS date_played, ' \
'SUM(CASE WHEN media_type = "episode" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tv_count, ' \
'SUM(CASE WHEN media_type = "movie" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS movie_count, ' \
'SUM(CASE WHEN media_type = "track" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS music_count ' \
'FROM session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
'GROUP BY date_played ' \
'ORDER BY started ASC' % time_range
result = monitor_db.select(query)
except:
logger.warn("Unable to execute database query.")
except Exception as e:
logger.warn(u"PlexPy Graphs :: Unable to execute database query for get_total_plays_per_day: %s." % e)
return None
# create our date range as some days may not have any data
@ -108,49 +108,51 @@ class Graphs(object):
if not time_range.isdigit():
time_range = '30'
try:
if y_axis == 'plays':
query = 'SELECT strftime("%w", datetime(started, "unixepoch", "localtime")) as daynumber, ' \
'case cast (strftime("%w", datetime(started, "unixepoch", "localtime")) as integer) ' \
'when 0 then "Sunday" ' \
'when 1 then "Monday" ' \
'when 2 then "Tuesday" ' \
'when 3 then "Wednesday" ' \
'when 4 then "Thursday" ' \
'when 5 then "Friday" ' \
'else "Saturday" end as dayofweek, ' \
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count, ' \
'SUM(case when media_type = "track" then 1 else 0 end) as music_count ' \
query = 'SELECT strftime("%%w", datetime(started, "unixepoch", "localtime")) AS daynumber, ' \
'(CASE CAST(strftime("%%w", datetime(started, "unixepoch", "localtime")) AS INTEGER) ' \
'WHEN 0 THEN "Sunday" ' \
'WHEN 1 THEN "Monday" ' \
'WHEN 2 THEN "Tuesday" ' \
'WHEN 3 THEN "Wednesday" ' \
'WHEN 4 THEN "Thursday" ' \
'WHEN 5 THEN "Friday" ' \
'ELSE "Saturday" END) AS dayofweek, ' \
'SUM(CASE WHEN media_type = "episode" THEN 1 ELSE 0 END) AS tv_count, ' \
'SUM(CASE WHEN media_type = "movie" THEN 1 ELSE 0 END) AS movie_count, ' \
'SUM(CASE WHEN media_type = "track" THEN 1 ELSE 0 END) AS music_count ' \
'FROM session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime") ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
'GROUP BY dayofweek ' \
'ORDER BY daynumber'
'ORDER BY daynumber' % time_range
result = monitor_db.select(query)
else:
query = 'SELECT strftime("%w", datetime(started, "unixepoch", "localtime")) as daynumber, ' \
'case cast (strftime("%w", datetime(started, "unixepoch", "localtime")) as integer) ' \
'when 0 then "Sunday" ' \
'when 1 then "Monday" ' \
'when 2 then "Tuesday" ' \
'when 3 then "Wednesday" ' \
'when 4 then "Thursday" ' \
'when 5 then "Friday" ' \
'else "Saturday" end as dayofweek, ' \
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_count, ' \
'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_count ' \
query = 'SELECT strftime("%%w", datetime(started, "unixepoch", "localtime")) AS daynumber, ' \
'(CASE CAST(strftime("%%w", datetime(started, "unixepoch", "localtime")) AS INTEGER) ' \
'WHEN 0 THEN "Sunday" ' \
'WHEN 1 THEN "Monday" ' \
'WHEN 2 THEN "Tuesday" ' \
'WHEN 3 THEN "Wednesday" ' \
'WHEN 4 THEN "Thursday" ' \
'WHEN 5 THEN "Friday" ' \
'ELSE "Saturday" END) AS dayofweek, ' \
'SUM(CASE WHEN media_type = "episode" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tv_count, ' \
'SUM(CASE WHEN media_type = "movie" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS movie_count, ' \
'SUM(CASE WHEN media_type = "track" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS music_count ' \
'FROM session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime") ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
'GROUP BY dayofweek ' \
'ORDER BY daynumber'
'ORDER BY daynumber' % time_range
result = monitor_db.select(query)
except Exception as e:
logger.warn(u"PlexPy Graphs :: Unable to execute database query for get_total_plays_per_dayofweek: %s." % e)
return None
days_list = ['Sunday', 'Monday', 'Tuesday', 'Wednesday',
'Thursday', 'Friday', 'Saturday']
@ -197,33 +199,35 @@ class Graphs(object):
if not time_range.isdigit():
time_range = '30'
try:
if y_axis == 'plays':
query = 'select strftime("%H", datetime(started, "unixepoch", "localtime")) as hourofday, ' \
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count, ' \
'SUM(case when media_type = "track" then 1 else 0 end) as music_count ' \
query = 'SELECT strftime("%%H", datetime(started, "unixepoch", "localtime")) AS hourofday, ' \
'SUM(CASE WHEN media_type = "episode" THEN 1 ELSE 0 END) AS tv_count, ' \
'SUM(CASE WHEN media_type = "movie" THEN 1 ELSE 0 END) AS movie_count, ' \
'SUM(CASE WHEN media_type = "track" THEN 1 ELSE 0 END) AS music_count ' \
'FROM session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime") ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
'GROUP BY hourofday ' \
'ORDER BY hourofday'
'ORDER BY hourofday' % time_range
result = monitor_db.select(query)
else:
query = 'select strftime("%H", datetime(started, "unixepoch", "localtime")) as hourofday, ' \
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_count, ' \
'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_count ' \
query = 'SELECT strftime("%%H", datetime(started, "unixepoch", "localtime")) AS hourofday, ' \
'SUM(CASE WHEN media_type = "episode" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tv_count, ' \
'SUM(CASE WHEN media_type = "movie" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS movie_count, ' \
'SUM(CASE WHEN media_type = "track" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS music_count ' \
'FROM session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime") ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
'GROUP BY hourofday ' \
'ORDER BY hourofday'
'ORDER BY hourofday' % time_range
result = monitor_db.select(query)
except Exception as e:
logger.warn(u"PlexPy Graphs :: Unable to execute database query for get_total_plays_per_hourofday: %s." % e)
return None
hours_list = ['00','01','02','03','04','05',
'06','07','08','09','10','11',
@ -270,11 +274,13 @@ class Graphs(object):
import time as time
monitor_db = database.MonitorDatabase()
try:
if y_axis == 'plays':
query = 'SELECT strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) as datestring, ' \
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count, ' \
'SUM(case when media_type = "track" then 1 else 0 end) as music_count ' \
query = 'SELECT strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) AS datestring, ' \
'SUM(CASE WHEN media_type = "episode" THEN 1 ELSE 0 END) AS tv_count, ' \
'SUM(CASE WHEN media_type = "movie" THEN 1 ELSE 0 END) AS movie_count, ' \
'SUM(CASE WHEN media_type = "track" THEN 1 ELSE 0 END) AS music_count ' \
'FROM session_history ' \
'WHERE datetime(started, "unixepoch", "localtime") >= datetime("now", "-12 months", "localtime") ' \
'GROUP BY strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) ' \
@ -282,19 +288,22 @@ class Graphs(object):
result = monitor_db.select(query)
else:
query = 'SELECT strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) as datestring, ' \
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_count, ' \
'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_count ' \
query = 'SELECT strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) AS datestring, ' \
'SUM(CASE WHEN media_type = "episode" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tv_count, ' \
'SUM(CASE WHEN media_type = "movie" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS movie_count, ' \
'SUM(CASE WHEN media_type = "track" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS music_count ' \
'FROM session_history ' \
'WHERE datetime(started, "unixepoch", "localtime") >= datetime("now", "-12 months", "localtime") ' \
'GROUP BY strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) ' \
'ORDER BY datestring DESC LIMIT 12'
result = monitor_db.select(query)
except Exception as e:
logger.warn(u"PlexPy Graphs :: Unable to execute database query for get_total_plays_per_month: %s." % e)
return None
# create our date range as some months may not have any data
# but we still want to display them
@ -348,38 +357,40 @@ class Graphs(object):
if not time_range.isdigit():
time_range = '30'
try:
if y_axis == 'plays':
query = 'SELECT platform, ' \
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count, ' \
'SUM(case when media_type = "track" then 1 else 0 end) as music_count, ' \
'COUNT(id) as total_count ' \
'SUM(CASE WHEN media_type = "episode" THEN 1 ELSE 0 END) AS tv_count, ' \
'SUM(CASE WHEN media_type = "movie" THEN 1 ELSE 0 END) AS movie_count, ' \
'SUM(CASE WHEN media_type = "track" THEN 1 ELSE 0 END) AS music_count, ' \
'COUNT(id) AS total_count ' \
'FROM session_history ' \
'WHERE (datetime(stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime")) ' \
'WHERE (datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime")) ' \
'GROUP BY platform ' \
'ORDER BY total_count DESC ' \
'LIMIT 10'
'LIMIT 10' % time_range
result = monitor_db.select(query)
else:
query = 'SELECT platform, ' \
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_count, ' \
'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_count, ' \
'SUM(case when stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \
'SUM(CASE WHEN media_type = "episode" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tv_count, ' \
'SUM(CASE WHEN media_type = "movie" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS movie_count, ' \
'SUM(CASE WHEN media_type = "track" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS music_count, ' \
'SUM(CASE WHEN stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS total_duration ' \
'FROM session_history ' \
'WHERE (datetime(stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime")) ' \
'WHERE (datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime")) ' \
'GROUP BY platform ' \
'ORDER BY total_duration DESC ' \
'LIMIT 10'
'LIMIT 10' % time_range
result = monitor_db.select(query)
except Exception as e:
logger.warn(u"PlexPy Graphs :: Unable to execute database query for get_total_plays_by_top_10_platforms: %s." % e)
return None
categories = []
series_1 = []
@ -409,44 +420,44 @@ class Graphs(object):
if not time_range.isdigit():
time_range = '30'
try:
if y_axis == 'plays':
query = 'SELECT ' \
'(case when users.friendly_name is null then users.username else ' \
'users.friendly_name end) as friendly_name,' \
'SUM(case when media_type = "episode" then 1 else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" then 1 else 0 end) as movie_count, ' \
'SUM(case when media_type = "track" then 1 else 0 end) as music_count, ' \
'COUNT(session_history.id) as total_count ' \
'(CASE WHEN users.friendly_name IS NULL THEN users.username ELSE users.friendly_name END) AS friendly_name,' \
'SUM(CASE WHEN media_type = "episode" THEN 1 ELSE 0 END) AS tv_count, ' \
'SUM(CASE WHEN media_type = "movie" THEN 1 ELSE 0 END) AS movie_count, ' \
'SUM(CASE WHEN media_type = "track" THEN 1 ELSE 0 END) AS music_count, ' \
'COUNT(session_history.id) AS total_count ' \
'FROM session_history ' \
'JOIN users on session_history.user_id = users.user_id ' \
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime")) ' \
'JOIN users ON session_history.user_id = users.user_id ' \
'WHERE (datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime")) ' \
'GROUP BY session_history.user_id ' \
'ORDER BY total_count DESC ' \
'LIMIT 10'
'LIMIT 10' % time_range
result = monitor_db.select(query)
else:
query = 'SELECT ' \
'(case when users.friendly_name is null then users.username else ' \
'users.friendly_name end) as friendly_name,' \
'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_count, ' \
'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_count, ' \
'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_count, ' \
'SUM(case when stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \
'(CASE WHEN users.friendly_name IS NULL THEN users.username ELSE users.friendly_name END) AS friendly_name,' \
'SUM(CASE WHEN media_type = "episode" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tv_count, ' \
'SUM(CASE WHEN media_type = "movie" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS movie_count, ' \
'SUM(CASE WHEN media_type = "track" AND stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS music_count, ' \
'SUM(CASE WHEN stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS total_duration ' \
'FROM session_history ' \
'JOIN users on session_history.user_id = users.user_id ' \
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime")) ' \
'JOIN users ON session_history.user_id = users.user_id ' \
'WHERE (datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime")) ' \
'GROUP BY session_history.user_id ' \
'ORDER BY total_duration DESC ' \
'LIMIT 10'
'LIMIT 10' % time_range
result = monitor_db.select(query)
except Exception as e:
logger.warn(u"PlexPy Graphs :: Unable to execute database query for get_total_plays_by_top_10_users: %s." % e)
return None
categories = []
series_1 = []
@ -478,16 +489,13 @@ class Graphs(object):
try:
if y_axis == 'plays':
query = 'SELECT date(session_history.started, "unixepoch", "localtime") as date_played, ' \
'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
'or session_history_media_info.audio_decision = "direct play") ' \
'then 1 else 0 end) as dp_count, ' \
'SUM(case when (session_history_media_info.video_decision != "transcode" ' \
'and session_history_media_info.audio_decision = "copy") ' \
'then 1 else 0 end) as ds_count, ' \
'SUM(case when (session_history_media_info.video_decision = "transcode" ' \
'or session_history_media_info.audio_decision = "transcode") ' \
'then 1 else 0 end) as tc_count ' \
query = 'SELECT date(session_history.started, "unixepoch", "localtime") AS date_played, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
'OR session_history_media_info.audio_decision = "direct play") THEN 1 ELSE 0 END) AS dp_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
'AND session_history_media_info.audio_decision = "copy") THEN 1 ELSE 0 END) AS ds_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
'OR session_history_media_info.audio_decision = "transcode") THEN 1 ELSE 0 END) AS tc_count ' \
'FROM session_history ' \
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
@ -498,19 +506,19 @@ class Graphs(object):
result = monitor_db.select(query)
else:
query = 'SELECT date(session_history.started, "unixepoch", "localtime") as date_played, ' \
'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
'or session_history_media_info.audio_decision = "direct play") ' \
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_count, ' \
'SUM(case when (session_history_media_info.video_decision != "transcode" ' \
'and session_history_media_info.audio_decision = "copy") ' \
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_count, ' \
'SUM(case when (session_history_media_info.video_decision = "transcode" ' \
'or session_history_media_info.audio_decision = "transcode") ' \
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_count ' \
query = 'SELECT date(session_history.started, "unixepoch", "localtime") AS date_played, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
'OR session_history_media_info.audio_decision = "direct play") ' \
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS dp_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
'AND session_history_media_info.audio_decision = "copy") ' \
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS ds_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
'OR session_history_media_info.audio_decision = "transcode") ' \
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tc_count ' \
'FROM session_history ' \
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
'WHERE datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
@ -520,8 +528,8 @@ class Graphs(object):
'ORDER BY started ASC' % time_range
result = monitor_db.select(query)
except:
logger.warn("Unable to execute database query.")
except Exception as e:
logger.warn(u"PlexPy Graphs :: Unable to execute database query for get_total_plays_per_stream_type: %s." % e)
return None
# create our date range as some days may not have any data
@ -572,54 +580,55 @@ class Graphs(object):
if not time_range.isdigit():
time_range = '30'
try:
if y_axis == 'plays':
query = 'SELECT session_history_media_info.video_resolution AS resolution, ' \
'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
'or session_history_media_info.audio_decision = "direct play") ' \
'then 1 else 0 end) as dp_count, ' \
'SUM(case when (session_history_media_info.video_decision != "transcode" ' \
'and session_history_media_info.audio_decision = "copy") ' \
'then 1 else 0 end) as ds_count, ' \
'SUM(case when (session_history_media_info.video_decision = "transcode" ' \
'or session_history_media_info.audio_decision = "transcode") ' \
'then 1 else 0 end) as tc_count, ' \
'COUNT(session_history.id) as total_count ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
'OR session_history_media_info.audio_decision = "direct play") THEN 1 ELSE 0 END) AS dp_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
'AND session_history_media_info.audio_decision = "copy") THEN 1 ELSE 0 END) AS ds_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
'OR session_history_media_info.audio_decision = "transcode") THEN 1 ELSE 0 END) AS tc_count, ' \
'COUNT(session_history.id) AS total_count ' \
'FROM session_history ' \
'JOIN session_history_media_info on session_history.id = session_history_media_info.id ' \
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
'datetime("now", "-%s days", "localtime")) AND ' \
'(session_history.media_type = "episode" OR session_history.media_type = "movie") ' \
'GROUP BY resolution ' \
'ORDER BY total_count DESC ' \
'LIMIT 10'
'LIMIT 10' % time_range
result = monitor_db.select(query)
else:
query = 'SELECT session_history_media_info.video_resolution AS resolution,' \
'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
'or session_history_media_info.audio_decision = "direct play") ' \
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_count, ' \
'SUM(case when (session_history_media_info.video_decision != "transcode" ' \
'and session_history_media_info.audio_decision = "copy") ' \
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_count, ' \
'SUM(case when (session_history_media_info.video_decision = "transcode" ' \
'or session_history_media_info.audio_decision = "transcode") ' \
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_count, ' \
'SUM(case when stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
'OR session_history_media_info.audio_decision = "direct play") ' \
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS dp_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
'AND session_history_media_info.audio_decision = "copy") ' \
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS ds_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
'OR session_history_media_info.audio_decision = "transcode") ' \
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tc_count, ' \
'SUM(CASE WHEN stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS total_duration ' \
'FROM session_history ' \
'JOIN session_history_media_info on session_history.id = session_history_media_info.id ' \
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
'datetime("now", "-%s days", "localtime")) AND ' \
'(session_history.media_type = "episode" OR session_history.media_type = "movie") ' \
'GROUP BY resolution ' \
'ORDER BY total_duration DESC ' \
'LIMIT 10'
'LIMIT 10' % time_range
result = monitor_db.select(query)
except Exception as e:
logger.warn(u"PlexPy Graphs :: Unable to execute database query for get_total_plays_by_source_resolution: %s." % e)
return None
categories = []
series_1 = []
@ -649,74 +658,75 @@ class Graphs(object):
if not time_range.isdigit():
time_range = '30'
try:
if y_axis == 'plays':
query = 'SELECT ' \
'(case when session_history_media_info.video_decision = "transcode" then ' \
'(case ' \
'when session_history_media_info.transcode_height <= 360 then "sd" ' \
'when session_history_media_info.transcode_height <= 480 then "480" ' \
'when session_history_media_info.transcode_height <= 576 then "576" ' \
'when session_history_media_info.transcode_height <= 720 then "720" ' \
'when session_history_media_info.transcode_height <= 1080 then "1080" ' \
'when session_history_media_info.transcode_height <= 1440 then "QHD" ' \
'when session_history_media_info.transcode_height <= 2160 then "4K" ' \
'else "unknown" end) else session_history_media_info.video_resolution end) as resolution, ' \
'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
'or session_history_media_info.audio_decision = "direct play") ' \
'then 1 else 0 end) as dp_count, ' \
'SUM(case when (session_history_media_info.video_decision != "transcode" ' \
'and session_history_media_info.audio_decision = "copy") ' \
'then 1 else 0 end) as ds_count, ' \
'SUM(case when (session_history_media_info.video_decision = "transcode" '\
'or session_history_media_info.audio_decision = "transcode") ' \
'then 1 else 0 end) as tc_count, ' \
'COUNT(session_history.id) as total_count ' \
'(CASE WHEN session_history_media_info.video_decision = "transcode" THEN ' \
'(CASE ' \
'WHEN session_history_media_info.transcode_height <= 360 THEN "sd" ' \
'WHEN session_history_media_info.transcode_height <= 480 THEN "480" ' \
'WHEN session_history_media_info.transcode_height <= 576 THEN "576" ' \
'WHEN session_history_media_info.transcode_height <= 720 THEN "720" ' \
'WHEN session_history_media_info.transcode_height <= 1080 THEN "1080" ' \
'WHEN session_history_media_info.transcode_height <= 1440 THEN "QHD" ' \
'WHEN session_history_media_info.transcode_height <= 2160 THEN "4K" ' \
'ELSE "unknown" END) ELSE session_history_media_info.video_resolution END) AS resolution, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
'OR session_history_media_info.audio_decision = "direct play") THEN 1 ELSE 0 END) AS dp_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
'AND session_history_media_info.audio_decision = "copy") THEN 1 ELSE 0 END) AS ds_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" '\
'OR session_history_media_info.audio_decision = "transcode") THEN 1 ELSE 0 END) AS tc_count, ' \
'COUNT(session_history.id) AS total_count ' \
'FROM session_history ' \
'JOIN session_history_media_info on session_history.id = session_history_media_info.id ' \
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
'datetime("now", "-%s days", "localtime")) AND ' \
'(session_history.media_type = "episode" OR session_history.media_type = "movie") ' \
'GROUP BY resolution ' \
'ORDER BY total_count DESC ' \
'LIMIT 10'
'LIMIT 10' % time_range
result = monitor_db.select(query)
else:
query = 'SELECT ' \
'(case when session_history_media_info.video_decision = "transcode" then ' \
'(case ' \
'when session_history_media_info.transcode_height <= 360 then "sd" ' \
'when session_history_media_info.transcode_height <= 480 then "480" ' \
'when session_history_media_info.transcode_height <= 576 then "576" ' \
'when session_history_media_info.transcode_height <= 720 then "720" ' \
'when session_history_media_info.transcode_height <= 1080 then "1080" ' \
'when session_history_media_info.transcode_height <= 1440 then "QHD" ' \
'when session_history_media_info.transcode_height <= 2160 then "4K" ' \
'else "unknown" end) else session_history_media_info.video_resolution end) as resolution, ' \
'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
'or session_history_media_info.audio_decision = "direct play") ' \
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_count, ' \
'SUM(case when (session_history_media_info.video_decision != "transcode" ' \
'and session_history_media_info.audio_decision = "copy") ' \
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_count, ' \
'SUM(case when (session_history_media_info.video_decision = "transcode" ' \
'or session_history_media_info.audio_decision = "transcode") ' \
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_count, ' \
'SUM(case when stopped > 0 then (stopped - started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \
'(CASE WHEN session_history_media_info.video_decision = "transcode" THEN ' \
'(CASE ' \
'WHEN session_history_media_info.transcode_height <= 360 THEN "sd" ' \
'WHEN session_history_media_info.transcode_height <= 480 THEN "480" ' \
'WHEN session_history_media_info.transcode_height <= 576 THEN "576" ' \
'WHEN session_history_media_info.transcode_height <= 720 THEN "720" ' \
'WHEN session_history_media_info.transcode_height <= 1080 THEN "1080" ' \
'WHEN session_history_media_info.transcode_height <= 1440 THEN "QHD" ' \
'WHEN session_history_media_info.transcode_height <= 2160 THEN "4K" ' \
'ELSE "unknown" END) ELSE session_history_media_info.video_resolution END) AS resolution, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
'OR session_history_media_info.audio_decision = "direct play") ' \
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS dp_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
'AND session_history_media_info.audio_decision = "copy") ' \
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS ds_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
'OR session_history_media_info.audio_decision = "transcode") ' \
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tc_count, ' \
'SUM(CASE WHEN stopped > 0 THEN (stopped - started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS total_duration ' \
'FROM session_history ' \
'JOIN session_history_media_info on session_history.id = session_history_media_info.id ' \
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
'WHERE (datetime(session_history.stopped, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime")) AND ' \
'datetime("now", "-%s days", "localtime")) AND ' \
'(session_history.media_type = "episode" OR session_history.media_type = "movie") ' \
'GROUP BY resolution ' \
'ORDER BY total_duration DESC ' \
'LIMIT 10'
'LIMIT 10' % time_range
result = monitor_db.select(query)
except Exception as e:
logger.warn(u"PlexPy Graphs :: Unable to execute database query for get_total_plays_by_stream_resolution: %s." % e)
return None
categories = []
series_1 = []
@ -746,55 +756,54 @@ class Graphs(object):
if not time_range.isdigit():
time_range = '30'
try:
if y_axis == 'plays':
query = 'SELECT ' \
'session_history.platform as platform, ' \
'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
'or session_history_media_info.audio_decision = "direct play") ' \
'then 1 else 0 end) as dp_count, ' \
'SUM(case when (session_history_media_info.video_decision != "transcode" ' \
'and session_history_media_info.audio_decision = "copy") ' \
'then 1 else 0 end) as ds_count, ' \
'SUM(case when (session_history_media_info.video_decision = "transcode" ' \
'or session_history_media_info.audio_decision = "transcode") ' \
'then 1 else 0 end) as tc_count, ' \
'COUNT(session_history.id) as total_count ' \
query = 'SELECT session_history.platform AS platform, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
'OR session_history_media_info.audio_decision = "direct play") THEN 1 ELSE 0 END) AS dp_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
'AND session_history_media_info.audio_decision = "copy") THEN 1 ELSE 0 END) AS ds_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
'OR session_history_media_info.audio_decision = "transcode") THEN 1 ELSE 0 END) AS tc_count, ' \
'COUNT(session_history.id) AS total_count ' \
'FROM session_history ' \
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
'WHERE datetime(session_history.started, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime") AND ' \
'datetime("now", "-%s days", "localtime") AND ' \
'(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
'GROUP BY platform ' \
'ORDER BY total_count DESC LIMIT 10'
'ORDER BY total_count DESC LIMIT 10' % time_range
result = monitor_db.select(query)
else:
query = 'SELECT ' \
'session_history.platform as platform, ' \
'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
'or session_history_media_info.audio_decision = "direct play") ' \
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_count, ' \
'SUM(case when (session_history_media_info.video_decision != "transcode" ' \
'and session_history_media_info.audio_decision = "copy") ' \
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_count, ' \
'SUM(case when (session_history_media_info.video_decision = "transcode" ' \
'and session_history_media_info.audio_decision = "transcode") ' \
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_count, ' \
'SUM(case when session_history.stopped > 0 ' \
'then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \
query = 'SELECT session_history.platform AS platform, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
'OR session_history_media_info.audio_decision = "direct play") ' \
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS dp_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
'AND session_history_media_info.audio_decision = "copy") ' \
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS ds_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
'AND session_history_media_info.audio_decision = "transcode") ' \
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tc_count, ' \
'SUM(CASE WHEN session_history.stopped > 0 ' \
'THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS total_duration ' \
'FROM session_history ' \
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
'WHERE datetime(session_history.started, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime") AND ' \
'datetime("now", "-%s days", "localtime") AND ' \
'(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
'GROUP BY platform ' \
'ORDER BY total_duration DESC LIMIT 10'
'ORDER BY total_duration DESC LIMIT 10' % time_range
result = monitor_db.select(query)
except Exception as e:
logger.warn(u"PlexPy Graphs :: Unable to execute database query for get_stream_type_by_top_10_platforms: %s." % e)
return None
categories = []
series_1 = []
@ -825,57 +834,58 @@ class Graphs(object):
if not time_range.isdigit():
time_range = '30'
try:
if y_axis == 'plays':
query = 'SELECT ' \
'CASE WHEN users.friendly_name is null then users.username else users.friendly_name end as username, ' \
'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
'or session_history_media_info.audio_decision = "direct play") ' \
'then 1 else 0 end) as dp_count, ' \
'SUM(case when (session_history_media_info.video_decision != "transcode" ' \
'and session_history_media_info.audio_decision = "copy") ' \
'then 1 else 0 end) as ds_count, ' \
'SUM(case when (session_history_media_info.video_decision = "transcode" ' \
'or session_history_media_info.audio_decision = "transcode") ' \
'then 1 else 0 end) as tc_count, ' \
'COUNT(session_history.id) as total_count ' \
'(CASE WHEN users.friendly_name IS NULL THEN users.username ELSE users.friendly_name END) AS username, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
'OR session_history_media_info.audio_decision = "direct play") THEN 1 ELSE 0 END) AS dp_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
'AND session_history_media_info.audio_decision = "copy") THEN 1 ELSE 0 END) AS ds_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
'OR session_history_media_info.audio_decision = "transcode") THEN 1 ELSE 0 END) AS tc_count, ' \
'COUNT(session_history.id) AS total_count ' \
'FROM session_history ' \
'JOIN users ON session_history.user_id = users.user_id ' \
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
'WHERE datetime(session_history.started, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime") AND ' \
'datetime("now", "-%s days", "localtime") AND ' \
'(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
'GROUP BY username ' \
'ORDER BY total_count DESC LIMIT 10'
'ORDER BY total_count DESC LIMIT 10' % time_range
result = monitor_db.select(query)
else:
query = 'SELECT ' \
'CASE WHEN users.friendly_name is null then users.username else users.friendly_name end as username, ' \
'SUM(case when (session_history_media_info.video_decision = "direct play" ' \
'or session_history_media_info.audio_decision = "direct play") ' \
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_count, ' \
'SUM(case when (session_history_media_info.video_decision != "transcode" ' \
'and session_history_media_info.audio_decision = "copy") ' \
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_count, ' \
'SUM(case when (session_history_media_info.video_decision = "transcode" ' \
'and session_history_media_info.audio_decision = "transcode") ' \
'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_count, ' \
'SUM(case when session_history.stopped > 0 ' \
'then (session_history.stopped - session_history.started) ' \
' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \
'(CASE WHEN users.friendly_name IS NULL THEN users.username ELSE users.friendly_name END) AS username, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "direct play" ' \
'OR session_history_media_info.audio_decision = "direct play") ' \
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS dp_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision != "transcode" ' \
'AND session_history_media_info.audio_decision = "copy") ' \
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS ds_count, ' \
'SUM(CASE WHEN (session_history_media_info.video_decision = "transcode" ' \
'AND session_history_media_info.audio_decision = "transcode") ' \
'AND session_history.stopped > 0 THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS tc_count, ' \
'SUM(CASE WHEN session_history.stopped > 0 ' \
'THEN (session_history.stopped - session_history.started) ' \
' - (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) AS total_duration ' \
'FROM session_history ' \
'JOIN users ON session_history.user_id = users.user_id ' \
'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \
'WHERE datetime(session_history.started, "unixepoch", "localtime") >= ' \
'datetime("now", "-' + time_range + ' days", "localtime") AND ' \
'datetime("now", "-%s days", "localtime") AND ' \
'(session_history.media_type = "episode" OR session_history.media_type = "movie" OR session_history.media_type = "track") ' \
'GROUP BY username ' \
'ORDER BY total_duration DESC LIMIT 10'
'ORDER BY total_duration DESC LIMIT 10' % time_range
result = monitor_db.select(query)
except Exception as e:
logger.warn(u"PlexPy Graphs :: Unable to execute database query for get_stream_type_by_top_10_users: %s." % e)
return None
categories = []
series_1 = []

View file

@ -150,7 +150,7 @@ def human_duration(s, sig='dhms'):
hd = ''
if str(s).isdigit():
if str(s).isdigit() and s > 0:
d = int(s / 84600)
h = int((s % 84600) / 3600)
m = int(((s % 84600) % 3600) / 60)
@ -173,6 +173,8 @@ def human_duration(s, sig='dhms'):
hd_list.append(str(s) + ' secs')
hd = ' '.join(hd_list)
else:
hd = '0'
return hd
@ -364,13 +366,18 @@ def create_https_certificates(ssl_cert, ssl_key):
return True
def cast_to_int(s):
try:
return int(s)
except ValueError:
return -1
def cast_to_float(s):
try:
return float(s)
except ValueError:
return -1
def convert_xml_to_json(xml):
o = xmltodict.parse(xml)
return json.dumps(o)

835
plexpy/libraries.py Normal file
View file

@ -0,0 +1,835 @@
# This file is part of PlexPy.
#
# PlexPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PlexPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
from plexpy import logger, datatables, common, database, helpers
import plexpy
def update_section_ids():
from plexpy import pmsconnect, activity_pinger
plexpy.CONFIG.UPDATE_SECTION_IDS = -1
logger.info(u"PlexPy Libraries :: Updating section_id's in database.")
logger.debug(u"PlexPy Libraries :: Disabling monitoring while update in progress.")
plexpy.schedule_job(activity_pinger.check_active_sessions, 'Check for active sessions',
hours=0, minutes=0, seconds=0)
plexpy.schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
hours=0, minutes=0, seconds=0)
plexpy.schedule_job(activity_pinger.check_server_response, 'Check for server response',
hours=0, minutes=0, seconds=0)
monitor_db = database.MonitorDatabase()
try:
query = 'SELECT id, rating_key FROM session_history_metadata WHERE section_id IS NULL'
result = monitor_db.select(query=query)
except Exception as e:
logger.warn(u"PlexPy Libraries :: Unable to execute database query for update_section_ids: %s." % e)
logger.debug(u"PlexPy Libraries :: Unable to update section_id's in database.")
plexpy.CONFIG.__setattr__('UPDATE_SECTION_IDS', 1)
plexpy.CONFIG.write()
logger.debug(u"PlexPy Libraries :: Re-enabling monitoring.")
plexpy.initialize_scheduler()
return None
pms_connect = pmsconnect.PmsConnect()
error_keys = set()
for item in result:
id = item['id']
rating_key = item['rating_key']
metadata = pms_connect.get_metadata_details(rating_key=rating_key)
if metadata:
metadata = metadata['metadata']
section_keys = {'id': id}
section_values = {'section_id': metadata['section_id']}
monitor_db.upsert('session_history_metadata', key_dict=section_keys, value_dict=section_values)
else:
error_keys.add(rating_key)
if error_keys:
logger.debug(u"PlexPy Libraries :: Updated all section_id's in database except for rating_keys: %s." %
', '.join(str(key) for key in error_keys))
else:
logger.debug(u"PlexPy Libraries :: Updated all section_id's in database.")
plexpy.CONFIG.__setattr__('UPDATE_SECTION_IDS', 0)
plexpy.CONFIG.write()
logger.debug(u"PlexPy Libraries :: Re-enabling monitoring.")
plexpy.initialize_scheduler()
return True
class Libraries(object):
def __init__(self):
pass
def get_datatables_list(self, kwargs=None):
data_tables = datatables.DataTables()
custom_where = ['library_sections.deleted_section', 0]
columns = ['library_sections.section_id',
'library_sections.section_name',
'library_sections.section_type',
'library_sections.count',
'library_sections.parent_count',
'library_sections.child_count',
'library_sections.thumb AS library_thumb',
'library_sections.custom_thumb_url AS custom_thumb',
'library_sections.art',
'COUNT(session_history.id) AS plays',
'MAX(session_history.started) AS last_accessed',
'session_history_metadata.full_title AS last_watched',
'session_history_metadata.thumb',
'session_history_metadata.parent_thumb',
'session_history_metadata.grandparent_thumb',
'session_history_metadata.media_type',
'session_history.rating_key',
'session_history_media_info.video_decision',
'library_sections.do_notify',
'library_sections.do_notify_created',
'library_sections.keep_history'
]
try:
query = data_tables.ssp_query(table_name='library_sections',
columns=columns,
custom_where=[custom_where],
group_by=['library_sections.section_id'],
join_types=['LEFT OUTER JOIN',
'LEFT OUTER JOIN',
'LEFT OUTER JOIN'],
join_tables=['session_history_metadata',
'session_history',
'session_history_media_info'],
join_evals=[['session_history_metadata.section_id', 'library_sections.section_id'],
['session_history_metadata.id', 'session_history.id'],
['session_history_metadata.id', 'session_history_media_info.id']],
kwargs=kwargs)
except Exception as e:
logger.warn(u"PlexPy Libraries :: Unable to execute database query for get_list: %s." % e)
return {'recordsFiltered': 0,
'recordsTotal': 0,
'draw': 0,
'data': 'null',
'error': 'Unable to execute database query.'}
result = query['result']
rows = []
for item in result:
if item['media_type'] == 'episode' and item['parent_thumb']:
thumb = item['parent_thumb']
elif item['media_type'] == 'episode':
thumb = item['grandparent_thumb']
else:
thumb = item['thumb']
if item['custom_thumb'] and item['custom_thumb'] != item['library_thumb']:
library_thumb = item['custom_thumb']
elif item['library_thumb']:
library_thumb = item['library_thumb']
else:
library_thumb = common.DEFAULT_COVER_THUMB
row = {'section_id': item['section_id'],
'section_name': item['section_name'],
'section_type': item['section_type'].capitalize(),
'count': item['count'],
'parent_count': item['parent_count'],
'child_count': item['child_count'],
'library_thumb': library_thumb,
'library_art': item['art'],
'plays': item['plays'],
'last_accessed': item['last_accessed'],
'last_watched': item['last_watched'],
'thumb': thumb,
'media_type': item['media_type'],
'rating_key': item['rating_key'],
'video_decision': item['video_decision'],
'do_notify': helpers.checked(item['do_notify']),
'do_notify_created': helpers.checked(item['do_notify_created']),
'keep_history': helpers.checked(item['keep_history'])
}
rows.append(row)
dict = {'recordsFiltered': query['filteredCount'],
'recordsTotal': query['totalCount'],
'data': rows,
'draw': query['draw']
}
return dict
def get_datatables_media_info(self, section_id=None, section_type=None, rating_key=None, refresh=False, kwargs=None):
from plexpy import pmsconnect
import json, os
default_return = {'recordsFiltered': 0,
'recordsTotal': 0,
'draw': 0,
'data': None,
'error': 'Unable to execute database query.'}
if section_id and not str(section_id).isdigit():
logger.warn(u"PlexPy Libraries :: Datatable media info called by invalid section_id provided.")
return default_return
elif rating_key and not str(rating_key).isdigit():
logger.warn(u"PlexPy Libraries :: Datatable media info called by invalid rating_key provided.")
return default_return
# Get the library details
library_details = self.get_details(section_id=section_id)
if library_details['section_id'] == None:
logger.debug(u"PlexPy Libraries :: Library section_id %s not found." % section_id)
return default_return
if not section_type:
section_type = library_details['section_type']
# Get play counts from the database
monitor_db = database.MonitorDatabase()
if plexpy.CONFIG.GROUP_HISTORY_TABLES:
count_by = 'reference_id'
else:
count_by = 'id'
if section_type == 'show' or section_type == 'artist':
group_by = 'grandparent_rating_key'
elif section_type == 'season' or section_type == 'album':
group_by = 'parent_rating_key'
else:
group_by = 'rating_key'
try:
query = 'SELECT MAX(session_history.started) AS last_watched, COUNT(DISTINCT session_history.%s) AS play_count, ' \
'session_history.rating_key, session_history.parent_rating_key, session_history.grandparent_rating_key ' \
'FROM session_history ' \
'JOIN session_history_metadata ON session_history.id = session_history_metadata.id ' \
'WHERE session_history_metadata.section_id = ? ' \
'GROUP BY session_history.%s ' % (count_by, group_by)
result = monitor_db.select(query, args=[section_id])
except Exception as e:
logger.warn(u"PlexPy Libraries :: Unable to execute database query for get_datatables_media_info2: %s." % e)
return default_return
watched_list = {}
for item in result:
watched_list[str(item[group_by])] = {'last_watched': item['last_watched'],
'play_count': item['play_count']}
rows = []
# Import media info cache from json file
if rating_key:
try:
inFilePath = os.path.join(plexpy.CONFIG.CACHE_DIR,'media_info_%s-%s.json' % (section_id, rating_key))
with open(inFilePath, 'r') as inFile:
rows = json.load(inFile)
library_count = len(rows)
except IOError as e:
#logger.debug(u"PlexPy Libraries :: No JSON file for rating_key %s." % rating_key)
#logger.debug(u"PlexPy Libraries :: Refreshing data and creating new JSON file for rating_key %s." % rating_key)
pass
elif section_id:
try:
inFilePath = os.path.join(plexpy.CONFIG.CACHE_DIR,'media_info_%s.json' % section_id)
with open(inFilePath, 'r') as inFile:
rows = json.load(inFile)
library_count = len(rows)
except IOError as e:
#logger.debug(u"PlexPy Libraries :: No JSON file for library section_id %s." % section_id)
#logger.debug(u"PlexPy Libraries :: Refreshing data and creating new JSON file for section_id %s." % section_id)
pass
# If no cache was imported, get all library children items
cached_items = {d['rating_key']: d['file_size'] for d in rows}
if refresh or not rows:
pms_connect = pmsconnect.PmsConnect()
if rating_key:
library_children = pms_connect.get_library_children_details(rating_key=rating_key,
get_media_info=True)
elif section_id:
library_children = pms_connect.get_library_children_details(section_id=section_id,
section_type=section_type,
get_media_info=True)
if library_children:
library_count = library_children['library_count']
children_list = library_children['childern_list']
else:
logger.warn(u"PlexPy Libraries :: Unable to get a list of library items.")
return default_return
for item in children_list:
cached_file_size = cached_items.get(item['rating_key'], None)
file_size = cached_file_size if cached_file_size else item.get('file_size', '')
row = {'section_id': library_details['section_id'],
'section_type': library_details['section_type'],
'added_at': item['added_at'],
'media_type': item['media_type'],
'rating_key': item['rating_key'],
'parent_rating_key': item['parent_rating_key'],
'grandparent_rating_key': item['grandparent_rating_key'],
'title': item['title'],
'year': item['year'],
'media_index': item['media_index'],
'parent_media_index': item['parent_media_index'],
'thumb': item['thumb'],
'container': item.get('container', ''),
'bitrate': item.get('bitrate', ''),
'video_codec': item.get('video_codec', ''),
'video_resolution': item.get('video_resolution', ''),
'video_framerate': item.get('video_framerate', ''),
'audio_codec': item.get('audio_codec', ''),
'audio_channels': item.get('audio_channels', ''),
'file_size': file_size
}
rows.append(row)
if not rows:
return default_return
# Cache the media info to a json file
if rating_key:
try:
outFilePath = os.path.join(plexpy.CONFIG.CACHE_DIR,'media_info_%s-%s.json' % (section_id, rating_key))
with open(outFilePath, 'w') as outFile:
json.dump(rows, outFile)
except IOError as e:
logger.debug(u"PlexPy Libraries :: Unable to create cache file for rating_key %s." % rating_key)
elif section_id:
try:
outFilePath = os.path.join(plexpy.CONFIG.CACHE_DIR,'media_info_%s.json' % section_id)
with open(outFilePath, 'w') as outFile:
json.dump(rows, outFile)
except IOError as e:
logger.debug(u"PlexPy Libraries :: Unable to create cache file for section_id %s." % section_id)
# Update the last_watched and play_count
for item in rows:
watched_item = watched_list.get(item['rating_key'], None)
if watched_item:
item['last_watched'] = watched_item['last_watched']
item['play_count'] = watched_item['play_count']
else:
item['last_watched'] = None
item['play_count'] = None
results = []
# Get datatables JSON data
if kwargs.get('json_data'):
json_data = helpers.process_json_kwargs(json_kwargs=kwargs.get('json_data'))
#print json_data
# Search results
search_value = json_data['search']['value'].lower()
if search_value:
searchable_columns = [d['data'] for d in json_data['columns'] if d['searchable']]
for row in rows:
for k,v in row.iteritems():
if k in searchable_columns and search_value in v.lower():
results.append(row)
break
else:
results = rows
filtered_count = len(results)
# Sort results
results = sorted(results, key=lambda k: k['title'])
sort_order = json_data['order']
for order in reversed(sort_order):
sort_key = json_data['columns'][int(order['column'])]['data']
reverse = True if order['dir'] == 'desc' else False
if rating_key and sort_key == 'title':
results = sorted(results, key=lambda k: helpers.cast_to_int(k['media_index']), reverse=reverse)
elif sort_key == 'file_size' or sort_key == 'bitrate':
results = sorted(results, key=lambda k: helpers.cast_to_int(k[sort_key]), reverse=reverse)
else:
results = sorted(results, key=lambda k: k[sort_key], reverse=reverse)
total_file_size = sum([helpers.cast_to_int(d['file_size']) for d in results])
# Paginate results
results = results[json_data['start']:(json_data['start'] + json_data['length'])]
filtered_file_size = sum([helpers.cast_to_int(d['file_size']) for d in results])
dict = {'recordsFiltered': filtered_count,
'recordsTotal': library_count,
'data': results,
'draw': int(json_data['draw']),
'filtered_file_size': filtered_file_size,
'total_file_size': total_file_size
}
return dict
def get_media_info_file_sizes(self, section_id=None, rating_key=None):
from plexpy import pmsconnect
import json, os
if section_id and not str(section_id).isdigit():
logger.warn(u"PlexPy Libraries :: Datatable media info file size called by invalid section_id provided.")
return False
elif rating_key and not str(rating_key).isdigit():
logger.warn(u"PlexPy Libraries :: Datatable media info file size called by invalid rating_key provided.")
return False
# Get the library details
library_details = self.get_details(section_id=section_id)
if library_details['section_id'] == None:
logger.debug(u"PlexPy Libraries :: Library section_id %s not found." % section_id)
return False
if library_details['section_type'] == 'photo':
return False
rows = []
# Import media info cache from json file
if rating_key:
#logger.debug(u"PlexPy Libraries :: Getting file sizes for rating_key %s." % rating_key)
try:
inFilePath = os.path.join(plexpy.CONFIG.CACHE_DIR,'media_info_%s-%s.json' % (section_id, rating_key))
with open(inFilePath, 'r') as inFile:
rows = json.load(inFile)
except IOError as e:
#logger.debug(u"PlexPy Libraries :: No JSON file for rating_key %s." % rating_key)
#logger.debug(u"PlexPy Libraries :: Refreshing data and creating new JSON file for rating_key %s." % rating_key)
pass
elif section_id:
logger.debug(u"PlexPy Libraries :: Getting file sizes for section_id %s." % section_id)
try:
inFilePath = os.path.join(plexpy.CONFIG.CACHE_DIR,'media_info_%s.json' % section_id)
with open(inFilePath, 'r') as inFile:
rows = json.load(inFile)
except IOError as e:
#logger.debug(u"PlexPy Libraries :: No JSON file for library section_id %s." % section_id)
#logger.debug(u"PlexPy Libraries :: Refreshing data and creating new JSON file for section_id %s." % section_id)
pass
# Get the total file size for each item
pms_connect = pmsconnect.PmsConnect()
for item in rows:
if item['rating_key'] and not item['file_size']:
file_size = 0
child_metadata = pms_connect.get_metadata_children_details(rating_key=item['rating_key'],
get_children=True,
get_media_info=True)
metadata_list = child_metadata['metadata']
for child_metadata in metadata_list:
file_size += helpers.cast_to_int(child_metadata.get('file_size', 0))
item['file_size'] = file_size
# Cache the media info to a json file
if rating_key:
try:
outFilePath = os.path.join(plexpy.CONFIG.CACHE_DIR,'media_info_%s-%s.json' % (section_id, rating_key))
with open(outFilePath, 'w') as outFile:
json.dump(rows, outFile)
except IOError as e:
logger.debug(u"PlexPy Libraries :: Unable to create cache file with file sizes for rating_key %s." % rating_key)
elif section_id:
try:
outFilePath = os.path.join(plexpy.CONFIG.CACHE_DIR,'media_info_%s.json' % section_id)
with open(outFilePath, 'w') as outFile:
json.dump(rows, outFile)
except IOError as e:
logger.debug(u"PlexPy Libraries :: Unable to create cache file with file sizes for section_id %s." % section_id)
if rating_key:
#logger.debug(u"PlexPy Libraries :: File sizes updated for rating_key %s." % rating_key)
pass
elif section_id:
logger.debug(u"PlexPy Libraries :: File sizes updated for section_id %s." % section_id)
return True
def set_config(self, section_id=None, custom_thumb='', do_notify=1, keep_history=1, do_notify_created=1):
if section_id:
monitor_db = database.MonitorDatabase()
key_dict = {'section_id': section_id}
value_dict = {'custom_thumb_url': custom_thumb,
'do_notify': do_notify,
'do_notify_created': do_notify_created,
'keep_history': keep_history}
try:
monitor_db.upsert('library_sections', value_dict, key_dict)
except:
logger.warn(u"PlexPy Libraries :: Unable to execute database query for set_config: %s." % e)
def get_details(self, section_id=None):
from plexpy import pmsconnect
monitor_db = database.MonitorDatabase()
try:
if section_id:
query = 'SELECT section_id, section_name, section_type, count, parent_count, child_count, ' \
'thumb AS library_thumb, custom_thumb_url AS custom_thumb, art, ' \
'do_notify, do_notify_created, keep_history ' \
'FROM library_sections ' \
'WHERE section_id = ? '
result = monitor_db.select(query, args=[section_id])
else:
result = []
except Exception as e:
logger.warn(u"PlexPy Libraries :: Unable to execute database query for get_details: %s." % e)
result = []
if result:
library_details = {}
for item in result:
if item['custom_thumb'] and item['custom_thumb'] != item['library_thumb']:
library_thumb = item['custom_thumb']
elif item['library_thumb']:
library_thumb = item['library_thumb']
else:
library_thumb = common.DEFAULT_COVER_THUMB
library_details = {'section_id': item['section_id'],
'section_name': item['section_name'],
'section_type': item['section_type'],
'library_thumb': library_thumb,
'library_art': item['art'],
'count': item['count'],
'parent_count': item['parent_count'],
'child_count': item['child_count'],
'do_notify': item['do_notify'],
'do_notify_created': item['do_notify_created'],
'keep_history': item['keep_history']
}
return library_details
else:
logger.warn(u"PlexPy Libraries :: Unable to retrieve library from local database. Requesting library list refresh.")
# Let's first refresh the user list to make sure the user isn't newly added and not in the db yet
try:
if section_id:
# Refresh libraries
pmsconnect.refresh_libraries()
query = 'SELECT section_id, section_name, section_type, count, parent_count, child_count, ' \
'thumb AS library_thumb, custom_thumb_url AS custom_thumb, art, ' \
'do_notify, do_notify_created, keep_history ' \
'FROM library_sections ' \
'WHERE section_id = ? '
result = monitor_db.select(query, args=[section_id])
else:
result = []
except:
logger.warn(u"PlexPy Libraries :: Unable to execute database query for get_details: %s." % e)
result = []
if result:
library_details = {}
for item in result:
if item['custom_thumb'] and item['custom_thumb'] != item['library_thumb']:
library_thumb = item['custom_thumb']
elif item['library_thumb']:
library_thumb = item['library_thumb']
else:
library_thumb = common.DEFAULT_COVER_THUMB
library_details = {'section_id': item['section_id'],
'section_name': item['section_name'],
'section_type': item['section_type'],
'library_thumb': library_thumb,
'library_art': item['art'],
'count': item['count'],
'parent_count': item['parent_count'],
'child_count': item['child_count'],
'do_notify': item['do_notify'],
'do_notify_created': item['do_notify_created'],
'keep_history': item['keep_history']
}
return library_details
else:
# If there is no library data we must return something
# Use "Local" user to retain compatibility with PlexWatch database value
return {'section_id': None,
'section_name': 'Local',
'section_type': '',
'library_thumb': common.DEFAULT_COVER_THUMB,
'library_art': '',
'count': 0,
'parent_count': 0,
'child_count': 0,
'do_notify': 0,
'do_notify_created': 0,
'keep_history': 0
}
def get_watch_time_stats(self, section_id=None):
monitor_db = database.MonitorDatabase()
time_queries = [1, 7, 30, 0]
library_watch_time_stats = []
for days in time_queries:
try:
if days > 0:
if str(section_id).isdigit():
query = 'SELECT (SUM(stopped - started) - ' \
'SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \
'COUNT(session_history.id) AS total_plays ' \
'FROM session_history ' \
'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
'AND section_id = ?' % days
result = monitor_db.select(query, args=[section_id])
else:
result = []
else:
if str(section_id).isdigit():
query = 'SELECT (SUM(stopped - started) - ' \
'SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \
'COUNT(session_history.id) AS total_plays ' \
'FROM session_history ' \
'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
'WHERE section_id = ?'
result = monitor_db.select(query, args=[section_id])
else:
result = []
except Exception as e:
logger.warn(u"PlexPy Libraries :: Unable to execute database query for get_watch_time_stats: %s." % e)
result = []
for item in result:
if item['total_time']:
total_time = item['total_time']
total_plays = item['total_plays']
else:
total_time = 0
total_plays = 0
row = {'query_days': days,
'total_time': total_time,
'total_plays': total_plays
}
library_watch_time_stats.append(row)
return library_watch_time_stats
def get_user_stats(self, section_id=None):
monitor_db = database.MonitorDatabase()
user_stats = []
try:
if str(section_id).isdigit():
query = 'SELECT (CASE WHEN users.friendly_name IS NULL THEN users.username ' \
'ELSE users.friendly_name END) AS user, users.user_id, users.thumb, COUNT(user) AS user_count ' \
'FROM session_history ' \
'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
'JOIN users ON users.user_id = session_history.user_id ' \
'WHERE section_id = ? ' \
'GROUP BY user ' \
'ORDER BY user_count DESC'
result = monitor_db.select(query, args=[section_id])
else:
result = []
except Exception as e:
logger.warn(u"PlexPy Libraries :: Unable to execute database query for get_user_stats: %s." % e)
result = []
for item in result:
row = {'user': item['user'],
'user_id': item['user_id'],
'thumb': item['thumb'],
'total_plays': item['user_count']
}
user_stats.append(row)
return user_stats
def get_recently_watched(self, section_id=None, limit='10'):
monitor_db = database.MonitorDatabase()
recently_watched = []
if not limit.isdigit():
limit = '10'
try:
if str(section_id).isdigit():
query = 'SELECT session_history.id, session_history.media_type, session_history.rating_key, session_history.parent_rating_key, ' \
'title, parent_title, grandparent_title, thumb, parent_thumb, grandparent_thumb, media_index, parent_media_index, ' \
'year, started, user ' \
'FROM session_history_metadata ' \
'JOIN session_history ON session_history_metadata.id = session_history.id ' \
'WHERE section_id = ? ' \
'GROUP BY (CASE WHEN session_history.media_type = "track" THEN session_history.parent_rating_key ' \
' ELSE session_history.rating_key END) ' \
'ORDER BY started DESC LIMIT ?'
result = monitor_db.select(query, args=[section_id, limit])
else:
result = []
except Exception as e:
logger.warn(u"PlexPy Libraries :: Unable to execute database query for get_recently_watched: %s." % e)
result = []
for row in result:
if row['media_type'] == 'episode' and row['parent_thumb']:
thumb = row['parent_thumb']
elif row['media_type'] == 'episode':
thumb = row['grandparent_thumb']
else:
thumb = row['thumb']
recent_output = {'row_id': row['id'],
'media_type': row['media_type'],
'rating_key': row['rating_key'],
'title': row['title'],
'parent_title': row['parent_title'],
'grandparent_title': row['grandparent_title'],
'thumb': thumb,
'media_index': row['media_index'],
'parent_media_index': row['parent_media_index'],
'year': row['year'],
'time': row['started'],
'user': row['user']
}
recently_watched.append(recent_output)
return recently_watched
def get_sections(self):
monitor_db = database.MonitorDatabase()
try:
query = 'SELECT section_id, section_name FROM library_sections WHERE deleted_section = 0'
result = monitor_db.select(query=query)
except Exception as e:
logger.warn(u"PlexPy Libraries :: Unable to execute database query for get_sections: %s." % e)
return None
libraries = []
for item in result:
library = {'section_id': item['section_id'],
'section_name': item['section_name']
}
libraries.append(library)
return libraries
def delete_all_history(self, section_id=None):
monitor_db = database.MonitorDatabase()
try:
if section_id.isdigit():
logger.info(u"PlexPy Libraries :: Deleting all history for library id %s from database." % section_id)
session_history_media_info_del = \
monitor_db.action('DELETE FROM '
'session_history_media_info '
'WHERE session_history_media_info.id IN (SELECT session_history_media_info.id '
'FROM session_history_media_info '
'JOIN session_history_metadata ON session_history_media_info.id = session_history_metadata.id '
'WHERE session_history_metadata.section_id = ?)', [section_id])
session_history_del = \
monitor_db.action('DELETE FROM '
'session_history '
'WHERE session_history.id IN (SELECT session_history.id '
'FROM session_history '
'JOIN session_history_metadata ON session_history.id = session_history_metadata.id '
'WHERE session_history_metadata.section_id = ?)', [section_id])
session_history_metadata_del = \
monitor_db.action('DELETE FROM '
'session_history_metadata '
'WHERE session_history_metadata.section_id = ?', [section_id])
return 'Deleted all items for section_id %s.' % section_id
else:
return 'Unable to delete items, section_id not valid.'
except Exception as e:
logger.warn(u"PlexPy Libraries :: Unable to execute database query for delete_all_history: %s." % e)
def delete(self, section_id=None):
monitor_db = database.MonitorDatabase()
try:
if section_id.isdigit():
self.delete_all_history(section_id)
logger.info(u"PlexPy Libraries :: Deleting library with id %s from database." % section_id)
monitor_db.action('UPDATE library_sections SET deleted_section = 1 WHERE section_id = ?', [section_id])
monitor_db.action('UPDATE library_sections SET keep_history = 0 WHERE section_id = ?', [section_id])
monitor_db.action('UPDATE library_sections SET do_notify = 0 WHERE section_id = ?', [section_id])
monitor_db.action('UPDATE library_sections SET do_notify_created = 0 WHERE section_id = ?', [section_id])
library_cards = plexpy.CONFIG.HOME_LIBRARY_CARDS
if section_id in library_cards:
library_cards.remove(section_id)
plexpy.CONFIG.__setattr__('HOME_LIBRARY_CARDS', library_cards)
plexpy.CONFIG.write()
return 'Deleted library with id %s.' % section_id
else:
return 'Unable to delete library, section_id not valid.'
except Exception as e:
logger.warn(u"PlexPy Libraries :: Unable to execute database query for delete: %s." % e)
def undelete(self, section_id=None, section_name=None):
monitor_db = database.MonitorDatabase()
try:
if section_id and section_id.isdigit():
logger.info(u"PlexPy Libraries :: Re-adding library with id %s to database." % section_id)
monitor_db.action('UPDATE library_sections SET deleted_section = 0 WHERE section_id = ?', [section_id])
monitor_db.action('UPDATE library_sections SET keep_history = 1 WHERE section_id = ?', [section_id])
monitor_db.action('UPDATE library_sections SET do_notify = 1 WHERE section_id = ?', [section_id])
monitor_db.action('UPDATE library_sections SET do_notify_created = 1 WHERE section_id = ?', [section_id])
return 'Re-added library with id %s.' % section_id
elif section_name:
logger.info(u"PlexPy Libraries :: Re-adding library with name %s to database." % section_name)
monitor_db.action('UPDATE library_sections SET deleted_section = 0 WHERE section_name = ?', [section_name])
monitor_db.action('UPDATE library_sections SET keep_history = 1 WHERE section_name = ?', [section_name])
monitor_db.action('UPDATE library_sections SET do_notify = 1 WHERE section_name = ?', [section_name])
monitor_db.action('UPDATE library_sections SET do_notify_created = 1 WHERE section_name = ?', [section_name])
return 'Re-added library with section_name %s.' % section_name
else:
return 'Unable to re-add library, section_id or section_name not valid.'
except Exception as e:
logger.warn(u"PlexPy Libraries :: Unable to execute database query for undelete: %s." % e)
def delete_datatable_media_info_cache(self, section_id=None):
import os
try:
if section_id.isdigit():
[os.remove(os.path.join(plexpy.CONFIG.CACHE_DIR, f)) for f in os.listdir(plexpy.CONFIG.CACHE_DIR)
if f.startswith('media_info-%s' % section_id) and f.endswith('.json')]
logger.debug(u"PlexPy Libraries :: Deleted media info table cache for section_id %s." % section_id)
return 'Deleted media info table cache for library with id %s.' % section_id
else:
return 'Unable to delete media info table cache, section_id not valid.'
except Exception as e:
logger.warn(u"PlexPy Libraries :: Unable to delete media info table cache: %s." % e)

View file

@ -16,20 +16,28 @@
import re
import time
import re
from plexpy import logger, config, notifiers, database, helpers, plextv, pmsconnect
import plexpy
def notify(stream_data=None, notify_action=None):
from plexpy import users
from plexpy import users, libraries
if stream_data and notify_action:
# Check if notifications enabled for user
# Check if notifications enabled for user and library
user_data = users.Users()
user_details = user_data.get_user_friendly_name(user=stream_data['user'])
user_details = user_data.get_details(user_id=stream_data['user_id'])
library_data = libraries.Libraries()
library_details = library_data.get_details(section_id=stream_data['section_id'])
if not user_details['do_notify']:
# logger.debug(u"PlexPy NotificationHandler :: Notifications for user '%s' is disabled." % user_details['username'])
return
elif not library_details['do_notify']:
# logger.debug(u"PlexPy NotificationHandler :: Notifications for library '%s' is disabled." % library_details['section_name'])
return
if (stream_data['media_type'] == 'movie' and plexpy.CONFIG.MOVIE_NOTIFY_ENABLE) \
@ -195,10 +203,10 @@ def notify(stream_data=None, notify_action=None):
elif stream_data['media_type'] == 'clip':
pass
else:
#logger.debug(u"PlexPy Notifier :: Notify called with unsupported media type.")
#logger.debug(u"PlexPy NotificationHandler :: Notify called with unsupported media type.")
pass
else:
logger.debug(u"PlexPy Notifier :: Notify called but incomplete data received.")
logger.debug(u"PlexPy NotificationHandler :: Notify called but incomplete data received.")
def notify_timeline(timeline_data=None, notify_action=None):
@ -256,7 +264,7 @@ def notify_timeline(timeline_data=None, notify_action=None):
notify_action=notify_action,
script_args=notify_strings[2])
else:
logger.debug(u"PlexPy Notifier :: Notify timeline called but incomplete data received.")
logger.debug(u"PlexPy NotificationHandler :: Notify timeline called but incomplete data received.")
def get_notify_state(session):
@ -334,11 +342,10 @@ def set_notify_state(session, state, agent_info):
monitor_db.upsert(table_name='notify_log', key_dict=keys, value_dict=values)
else:
logger.error('PlexPy Notifier :: Unable to set notify state.')
logger.error(u"PlexPy NotificationHandler :: Unable to set notify state.")
def build_notify_text(session=None, timeline=None, state=None):
# Get the server name
server_name = plexpy.CONFIG.PMS_NAME
@ -350,7 +357,7 @@ def build_notify_text(session=None, timeline=None, state=None):
updated_at = server_times[0]['updated_at']
server_uptime = helpers.human_duration(int(time.time() - helpers.cast_to_float(updated_at)))
else:
logger.error(u"PlexPy Notifier :: Unable to retrieve server uptime.")
logger.error(u"PlexPy NotificationHandler :: Unable to retrieve server uptime.")
server_uptime = 'N/A'
# Get metadata feed for item
@ -367,7 +374,7 @@ def build_notify_text(session=None, timeline=None, state=None):
if metadata_list:
metadata = metadata_list['metadata']
else:
logger.error(u"PlexPy Notifier :: Unable to retrieve metadata for rating_key %s" % str(rating_key))
logger.error(u"PlexPy NotificationHandler :: Unable to retrieve metadata for rating_key %s" % str(rating_key))
return []
# Check for exclusion tags
@ -547,15 +554,16 @@ def build_notify_text(session=None, timeline=None, state=None):
'transcode_audio_codec': transcode_audio_codec,
'transcode_audio_channels': transcode_audio_channels,
'title': full_title,
'library_name': metadata['library_name'],
'show_name': show_name,
'episode_name': episode_name,
'artist_name': artist_name,
'album_name': album_name,
'track_name': track_name,
'season_num': metadata['parent_index'].zfill(1),
'season_num00': metadata['parent_index'].zfill(2),
'episode_num': metadata['index'].zfill(1),
'episode_num00': metadata['index'].zfill(2),
'season_num': metadata['parent_media_index'].zfill(1),
'season_num00': metadata['parent_media_index'].zfill(2),
'episode_num': metadata['media_index'].zfill(1),
'episode_num00': metadata['media_index'].zfill(2),
'year': metadata['year'],
'studio': metadata['studio'],
'content_rating': metadata['content_rating'],
@ -597,16 +605,16 @@ def build_notify_text(session=None, timeline=None, state=None):
try:
subject_text = unicode(on_start_subject).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification subject. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification subject. Using fallback.")
try:
body_text = unicode(on_start_body).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification body. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args]
else:
@ -621,16 +629,16 @@ def build_notify_text(session=None, timeline=None, state=None):
try:
subject_text = unicode(on_stop_subject).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification subject. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification subject. Using fallback.")
try:
body_text = unicode(on_stop_body).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification body. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args]
else:
@ -645,16 +653,16 @@ def build_notify_text(session=None, timeline=None, state=None):
try:
subject_text = unicode(on_pause_subject).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification subject. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification subject. Using fallback.")
try:
body_text = unicode(on_pause_body).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification body. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args]
else:
@ -669,16 +677,16 @@ def build_notify_text(session=None, timeline=None, state=None):
try:
subject_text = unicode(on_resume_subject).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification subject. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification subject. Using fallback.")
try:
body_text = unicode(on_resume_body).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification body. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args]
else:
@ -693,16 +701,16 @@ def build_notify_text(session=None, timeline=None, state=None):
try:
subject_text = unicode(on_buffer_subject).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification subject. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification subject. Using fallback.")
try:
body_text = unicode(on_buffer_body).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification body. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args]
else:
@ -717,16 +725,16 @@ def build_notify_text(session=None, timeline=None, state=None):
try:
subject_text = unicode(on_watched_subject).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification subject. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification subject. Using fallback.")
try:
body_text = unicode(on_watched_body).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification body. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args]
else:
@ -739,16 +747,16 @@ def build_notify_text(session=None, timeline=None, state=None):
try:
subject_text = unicode(on_created_subject).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification subject. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification subject. Using fallback.")
try:
body_text = unicode(on_created_body).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification body. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args]
else:
@ -769,7 +777,7 @@ def build_server_notify_text(state=None):
updated_at = server_times[0]['updated_at']
server_uptime = helpers.human_duration(int(time.time() - helpers.cast_to_float(updated_at)))
else:
logger.error(u"PlexPy Notifier :: Unable to retrieve server uptime.")
logger.error(u"PlexPy NotificationHandler :: Unable to retrieve server uptime.")
server_uptime = 'N/A'
on_extdown_subject = plexpy.CONFIG.NOTIFY_ON_EXTDOWN_SUBJECT_TEXT
@ -812,14 +820,14 @@ def build_server_notify_text(state=None):
try:
subject_text = unicode(on_extdown_subject).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification subject. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification subject. Using fallback.")
try:
body_text = unicode(on_extdown_body).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification body. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
@ -835,16 +843,16 @@ def build_server_notify_text(state=None):
try:
subject_text = unicode(on_intdown_subject).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification subject. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification subject. Using fallback.")
try:
body_text = unicode(on_intdown_body).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification body. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args]
else:
@ -857,16 +865,16 @@ def build_server_notify_text(state=None):
try:
subject_text = unicode(on_extup_subject).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification subject. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification subject. Using fallback.")
try:
body_text = unicode(on_extup_body).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification body. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args]
else:
@ -879,16 +887,16 @@ def build_server_notify_text(state=None):
try:
subject_text = unicode(on_intup_subject).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification subject. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification subject. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification subject. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification subject. Using fallback.")
try:
body_text = unicode(on_intup_body).format(**available_params)
except LookupError, e:
logger.error(u"PlexPy Notifier :: Unable to parse field %s in notification body. Using fallback." % e)
logger.error(u"PlexPy NotificationHandler :: Unable to parse field %s in notification body. Using fallback." % e)
except:
logger.error(u"PlexPy Notifier :: Unable to parse custom notification body. Using fallback.")
logger.error(u"PlexPy NotificationHandler :: Unable to parse custom notification body. Using fallback.")
return [subject_text, body_text, script_args]
else:
@ -899,7 +907,5 @@ def build_server_notify_text(state=None):
def strip_tag(data):
import re
p = re.compile(r'<.*?>')
return p.sub('', data)

View file

@ -525,10 +525,10 @@ class GROWL(object):
try:
growl.register()
except gntp.notifier.errors.NetworkError:
logger.warning(u'Growl notification failed: network error')
logger.warn(u"PlexPy Notifier :: Growl notification failed: network error")
return
except gntp.notifier.errors.AuthError:
logger.warning(u'Growl notification failed: authentication error')
logger.warn(u"PlexPy Notifier :: Growl notification failed: authentication error")
return
# Fix message
@ -548,11 +548,11 @@ class GROWL(object):
description=message,
icon=image
)
logger.info(u"PlexPy Notifier :: Growl notification sent.")
except gntp.notifier.errors.NetworkError:
logger.warning(u'Growl notification failed: network error')
logger.warn(u"PlexPy Notifier :: Growl notification failed: network error")
return
logger.info(u"Growl notifications sent.")
def updateLibrary(self):
# For uniformity reasons not removed
@ -617,13 +617,13 @@ class PROWL(object):
request_status = response.status
if request_status == 200:
logger.info(u"Prowl notifications sent.")
logger.info(u"PlexPy Notifier :: Prowl notification sent.")
return True
elif request_status == 401:
logger.info(u"Prowl auth failed: %s" % response.reason)
logger.warn(u"PlexPy Notifier :: Prowl notification failed: %s" % response.reason)
return False
else:
logger.info(u"Prowl notification failed.")
logger.warn(u"PlexPy Notifier :: Prowl notification failed.")
return False
def updateLibrary(self):
@ -698,7 +698,7 @@ class XBMC(object):
time = "3000" # in ms
for host in hosts:
logger.info('Sending notification command to XMBC @ ' + host)
logger.info(u"PlexPy Notifier :: Sending notification command to XMBC @ " + host)
try:
version = self._sendjson(host, 'Application.GetProperties', {'properties': ['version']})['version']['major']
@ -713,15 +713,17 @@ class XBMC(object):
if not request:
raise Exception
else:
logger.info(u"PlexPy Notifier :: XBMC notification sent.")
except Exception:
logger.error('Error sending notification request to XBMC')
logger.warn(u"PlexPy Notifier :: XBMC notification filed.")
def return_config_options(self):
config_option = [{'label': 'XBMC Host:Port',
'value': self.hosts,
'name': 'xbmc_host',
'description': 'Host running XBMC (e.g. http://localhost:8080). Separate multiple hosts with commas.',
'description': 'Host running XBMC (e.g. http://localhost:8080). Separate multiple hosts with commas (,).',
'input_type': 'text'
},
{'label': 'XBMC Username',
@ -763,12 +765,12 @@ class Plex(object):
base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
req.add_header("Authorization", "Basic %s" % base64string)
logger.info('Plex url: %s' % url)
# logger.info(u"PlexPy Notifier :: Plex url: %s" % url)
try:
handle = urllib2.urlopen(req)
except Exception as e:
logger.warn('Error opening Plex url: %s' % e)
logger.error(u"PlexPy Notifier :: Error opening Plex url: %s" % e)
return
response = handle.read().decode(plexpy.SYS_ENCODING)
@ -784,7 +786,7 @@ class Plex(object):
time = "3000" # in ms
for host in hosts:
logger.info('Sending notification command to Plex Media Server @ ' + host)
logger.info(u"PlexPy Notifier :: Sending notification command to Plex Media Server @ " + host)
try:
notification = header + "," + message + "," + time
notifycommand = {'command': 'ExecBuiltIn', 'parameter': 'Notification(' + notification + ')'}
@ -792,9 +794,11 @@ class Plex(object):
if not request:
raise Exception
else:
logger.info(u"PlexPy Notifier :: Plex notification sent.")
except:
logger.warn('Error sending notification request to Plex Media Server')
logger.warn(u"PlexPy Notifier :: Plex notification failed.")
def return_config_options(self):
config_option = [{'label': 'Plex Client Host:Port',
@ -855,9 +859,10 @@ class NMA(object):
response = p.push(title, event, message, priority=nma_priority, batch_mode=batch)
if not response[api][u'code'] == u'200':
logger.error(u'Could not send notification to NotifyMyAndroid')
logger.warn(u"PlexPy Notifier :: NotifyMyAndroid notification failed.")
return False
else:
logger.info(u"PlexPy Notifier :: NotifyMyAndroid notification sent.")
return True
def return_config_options(self):
@ -918,13 +923,13 @@ class PUSHBULLET(object):
# logger.debug(u"PushBullet response body: %r" % response.read())
if request_status == 200:
logger.info(u"PushBullet notifications sent.")
logger.info(u"PlexPy Notifier :: PushBullet notification sent.")
return True
elif request_status >= 400 and request_status < 500:
logger.info(u"PushBullet request failed: %s" % response.reason)
logger.warn(u"PlexPy Notifier :: PushBullet notification failed: %s" % response.reason)
return False
else:
logger.info(u"PushBullet notification failed serverside.")
logger.warn(u"PlexPy Notifier :: PushBullet notification failed.")
return False
def test(self, apikey, deviceid):
@ -952,10 +957,10 @@ class PUSHBULLET(object):
devices.update({'': ''})
return devices
elif request_status >= 400 and request_status < 500:
logger.info(u"Unable to retrieve Pushbullet devices list: %s" % response.reason)
logger.warn(u"PlexPy Notifier :: Unable to retrieve Pushbullet devices list: %s" % response.reason)
return {'': ''}
else:
logger.info(u"Unable to retrieve Pushbullet devices list.")
logger.warn(u"PlexPy Notifier :: Unable to retrieve Pushbullet devices list.")
return {'': ''}
else:
@ -1020,13 +1025,13 @@ class PUSHALOT(object):
# logger.debug(u"Pushalot response body: %r" % response.read())
if request_status == 200:
logger.info(u"Pushalot notifications sent.")
logger.info(u"PlexPy Notifier :: Pushalot notification sent.")
return True
elif request_status == 410:
logger.info(u"Pushalot auth failed: %s" % response.reason)
logger.warn(u"PlexPy Notifier :: Pushalot notification failed: %s" % response.reason)
return False
else:
logger.info(u"Pushalot notification failed.")
logger.warn(u"PlexPy Notifier :: Pushalot notification failed.")
return False
def return_config_options(self):
@ -1077,13 +1082,13 @@ class PUSHOVER(object):
# logger.debug(u"Pushover response body: %r" % response.read())
if request_status == 200:
logger.info(u"Pushover notifications sent.")
logger.info(u"PlexPy Notifier :: Pushover notification sent.")
return True
elif request_status >= 400 and request_status < 500:
logger.info(u"Pushover request failed: %s" % response.reason)
logger.warn(u"PlexPy Notifier :: Pushover notification failed: %s" % response.reason)
return False
else:
logger.info(u"Pushover notification failed.")
logger.warn(u"PlexPy Notifier :: Pushover notification failed.")
return False
def updateLibrary(self):
@ -1111,10 +1116,10 @@ class PUSHOVER(object):
sounds.update({'': ''})
return sounds
elif request_status >= 400 and request_status < 500:
logger.info(u"Unable to retrieve Pushover notification sounds list: %s" % response.reason)
logger.warn(u"PlexPy Notifier :: Unable to retrieve Pushover notification sounds list: %s" % response.reason)
return {'': ''}
else:
logger.info(u"Unable to retrieve Pushover notification sounds list.")
logger.warn(u"PlexPy Notifier :: Unable to retrieve Pushover notification sounds list.")
return {'': ''}
else:
@ -1177,12 +1182,12 @@ class TwitterNotifier(object):
oauth_consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret)
oauth_client = oauth.Client(oauth_consumer)
logger.info('Requesting temp token from Twitter')
logger.info("PlexPy Notifier :: Requesting temp token from Twitter")
resp, content = oauth_client.request(self.REQUEST_TOKEN_URL, 'GET')
if resp['status'] != '200':
logger.info('Invalid respond from Twitter requesting temp token: %s' % resp['status'])
logger.warn("PlexPy Notifier :: Invalid respond from Twitter requesting temp token: %s" % resp['status'])
else:
request_token = dict(parse_qsl(content))
@ -1201,25 +1206,25 @@ class TwitterNotifier(object):
token = oauth.Token(request_token['oauth_token'], request_token['oauth_token_secret'])
token.set_verifier(key)
logger.info('Generating and signing request for an access token using key ' + key)
# logger.debug(u"Generating and signing request for an access token using key " + key)
oauth_consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret)
# logger.debug('oauth_consumer: ' + str(oauth_consumer))
# logger.debug(u"oauth_consumer: " + str(oauth_consumer))
oauth_client = oauth.Client(oauth_consumer, token)
# logger.info('oauth_client: ' + str(oauth_client))
# logger.debug(u"oauth_client: " + str(oauth_client))
resp, content = oauth_client.request(self.ACCESS_TOKEN_URL, method='POST', body='oauth_verifier=%s' % key)
# logger.info('resp, content: ' + str(resp) + ',' + str(content))
# logger.debug(u"resp, content: " + str(resp) + ',' + str(content))
access_token = dict(parse_qsl(content))
# logger.info('access_token: ' + str(access_token))
# logger.debug(u"access_token: " + str(access_token))
# logger.info('resp[status] = ' + str(resp['status']))
# logger.debug(u"resp[status] = " + str(resp['status']))
if resp['status'] != '200':
logger.info('The request for a token with did not succeed: ' + str(resp['status']), logger.ERROR)
logger.error(u"PlexPy Notifier :: The request for a Twitter token did not succeed: " + str(resp['status']), logger.ERROR)
return False
else:
logger.info('Your Twitter Access Token key: %s' % access_token['oauth_token'])
logger.info('Access Token secret: %s' % access_token['oauth_token_secret'])
# logger.info(u"PlexPy Notifier :: Your Twitter Access Token key: %s" % access_token['oauth_token'])
# logger.info(u"PlexPy Notifier :: Access Token secret: %s" % access_token['oauth_token_secret'])
plexpy.CONFIG.TWITTER_USERNAME = access_token['oauth_token']
plexpy.CONFIG.TWITTER_PASSWORD = access_token['oauth_token_secret']
plexpy.CONFIG.write()
@ -1231,15 +1236,15 @@ class TwitterNotifier(object):
access_token_key = plexpy.CONFIG.TWITTER_USERNAME
access_token_secret = plexpy.CONFIG.TWITTER_PASSWORD
# logger.info(u"Sending tweet: " + message)
# logger.info(u"PlexPy Notifier :: Sending tweet: " + message)
api = twitter.Api(username, password, access_token_key, access_token_secret)
try:
api.PostUpdate(message)
logger.info(u"Twitter notifications sent.")
logger.info(u"PlexPy Notifier :: Twitter notification sent")
except Exception as e:
logger.info(u"Error sending Tweet: %s" % e)
logger.warn(u"PlexPy Notifier :: Twitter notification failed: %s" % e)
return False
return True
@ -1335,13 +1340,13 @@ class OSX_NOTIFY(object):
notification_center = NSUserNotificationCenter.defaultUserNotificationCenter()
notification_center.deliverNotification_(notification)
logger.info(u"OSX Notify notifications sent.")
logger.info(u"PlexPy Notifier :: OSX Notify notification sent.")
del pool
return True
except Exception as e:
logger.warn('Error sending OS X Notification: %s' % e)
logger.warn(u"PlexPy Notifier :: OSX notification failed: %s" % e)
return False
def swizzled_bundleIdentifier(self, original, swizzled):
@ -1382,26 +1387,15 @@ class BOXCAR(object):
req = urllib2.Request(self.url)
handle = urllib2.urlopen(req, data)
handle.close()
logger.info(u"Boxcar2 notifications sent.")
logger.info(u"PlexPy Notifier :: Boxcar2 notification sent.")
return True
except urllib2.URLError as e:
logger.warn('Error sending Boxcar2 Notification: %s' % e)
logger.warn(u"PlexPy Notifier :: Boxcar2 notification failed: %s" % e)
return False
def return_config_options(self):
config_option = [{'label': 'Boxcar Access Token',
'value': plexpy.CONFIG.BOXCAR_TOKEN,
'name': 'boxcar_token',
'description': 'Your Boxcar access token.',
'input_type': 'text'
},
{'label': 'Sound',
'value': self.sound,
'name': 'boxcar_sound',
'description': 'Set the notification sound. Leave blank for the default sound.',
'input_type': 'select',
'select_options': {'': '',
def get_sounds(self):
sounds = {'': '',
'beep-crisp': 'Beep (Crisp)',
'beep-soft': 'Beep (Soft)',
'bell-modern': 'Bell (Modern)',
@ -1431,6 +1425,22 @@ class BOXCAR(object):
'score': 'Score',
'success': 'Success',
'up': 'Up'}
return sounds
def return_config_options(self):
config_option = [{'label': 'Boxcar Access Token',
'value': plexpy.CONFIG.BOXCAR_TOKEN,
'name': 'boxcar_token',
'description': 'Your Boxcar access token.',
'input_type': 'text'
},
{'label': 'Sound',
'value': self.sound,
'name': 'boxcar_sound',
'description': 'Set the notification sound. Leave blank for the default sound.',
'input_type': 'select',
'select_options': self.get_sounds()
}
]
@ -1471,11 +1481,11 @@ class Email(object):
mailserver.sendmail(plexpy.CONFIG.EMAIL_FROM, recipients, message.as_string())
mailserver.quit()
logger.info(u"Email notifications sent.")
logger.info(u"PlexPy Notifier :: Email notification sent.")
return True
except Exception, e:
logger.warn('Error sending Email: %s' % e)
except Exception as e:
logger.warn(u"PlexPy Notifier :: Email notification failed: %s" % e)
return False
def return_config_options(self):
@ -1558,7 +1568,7 @@ class IFTTT(object):
data = {'value1': subject.encode("utf-8"),
'value2': message.encode("utf-8")}
# logger.debug("Ifttt SENDING: %s" % json.dumps(data))
# logger.debug(u"Ifttt SENDING: %s" % json.dumps(data))
http_handler.request("POST",
"/trigger/%s/with/key/%s" % (self.event, self.apikey),
@ -1571,13 +1581,13 @@ class IFTTT(object):
# logger.debug(u"Ifttt response body: %r" % response.read())
if request_status == 200:
logger.info(u"Ifttt notifications sent.")
logger.info(u"PlexPy Notifier :: Ifttt notification sent.")
return True
elif request_status >= 400 and request_status < 500:
logger.info(u"Ifttt request failed: %s" % response.reason)
logger.warn(u"PlexPy Notifier :: Ifttt notification failed: %s" % response.reason)
return False
else:
logger.info(u"Ifttt notification failed serverside.")
logger.warn(u"PlexPy Notifier :: Ifttt notification failed.")
return False
def test(self):
@ -1631,13 +1641,13 @@ class TELEGRAM(object):
request_status = response.status
if request_status == 200:
logger.info(u"Telegram notifications sent.")
logger.info(u"PlexPy Notifier :: Telegram notification sent.")
return True
elif request_status >= 400 and request_status < 500:
logger.info(u"Telegram request failed: %s" % response.reason)
logger.warn(u"PlexPy Notifier :: Telegram notification failed: %s" % response.reason)
return False
else:
logger.info(u"Telegram notification failed serverside.")
logger.warn(u"PlexPy Notifier :: Telegram notification failed.")
return False
def updateLibrary(self):
@ -1708,13 +1718,13 @@ class SLACK(object):
request_status = response.status
if request_status == 200:
logger.info(u"Slack notifications sent.")
logger.info(u"PlexPy Notifier :: Slack notification sent.")
return True
elif request_status >= 400 and request_status < 500:
logger.info(u"Slack request failed: %s" % response.reason)
logger.warn(u"PlexPy Notifier :: Slack notification failed: %s" % response.reason)
return False
else:
logger.info(u"Slack notification failed serverside.")
logger.warn(u"PlexPy Notifier :: Slack notification failed.")
return False
def updateLibrary(self):
@ -2034,7 +2044,7 @@ class FacebookNotifier(object):
self._post_facebook(subject + ': ' + message)
def test_notify(self):
return self._post_facebook("This is a test notification from PlexPy at " + helpers.now())
return self._post_facebook(u"PlexPy Notifiers :: This is a test notification from PlexPy at " + helpers.now())
def _get_authorization(self):
return facebook.auth_url(app_id=self.app_id,
@ -2042,7 +2052,7 @@ class FacebookNotifier(object):
perms=['user_managed_groups','publish_actions'])
def _get_credentials(self, code):
logger.info('Requesting access token from Facebook')
logger.info(u"PlexPy Notifiers :: Requesting access token from Facebook")
try:
# Request user access token
@ -2062,7 +2072,7 @@ class FacebookNotifier(object):
plexpy.CONFIG.FACEBOOK_TOKEN = access_token
plexpy.CONFIG.write()
except Exception as e:
logger.info(u"Error requesting Facebook access token: %s" % e)
logger.info(u"PlexPy Notifiers :: Error requesting Facebook access token: %s" % e)
return False
return True
@ -2076,21 +2086,23 @@ class FacebookNotifier(object):
try:
api.put_wall_post(profile_id=group_id, message=message)
logger.info(u"Facebook notifications sent.")
logger.info(u"PlexPy Notifiers :: Facebook notifications sent.")
except Exception as e:
logger.info(u"Error sending Facebook post: %s" % e)
logger.info(u"PlexPy Notifiers :: Error sending Facebook post: %s" % e)
return False
return True
else:
logger.info('Error sending Facebook post: No Facebook Group ID provided.')
logger.info(u"PlexPy Notifiers :: Error sending Facebook post: No Facebook Group ID provided.")
return False
def return_config_options(self):
config_option = [{'label': 'Instructions',
'description': '<strong>Facebook notifications are currently experimental!</strong><br><br> \
Step 1: Visit <a href="https://developers.facebook.com/apps/" target="_blank">Facebook Developers</a> to create a new app using <strong>advanced setup</strong>.<br>\
Step 2: Go to <strong>Settings > Advanced</strong> and fill in <strong>Valid OAuth redirect URIs</strong> with your PlexPy URL (i.e. http://localhost:8181).<br>\
Step 1: Visit <a href="https://developers.facebook.com/apps/" target="_blank"> \
Facebook Developers</a> to create a new app using <strong>advanced setup</strong>.<br>\
Step 2: Go to <strong>Settings > Advanced</strong> and fill in \
<strong>Valid OAuth redirect URIs</strong> with your PlexPy URL (i.e. http://localhost:8181).<br>\
Step 3: Fill in the <strong>App ID</strong> and <strong>App Secret</strong> below.<br>\
Step 4: Click the <strong>Request Authorization</strong> button below.',
'input_type': 'help'

View file

@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
from plexpy import logger, helpers, users, http_handler, database
from plexpy import logger, helpers, http_handler, database, users
import xmltodict
import json
from xml.dom import minidom
@ -26,7 +26,7 @@ import plexpy
def refresh_users():
logger.info("Requesting users list refresh...")
logger.info(u"PlexPy PlexTV :: Requesting users list refresh...")
result = PlexTV().get_full_users_list()
monitor_db = database.MonitorDatabase()
@ -55,13 +55,13 @@ def refresh_users():
monitor_db.upsert('users', new_value_dict, control_value_dict)
logger.info("Users list refreshed.")
logger.info(u"PlexPy PlexTV :: Users list refreshed.")
else:
logger.warn("Unable to refresh users list.")
logger.warn(u"PlexPy PlexTV :: Unable to refresh users list.")
def get_real_pms_url():
logger.info("Requesting URLs for server...")
logger.info(u"PlexPy PlexTV :: Requesting URLs for server...")
# Reset any current PMS_URL value
plexpy.CONFIG.__setattr__('PMS_URL', '')
@ -85,15 +85,15 @@ def get_real_pms_url():
if plexpy.CONFIG.PMS_IS_REMOTE and item['local'] == '0':
plexpy.CONFIG.__setattr__('PMS_URL', item['uri'])
plexpy.CONFIG.write()
logger.info("Server URL retrieved.")
logger.info(u"PlexPy PlexTV :: Server URL retrieved.")
if not plexpy.CONFIG.PMS_IS_REMOTE and item['local'] == '1':
plexpy.CONFIG.__setattr__('PMS_URL', item['uri'])
plexpy.CONFIG.write()
logger.info("Server URL retrieved.")
logger.info(u"PlexPy PlexTV :: Server URL retrieved.")
else:
plexpy.CONFIG.__setattr__('PMS_URL', fallback_url)
plexpy.CONFIG.write()
logger.warn("Unable to retrieve server URLs. Using user-defined value.")
logger.warn(u"PlexPy PlexTV :: Unable to retrieve server URLs. Using user-defined value.")
else:
plexpy.CONFIG.__setattr__('PMS_URL', fallback_url)
plexpy.CONFIG.write()
@ -139,12 +139,15 @@ class PlexTV(object):
plextv_response = self.get_plex_auth(output_format='xml')
if plextv_response:
try:
xml_head = plextv_response.getElementsByTagName('user')
if not xml_head:
logger.warn("Error parsing XML for Plex.tv token")
return []
if xml_head:
auth_token = xml_head[0].getAttribute('authenticationToken')
else:
logger.warn(u"PlexPy PlexTV :: Could not get Plex authentication token.")
except Exception as e:
logger.warn(u"PlexPy PlexTV :: Unable to parse XML for get_token: %s." % e)
return []
return auth_token
else:
@ -213,16 +216,16 @@ class PlexTV(object):
try:
xml_parse = minidom.parseString(own_account)
except Exception, e:
logger.warn("Error parsing XML for Plex account details: %s" % e)
except Exception as e:
logger.warn(u"PlexPy PlexTV :: Unable to parse XML for get_full_users_list own account: %s" % e)
return []
except:
logger.warn("Error parsing XML for Plex account details.")
logger.warn(u"PlexPy PlexTV :: Unable to parse XML for get_full_users_list own account.")
return []
xml_head = xml_parse.getElementsByTagName('user')
if not xml_head:
logger.warn("Error parsing XML for Plex account details.")
logger.warn(u"PlexPy PlexTV :: Unable to parse XML for get_full_users_list.")
else:
for a in xml_head:
own_details = {"user_id": helpers.get_xml_attr(a, 'id'),
@ -238,14 +241,16 @@ class PlexTV(object):
try:
xml_parse = minidom.parseString(friends_list)
except Exception, e:
logger.warn("Error parsing XML for Plex friends list: %s" % e)
except Exception as e:
logger.warn(u"PlexPy PlexTV :: Unable to parse XML for get_full_users_list friends list: %s" % e)
return []
except:
logger.warn("Error parsing XML for Plex friends list.")
logger.warn(u"PlexPy PlexTV :: Unable to parse XML for get_full_users_list friends list.")
return []
xml_head = xml_parse.getElementsByTagName('User')
if not xml_head:
logger.warn("Error parsing XML for Plex friends list.")
logger.warn(u"PlexPy PlexTV :: Unable to parse XML for get_full_users_list.")
else:
for a in xml_head:
friend = {"user_id": helpers.get_xml_attr(a, 'id'),
@ -269,17 +274,17 @@ class PlexTV(object):
try:
xml_parse = minidom.parseString(sync_list)
except Exception, e:
logger.warn("Error parsing XML for Plex sync lists: %s" % e)
except Exception as e:
logger.warn(u"PlexPy PlexTV :: Unable to parse XML for get_synced_items: %s" % e)
return []
except:
logger.warn("Error parsing XML for Plex sync lists.")
logger.warn(u"PlexPy PlexTV :: Unable to parse XML for get_synced_items.")
return []
xml_head = xml_parse.getElementsByTagName('SyncList')
if not xml_head:
logger.warn("Error parsing XML for Plex sync lists.")
logger.warn(u"PlexPy PlexTV :: Unable to parse XML for get_synced_items.")
else:
for a in xml_head:
client_id = helpers.get_xml_attr(a, 'id')
@ -287,8 +292,8 @@ class PlexTV(object):
for device in sync_device:
device_user_id = helpers.get_xml_attr(device, 'userID')
try:
device_username = user_data.get_user_details(user_id=device_user_id)['username']
device_friendly_name = user_data.get_user_details(user_id=device_user_id)['friendly_name']
device_username = user_data.get_details(user_id=device_user_id)['username']
device_friendly_name = user_data.get_details(user_id=device_user_id)['friendly_name']
except:
device_username = ''
device_friendly_name = ''
@ -375,7 +380,7 @@ class PlexTV(object):
if plexpy.CONFIG.PMS_IDENTIFIER:
server_id = plexpy.CONFIG.PMS_IDENTIFIER
else:
logger.error('PlexPy PlexTV connector :: Unable to retrieve server identity.')
logger.error(u"PlexPy PlexTV :: Unable to retrieve server identity.")
return []
plextv_resources = self.get_plextv_resources(include_https=include_https)
@ -383,17 +388,17 @@ class PlexTV(object):
try:
xml_parse = minidom.parseString(plextv_resources)
except Exception, e:
logger.warn("Error parsing XML for Plex resources: %s" % e)
except Exception as e:
logger.warn(u"PlexPy PlexTV :: Unable to parse XML for get_server_urls: %s" % e)
return []
except:
logger.warn("Error parsing XML for Plex resources.")
logger.warn(u"PlexPy PlexTV :: Unable to parse XML for get_server_urls.")
return []
try:
xml_head = xml_parse.getElementsByTagName('Device')
except:
logger.warn("Error parsing XML for Plex resources.")
except Exception as e:
logger.warn(u"PlexPy PlexTV :: Unable to parse XML for get_server_urls: %s." % e)
return []
for a in xml_head:
@ -430,49 +435,14 @@ class PlexTV(object):
return server_urls
def discover(self):
""" Query plex for all servers online. Returns the ones you own in a selectize format """
result = self.get_plextv_resources(include_https=True, output_format='raw')
servers = xmltodict.parse(result, process_namespaces=True, attr_prefix='')
clean_servers = []
try:
if servers:
# Fix if its only one "device"
if int(servers['MediaContainer']['size']) == 1:
servers['MediaContainer']['Device'] = [servers['MediaContainer']['Device']]
for server in servers['MediaContainer']['Device']:
# Only grab servers online and own
if server.get('presence', None) == '1' and server.get('owned', None) == '1' and server.get('provides', None) == 'server':
# If someone only has one connection..
if isinstance(server['Connection'], dict):
server['Connection'] = [server['Connection']]
for s in server['Connection']:
# to avoid circular ref
d = {}
d.update(s)
d.update(server)
d['label'] = d['name']
d['value'] = d['address']
del d['Connection']
clean_servers.append(d)
except Exception as e:
logger.warn('Failed to get servers from plex %s' % e)
return clean_servers
return json.dumps(clean_servers, indent=4)
def get_server_times(self):
servers = self.get_plextv_server_list(output_format='xml')
server_times = []
try:
xml_head = servers.getElementsByTagName('Server')
except:
logger.warn("Error parsing XML for Plex servers.")
except Exception as e:
logger.warn(u"PlexPy PlexTV :: Unable to parse XML for get_server_times: %s." % e)
return []
for a in xml_head:
@ -483,3 +453,41 @@ class PlexTV(object):
break
return server_times
def discover(self):
""" Query plex for all servers online. Returns the ones you own in a selectize format """
servers = self.get_plextv_resources(include_https=True, output_format='xml')
clean_servers = []
try:
xml_head = servers.getElementsByTagName('MediaContainer')
except Exception as e:
logger.warn(u"PlexPy PlexTV :: Failed to get servers from plex: %s." % e)
return []
for a in xml_head:
if a.getAttribute('size'):
if a.getAttribute('size') == '0':
return []
if a.getElementsByTagName('Device'):
devices = a.getElementsByTagName('Device')
for d in devices:
if helpers.get_xml_attr(d, 'presence') == '1' and \
helpers.get_xml_attr(d, 'owned') == '1' and \
helpers.get_xml_attr(d, 'provides') == 'server':
connections = d.getElementsByTagName('Connection')
for c in connections:
server = {'httpsRequired': helpers.get_xml_attr(d, 'httpsRequired'),
'clientIdentifier': helpers.get_xml_attr(d, 'clientIdentifier'),
'label': helpers.get_xml_attr(d, 'name'),
'ip': helpers.get_xml_attr(c, 'address'),
'port': helpers.get_xml_attr(c, 'port'),
'local': helpers.get_xml_attr(c, 'local'),
'value': helpers.get_xml_attr(c, 'address')
}
clean_servers.append(server)
return clean_servers

View file

@ -26,12 +26,12 @@ def extract_plexwatch_xml(xml=None):
try:
xml_parse = minidom.parseString(clean_xml)
except:
logger.warn("Error parsing XML for Plexwatch database.")
logger.warn(u"PlexPy Importer :: Error parsing XML for Plexwatch database.")
return None
xml_head = xml_parse.getElementsByTagName('opt')
if not xml_head:
logger.warn("Error parsing XML for Plexwatch database.")
logger.warn(u"PlexPy Importer :: Error parsing XML for Plexwatch database.")
return None
for a in xml_head:
@ -41,6 +41,7 @@ def extract_plexwatch_xml(xml=None):
grandparent_thumb = helpers.get_xml_attr(a, 'grandparentThumb')
grandparent_title = helpers.get_xml_attr(a, 'grandparentTitle')
guid = helpers.get_xml_attr(a, 'guid')
section_id = helpers.get_xml_attr(a, 'librarySectionID')
media_index = helpers.get_xml_attr(a, 'index')
originally_available_at = helpers.get_xml_attr(a, 'originallyAvailableAt')
last_viewed_at = helpers.get_xml_attr(a, 'lastViewedAt')
@ -156,6 +157,7 @@ def extract_plexwatch_xml(xml=None):
'title': title,
'tagline': tagline,
'guid': guid,
'section_id': section_id,
'media_index': media_index,
'originally_available_at': originally_available_at,
'last_viewed_at': last_viewed_at,
@ -203,23 +205,23 @@ def validate_database(database=None, table_name=None):
try:
connection = sqlite3.connect(database, timeout=20)
except sqlite3.OperationalError:
logger.error('PlexPy Importer :: Invalid database specified.')
logger.error(u"PlexPy Importer :: Invalid database specified.")
return 'Invalid database specified.'
except ValueError:
logger.error('PlexPy Importer :: Invalid database specified.')
logger.error(u"PlexPy Importer :: Invalid database specified.")
return 'Invalid database specified.'
except:
logger.error('PlexPy Importer :: Uncaught exception.')
logger.error(u"PlexPy Importer :: Uncaught exception.")
return 'Uncaught exception.'
try:
connection.execute('SELECT ratingKey from %s' % table_name)
connection.close()
except sqlite3.OperationalError:
logger.error('PlexPy Importer :: Invalid database specified.')
logger.error(u"PlexPy Importer :: Invalid database specified.")
return 'Invalid database specified.'
except:
logger.error('PlexPy Importer :: Uncaught exception.')
logger.error(u"PlexPy Importer :: Uncaught exception.")
return 'Uncaught exception.'
return 'success'
@ -230,16 +232,16 @@ def import_from_plexwatch(database=None, table_name=None, import_ignore_interval
connection = sqlite3.connect(database, timeout=20)
connection.row_factory = sqlite3.Row
except sqlite3.OperationalError:
logger.error('PlexPy Importer :: Invalid filename.')
logger.error(u"PlexPy Importer :: Invalid filename.")
return None
except ValueError:
logger.error('PlexPy Importer :: Invalid filename.')
logger.error(u"PlexPy Importer :: Invalid filename.")
return None
try:
connection.execute('SELECT ratingKey from %s' % table_name)
except sqlite3.OperationalError:
logger.error('PlexPy Importer :: Database specified does not contain the required fields.')
logger.error(u"PlexPy Importer :: Database specified does not contain the required fields.")
return None
logger.debug(u"PlexPy Importer :: PlexWatch data import in progress...")
@ -249,6 +251,8 @@ def import_from_plexwatch(database=None, table_name=None, import_ignore_interval
hours=0, minutes=0, seconds=0)
plexpy.schedule_job(activity_pinger.check_recently_added, 'Check for recently added items',
hours=0, minutes=0, seconds=0)
plexpy.schedule_job(activity_pinger.check_server_response, 'Check for server response',
hours=0, minutes=0, seconds=0)
ap = activity_processor.ActivityProcessor()
user_data = users.Users()
@ -352,8 +356,8 @@ def import_from_plexwatch(database=None, table_name=None, import_ignore_interval
'title': row['title'],
'parent_title': extracted_xml['parent_title'],
'grandparent_title': row['grandparent_title'],
'index': extracted_xml['media_index'],
'parent_index': extracted_xml['parent_media_index'],
'media_index': extracted_xml['media_index'],
'parent_media_index': extracted_xml['parent_media_index'],
'thumb': extracted_xml['thumb'],
'parent_thumb': extracted_xml['parent_thumb'],
'grandparent_thumb': extracted_xml['grandparent_thumb'],
@ -370,6 +374,7 @@ def import_from_plexwatch(database=None, table_name=None, import_ignore_interval
'rating': extracted_xml['rating'],
'duration': extracted_xml['duration'],
'guid': extracted_xml['guid'],
'section_id': extracted_xml['section_id'],
'directors': extracted_xml['directors'],
'writers': extracted_xml['writers'],
'actors': extracted_xml['actors'],
@ -409,4 +414,3 @@ def import_users():
logger.debug(u"PlexPy Importer :: Users imported.")
except:
logger.debug(u"PlexPy Importer :: Failed to import users.")

File diff suppressed because it is too large Load diff

View file

@ -21,29 +21,28 @@ class Users(object):
def __init__(self):
pass
def get_user_list(self, kwargs=None):
def get_datatables_list(self, kwargs=None):
data_tables = datatables.DataTables()
custom_where = ['users.deleted_user', 0]
columns = ['session_history.id',
'users.user_id as user_id',
'users.custom_avatar_url as user_thumb',
'(case when users.friendly_name is null then users.username else \
users.friendly_name end) as friendly_name',
'MAX(session_history.started) as last_seen',
'session_history.ip_address as ip_address',
'COUNT(session_history.id) as plays',
'session_history.platform as platform',
'session_history.player as player',
'session_history_metadata.full_title as last_watched',
columns = ['users.user_id',
'users.username',
'users.friendly_name',
'users.thumb AS user_thumb',
'users.custom_avatar_url AS custom_thumb',
'COUNT(session_history.id) AS plays',
'MAX(session_history.started) AS last_seen',
'session_history_metadata.full_title AS last_watched',
'session_history.ip_address',
'session_history.platform',
'session_history.player',
'session_history_metadata.thumb',
'session_history_metadata.parent_thumb',
'session_history_metadata.grandparent_thumb',
'session_history_metadata.media_type',
'session_history.rating_key as rating_key',
'session_history.rating_key',
'session_history_media_info.video_decision',
'users.username as user',
'users.do_notify as do_notify',
'users.keep_history as keep_history'
]
@ -62,8 +61,8 @@ class Users(object):
['session_history.id', 'session_history_metadata.id'],
['session_history.id', 'session_history_media_info.id']],
kwargs=kwargs)
except:
logger.warn("Unable to execute database query.")
except Exception as e:
logger.warn(u"PlexPy Users :: Unable to execute database query for get_list: %s." % e)
return {'recordsFiltered': 0,
'recordsTotal': 0,
'draw': 0,
@ -74,38 +73,44 @@ class Users(object):
rows = []
for item in users:
if item["media_type"] == 'episode' and item["parent_thumb"]:
thumb = item["parent_thumb"]
elif item["media_type"] == 'episode':
thumb = item["grandparent_thumb"]
if item['friendly_name']:
friendly_name = item['friendly_name']
else:
thumb = item["thumb"]
friendly_name = item['username']
if not item['user_thumb'] or item['user_thumb'] == '':
user_thumb = common.DEFAULT_USER_THUMB
if item['media_type'] == 'episode' and item['parent_thumb']:
thumb = item['parent_thumb']
elif item['media_type'] == 'episode':
thumb = item['grandparent_thumb']
else:
thumb = item['thumb']
if item['custom_thumb'] and item['custom_thumb'] != item['user_thumb']:
user_thumb = item['custom_thumb']
elif item['user_thumb']:
user_thumb = item['user_thumb']
else:
user_thumb = common.DEFAULT_USER_THUMB
# Rename Mystery platform names
platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])
platform = common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform'])
row = {"id": item['id'],
"plays": item['plays'],
"last_seen": item['last_seen'],
"friendly_name": item['friendly_name'],
"ip_address": item['ip_address'],
"platform": platform,
"player": item["player"],
"last_watched": item['last_watched'],
"thumb": thumb,
"media_type": item['media_type'],
"rating_key": item['rating_key'],
"video_decision": item['video_decision'],
"user_thumb": user_thumb,
"user": item["user"],
"user_id": item['user_id'],
"do_notify": helpers.checked(item['do_notify']),
"keep_history": helpers.checked(item['keep_history'])
row = {'user_id': item['user_id'],
'username': item['username'],
'friendly_name': friendly_name,
'user_thumb': user_thumb,
'plays': item['plays'],
'last_seen': item['last_seen'],
'last_watched': item['last_watched'],
'ip_address': item['ip_address'],
'platform': platform,
'player': item['player'],
'thumb': thumb,
'media_type': item['media_type'],
'rating_key': item['rating_key'],
'video_decision': item['video_decision'],
'do_notify': helpers.checked(item['do_notify']),
'keep_history': helpers.checked(item['keep_history'])
}
rows.append(row)
@ -118,35 +123,34 @@ class Users(object):
return dict
def get_user_unique_ips(self, kwargs=None, custom_where=None):
def get_datatables_unique_ips(self, user_id=None, kwargs=None):
data_tables = datatables.DataTables()
# Change custom_where column name due to ambiguous column name after JOIN
custom_where[0][0] = 'custom_user_id' if custom_where[0][0] == 'user_id' else custom_where[0][0]
custom_where = ['users.user_id', user_id]
columns = ['session_history.id',
'session_history.started as last_seen',
'session_history.ip_address as ip_address',
'COUNT(session_history.id) as play_count',
'session_history.platform as platform',
'session_history.player as player',
'session_history_metadata.full_title as last_watched',
'session_history.started AS last_seen',
'session_history.ip_address',
'COUNT(session_history.id) AS play_count',
'session_history.platform',
'session_history.player',
'session_history_metadata.full_title AS last_watched',
'session_history_metadata.thumb',
'session_history_metadata.parent_thumb',
'session_history_metadata.grandparent_thumb',
'session_history_metadata.media_type',
'session_history.rating_key as rating_key',
'session_history.rating_key',
'session_history_media_info.video_decision',
'session_history.user as user',
'session_history.user',
'session_history.user_id as custom_user_id',
'(case when users.friendly_name is null then users.username else \
users.friendly_name end) as friendly_name'
'(CASE WHEN users.friendly_name IS NULL THEN users.username ELSE \
users.friendly_name END) AS friendly_name'
]
try:
query = data_tables.ssp_query(table_name='session_history',
columns=columns,
custom_where=custom_where,
custom_where=[custom_where],
group_by=['ip_address'],
join_types=['JOIN',
'JOIN',
@ -158,8 +162,8 @@ class Users(object):
['session_history.id', 'session_history_metadata.id'],
['session_history.id', 'session_history_media_info.id']],
kwargs=kwargs)
except:
logger.warn("Unable to execute database query.")
except Exception as e:
logger.warn(u"PlexPy Users :: Unable to execute database query for get_unique_ips: %s." % e)
return {'recordsFiltered': 0,
'recordsTotal': 0,
'draw': 0,
@ -180,18 +184,18 @@ class Users(object):
# Rename Mystery platform names
platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])
row = {"id": item['id'],
"last_seen": item['last_seen'],
"ip_address": item['ip_address'],
"play_count": item['play_count'],
"platform": platform,
"player": item['player'],
"last_watched": item['last_watched'],
"thumb": thumb,
"media_type": item['media_type'],
"rating_key": item['rating_key'],
"video_decision": item['video_decision'],
"friendly_name": item['friendly_name']
row = {'id': item['id'],
'last_seen': item['last_seen'],
'ip_address': item['ip_address'],
'play_count': item['play_count'],
'platform': platform,
'player': item['player'],
'last_watched': item['last_watched'],
'thumb': thumb,
'media_type': item['media_type'],
'rating_key': item['rating_key'],
'video_decision': item['video_decision'],
'friendly_name': item['friendly_name']
}
rows.append(row)
@ -204,264 +208,147 @@ class Users(object):
return dict
# TODO: The getter and setter for this needs to become a config getter/setter for more than just friendlyname
def set_user_friendly_name(self, user=None, user_id=None, friendly_name=None, do_notify=0, keep_history=1):
if user_id:
if friendly_name.strip() == '':
friendly_name = None
def set_config(self, user_id=None, friendly_name='', custom_thumb='', do_notify=1, keep_history=1):
if str(user_id).isdigit():
monitor_db = database.MonitorDatabase()
control_value_dict = {"user_id": user_id}
new_value_dict = {"friendly_name": friendly_name,
"do_notify": do_notify,
"keep_history": keep_history}
key_dict = {'user_id': user_id}
value_dict = {'friendly_name': friendly_name,
'custom_avatar_url': custom_thumb,
'do_notify': do_notify,
'keep_history': keep_history}
try:
monitor_db.upsert('users', new_value_dict, control_value_dict)
except Exception, e:
logger.debug(u"Uncaught exception %s" % e)
if user:
if friendly_name.strip() == '':
friendly_name = None
monitor_db.upsert('users', value_dict, key_dict)
except Exception as e:
logger.warn(u"PlexPy Users :: Unable to execute database query for set_config: %s." % e)
monitor_db = database.MonitorDatabase()
control_value_dict = {"username": user}
new_value_dict = {"friendly_name": friendly_name,
"do_notify": do_notify,
"keep_history": keep_history}
try:
monitor_db.upsert('users', new_value_dict, control_value_dict)
except Exception, e:
logger.debug(u"Uncaught exception %s" % e)
def set_user_profile_url(self, user=None, user_id=None, profile_url=None):
if user_id:
if profile_url.strip() == '':
profile_url = None
monitor_db = database.MonitorDatabase()
control_value_dict = {"user_id": user_id}
new_value_dict = {"custom_avatar_url": profile_url}
try:
monitor_db.upsert('users', new_value_dict, control_value_dict)
except Exception, e:
logger.debug(u"Uncaught exception %s" % e)
if user:
if profile_url.strip() == '':
profile_url = None
monitor_db = database.MonitorDatabase()
control_value_dict = {"username": user}
new_value_dict = {"custom_avatar_url": profile_url}
try:
monitor_db.upsert('users', new_value_dict, control_value_dict)
except Exception, e:
logger.debug(u"Uncaught exception %s" % e)
def get_user_friendly_name(self, user=None, user_id=None):
if user_id:
monitor_db = database.MonitorDatabase()
query = 'select username, ' \
'(CASE WHEN friendly_name IS NULL THEN username ELSE friendly_name END) as friendly_name,' \
'do_notify, keep_history, custom_avatar_url as thumb ' \
'FROM users WHERE user_id = ?'
result = monitor_db.select(query, args=[user_id])
if result:
user_detail = {'user_id': user_id,
'user': result[0]['username'],
'friendly_name': result[0]['friendly_name'],
'thumb': result[0]['thumb'],
'do_notify': helpers.checked(result[0]['do_notify']),
'keep_history': helpers.checked(result[0]['keep_history'])
}
return user_detail
else:
user_detail = {'user_id': user_id,
'user': '',
'friendly_name': '',
'do_notify': '',
'thumb': '',
'keep_history': ''}
return user_detail
elif user:
monitor_db = database.MonitorDatabase()
query = 'select user_id, ' \
'(CASE WHEN friendly_name IS NULL THEN username ELSE friendly_name END) as friendly_name,' \
'do_notify, keep_history, custom_avatar_url as thumb ' \
'FROM users WHERE username = ?'
result = monitor_db.select(query, args=[user])
if result:
user_detail = {'user_id': result[0]['user_id'],
'user': user,
'friendly_name': result[0]['friendly_name'],
'thumb': result[0]['thumb'],
'do_notify': helpers.checked(result[0]['do_notify']),
'keep_history': helpers.checked(result[0]['keep_history'])}
return user_detail
else:
user_detail = {'user_id': None,
'user': user,
'friendly_name': '',
'do_notify': '',
'thumb': '',
'keep_history': ''}
return user_detail
return None
def get_user_id(self, user=None):
if user:
try:
monitor_db = database.MonitorDatabase()
query = 'select user_id FROM users WHERE username = ?'
result = monitor_db.select_single(query, args=[user])
if result:
return result['user_id']
else:
return None
except:
return None
return None
def get_user_details(self, user=None, user_id=None):
def get_details(self, user_id=None, user=None):
from plexpy import plextv
monitor_db = database.MonitorDatabase()
if user:
query = 'SELECT user_id, username, friendly_name, email, ' \
'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \
try:
if str(user_id).isdigit():
query = 'SELECT user_id, username, friendly_name, thumb AS user_thumb, custom_avatar_url AS custom_thumb, ' \
'email, is_home_user, is_allow_sync, is_restricted, do_notify, keep_history ' \
'FROM users ' \
'WHERE username = ? ' \
'UNION ALL ' \
'SELECT null, user, null, null, null, null, null, null, null ' \
'FROM session_history ' \
'WHERE user = ? ' \
'GROUP BY user ' \
'LIMIT 1'
result = monitor_db.select(query, args=[user, user])
elif user_id:
query = 'SELECT user_id, username, friendly_name, email, ' \
'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \
'WHERE user_id = ? '
result = monitor_db.select(query, args=[user_id])
elif user:
query = 'SELECT user_id, username, friendly_name, thumb AS user_thumb, custom_avatar_url AS custom_thumb, ' \
'email, is_home_user, is_allow_sync, is_restricted, do_notify, keep_history ' \
'FROM users ' \
'WHERE user_id = ? ' \
'UNION ALL ' \
'SELECT user_id, user, null, null, null, null, null, null, null ' \
'FROM session_history ' \
'WHERE user_id = ? ' \
'GROUP BY user ' \
'LIMIT 1'
result = monitor_db.select(query, args=[user_id, user_id])
'WHERE username = ? '
result = monitor_db.select(query, args=[user])
else:
result = None
result = []
except Exception as e:
logger.warn(u"PlexPy Users :: Unable to execute database query for get_details: %s." % e)
result = []
if result:
user_details = {}
for item in result:
if not item['friendly_name']:
friendly_name = item['username']
else:
if item['friendly_name']:
friendly_name = item['friendly_name']
if not item['thumb'] or item['thumb'] == '':
user_thumb = common.DEFAULT_USER_THUMB
else:
user_thumb = item['thumb']
friendly_name = item['username']
user_details = {"user_id": item['user_id'],
"username": item['username'],
"friendly_name": friendly_name,
"email": item['email'],
"thumb": user_thumb,
"is_home_user": item['is_home_user'],
"is_allow_sync": item['is_allow_sync'],
"is_restricted": item['is_restricted'],
"do_notify": item['do_notify']
if item['custom_thumb'] and item['custom_thumb'] != item['user_thumb']:
user_thumb = item['custom_thumb']
elif item['user_thumb']:
user_thumb = item['user_thumb']
else:
user_thumb = common.DEFAULT_USER_THUMB
user_details = {'user_id': item['user_id'],
'username': item['username'],
'friendly_name': friendly_name,
'user_thumb': user_thumb,
'email': item['email'],
'is_home_user': item['is_home_user'],
'is_allow_sync': item['is_allow_sync'],
'is_restricted': item['is_restricted'],
'do_notify': item['do_notify'],
'keep_history': item['keep_history']
}
return user_details
else:
logger.warn(u"PlexPy :: Unable to retrieve user from local database. Requesting user list refresh.")
logger.warn(u"PlexPy Users :: Unable to retrieve user from local database. Requesting user list refresh.")
# Let's first refresh the user list to make sure the user isn't newly added and not in the db yet
if user:
try:
if str(user_id).isdigit():
# Refresh users
plextv.refresh_users()
query = 'SELECT user_id, username, friendly_name, email, ' \
'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \
query = 'SELECT user_id, username, friendly_name, thumb AS user_thumb, custom_avatar_url AS custom_thumb, ' \
'email, is_home_user, is_allow_sync, is_restricted, do_notify, keep_history ' \
'FROM users ' \
'WHERE username = ? ' \
'UNION ALL ' \
'SELECT null, user, null, null, null, null, null, null, null ' \
'FROM session_history ' \
'WHERE user = ? ' \
'GROUP BY user ' \
'LIMIT 1'
result = monitor_db.select(query, args=[user, user])
elif user_id:
# Refresh users
plextv.refresh_users()
query = 'SELECT user_id, username, friendly_name, email, ' \
'custom_avatar_url as thumb, is_home_user, is_allow_sync, is_restricted, do_notify ' \
'WHERE user_id = ? '
result = monitor_db.select(query, args=[user_id])
elif user:
query = 'SELECT user_id, username, friendly_name, thumb AS user_thumb, custom_avatar_url AS custom_thumb, ' \
'email, is_home_user, is_allow_sync, is_restricted, do_notify, keep_history ' \
'FROM users ' \
'WHERE user_id = ? ' \
'UNION ALL ' \
'SELECT user_id, user, null, null, null, null, null, null, null ' \
'FROM session_history ' \
'WHERE user_id = ? ' \
'GROUP BY user ' \
'LIMIT 1'
result = monitor_db.select(query, args=[user_id, user_id])
'WHERE username = ? '
result = monitor_db.select(query, args=[user])
else:
result = None
result = []
except Exception as e:
logger.warn(u"PlexPy Users :: Unable to execute database query for get_details: %s." % e)
result = []
if result:
user_details = {}
for item in result:
if not item['friendly_name']:
friendly_name = item['username']
else:
if item['friendly_name']:
friendly_name = item['friendly_name']
if not item['thumb'] or item['thumb'] == '':
user_thumb = common.DEFAULT_USER_THUMB
else:
user_thumb = item['thumb']
friendly_name = item['username']
user_details = {"user_id": item['user_id'],
"username": item['username'],
"friendly_name": friendly_name,
"email": item['email'],
"thumb": user_thumb,
"is_home_user": item['is_home_user'],
"is_allow_sync": item['is_allow_sync'],
"is_restricted": item['is_restricted'],
"do_notify": item['do_notify']
if item['custom_thumb'] and item['custom_thumb'] != item['user_thumb']:
user_thumb = item['custom_thumb']
elif item['user_thumb']:
user_thumb = item['user_thumb']
else:
user_thumb = common.DEFAULT_USER_THUMB
user_details = {'user_id': item['user_id'],
'username': item['username'],
'friendly_name': friendly_name,
'user_thumb': user_thumb,
'email': item['email'],
'is_home_user': item['is_home_user'],
'is_allow_sync': item['is_allow_sync'],
'is_restricted': item['is_restricted'],
'do_notify': item['do_notify'],
'keep_history': item['keep_history']
}
return user_details
else:
# If there is no user data we must return something
# Use "Local" user to retain compatibility with PlexWatch database value
return {"user_id": None,
"username": 'Local',
"friendly_name": 'Local',
"email": '',
"thumb": '',
"is_home_user": 0,
"is_allow_sync": 0,
"is_restricted": 0,
"do_notify": 0
return {'user_id': None,
'username': 'Local',
'friendly_name': 'Local',
'user_thumb': common.DEFAULT_USER_THUMB,
'email': '',
'is_home_user': 0,
'is_allow_sync': 0,
'is_restricted': 0,
'do_notify': 0,
'keep_history': 0
}
def get_user_watch_time_stats(self, user=None, user_id=None):
def get_watch_time_stats(self, user_id=None):
monitor_db = database.MonitorDatabase()
time_queries = [1, 7, 30, 0]
user_watch_time_stats = []
for days in time_queries:
try:
if days > 0:
if user_id:
if str(user_id).isdigit():
query = 'SELECT (SUM(stopped - started) - ' \
' SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \
'COUNT(id) AS total_plays ' \
@ -469,21 +356,21 @@ class Users(object):
'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
'AND user_id = ?' % days
result = monitor_db.select(query, args=[user_id])
elif user:
query = 'SELECT (SUM(stopped - started) - ' \
'SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \
'COUNT(id) AS total_plays ' \
'FROM session_history ' \
'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
'AND user = ?' % days
result = monitor_db.select(query, args=[user])
else:
result = []
else:
if str(user_id).isdigit():
query = 'SELECT (SUM(stopped - started) - ' \
' SUM(CASE WHEN paused_counter is null THEN 0 ELSE paused_counter END)) as total_time, ' \
'COUNT(id) AS total_plays ' \
'FROM session_history ' \
'WHERE user = ?'
result = monitor_db.select(query, args=[user])
'WHERE user_id = ?'
result = monitor_db.select(query, args=[user_id])
else:
result = []
except Exception as e:
logger.warn(u"PlexPy Users :: Unable to execute database query for get_watch_time_stats: %s." % e)
result = []
for item in result:
if item['total_time']:
@ -502,14 +389,14 @@ class Users(object):
return user_watch_time_stats
def get_user_player_stats(self, user=None, user_id=None):
def get_player_stats(self, user_id=None):
monitor_db = database.MonitorDatabase()
player_stats = []
result_id = 0
try:
if user_id:
if str(user_id).isdigit():
query = 'SELECT player, COUNT(player) as player_count, platform ' \
'FROM session_history ' \
'WHERE user_id = ? ' \
@ -517,15 +404,10 @@ class Users(object):
'ORDER BY player_count DESC'
result = monitor_db.select(query, args=[user_id])
else:
query = 'SELECT player, COUNT(player) as player_count, platform ' \
'FROM session_history ' \
'WHERE user = ? ' \
'GROUP BY player ' \
'ORDER BY player_count DESC'
result = monitor_db.select(query, args=[user])
except:
logger.warn("Unable to execute database query.")
return None
result = []
except Exception as e:
logger.warn(u"PlexPy Users :: Unable to execute database query for get_player_stats: %s." % e)
result = []
for item in result:
# Rename Mystery platform names
@ -540,3 +422,140 @@ class Users(object):
result_id += 1
return player_stats
def get_recently_watched(self, user_id=None, limit='10'):
monitor_db = database.MonitorDatabase()
recently_watched = []
if not limit.isdigit():
limit = '10'
try:
if str(user_id).isdigit():
query = 'SELECT session_history.id, session_history.media_type, session_history.rating_key, session_history.parent_rating_key, ' \
'title, parent_title, grandparent_title, thumb, parent_thumb, grandparent_thumb, media_index, parent_media_index, ' \
'year, started, user ' \
'FROM session_history_metadata ' \
'JOIN session_history ON session_history_metadata.id = session_history.id ' \
'WHERE user_id = ? ' \
'GROUP BY (CASE WHEN session_history.media_type = "track" THEN session_history.parent_rating_key ' \
' ELSE session_history.rating_key END) ' \
'ORDER BY started DESC LIMIT ?'
result = monitor_db.select(query, args=[user_id, limit])
else:
result = []
except Exception as e:
logger.warn(u"PlexPy Users :: Unable to execute database query for get_recently_watched: %s." % e)
result = []
for row in result:
if row['media_type'] == 'episode' and row['parent_thumb']:
thumb = row['parent_thumb']
elif row['media_type'] == 'episode':
thumb = row['grandparent_thumb']
else:
thumb = row['thumb']
recent_output = {'row_id': row['id'],
'media_type': row['media_type'],
'rating_key': row['rating_key'],
'title': row['title'],
'parent_title': row['parent_title'],
'grandparent_title': row['grandparent_title'],
'thumb': thumb,
'media_index': row['media_index'],
'parent_media_index': row['parent_media_index'],
'year': row['year'],
'time': row['started'],
'user': row['user']
}
recently_watched.append(recent_output)
return recently_watched
def delete_all_history(self, user_id=None):
monitor_db = database.MonitorDatabase()
try:
if str(user_id).isdigit():
logger.info(u"PlexPy DataFactory :: Deleting all history for user id %s from database." % user_id)
session_history_media_info_del = \
monitor_db.action('DELETE FROM '
'session_history_media_info '
'WHERE session_history_media_info.id IN (SELECT session_history_media_info.id '
'FROM session_history_media_info '
'JOIN session_history ON session_history_media_info.id = session_history.id '
'WHERE session_history.user_id = ?)', [user_id])
session_history_metadata_del = \
monitor_db.action('DELETE FROM '
'session_history_metadata '
'WHERE session_history_metadata.id IN (SELECT session_history_metadata.id '
'FROM session_history_metadata '
'JOIN session_history ON session_history_metadata.id = session_history.id '
'WHERE session_history.user_id = ?)', [user_id])
session_history_del = \
monitor_db.action('DELETE FROM '
'session_history '
'WHERE session_history.user_id = ?', [user_id])
return 'Deleted all items for user_id %s.' % user_id
else:
return 'Unable to delete items. Input user_id not valid.'
except Exception as e:
logger.warn(u"PlexPy Users :: Unable to execute database query for delete_all_history: %s." % e)
def delete(self, user_id=None):
monitor_db = database.MonitorDatabase()
try:
if str(user_id).isdigit():
self.delete_all_history(user_id)
logger.info(u"PlexPy DataFactory :: Deleting user with id %s from database." % user_id)
monitor_db.action('UPDATE users SET deleted_user = 1 WHERE user_id = ?', [user_id])
monitor_db.action('UPDATE users SET keep_history = 0 WHERE user_id = ?', [user_id])
monitor_db.action('UPDATE users SET do_notify = 0 WHERE user_id = ?', [user_id])
return 'Deleted user with id %s.' % user_id
else:
return 'Unable to delete user, user_id not valid.'
except Exception as e:
logger.warn(u"PlexPy Users :: Unable to execute database query for delete: %s." % e)
def undelete(self, user_id=None, username=None):
monitor_db = database.MonitorDatabase()
try:
if user_id and str(user_id).isdigit():
logger.info(u"PlexPy DataFactory :: Re-adding user with id %s to database." % user_id)
monitor_db.action('UPDATE users SET deleted_user = 0 WHERE user_id = ?', [user_id])
monitor_db.action('UPDATE users SET keep_history = 1 WHERE user_id = ?', [user_id])
monitor_db.action('UPDATE users SET do_notify = 1 WHERE user_id = ?', [user_id])
return 'Re-added user with id %s.' % user_id
elif username:
logger.info(u"PlexPy DataFactory :: Re-adding user with username %s to database." % username)
monitor_db.action('UPDATE users SET deleted_user = 0 WHERE username = ?', [username])
monitor_db.action('UPDATE users SET keep_history = 1 WHERE username = ?', [username])
monitor_db.action('UPDATE users SET do_notify = 1 WHERE username = ?', [username])
return 'Re-added user with username %s.' % username
else:
return 'Unable to re-add user, user_id or username not valid.'
except Exception as e:
logger.warn(u"PlexPy Users :: Unable to execute database query for undelete: %s." % e)
# Keep method for PlexWatch import
def get_user_id(self, user=None):
if user:
try:
monitor_db = database.MonitorDatabase()
query = 'SELECT user_id FROM users WHERE username = ?'
result = monitor_db.select_single(query, args=[user])
if result:
return result['user_id']
else:
return None
except:
return None
return None

File diff suppressed because it is too large Load diff