Massive code cleanup

* Finish up library pages (toggles and notifications)
* Update user pages to match library pages
* Fix no current activity bif thumbnail at the start of a stream
* Improved logging throughout PlexPy
This commit is contained in:
Jonathan Wong 2016-01-05 20:46:27 -08:00
parent 5fedac691d
commit 636f898da8
31 changed files with 2873 additions and 2715 deletions

View file

@ -174,42 +174,42 @@ from plexpy import version
</div>
</form>
</li>
% if title=="Home":
% if title == "Home":
<li class="active"><a href="home"><i class="fa fa-lg fa-home"></i></a></li>
% else:
<li><a href="home"><i class="fa fa-lg fa-home"></i></a></li>
% endif
% if title=="Libraries" or title=="Library":
% 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":
% if title == "Users" or title == "User":
<li class="active"><a href="users">Users</a></li>
% else:
<li><a href="users">Users</a></li>
% endif
% if title=="History":
% if title == "History":
<li class="active"><a href="history">History</a></li>
% else:
<li><a href="history">History</a></li>
% endif
% if title=="Graphs":
% if title == "Graphs":
<li class="active"><a href="graphs">Graphs</a></li>
% else:
<li><a href="graphs">Graphs</a></li>
% endif
% if title=="Synced Items":
% if title == "Synced Items":
<li class="active"><a href="sync">Synced Items</a></li>
% else:
<li><a href="sync">Synced Items</a></li>
% endif
% if title=="Log":
% if title == "Log":
<li class="active"><a href="logs">Logs</a></li>
% else:
<li><a href="logs">Logs</a></li>
% endif
% if title=="Settings":
% if title == "Settings":
<li class="active"><a href="settings">Settings</a></li>
% else:
<li><a href="settings">Settings</a></li>

View file

@ -2463,6 +2463,7 @@ a .home-platforms-instance-list-oval:hover,
}
#users-to-delete > li,
#users-to-purge > li,
#libraries-to-delete > li,
#libraries-to-purge > li {
color: #e9a049;
}

View file

@ -72,9 +72,9 @@ DOCUMENTATION :: END
<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>

View file

