mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-07 05:31:15 -07:00
We have initial implementation of sync lists (may still be buggy)
To get usernames in sync lists go to Users->Refresh Users.
This commit is contained in:
parent
6927753db6
commit
d2db662e14
7 changed files with 103 additions and 36 deletions
|
@ -113,6 +113,19 @@ from plexpy import version
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
% endif
|
% endif
|
||||||
|
% if title=="Synced Items":
|
||||||
|
<li class="active">
|
||||||
|
<a href="sync">
|
||||||
|
<i class="fa fa-cloud-download fa-2x" data-toggle="tooltip" data-placement="bottom" title="Synced Items" id="sync"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
% else:
|
||||||
|
<li>
|
||||||
|
<a href="sync">
|
||||||
|
<i class="fa fa-cloud-download fa-2x" data-toggle="tooltip" data-placement="bottom" title="Synced Items" id="sync"></i>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
% endif
|
||||||
% if title=="Log":
|
% if title=="Log":
|
||||||
<li class="active">
|
<li class="active">
|
||||||
<a href="logs">
|
<a href="logs">
|
||||||
|
@ -161,6 +174,9 @@ ${next.body()}
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('#graphs').tooltip();
|
$('#graphs').tooltip();
|
||||||
});
|
});
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#sync').tooltip();
|
||||||
|
});
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('#logs').tooltip();
|
$('#logs').tooltip();
|
||||||
});
|
});
|
||||||
|
|
|
@ -57,12 +57,12 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="http_host">HTTP Host</label>
|
<label for="http_host">HTTP Host</label>
|
||||||
<input type="text" id="http_host" name="http_host" value="${config['http_host']}" size="30">
|
<input type="text" id="http_host" name="http_host" value="${config['http_host']}" size="30" required="required">
|
||||||
<p class="help-block">e.g. localhost or an IP, such as 0.0.0.0</p>
|
<p class="help-block">e.g. localhost or an IP, such as 0.0.0.0</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="http_port">HTTP Port</label>
|
<label for="http_port">HTTP Port</label>
|
||||||
<input type="text" id="http_port" name="http_port" value="${config['http_port']}" size="10">
|
<input type="text" id="http_port" name="http_port" value="${config['http_port']}" size="10" required="required">
|
||||||
<p class="help-block">Port to bind web server to. Note that ports below 1024 may require root.</p>
|
<p class="help-block">Port to bind web server to. Note that ports below 1024 may require root.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
|
@ -136,12 +136,12 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="date_format">Date Format</label>
|
<label for="date_format">Date Format</label>
|
||||||
<input type="text" id="date_format" name="date_format" value="${config['date_format']}" size="30">
|
<input type="text" id="date_format" name="date_format" value="${config['date_format']}" size="30" required="required">
|
||||||
<p class="help-block">Set your preferred date format. <a href="#dateTimeOptionsModal" data-toggle="modal">Click here</a> to see the parameter list.</p>
|
<p class="help-block">Set your preferred date format. <a href="#dateTimeOptionsModal" data-toggle="modal">Click here</a> to see the parameter list.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="date_format">Time Format</label>
|
<label for="date_format">Time Format</label>
|
||||||
<input type="text" id="time_format" name="time_format" value="${config['time_format']}" size="30">
|
<input type="text" id="time_format" name="time_format" value="${config['time_format']}" size="30" required="required">
|
||||||
<p class="help-block">Set your preferred time format. <a href="#dateTimeOptionsModal" data-toggle="modal">Click here</a> to see the parameter list.</p>
|
<p class="help-block">Set your preferred time format. <a href="#dateTimeOptionsModal" data-toggle="modal">Click here</a> to see the parameter list.</p>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
@ -153,12 +153,12 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pms_ip">PMS IP</label>
|
<label for="pms_ip">PMS IP</label>
|
||||||
<input type="text" id="pms_ip" name="pms_ip" value="${config['pms_ip']}" size="30">
|
<input type="text" id="pms_ip" name="pms_ip" value="${config['pms_ip']}" size="30" required="required">
|
||||||
<p class="help-block">IP Address for Plex Media Server.</p>
|
<p class="help-block">IP Address for Plex Media Server.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pms_port">PMS Port</label>
|
<label for="pms_port">PMS Port</label>
|
||||||
<input type="text" id="pms_port" name="pms_port" value="${config['pms_port']}" size="30">
|
<input type="text" id="pms_port" name="pms_port" value="${config['pms_port']}" size="30" required="required">
|
||||||
<p class="help-block">Port that Plex Media Server is listening on.</p>
|
<p class="help-block">Port that Plex Media Server is listening on.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -176,7 +176,7 @@
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="plexwatch_database">PlexWatch Database</label>
|
<label for="plexwatch_database">PlexWatch Database</label>
|
||||||
<input type="text" id="plexwatch_database" name="plexwatch_database" value="${config['plexwatch_database']}" size="30">
|
<input type="text" id="plexwatch_database" name="plexwatch_database" value="${config['plexwatch_database']}" size="30" required="required">
|
||||||
<p class="help-block">Full path and file name of your PlexWatch database.</p>
|
<p class="help-block">Full path and file name of your PlexWatch database.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
|
@ -697,12 +697,12 @@
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pms_username">PMS Username</label>
|
<label for="pms_username">PMS Username</label>
|
||||||
<input type="text" id="pms_username" name="pms_username" size="30">
|
<input type="text" id="pms_username" name="pms_username" size="30" required="required">
|
||||||
<p class="help-block">Username for Plex.tv authentication.</p>
|
<p class="help-block">Username for Plex.tv authentication.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="pms_password">PMS Password</label>
|
<label for="pms_password">PMS Password</label>
|
||||||
<input type="password" id="pms_password" name="pms_password" size="30">
|
<input type="password" id="pms_password" name="pms_password" size="30" required="required">
|
||||||
<p class="help-block">Password for Plex.tv authentication.</p>
|
<p class="help-block">Password for Plex.tv authentication.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
$('#sync_table').dataTable( {
|
sync_table_options = {
|
||||||
"responsive": {
|
"responsive": {
|
||||||
details: false
|
details: false
|
||||||
},
|
},
|
||||||
"processing": false,
|
"processing": false,
|
||||||
"serverSide": false,
|
"serverSide": false,
|
||||||
"ajax": {
|
|
||||||
"url": "get_sync"
|
|
||||||
},
|
|
||||||
"sPaginationType": "bootstrap",
|
"sPaginationType": "bootstrap",
|
||||||
"order": [ 0, 'desc'],
|
"order": [ 0, 'desc'],
|
||||||
"pageLength": 25,
|
"pageLength": 25,
|
||||||
|
@ -108,10 +105,5 @@ $('#sync_table').dataTable( {
|
||||||
"drawCallback": function (settings) {
|
"drawCallback": function (settings) {
|
||||||
// Jump to top of page
|
// Jump to top of page
|
||||||
$('html,body').scrollTop(0);
|
$('html,body').scrollTop(0);
|
||||||
$('#ajaxMsg').addClass('success').fadeOut();
|
|
||||||
},
|
|
||||||
"preDrawCallback": function(settings) {
|
|
||||||
$('#ajaxMsg').html("<div class='msg'><span class='ui-icon ui-icon-check'></span>Fetching rows...</div>");
|
|
||||||
$('#ajaxMsg').addClass('success').fadeIn();
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ from plexpy import helpers
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div class="wellheader-bg">
|
<div class="wellheader-bg">
|
||||||
<div class="dashboard-wellheader-no-chevron">
|
<div class="dashboard-wellheader-no-chevron">
|
||||||
<h2><i class="fa fa-book"></i> Synced Items</h2>
|
<h2><i class="fa fa-cloud-download"></i> Synced Items</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -62,5 +62,12 @@ from plexpy import helpers
|
||||||
<script src="interfaces/default/js/dataTables.responsive.js"></script>
|
<script src="interfaces/default/js/dataTables.responsive.js"></script>
|
||||||
<script src="interfaces/default/js/jquery.dataTables.bootstrap.pagination.integration.js"></script>
|
<script src="interfaces/default/js/jquery.dataTables.bootstrap.pagination.integration.js"></script>
|
||||||
<script src="interfaces/default/js/tables/sync_table.js"></script>
|
<script src="interfaces/default/js/tables/sync_table.js"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
sync_table_options.ajax = {
|
||||||
|
"url": "get_sync"
|
||||||
|
}
|
||||||
|
sync_table = $('#sync_table').DataTable(sync_table_options);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
|
@ -49,6 +49,7 @@ from plexpy import helpers
|
||||||
<li class="active"><a href="#profile" data-toggle="tab">Profile</a></li>
|
<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="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="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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -208,6 +209,40 @@ from plexpy import helpers
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane" id="userSyncItems">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span12">
|
||||||
|
<div class="table-card-back">
|
||||||
|
<h3>Synced Items for <strong>
|
||||||
|
<span class="set-username">${data['friendly_name']}</span>
|
||||||
|
</strong></h3>
|
||||||
|
</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="device">Device</th>
|
||||||
|
<th align='left' id="sync_platform">Platform</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>
|
</div>
|
||||||
<footer></footer>
|
<footer></footer>
|
||||||
</%def>
|
</%def>
|
||||||
|
@ -219,6 +254,7 @@ from plexpy import helpers
|
||||||
<script src="interfaces/default/js/moment-with-locale.js"></script>
|
<script src="interfaces/default/js/moment-with-locale.js"></script>
|
||||||
<script src="interfaces/default/js/tables/history_table.js"></script>
|
<script src="interfaces/default/js/tables/history_table.js"></script>
|
||||||
<script src="interfaces/default/js/tables/user_ips.js"></script>
|
<script src="interfaces/default/js/tables/user_ips.js"></script>
|
||||||
|
<script src="interfaces/default/js/tables/sync_table.js"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
||||||
|
@ -275,6 +311,17 @@ from plexpy import helpers
|
||||||
user_ip_table = $('#user_ip_table').DataTable(user_ip_table_options);
|
user_ip_table = $('#user_ip_table').DataTable(user_ip_table_options);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$( "#sync-tab-btn" ).one( "click", function() {
|
||||||
|
// Build user sync table
|
||||||
|
sync_table_options.ajax = {
|
||||||
|
"url": "get_sync",
|
||||||
|
"data": function(d) {
|
||||||
|
d.user_id = "${data['user_id']}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sync_table = $('#sync_table').DataTable(sync_table_options);
|
||||||
|
});
|
||||||
|
|
||||||
// Load edit user modal
|
// Load edit user modal
|
||||||
$("#toggle-edit-user-modal").click(function() {
|
$("#toggle-edit-user-modal").click(function() {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
|
|
@ -284,7 +284,7 @@ class PlexTV(object):
|
||||||
|
|
||||||
return users_list
|
return users_list
|
||||||
|
|
||||||
def get_synced_items(self, machine_id=None):
|
def get_synced_items(self, machine_id=None, user_id=None):
|
||||||
sync_list = self.get_plextv_sync_lists(machine_id)
|
sync_list = self.get_plextv_sync_lists(machine_id)
|
||||||
plex_watch = plexwatch.PlexWatch()
|
plex_watch = plexwatch.PlexWatch()
|
||||||
|
|
||||||
|
@ -304,9 +304,15 @@ class PlexTV(object):
|
||||||
else:
|
else:
|
||||||
for a in xml_head:
|
for a in xml_head:
|
||||||
client_id = self.get_xml_attr(a, 'id')
|
client_id = self.get_xml_attr(a, 'id')
|
||||||
|
|
||||||
sync_device = a.getElementsByTagName('Device')
|
sync_device = a.getElementsByTagName('Device')
|
||||||
for device in sync_device:
|
for device in sync_device:
|
||||||
|
device_user_id = self.get_xml_attr(device, 'userID')
|
||||||
|
try:
|
||||||
|
device_username = plex_watch.get_user_details(user_id=device_user_id)['username']
|
||||||
|
device_friendly_name = plex_watch.get_user_details(user_id=device_user_id)['friendly_name']
|
||||||
|
except:
|
||||||
|
device_username = ''
|
||||||
|
device_friendly_name = ''
|
||||||
device_name = self.get_xml_attr(device, 'name')
|
device_name = self.get_xml_attr(device, 'name')
|
||||||
device_product = self.get_xml_attr(device, 'product')
|
device_product = self.get_xml_attr(device, 'product')
|
||||||
device_product_version = self.get_xml_attr(device, 'productVersion')
|
device_product_version = self.get_xml_attr(device, 'productVersion')
|
||||||
|
@ -315,13 +321,10 @@ class PlexTV(object):
|
||||||
device_type = self.get_xml_attr(device, 'device')
|
device_type = self.get_xml_attr(device, 'device')
|
||||||
device_model = self.get_xml_attr(device, 'model')
|
device_model = self.get_xml_attr(device, 'model')
|
||||||
device_last_seen = self.get_xml_attr(device, 'lastSeenAt')
|
device_last_seen = self.get_xml_attr(device, 'lastSeenAt')
|
||||||
device_user_id = self.get_xml_attr(device, 'userID')
|
|
||||||
try:
|
# Filter by user_id
|
||||||
device_username = plex_watch.get_user_details(user_id=device_user_id)['username']
|
if user_id and user_id != device_user_id:
|
||||||
device_friendly_name = plex_watch.get_user_details(user_id=device_user_id)['friendly_name']
|
break
|
||||||
except:
|
|
||||||
device_username = ''
|
|
||||||
device_friendly_name = ''
|
|
||||||
|
|
||||||
for synced in a.getElementsByTagName('SyncItems'):
|
for synced in a.getElementsByTagName('SyncItems'):
|
||||||
sync_item = synced.getElementsByTagName('SyncItem')
|
sync_item = synced.getElementsByTagName('SyncItem')
|
||||||
|
|
|
@ -897,20 +897,22 @@ class WebInterface(object):
|
||||||
raise cherrypy.HTTPRedirect("users")
|
raise cherrypy.HTTPRedirect("users")
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def get_sync(self, machine_id='', **kwargs):
|
def get_sync(self, machine_id=None, user_id=None, **kwargs):
|
||||||
|
|
||||||
pms_connect = pmsconnect.PmsConnect()
|
pms_connect = pmsconnect.PmsConnect()
|
||||||
server_info = pms_connect.get_servers_info()
|
server_info = pms_connect.get_servers_info()
|
||||||
|
|
||||||
plex_tv = plextv.PlexTV()
|
plex_tv = plextv.PlexTV()
|
||||||
if not machine_id:
|
if not machine_id:
|
||||||
result = plex_tv.get_synced_items(machine_id=server_info[0]['machine_identifier'])
|
result = plex_tv.get_synced_items(machine_id=server_info[0]['machine_identifier'], user_id=user_id)
|
||||||
else:
|
else:
|
||||||
result = plex_tv.get_synced_items(machine_id=machine_id)
|
result = plex_tv.get_synced_items(machine_id=machine_id, user_id=user_id)
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
cherrypy.response.headers['Content-type'] = 'application/json'
|
|
||||||
output = {"data": result}
|
output = {"data": result}
|
||||||
return json.dumps(output)
|
|
||||||
else:
|
else:
|
||||||
logger.warn('Unable to retrieve data.')
|
logger.warn('Unable to retrieve sync data for user.')
|
||||||
|
output = {"data": []}
|
||||||
|
|
||||||
|
cherrypy.response.headers['Content-type'] = 'application/json'
|
||||||
|
return json.dumps(output)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue