diff --git a/data/interfaces/default/js/tables/user_ips.js b/data/interfaces/default/js/tables/user_ips.js
index e73c7635..084b0378 100644
--- a/data/interfaces/default/js/tables/user_ips.js
+++ b/data/interfaces/default/js/tables/user_ips.js
@@ -167,7 +167,7 @@ $('.user_ip_table').on('click', 'td.modal-control', function () {
function showStreamDetails() {
$.ajax({
url: 'get_stream_data',
- data: { row_id: rowData['id'], user: rowData['friendly_name'] },
+ data: { row_id: rowData['history_row_id'], user: rowData['friendly_name'] },
cache: false,
async: true,
complete: function (xhr, status) {
diff --git a/data/interfaces/default/js/tables/users.js b/data/interfaces/default/js/tables/users.js
index 58918d46..15c64fbc 100644
--- a/data/interfaces/default/js/tables/users.js
+++ b/data/interfaces/default/js/tables/users.js
@@ -44,8 +44,8 @@ users_list_table_options = {
"data": null,
"createdCell": function (td, cellData, rowData, row, col) {
$(td).html('
' +
+ $(td).html('
');
@@ -256,10 +256,10 @@ users_list_table_options = {
},
"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');
+ $(row).find('button.delete-user[data-id="' + rowData['row_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
}
if ($.inArray(rowData['user_id'], users_to_purge) !== -1) {
- $(row).find('button.purge-user[data-id="' + rowData['user_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
+ $(row).find('button.purge-user[data-id="' + rowData['row_id'] + '"]').toggleClass('btn-warning').toggleClass('btn-danger');
}
}
}
@@ -270,7 +270,7 @@ $('#users_list_table').on('click', 'td.modal-control', function () {
var rowData = row.data();
$.get('get_stream_data', {
- row_id: rowData['id'],
+ row_id: rowData['history_row_id'],
user: rowData['friendly_name']
}).then(function (jqXHR) {
$("#info-modal").html(jqXHR);
@@ -328,11 +328,11 @@ $('#users_list_table').on('click', 'td.edit-control > .edit-user-toggles > butto
var row = users_list_table.row(tr);
var rowData = row.data();
- var index_delete = $.inArray(rowData['user_id'], users_to_delete);
- var index_purge = $.inArray(rowData['user_id'], users_to_purge);
+ var index_delete = $.inArray(rowData['row_id'], users_to_delete);
+ var index_purge = $.inArray(rowData['row_id'], users_to_purge);
if (index_delete === -1) {
- users_to_delete.push(rowData['user_id']);
+ users_to_delete.push(rowData['row_id']);
if (index_purge === -1) {
tr.find('button.purge-user').click();
}
@@ -351,11 +351,11 @@ $('#users_list_table').on('click', 'td.edit-control > .edit-user-toggles > butto
var row = users_list_table.row(tr);
var rowData = row.data();
- var index_delete = $.inArray(rowData['user_id'], users_to_delete);
- var index_purge = $.inArray(rowData['user_id'], users_to_purge);
+ var index_delete = $.inArray(rowData['row_id'], users_to_delete);
+ var index_purge = $.inArray(rowData['row_id'], users_to_purge);
if (index_purge === -1) {
- users_to_purge.push(rowData['user_id']);
+ users_to_purge.push(rowData['row_id']);
} else {
users_to_purge.splice(index_purge, 1);
if (index_delete != -1) {
diff --git a/data/interfaces/default/users.html b/data/interfaces/default/users.html
index 79f264e3..f3595148 100644
--- a/data/interfaces/default/users.html
+++ b/data/interfaces/default/users.html
@@ -119,14 +119,14 @@
});
if (users_to_delete.length > 0) {
- $('#users-to-delete').prepend('
Are you REALLY sure you want to delete and purge all history for the following users:
')
+ $('#users-to-delete').prepend('
Are you REALLY sure you want to delete and purge all history for the following users:
');
for (var i = 0; i < users_to_delete.length; i++) {
$('#users-to-delete').append('
' + $('div[data-id=' + users_to_delete[i] + '] > input').val() + '');
}
}
if (users_to_purge.length > 0) {
- $('#users-to-purge').prepend('
Are you REALLY sure you want to purge all history for the following users:
')
+ $('#users-to-purge').prepend('
Are you REALLY sure you want to purge all history for the following users:
');
for (var i = 0; i < users_to_purge.length; i++) {
$('#users-to-purge').append('
' + $('div[data-id=' + users_to_purge[i] + '] > input').val() + '');
}
@@ -134,31 +134,27 @@
$('#confirm-modal-delete').modal();
$('#confirm-modal-delete').one('click', '#confirm-delete', function () {
- users_to_delete.forEach(function(row, idx) {
- $.ajax({
- url: 'delete_user',
- type: 'POST',
- data: { user_id: row },
- cache: false,
- async: true,
- success: function (data) {
- var msg = "User deleted";
- showMsg(msg, false, true, 2000);
- }
- });
+ $.ajax({
+ url: 'delete_user',
+ type: 'POST',
+ data: { row_ids: users_to_delete.join(',') },
+ cache: false,
+ async: true,
+ success: function (data) {
+ var msg = "User deleted";
+ showMsg(msg, false, true, 2000);
+ }
});
- users_to_purge.forEach(function(row, idx) {
- $.ajax({
- url: 'delete_all_user_history',
- type: 'POST',
- data: { user_id: row },
- cache: false,
- async: true,
- success: function (data) {
- var msg = "User history purged";
- showMsg(msg, false, true, 2000);
- }
- });
+ $.ajax({
+ url: 'delete_all_user_history',
+ type: 'POST',
+ data: { row_ids: users_to_purge.join(',') },
+ cache: false,
+ async: true,
+ success: function (data) {
+ var msg = "User history purged";
+ showMsg(msg, false, true, 2000);
+ }
});
users_list_table.draw();
});
@@ -192,7 +188,7 @@
complete: function (xhr, status) {
var result = $.parseJSON(xhr.responseText);
var msg = result.message;
- if (result.result == 'success') {
+ if (result.result === 'success') {
showMsg('
' + msg, false, true, 2000, false);
users_list_table.draw();
} else {
diff --git a/plexpy/database.py b/plexpy/database.py
index 203142eb..b55deaab 100644
--- a/plexpy/database.py
+++ b/plexpy/database.py
@@ -58,14 +58,6 @@ def delete_recently_added():
return clear_table('recently_added')
-def delete_session_history_rows(row_ids=None):
- if row_ids:
- for table in ('session_history', 'session_history_media_info', 'session_history_metadata'):
- delete_rows_from_table(table=table, row_ids=row_ids)
- return True
- return False
-
-
def delete_rows_from_table(table, row_ids):
if row_ids and isinstance(row_ids, basestring):
row_ids = map(helpers.cast_to_int, row_ids.split(','))
@@ -76,6 +68,26 @@ def delete_rows_from_table(table, row_ids):
monitor_db.action(query, row_ids)
+def delete_session_history_rows(row_ids=None):
+ if row_ids:
+ for table in ('session_history', 'session_history_media_info', 'session_history_metadata'):
+ delete_rows_from_table(table=table, row_ids=row_ids)
+ return True
+ return False
+
+
+def delete_user_history(user_id=None):
+ if str(user_id).isdigit():
+ monitor_db = MonitorDatabase()
+
+ # Get all history associated with the user_id
+ result = monitor_db.select('SELECT id FROM session_history WHERE user_id = ?', [user_id])
+ row_ids = [row['id'] for row in result]
+
+ logger.info(u"Tautulli Database :: Deleting all history for user id %s from database." % user_id)
+ return delete_session_history_rows(row_ids=row_ids)
+
+
def db_filename(filename=FILENAME):
""" Returns the filepath to the db """
diff --git a/plexpy/users.py b/plexpy/users.py
index ddac44fa..93efc810 100644
--- a/plexpy/users.py
+++ b/plexpy/users.py
@@ -99,7 +99,8 @@ class Users(object):
group_by = 'session_history.reference_id' if grouping else 'session_history.id'
- columns = ['users.user_id',
+ columns = ['users.id AS row_id',
+ 'users.user_id',
'(CASE WHEN users.friendly_name IS NULL OR TRIM(users.friendly_name) = "" \
THEN users.username ELSE users.friendly_name END) AS friendly_name',
'users.thumb AS user_thumb',
@@ -109,7 +110,7 @@ class Users(object):
ELSE 0 END) - SUM(CASE WHEN session_history.paused_counter IS NULL THEN 0 ELSE \
session_history.paused_counter END) AS duration',
'MAX(session_history.started) AS last_seen',
- 'MAX(session_history.id) AS id',
+ 'MAX(session_history.id) AS history_row_id',
'session_history_metadata.full_title AS last_played',
'session_history.ip_address',
'session_history.platform',
@@ -128,10 +129,10 @@ class Users(object):
'session_history_metadata.originally_available_at',
'session_history_metadata.guid',
'session_history_media_info.transcode_decision',
- 'users.do_notify as do_notify',
- 'users.keep_history as keep_history',
- 'users.allow_guest as allow_guest',
- 'users.is_active as is_active'
+ 'users.do_notify AS do_notify',
+ 'users.keep_history AS keep_history',
+ 'users.allow_guest AS allow_guest',
+ 'users.is_active AS is_active'
]
try:
query = data_tables.ssp_query(table_name='users',
@@ -173,14 +174,15 @@ class Users(object):
# Rename Mystery platform names
platform = common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform'])
- row = {'user_id': item['user_id'],
+ row = {'row_id': item['row_id'],
+ 'user_id': item['user_id'],
'friendly_name': item['friendly_name'],
'user_thumb': user_thumb,
'plays': item['plays'],
'duration': item['duration'],
'last_seen': item['last_seen'],
'last_played': item['last_played'],
- 'id': item['id'],
+ 'history_row_id': item['history_row_id'],
'ip_address': item['ip_address'],
'platform': platform,
'player': item['player'],
@@ -225,7 +227,7 @@ class Users(object):
custom_where = ['users.user_id', user_id]
- columns = ['session_history.id',
+ columns = ['session_history.id AS history_row_id',
'MAX(session_history.started) AS last_seen',
'session_history.ip_address',
'COUNT(session_history.id) AS play_count',
@@ -285,7 +287,7 @@ class Users(object):
# Rename Mystery platform names
platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])
- row = {'id': item['id'],
+ row = {'history_row_id': item['history_row_id'],
'last_seen': item['last_seen'],
'ip_address': item['ip_address'],
'play_count': item['play_count'],
@@ -334,7 +336,8 @@ class Users(object):
logger.warn(u"Tautulli Users :: Unable to execute database query for set_config: %s." % e)
def get_details(self, user_id=None, user=None, email=None):
- default_return = {'user_id': 0,
+ default_return = {'row_id': 0,
+ 'user_id': 0,
'username': 'Local',
'friendly_name': 'Local',
'user_thumb': common.DEFAULT_USER_THUMB,
@@ -359,7 +362,8 @@ class Users(object):
try:
if str(user_id).isdigit():
- query = 'SELECT user_id, username, friendly_name, thumb AS user_thumb, custom_avatar_url AS custom_thumb, ' \
+ query = 'SELECT id AS row_id, user_id, username, friendly_name, ' \
+ 'thumb AS user_thumb, custom_avatar_url AS custom_thumb, ' \
'email, is_active, is_admin, is_home_user, is_allow_sync, is_restricted, ' \
'do_notify, keep_history, deleted_user, ' \
'allow_guest, shared_libraries ' \
@@ -367,7 +371,8 @@ class Users(object):
'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, ' \
+ query = 'SELECT id AS row_id, user_id, username, friendly_name, ' \
+ 'thumb AS user_thumb, custom_avatar_url AS custom_thumb, ' \
'email, is_active, is_admin, is_home_user, is_allow_sync, is_restricted, ' \
'do_notify, keep_history, deleted_user, ' \
'allow_guest, shared_libraries ' \
@@ -375,7 +380,8 @@ class Users(object):
'WHERE username = ? COLLATE NOCASE '
result = monitor_db.select(query, args=[user])
elif email:
- query = 'SELECT user_id, username, friendly_name, thumb AS user_thumb, custom_avatar_url AS custom_thumb, ' \
+ query = 'SELECT id AS row_id, user_id, username, friendly_name, ' \
+ 'thumb AS user_thumb, custom_avatar_url AS custom_thumb, ' \
'email, is_active, is_admin, is_home_user, is_allow_sync, is_restricted, ' \
'do_notify, keep_history, deleted_user, ' \
'allow_guest, shared_libraries ' \
@@ -407,7 +413,8 @@ class Users(object):
shared_libraries = tuple(item['shared_libraries'].split(';')) if item['shared_libraries'] else ()
- user_details = {'user_id': item['user_id'],
+ user_details = {'row_id': item['row_id'],
+ 'user_id': item['user_id'],
'username': item['username'],
'friendly_name': friendly_name,
'user_thumb': user_thumb,
@@ -619,7 +626,7 @@ class Users(object):
monitor_db = database.MonitorDatabase()
try:
- query = 'SELECT user_id, username, friendly_name, thumb, custom_avatar_url, email, ' \
+ query = 'SELECT id AS row_id, user_id, username, friendly_name, thumb, custom_avatar_url, email, ' \
'is_active, is_admin, is_home_user, is_allow_sync, is_restricted, ' \
'do_notify, keep_history, allow_guest, server_token, shared_libraries, ' \
'filter_all, filter_movies, filter_tv, filter_music, filter_photos ' \
@@ -631,7 +638,8 @@ class Users(object):
users = []
for item in result:
- user = {'user_id': item['user_id'],
+ user = {'row_id': item['row_id'],
+ 'user_id': item['user_id'],
'username': item['username'],
'friendly_name': item['friendly_name'] or item['username'],
'thumb': item['custom_avatar_url'] or item['thumb'],
@@ -656,53 +664,38 @@ class Users(object):
return users
- def delete_all_history(self, user_id=None):
+ def delete(self, user_id=None, row_ids=None, purge_only=False):
monitor_db = database.MonitorDatabase()
- try:
- if str(user_id).isdigit():
- logger.info(u"Tautulli Users :: 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])
+ if row_ids and row_ids is not None:
+ row_ids = map(helpers.cast_to_int, row_ids.split(','))
- return 'Deleted all items for user_id %s.' % user_id
+ # Get the user_ids corresponding to the row_ids
+ result = monitor_db.select('SELECT user_id FROM users '
+ 'WHERE id IN ({})'.format(','.join(['?'] * len(row_ids))), row_ids)
+ user_ids = [user['user_id'] for user in result]
+
+ success = []
+ for user_id in user_ids:
+ success.append(self.delete(user_id=user_id, purge_only=purge_only))
+ return all(success)
+
+ elif str(user_id).isdigit():
+ database.delete_user_history(user_id=user_id)
+ if purge_only:
+ return True
else:
- return 'Unable to delete items. Input user_id not valid.'
- except Exception as e:
- logger.warn(u"Tautulli Users :: Unable to execute database query for delete_all_history: %s." % e)
+ logger.info(u"Tautulli Users :: Deleting user with user_id %s from database." % user_id)
+ try:
+ monitor_db.action('UPDATE users '
+ 'SET deleted_user = 1, keep_history = 0, do_notify = 0 '
+ 'WHERE user_id = ?', [user_id])
+ return True
+ except Exception as e:
+ logger.warn(u"Tautulli Users :: Unable to execute database query for delete: %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"Tautulli Users :: 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"Tautulli Users :: Unable to execute database query for delete: %s." % e)
+ else:
+ return False
def undelete(self, user_id=None, username=None):
monitor_db = database.MonitorDatabase()
diff --git a/plexpy/webserve.py b/plexpy/webserve.py
index 5329a755..16015876 100644
--- a/plexpy/webserve.py
+++ b/plexpy/webserve.py
@@ -1417,6 +1417,7 @@ class WebInterface(object):
"is_home_user": 1,
"is_restricted": 0,
"keep_history": 1,
+ "row_id": 1,
"shared_libraries": ["10", "1", "4", "5", "15", "20", "2"],
"user_id": 133788,
"user_thumb": "https://plex.tv/users/k10w42309cynaopq/avatar",
@@ -1529,7 +1530,7 @@ class WebInterface(object):
@cherrypy.tools.json_out()
@requireAuth(member_of("admin"))
@addtoapi()
- def delete_all_user_history(self, user_id, **kwargs):
+ def delete_all_user_history(self, user_id=None, row_ids=None, **kwargs):
""" Delete all Tautulli history for a specific user.
```
@@ -1537,25 +1538,27 @@ class WebInterface(object):
user_id (str): The id of the Plex user
Optional parameters:
- None
+ row_ids (str): Comma separated row ids to delete, e.g. "2,3,8"
Returns:
None
```
"""
- if user_id:
+ if user_id or row_ids:
user_data = users.Users()
- delete_row = user_data.delete_all_history(user_id=user_id)
- if delete_row:
- return {'message': delete_row}
+ success = user_data.delete(user_id=user_id, row_ids=row_ids, purge_only=True)
+ if success:
+ return {'result': 'success', 'message': 'Deleted user history.'}
+ else:
+ return {'result': 'error', 'message': 'Failed to delete user(s) history.'}
else:
- return {'message': 'no data received'}
+ return {'result': 'error', 'message': 'No user id or row ids received.'}
@cherrypy.expose
@cherrypy.tools.json_out()
@requireAuth(member_of("admin"))
@addtoapi()
- def delete_user(self, user_id, **kwargs):
+ def delete_user(self, user_id=None, row_ids=None, **kwargs):
""" Delete a user from Tautulli. Also erases all history for the user.
```
@@ -1563,19 +1566,21 @@ class WebInterface(object):
user_id (str): The id of the Plex user
Optional parameters:
- None
+ row_ids (str): Comma separated row ids to delete, e.g. "2,3,8"
Returns:
None
```
"""
- if user_id:
+ if user_id or row_ids:
user_data = users.Users()
- delete_row = user_data.delete(user_id=user_id)
- if delete_row:
- return {'message': delete_row}
+ success = user_data.delete(user_id=user_id, row_ids=row_ids)
+ if success:
+ return {'result': 'success', 'message': 'Deleted user.'}
+ else:
+ return {'result': 'error', 'message': 'Failed to delete user(s).'}
else:
- return {'message': 'no data received'}
+ return {'result': 'error', 'message': 'No user id or row ids received.'}
@cherrypy.expose
@cherrypy.tools.json_out()
@@ -5427,6 +5432,7 @@ class WebInterface(object):
"is_home_user": 1,
"is_restricted": 0,
"keep_history": 1,
+ "row_id": 1,
"server_token": "PU9cMuQZxJKFBtGqHk68",
"shared_libraries": "1;2;3",
"thumb": "https://plex.tv/users/k10w42309cynaopq/avatar",