@ -14,6 +14,8 @@ 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.
@ -40,11 +42,7 @@ DOCUMENTATION :: END
<label for="profile_url">Library Picture URL</label>
<div class="row">
<div class="col-md-8">
% if data['custom_thumb']:
<input type="text" class="form-control" id="custom_thumb_url" name="custom_thumb_url" value="${data['custom_thumb']}">
% else:
<input type="text" class="form-control" id="custom_thumb_url" name="custom_thumb_url" value="${data['library_thumb']}">
% endif
</div>
</div>
<p class="help-block">Change the library's picture in PlexPy. To reset to default, leave this field empty and save.</p>
@ -61,6 +59,12 @@ DOCUMENTATION :: END
</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>
@ -96,14 +100,18 @@ DOCUMENTATION :: END
</div>
</div>
<script>
// Set new friendly name
$("#save_library").click(function () {
// 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;
}
@ -112,9 +120,10 @@ DOCUMENTATION :: END
url: 'edit_library',
data: {
section_id: '${data["section_id"]}',
custom_thumb: custom_thumb,
do_notify: do_notify,
keep_history: keep_history,
custom_thumb: custom_thumb
do_notify_created: do_notify_created,
keep_history: keep_history
},
cache: false,
async: true,
@ -167,5 +176,4 @@ DOCUMENTATION :: END
});
});
</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},
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);
}
});
% 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
$.ajax({
url: 'edit_user',
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) {
location.reload();
}
});
});
$("#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

@ -11,15 +11,15 @@ data :: Usable parameters (if not applicable for media type, blank value will be
== Global keys ==
rating_key Returns the unique identifier for the media item.
media_type Returns the type of media. Either 'movie', 'show', 'season', 'episode', 'artist', 'album', or 'track'.
media_type Returns the type of media. Either 'movie', 'show', 'season', 'episode', 'artist', 'album', or 'track'.
art Returns the location of the item's artwork
title Returns the name of the movie, show, episode, artist, album, or track.
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.
@ -54,29 +54,29 @@ DOCUMENTATION :: END
<div class="summary-navbar-list">
<ul class="list-unstyled breadcrumb">
% if data['media_type'] == 'movie':
<li><a href="library?section_id=${data['library_id']}">${data['library_title']}</a></li>
<li><a href="library?section_id=${data['library_id']}">${data['library_name']}</a></li>
<li class="active">${data['title']}</li>
% elif data['media_type'] == 'show':
<li><a href="library?section_id=${data['library_id']}">${data['library_title']}</a></li>
<li><a href="library?section_id=${data['library_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="library?section_id=${data['library_id']}">${data['library_title']}</a></li>
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['library_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="library?section_id=${data['library_id']}">${data['library_title']}</a></li>
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['library_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="library?section_id=${data['library_id']}">${data['library_title']}</a></li>
<li><a href="library?section_id=${data['library_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="library?section_id=${data['library_id']}">${data['library_title']}</a></li>
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['library_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="library?section_id=${data['library_id']}">${data['library_title']}</a></li>
<li class="hidden-xs hidden-sm"><a href="library?section_id=${data['library_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>

View file

@ -1,3 +1,4 @@
var libraries_to_delete = [];
var libraries_to_purge = [];
libraries_list_table_options = {
@ -23,9 +24,12 @@ libraries_list_table_options = {
"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="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",
@ -37,10 +41,10 @@ libraries_list_table_options = {
"data": "library_thumb",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData === '') {
$(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>');
$(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>');
} else {
if (rowData['custom_thumb']) {
$(td).html('<a href="library?section_id=' + rowData['section_id'] + '"><div class="libraries-poster-face" style="background-image: url(' + rowData['custom_thumb'] + ');"></div></a>');
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>');
}
@ -56,7 +60,9 @@ libraries_list_table_options = {
"data": "section_name",
"createdCell": function (td, cellData, rowData, row, col) {
if (cellData !== '') {
$(td).html('<div data-id="' + rowData['section_id'] + '"><a href="library?section_id=' + rowData['section_id'] + '">' + cellData + '</a></div>');
$(td).html('<div data-id="' + rowData['section_id'] + '">' +
'<a href="library?section_id=' + rowData['section_id'] + '">' + cellData + '</a>' +
'</div>');
} else {
$(td).html(cellData);
}
@ -198,8 +204,11 @@ libraries_list_table_options = {
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[data-id="' + rowData['section_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
$(row).find('button.purge-library[data-id="' + rowData['section_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
}
}
}
@ -210,10 +219,14 @@ $('#libraries_list_table').on('change', 'td.edit-control > .edit-library-toggles
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;
}
@ -228,6 +241,7 @@ $('#libraries_list_table').on('change', 'td.edit-control > .edit-library-toggles
data: {
section_id: rowData['section_id'],
do_notify: do_notify,
do_notify_created: do_notify_created,
keep_history: keep_history,
custom_thumb: custom_thumb
},
@ -240,17 +254,44 @@ $('#libraries_list_table').on('change', 'td.edit-control > .edit-library-toggles
});
});
$('#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

@ -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,
@ -55,13 +55,10 @@ users_list_table_options = {
"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>');
} 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>');
}
$(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(cellData);
}
@ -206,8 +203,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

@ -43,15 +43,16 @@
<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>
<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-purge">Confirm</button>
<button type="button" class="btn btn-danger btn-ok" data-dismiss="modal" id="confirm-delete">Confirm</button>
</div>
</div>
</div>
@ -83,7 +84,8 @@
$('#row-edit-mode').on('click', function () {
$('#row-edit-mode-alert').fadeIn(200);
$('#libraries_to_purge').html('');
$('#libraries-to-delete').html('');
$('#libraries-to-purge').html('');
if ($(this).hasClass('active')) {
if (libraries_to_purge.length > 0) {
@ -91,6 +93,17 @@
$(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++) {
@ -99,7 +112,19 @@
}
$('#confirm-modal').modal();
$('#confirm-modal').one('click', '#confirm-purge', function () {
$('#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',
@ -122,6 +147,7 @@
});
} else {
libraries_to_delete = [];
libraries_to_purge = [];
$('.edit-control').each(function () {
$(this).find('button.btn-danger').toggleClass('btn-warning').toggleClass('btn-danger');
@ -137,13 +163,13 @@
cache: false,
async: true,
success: function (data) {
showMsg('<i class="fa fa-refresh"></i>&nbspLibraries list refresh started...', false, true, 2000, false)
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)
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)
showMsg('<i class="fa fa-exclamation-circle"></i>&nbspUnable to refresh libraries list.', false, true, 2000, true);
}
});
});

View file

@ -13,6 +13,8 @@ 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.
@ -31,8 +33,8 @@ DOCUMENTATION :: END
<link rel="stylesheet" href="interfaces/default/css/plexpy-dataTables.css">
</%def>
% if data != None:
<%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>
@ -50,12 +52,11 @@ DOCUMENTATION :: END
<div class="col-md-12">
<div class="table-card-back">
<div class="user-info-wrapper">
% if data['custom_thumb']:
<div class="library-info-poster-face" id="user-gravatar" style="background-image: url(${data['custom_thumb']});">
% 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" id="user-gravatar" style="background-image: url(pms_image_proxy?img=${data['library_thumb']}&width=80&height=80&fallback=poster);">
<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>
<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">
@ -201,6 +202,27 @@ DOCUMENTATION :: END
</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()">
@ -208,12 +230,13 @@ DOCUMENTATION :: END
<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>
$(document).ready(function () {
% if data['section_id']:
% if str(data['section_id']).isdigit():
var section_id = ${data['section_id']};
% else:
var section_id = null;
@ -348,18 +371,7 @@ DOCUMENTATION :: END
recentlyWatched();
});
});
</script>
<script>
$('div.art-face').animate({ opacity: 0.2 }, { duration: 1000 });
</script>
</%def>
% else:
<div class="clear"></div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span10 offset1">
<h3>Error retrieving library information. Please see the logs for more details.</h3>
</div>
</div>
</div>
% endif
% endif
</%def>

View file

@ -18,7 +18,7 @@ total_plays Returns the play count for the user.
DOCUMENTATION :: END
</%doc>
% if data != None:
% if data:
% for a in data:
<ul class="list-unstyled">
<div class="user-player-instance">
@ -38,6 +38,6 @@ DOCUMENTATION :: END
</ul>
% endfor
% 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

@ -800,7 +800,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>
@ -1167,6 +1167,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>

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,223 +35,261 @@ 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="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-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>
</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="ip-tab-btn" href="#userAddresses" data-toggle="tab">IP Addresses</a></li>
<li><a id="history-tab-btn" href="#userHistory" data-toggle="tab">History</a></li>
<li><a id="sync-tab-btn" href="#userSyncItems" data-toggle="tab">Synced Items</a></li>
</ul>
<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>
</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">
<div class="row">
<div class="summary-content-wrapper">
<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="user-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-television"></i> Player Stats</span>
</div>
</div>
<div class="table-card-back">
<div id="user-player-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="user-recently-watched">
<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="userAddresses">
<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-map-marker"></i> IP Addresses for <strong>
<div class="user-info-wrapper">
<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>
</strong></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">
<li class="active"><a href="#profile" data-toggle="tab">Profile</a></li>
<li><a id="ip-tab-btn" href="#userAddresses" data-toggle="tab">IP Addresses</a></li>
<li><a id="history-tab-btn" href="#userHistory" data-toggle="tab">History</a></li>
<li><a id="sync-tab-btn" href="#userSyncItems" data-toggle="tab">Synced Items</a></li>
</ul>
</div>
</div>
</div>
<div class="table-card-back">
<table id="user_ip_table" class="display" width="100%">
<thead>
<tr>
<th align="left">Last Seen</th>
<th align="left">IP Address</th>
<th align="left">Last Platform</th>
<th align="left">Last Player</th>
<th align="left">Last Watched</th>
<th align="left">Play Count</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane" id="userHistory">
<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['friendly_name']}</span>
</strong></span>
<div id="edit-user-modal" class="modal fade" tabindex="-1" role="dialog"
aria-labelledby="edit-user-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="user-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="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 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-television"></i> Player Stats</span>
</div>
</div>
<div class="table-card-back">
<div id="user-player-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="user-recently-watched">
<div class='muted'><i class="fa fa-refresh fa-spin"></i> Loading data...</div>
<br>
</div>
</div>
</div>
</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="userSyncItems">
<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-cloud-download"></i> Synced Items for <strong>
<span class="set-username">${data['friendly_name']}</span>
</strong></span>
</div>
<div class="colvis-button-bar hidden-xs" id="button-bar-sync">
<div class="tab-pane" id="userAddresses">
<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-map-marker"></i> IP Addresses for <strong>
<span class="set-username">${data['friendly_name']}</span>
</strong>
</span>
</div>
</div>
<div class="table-card-back">
<table id="user_ip_table" class="display" width="100%">
<thead>
<tr>
<th align="left">Last Seen</th>
<th align="left">IP Address</th>
<th align="left">Last Platform</th>
<th align="left">Last Player</th>
<th align="left">Last Watched</th>
<th align="left">Play Count</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="table-card-back">
<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>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="tab-pane" id="userHistory">
<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['friendly_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="userSyncItems">
<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-cloud-download"></i> Synced Items for <strong>
<span class="set-username">${data['friendly_name']}</span>
</strong>
</span>
</div>
<div class="colvis-button-bar hidden-xs" id="button-bar-sync">
</div>
</div>
<div class="table-card-back">
<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>
</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 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>
<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,10 +305,10 @@ from plexpy import helpers
<script>
$(document).ready(function () {
% if data['user_id']:
var user_id = ${data['user_id']};
% if str(data['user_id']).isdigit():
var user_id = ${data['user_id']};
% else:
var user_id = null;
var user_id = null;
% endif
var username = '${data['username'].replace("'", "\\'")}';
@ -303,7 +344,6 @@ from plexpy import helpers
return {
'json_data': JSON.stringify( d ),
'user_id': user_id,
'user': username,
'media_type': media_type
};
}
@ -313,7 +353,7 @@ from plexpy import helpers
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);
$('#history_table_filter').prepend('<div class="btn-group" data-toggle="buttons" id="media_type-selection" style="padding-right: 15px;"> \
@ -348,27 +388,25 @@ 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
'user_id': user_id
};
}
}
user_ip_table = $('#user_ip_table').DataTable(user_ip_table_options);
clearSearchButton('user_ip_table', user_ip_table);
});
$( "#sync-tab-btn" ).one( "click", function() {
// Build user sync table
sync_table_options.ajax = {
"url": "get_sync",
"data": function(d) {
d.user_id = user_id;
d.user = username;
url: 'get_sync',
data: function(d) {
d.user_id = user_id;
}
}
sync_table = $('#sync_table').DataTable(sync_table_options);
@ -376,7 +414,7 @@ from plexpy import helpers
var colvis_sync = new $.fn.dataTable.ColVis( sync_table, { buttonText: '<i class="fa fa-columns"></i> Select columns', buttonClass: 'btn btn-dark' } );
$( colvis_sync.button() ).appendTo('#button-bar-sync');
clearSearchButton('sync_table', sync_table);
});
@ -385,7 +423,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) {
@ -441,37 +479,25 @@ from plexpy import helpers
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);
}
});
}
recentlyWatched();
$(window).resize(function() {
recentlyWatched();
});
});
</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
% 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,6 @@ DOCUMENTATION :: END
</script>
% endfor
% 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

@ -28,7 +28,7 @@ year Returns the movie release year.
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:
@ -84,6 +84,6 @@ DOCUMENTATION :: END
</ul>
</div>
% 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

@ -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

@ -177,13 +177,13 @@
cache: false,
async: true,
success: function(data) {
showMsg('<i class="fa fa-check"></i>&nbspUsers 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)
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 users list.',false,true,2000,true)
showMsg('<i class="fa fa-exclamation-circle"></i>&nbspUnable to refresh users list.', false, true, 2000, true);
}
});
});

View file

@ -377,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, library_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, '
@ -441,7 +441,8 @@ def dbcheck():
'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, keep_history INTEGER DEFAULT 1)'
'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
@ -591,6 +592,15 @@ def dbcheck():
'ALTER TABLE sessions ADD COLUMN last_paused INTEGER'
)
# Upgrade sessions table from earlier versions
try:
c_db.execute('SELECT library_id from sessions')
except sqlite3.OperationalError:
logger.debug(u"Altering database. Updating database table sessions.")
c_db.execute(
'ALTER TABLE sessions ADD COLUMN library_id INTEGER'
)
# Upgrade session_history table from earlier versions
try:
c_db.execute('SELECT reference_id from session_history')

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):
@ -182,7 +173,7 @@ def check_active_sessions(ws_request=False):
if int_ping_count == 3:
# Fire off notifications
threading.Thread(target=notification_handler.notify_timeline,
kwargs=dict(notify_action='intdown')).start()
kwargs=dict(notify_action='intdown')).start()
def check_recently_added():
@ -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['library_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['library_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'],
'library_id': session['library_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['library_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...")
@ -331,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

@ -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,
@ -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,
@ -664,10 +666,10 @@ class DataFactory(object):
try:
query = 'SELECT section_id, section_name, section_type, thumb, count, parent_count, child_count ' \
'FROM library_sections ' \
'WHERE section_id = %s' % id
'WHERE section_id = %s ' % id
result = monitor_db.select(query)
except:
logger.warn("Unable to execute database query for get_library_stats.")
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:
@ -725,86 +727,6 @@ class DataFactory(object):
return stream_output
def get_recently_watched(self, user=None, user_id=None, library_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])
elif library_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 library_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=[library_id, 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,
'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_metadata_details(self, rating_key):
monitor_db = database.MonitorDatabase()
@ -866,89 +788,52 @@ class DataFactory(object):
'directors': directors,
'genres': genres,
'actors': actors,
'library_title': item['section_name'],
'library_name': item['section_name'],
'library_id': item['library_id']
}
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 = ''
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
def delete_all_user_history(self, user_id=None):
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()
@ -1037,8 +922,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, ' \
@ -1081,6 +966,22 @@ class DataFactory(object):
return key_list
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()
@ -1134,8 +1035,6 @@ class DataFactory(object):
return 'Updated metadata in database.'
else:
return 'Unable to update metadata in database. No changes were made.'
# for debugging
#return mapping
def update_metadata_details(self, old_rating_key='', new_rating_key='', metadata=None):
@ -1176,31 +1075,6 @@ class DataFactory(object):
monitor_db.action(query=query, args=args)
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 ' \
'JOIN session_history_metadata ON session_history_metadata.id = session_history.id ' \
'%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
def update_library_ids(self):
from plexpy import pmsconnect
@ -1210,8 +1084,8 @@ class DataFactory(object):
try:
query = 'SELECT id, rating_key FROM session_history_metadata WHERE library_id IS NULL'
result = monitor_db.select(query=query)
except:
logger.warn("Unable to execute database query for update_library_id.")
except Exception as e:
logger.warn(u"PlexPy DataFactory :: Unable to execute database query for update_library_id: %s." % e)
return None
for item in result:
@ -1230,60 +1104,4 @@ class DataFactory(object):
else:
continue
return True
def get_library_sections(self):
monitor_db = database.MonitorDatabase()
try:
query = 'SELECT section_id, section_name FROM library_sections'
result = monitor_db.select(query=query)
except:
logger.warn("Unable to execute database query for get_library_sections.")
return None
libraries = []
for item in result:
library = {'section_id': item['section_id'],
'section_name': item['section_name']
}
libraries.append(library)
return libraries
def update_library_sections(self):
from plexpy import pmsconnect
pms_connect = pmsconnect.PmsConnect()
library_sections = pms_connect.get_server_children()
if library_sections:
if library_sections['libraries_count'] != '0':
monitor_db = database.MonitorDatabase()
for section in library_sections['libraries_list']:
section_keys = {'section_id': section['key']}
section_values = {'section_id': section['key'],
'section_name': section['title'],
'section_type': section['type']}
monitor_db.upsert('library_sections', key_dict=section_keys, value_dict=section_values)
return True
def get_session_ip(self, session_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
ip_address = 'N/A'
for item in result:
ip_address = item['ip_address']
return ip_address
return True

View file

@ -14,6 +14,7 @@
# along with PlexPy. If not, see <http://www.gnu.org/licenses/>.
from plexpy import logger, datatables, common, database, helpers
import plexpy
class Libraries(object):
@ -21,22 +22,23 @@ class Libraries(object):
def __init__(self):
pass
def get_library_list(self, kwargs=None):
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 as count',
'library_sections.count',
'library_sections.parent_count',
'library_sections.child_count',
'library_sections.thumb AS library_thumb',
'(CASE WHEN library_sections.custom_thumb_url == library_sections.thumb \
THEN NULL ELSE custom_thumb_url END) AS custom_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',
'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',
@ -44,12 +46,13 @@ class Libraries(object):
'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=[custom_where],
group_by=['library_sections.section_id'],
join_types=['LEFT OUTER JOIN',
'LEFT OUTER JOIN',
@ -61,8 +64,8 @@ class Libraries(object):
['session_history_metadata.id', 'session_history.id'],
['session_history_metadata.id', 'session_history_media_info.id']],
kwargs=kwargs)
except:
logger.warn("Unable to execute database query for get_library_list.")
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,
@ -80,23 +83,30 @@ class Libraries(object):
else:
thumb = item['thumb']
row = {'plays': item['plays'],
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'],
'section_id': item['section_id'],
'section_name': item['section_name'],
'section_type': item['section_type'].capitalize(),
'count': item['count'],
'parent_count': item['parent_count'],
'library_thumb': item['library_thumb'],
'custom_thumb': item['custom_thumb'],
'library_art': item['art'],
'child_count': item['child_count'],
'do_notify': helpers.checked(item['do_notify']),
'do_notify_created': helpers.checked(item['do_notify_created']),
'keep_history': helpers.checked(item['keep_history'])
}
@ -110,123 +120,154 @@ class Libraries(object):
return dict
def set_library_config(self, section_id=None, do_notify=1, keep_history=1, custom_thumb=''):
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 = {'do_notify': do_notify,
'keep_history': keep_history,
'custom_thumb_url': custom_thumb}
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("Unable to execute database query for set_user_friendly_name.")
logger.warn(u"PlexPy Libraries :: Unable to execute database query for set_config: %s." % e)
def get_library_details(self, section_id=None):
def get_details(self, section_id=None):
from plexpy import pmsconnect
monitor_db = database.MonitorDatabase()
if section_id:
query = 'SELECT section_id, section_name, section_type, count, parent_count, child_count, ' \
'thumb AS library_thumb, (CASE WHEN library_sections.custom_thumb_url == library_sections.thumb ' \
' THEN NULL ELSE custom_thumb_url END) AS custom_thumb, art, do_notify, keep_history ' \
'FROM library_sections ' \
'WHERE section_id = ? '
result = monitor_db.select(query, args=[section_id])
else:
result = None
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': item['library_thumb'],
'custom_thumb': item['custom_thumb'],
'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 :: Unable to retrieve library from local database. Requesting library list refresh.")
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
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, (CASE WHEN library_sections.custom_thumb_url == library_sections.thumb ' \
' THEN NULL ELSE custom_thumb_url END) AS custom_thumb, art, do_notify, keep_history ' \
'FROM library_sections ' \
'WHERE section_id = ? '
result = monitor_db.select(query, args=[section_id])
else:
result = None
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': item['library_thumb'],
'custom_thumb': item['custom_thumb'],
'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 user_details
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': '',
'section_name': 'Local',
'section_type': '',
'library_thumb': '',
'custom_thumb': '',
'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_library_watch_time_stats(self, library_id=None):
def get_watch_time_stats(self, library_id=None):
monitor_db = database.MonitorDatabase()
time_queries = [1, 7, 30, 0]
library_watch_time_stats = []
for days in time_queries:
if days > 0:
if library_id:
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 library_id = ?' % days
result = monitor_db.select(query, args=[library_id])
else:
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 library_id = ?'
result = monitor_db.select(query, args=[library_id])
try:
if days > 0:
if str(library_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 library_id = ?' % days
result = monitor_db.select(query, args=[library_id])
else:
result = []
else:
if str(library_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 library_id = ?'
result = monitor_db.select(query, args=[library_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']:
@ -245,13 +286,13 @@ class Libraries(object):
return library_watch_time_stats
def get_library_user_stats(self, library_id=None):
def get_user_stats(self, library_id=None):
monitor_db = database.MonitorDatabase()
user_stats = []
try:
if library_id:
if str(library_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 ' \
@ -261,9 +302,11 @@ class Libraries(object):
'GROUP BY user ' \
'ORDER BY user_count DESC'
result = monitor_db.select(query, args=[library_id])
except:
logger.warn("Unable to execute database query for get_library_user_stats.")
return None
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'],
@ -275,30 +318,151 @@ class Libraries(object):
return user_stats
def delete_all_library_history(self, section_id=None):
def get_recently_watched(self, library_id=None, limit='10'):
monitor_db = database.MonitorDatabase()
recently_watched = []
if not limit.isdigit():
limit = '10'
try:
if str(library_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 library_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=[library_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'],
'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()
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.library_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.library_id = ?)', [section_id])
session_history_metadata_del = \
monitor_db.action('DELETE FROM '
'session_history_metadata '
'WHERE session_history_metadata.library_id = ?', [section_id])
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
return 'Deleted all items for library_id %s.' % section_id
else:
return 'Unable to delete items. Input library_id not valid.'
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.library_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.library_id = ?)', [section_id])
session_history_metadata_del = \
monitor_db.action('DELETE FROM '
'session_history_metadata '
'WHERE session_history_metadata.library_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)

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['library_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,14 +617,14 @@ class PROWL(object):
request_status = response.status
if request_status == 200:
logger.info(u"Prowl notifications sent.")
return True
logger.info(u"PlexPy Notifier :: Prowl notification sent.")
return True
elif request_status == 401:
logger.info(u"Prowl auth failed: %s" % response.reason)
return False
logger.warn(u"PlexPy Notifier :: Prowl notification failed: %s" % response.reason)
return False
else:
logger.info(u"Prowl notification failed.")
return False
logger.warn(u"PlexPy Notifier :: Prowl notification failed.")
return False
def updateLibrary(self):
# For uniformity reasons not removed
@ -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,14 +923,14 @@ class PUSHBULLET(object):
# logger.debug(u"PushBullet response body: %r" % response.read())
if request_status == 200:
logger.info(u"PushBullet notifications sent.")
return True
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)
return False
logger.warn(u"PlexPy Notifier :: PushBullet notification failed: %s" % response.reason)
return False
else:
logger.info(u"PushBullet notification failed serverside.")
return False
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,14 +1025,14 @@ class PUSHALOT(object):
# logger.debug(u"Pushalot response body: %r" % response.read())
if request_status == 200:
logger.info(u"Pushalot notifications sent.")
return True
logger.info(u"PlexPy Notifier :: Pushalot notification sent.")
return True
elif request_status == 410:
logger.info(u"Pushalot auth failed: %s" % response.reason)
return False
logger.warn(u"PlexPy Notifier :: Pushalot notification failed: %s" % response.reason)
return False
else:
logger.info(u"Pushalot notification failed.")
return False
logger.warn(u"PlexPy Notifier :: Pushalot notification failed.")
return False
def return_config_options(self):
config_option = [{'label': 'Pushalot API Key',
@ -1077,14 +1082,14 @@ class PUSHOVER(object):
# logger.debug(u"Pushover response body: %r" % response.read())
if request_status == 200:
logger.info(u"Pushover notifications sent.")
return True
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)
return False
logger.warn(u"PlexPy Notifier :: Pushover notification failed: %s" % response.reason)
return False
else:
logger.info(u"Pushover notification failed.")
return False
logger.warn(u"PlexPy Notifier :: Pushover notification failed.")
return False
def updateLibrary(self):
# For uniformity reasons not removed
@ -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,13 +1387,47 @@ 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 get_sounds(self):
sounds = {'': '',
'beep-crisp': 'Beep (Crisp)',
'beep-soft': 'Beep (Soft)',
'bell-modern': 'Bell (Modern)',
'bell-one-tone': 'Bell (One Tone)',
'bell-simple': 'Bell (Simple)',
'bell-triple': 'Bell (Triple)',
'bird-1': 'Bird (1)',
'bird-2': 'Bird (2)',
'boing': 'Boing',
'cash': 'Cash',
'clanging': 'Clanging',
'detonator-charge': 'Detonator Charge',
'digital-alarm': 'Digital Alarm',
'done': 'Done',
'echo': 'Echo',
'flourish': 'Flourish',
'harp': 'Harp',
'light': 'Light',
'magic-chime':'Magic Chime',
'magic-coin': 'Magic Coin',
'no-sound': 'No Sound',
'notifier-1': 'Notifier (1)',
'notifier-2': 'Notifier (2)',
'notifier-3': 'Notifier (3)',
'orchestral-long': 'Orchestral (Long)',
'orchestral-short': 'Orchestral (Short)',
'score': 'Score',
'success': 'Success',
'up': 'Up'}
return sounds
def return_config_options(self):
config_option = [{'label': 'Boxcar Access Token',
'value': plexpy.CONFIG.BOXCAR_TOKEN,
@ -1401,36 +1440,7 @@ class BOXCAR(object):
'name': 'boxcar_sound',
'description': 'Set the notification sound. Leave blank for the default sound.',
'input_type': 'select',
'select_options': {'': '',
'beep-crisp': 'Beep (Crisp)',
'beep-soft': 'Beep (Soft)',
'bell-modern': 'Bell (Modern)',
'bell-one-tone': 'Bell (One Tone)',
'bell-simple': 'Bell (Simple)',
'bell-triple': 'Bell (Triple)',
'bird-1': 'Bird (1)',
'bird-2': 'Bird (2)',
'boing': 'Boing',
'cash': 'Cash',
'clanging': 'Clanging',
'detonator-charge': 'Detonator Charge',
'digital-alarm': 'Digital Alarm',
'done': 'Done',
'echo': 'Echo',
'flourish': 'Flourish',
'harp': 'Harp',
'light': 'Light',
'magic-chime': 'Magic Chime',
'magic-coin': 'Magic Coin',
'no-sound': 'No Sound',
'notifier-1': 'Notifier (1)',
'notifier-2': 'Notifier (2)',
'notifier-3': 'Notifier (3)',
'orchestral-long': 'Orchestral (Long)',
'orchestral-short': 'Orchestral (Short)',
'score': 'Score',
'success': 'Success',
'up': 'Up'}
'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,14 +1581,14 @@ class IFTTT(object):
# logger.debug(u"Ifttt response body: %r" % response.read())
if request_status == 200:
logger.info(u"Ifttt notifications sent.")
return True
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)
return False
logger.warn(u"PlexPy Notifier :: Ifttt notification failed: %s" % response.reason)
return False
else:
logger.info(u"Ifttt notification failed serverside.")
return False
logger.warn(u"PlexPy Notifier :: Ifttt notification failed.")
return False
def test(self):
return self.notify('PlexPy', 'Test Message')
@ -1631,14 +1641,14 @@ class TELEGRAM(object):
request_status = response.status
if request_status == 200:
logger.info(u"Telegram notifications sent.")
return True
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)
return False
logger.warn(u"PlexPy Notifier :: Telegram notification failed: %s" % response.reason)
return False
else:
logger.info(u"Telegram notification failed serverside.")
return False
logger.warn(u"PlexPy Notifier :: Telegram notification failed.")
return False
def updateLibrary(self):
# For uniformity reasons not removed
@ -1708,14 +1718,14 @@ class SLACK(object):
request_status = response.status
if request_status == 200:
logger.info(u"Slack notifications sent.")
return True
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)
return False
logger.warn(u"PlexPy Notifier :: Slack notification failed: %s" % response.reason)
return False
else:
logger.info(u"Slack notification failed serverside.")
return False
logger.warn(u"PlexPy Notifier :: Slack notification failed.")
return False
def updateLibrary(self):
#For uniformity reasons not removed

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()
@ -213,7 +213,7 @@ class PlexTV(object):
try:
xml_parse = minidom.parseString(own_account)
except Exception, e:
except Exception as e:
logger.warn("Error parsing XML for Plex account details: %s" % e)
return []
except:
@ -238,7 +238,7 @@ class PlexTV(object):
try:
xml_parse = minidom.parseString(friends_list)
except Exception, e:
except Exception as e:
logger.warn("Error parsing XML for Plex friends list: %s" % e)
except:
logger.warn("Error parsing XML for Plex friends list.")
@ -269,7 +269,7 @@ class PlexTV(object):
try:
xml_parse = minidom.parseString(sync_list)
except Exception, e:
except Exception as e:
logger.warn("Error parsing XML for Plex sync lists: %s" % e)
return []
except:
@ -383,7 +383,7 @@ class PlexTV(object):
try:
xml_parse = minidom.parseString(plextv_resources)
except Exception, e:
except Exception as e:
logger.warn("Error parsing XML for Plex resources: %s" % e)
return []
except:
@ -430,6 +430,25 @@ class PlexTV(object):
return server_urls
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.")
return []
for a in xml_head:
if helpers.get_xml_attr(a, 'machineIdentifier') == plexpy.CONFIG.PMS_IDENTIFIER:
server_times.append({"created_at": helpers.get_xml_attr(a, 'createdAt'),
"updated_at": helpers.get_xml_attr(a, 'updatedAt')
})
break
return server_times
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')
@ -463,23 +482,4 @@ class PlexTV(object):
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.")
return []
for a in xml_head:
if helpers.get_xml_attr(a, 'machineIdentifier') == plexpy.CONFIG.PMS_IDENTIFIER:
server_times.append({"created_at": helpers.get_xml_attr(a, 'createdAt'),
"updated_at": helpers.get_xml_attr(a, 'updatedAt')
})
break
return server_times
return json.dumps(clean_servers, indent=4)

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:
@ -205,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'
@ -232,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...")

File diff suppressed because it is too large Load diff

View file

@ -21,28 +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 = ['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'
]
@ -61,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,
@ -80,28 +80,30 @@ class Users(object):
else:
thumb = item['thumb']
if not item['user_thumb'] or item['user_thumb'] == '':
user_thumb = common.DEFAULT_USER_THUMB
else:
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'])
row = {'plays': item['plays'],
'last_seen': item['last_seen'],
row = {'user_id': item['user_id'],
'username': item['username'],
'friendly_name': item['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'],
'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'])
}
@ -116,35 +118,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',
@ -156,8 +157,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,
@ -178,18 +179,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)
@ -202,286 +203,169 @@ 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 ' \
'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 ' \
'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])
else:
result = None
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 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 username = ? '
result = monitor_db.select(query, args=[user])
else:
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:
# 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 ' \
'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 ' \
'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])
else:
result = None
try:
if str(user_id).isdigit():
# Refresh users
plextv.refresh_users()
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 = ? '
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 username = ? '
result = monitor_db.select(query, args=[user])
else:
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:
if days > 0:
if user_id:
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_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:
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])
try:
if days > 0:
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 datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \
'AND user_id = ?' % days
result = monitor_db.select(query, args=[user_id])
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_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']:
@ -500,14 +384,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 = ? ' \
@ -515,15 +399,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
@ -538,3 +417,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'],
'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