mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-15 01:32:57 -07:00
Merge branch 'dev'
This commit is contained in:
commit
c3378e1653
19 changed files with 169 additions and 125 deletions
21
API.md
21
API.md
|
@ -1061,6 +1061,7 @@ Required parameters:
|
||||||
count (str): Number of items to return
|
count (str): Number of items to return
|
||||||
|
|
||||||
Optional parameters:
|
Optional parameters:
|
||||||
|
start (str): The item number to start at
|
||||||
section_id (str): The id of the Plex library section
|
section_id (str): The id of the Plex library section
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -1357,7 +1358,7 @@ Returns:
|
||||||
|
|
||||||
|
|
||||||
### get_user_logins
|
### get_user_logins
|
||||||
Get the data on PlexPy user login table.
|
Get the data on PlexPy user login table.
|
||||||
|
|
||||||
```
|
```
|
||||||
Required parameters:
|
Required parameters:
|
||||||
|
@ -1376,15 +1377,15 @@ Returns:
|
||||||
"recordsTotal": 2344,
|
"recordsTotal": 2344,
|
||||||
"recordsFiltered": 10,
|
"recordsFiltered": 10,
|
||||||
"data":
|
"data":
|
||||||
[{"browser": "Safari 7.0.3",
|
[{"browser": "Safari 7.0.3",
|
||||||
"friendly_name": "Jon Snow",
|
"friendly_name": "Jon Snow",
|
||||||
"host": "http://plexpy.castleblack.com",
|
"host": "http://plexpy.castleblack.com",
|
||||||
"ip_address": "xxx.xxx.xxx.xxx",
|
"ip_address": "xxx.xxx.xxx.xxx",
|
||||||
"os": "Mac OS X",
|
"os": "Mac OS X",
|
||||||
"timestamp": 1462591869,
|
"timestamp": 1462591869,
|
||||||
"user": "LordCommanderSnow",
|
"user": "LordCommanderSnow",
|
||||||
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A",
|
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A",
|
||||||
"user_group": "guest",
|
"user_group": "guest",
|
||||||
"user_id": 133788
|
"user_id": 133788
|
||||||
},
|
},
|
||||||
{...},
|
{...},
|
||||||
|
|
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -1,5 +1,25 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v1.4.1 (2016-05-20)
|
||||||
|
|
||||||
|
* New: HTTP Proxy checkbox in the settings. Enable this if using an SSL enabled reverse proxy in front of PlexPy.
|
||||||
|
* Fix: Check for blank username/password on login.
|
||||||
|
* Fix: Persist current activity artwork blur across refreshes when transcoding details are visible.
|
||||||
|
* Fix: Send notifications to multiple XBMC/Plex Home Theater devices.
|
||||||
|
* Fix: Reset PMS identifier when clicking verify server button in settings.
|
||||||
|
* Fix: Crash when trying to group current activity session in database.
|
||||||
|
* Fix: Check current activity returns sessions when refreshing.
|
||||||
|
* Fix: Logs sorted out of order.
|
||||||
|
* Fix: Resolution reported incorrectly in the stream info modal.
|
||||||
|
* Fix: PlexPy crashing when hashing password in the config file.
|
||||||
|
* Fix: CherryPy doubling the port number when accessing PlexPy locally with http_proxy enabled.
|
||||||
|
* Change: Sort by most recent for ties in watch statistics.
|
||||||
|
* Change: Refresh Join devices when changing the API key.
|
||||||
|
* Change: Format the Join device IDs.
|
||||||
|
* Change: Join notifications now sent with Python Requests module.
|
||||||
|
* Change: Add paging for recently added in the API.
|
||||||
|
|
||||||
|
|
||||||
## v1.4.0 (2016-05-15)
|
## v1.4.0 (2016-05-15)
|
||||||
|
|
||||||
* New: An HTML form login page with sessions support.
|
* New: An HTML form login page with sessions support.
|
||||||
|
|
|
@ -2954,4 +2954,14 @@ a.no-highlight:hover {
|
||||||
.datatable-wrap {
|
.datatable-wrap {
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
max-width: 250px;
|
max-width: 250px;
|
||||||
|
}
|
||||||
|
.inline-pre {
|
||||||
|
font-family: monospace;
|
||||||
|
margin: 0 2px;
|
||||||
|
padding: 2px 5px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #555;
|
||||||
|
border: 0px solid #444;
|
||||||
|
border-radius: 3px;
|
||||||
}
|
}
|
|
@ -71,7 +71,7 @@ DOCUMENTATION :: END
|
||||||
% else:
|
% else:
|
||||||
<a href="#">
|
<a href="#">
|
||||||
% endif
|
% endif
|
||||||
<div class="dashboard-activity-poster">
|
<div class="dashboard-activity-poster" id="poster-${data['session_key']}">
|
||||||
% if not data['art'].startswith('interfaces') or not data['thumb'].startswith('interfaces'):
|
% if not data['art'].startswith('interfaces') or not data['thumb'].startswith('interfaces'):
|
||||||
% if (data['media_type'] == 'movie' and not data['indexes']) or (data['indexes'] and not data['view_offset']):
|
% if (data['media_type'] == 'movie' and not data['indexes']) or (data['indexes'] and not data['view_offset']):
|
||||||
<div id="bif-${data['session_key']}" class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${data['art']}&width=500&height=280&fallback=art);"></div>
|
<div id="bif-${data['session_key']}" class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img=${data['art']}&width=500&height=280&fallback=art);"></div>
|
||||||
|
@ -105,7 +105,7 @@ DOCUMENTATION :: END
|
||||||
<div class="dashboard-activity-poster-face" style="background-image: url(${data['art']});"></div>
|
<div class="dashboard-activity-poster-face" style="background-image: url(${data['art']});"></div>
|
||||||
% endif
|
% endif
|
||||||
<div class="dashboard-activity-button-info">
|
<div class="dashboard-activity-button-info">
|
||||||
<button type="button" class="btn btn-activity-info btn-lg" data-target="#stream-${data['session_key']}">
|
<button type="button" class="btn btn-activity-info btn-lg" data-target="#stream-${data['session_key']}" data-id="${data['session_key']}">
|
||||||
<i class="fa fa-info-circle"></i>
|
<i class="fa fa-info-circle"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -107,6 +107,11 @@
|
||||||
$('#dashboard-checking-activity').remove();
|
$('#dashboard-checking-activity').remove();
|
||||||
|
|
||||||
var current_activity = $.parseJSON(xhr.responseText);
|
var current_activity = $.parseJSON(xhr.responseText);
|
||||||
|
if (!(current_activity)) {
|
||||||
|
$('#currentActivity').html('<div id="dashboard-no-activity" class="text-muted">There was an error communicating with your Plex Server.</div>');
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var stream_count = parseInt(current_activity.stream_count);
|
var stream_count = parseInt(current_activity.stream_count);
|
||||||
var sessions = current_activity.sessions;
|
var sessions = current_activity.sessions;
|
||||||
|
|
||||||
|
@ -150,6 +155,7 @@
|
||||||
bif_poster.animate({ opacity: 0 }, { duration: 1000, queue: false });
|
bif_poster.animate({ opacity: 0 }, { duration: 1000, queue: false });
|
||||||
bif_poster.after($('<div id="bif-' + key + '"class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img='
|
bif_poster.after($('<div id="bif-' + key + '"class="dashboard-activity-poster-face" style="background-image: url(pms_image_proxy?img='
|
||||||
+ s.bif_thumb + '&width=500&height=280&fallback=art);"></div>').fadeIn(1000, function () { bif_poster.remove() }));
|
+ s.bif_thumb + '&width=500&height=280&fallback=art);"></div>').fadeIn(1000, function () { bif_poster.remove() }));
|
||||||
|
blurArtwork(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if transcoding, update the transcode state
|
// if transcoding, update the transcode state
|
||||||
|
@ -210,14 +216,18 @@
|
||||||
getCurrentActivity();
|
getCurrentActivity();
|
||||||
}, 15000);
|
}, 15000);
|
||||||
|
|
||||||
|
function blurArtwork(session_key) {
|
||||||
|
var filterVal = $('#stream-' + session_key).is(':visible') ? 'blur(5px)' : '';
|
||||||
|
$($('#poster-' + session_key).find('.dashboard-activity-poster-face, .dashboard-activity-cover-face'))
|
||||||
|
.css('filter', filterVal).css('webkitFilter', filterVal).css('mozFilter', filterVal).css('oFilter', filterVal).css('msFilter', filterVal);
|
||||||
|
}
|
||||||
|
|
||||||
// Show/Hide activity info
|
// Show/Hide activity info
|
||||||
$('#currentActivity').on('click', '.btn-activity-info', function (e) {
|
$('#currentActivity').on('click', '.btn-activity-info', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$($(this).attr('data-target')).toggle();
|
$($(this).attr('data-target')).toggle();
|
||||||
var id = $(this).closest('.dashboard-instance').data('id');
|
var key = $(this).data('id');
|
||||||
var filterVal = $('#stream-' + id).is(':visible') ? 'blur(5px)' : '';
|
blurArtwork(key);
|
||||||
$($(this).closest('.dashboard-activity-poster').find('.dashboard-activity-poster-face, .dashboard-activity-cover-face'))
|
|
||||||
.css('filter',filterVal).css('webkitFilter',filterVal).css('mozFilter',filterVal).css('oFilter',filterVal).css('msFilter',filterVal);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add hover class to dashboard-instance
|
// Add hover class to dashboard-instance
|
||||||
|
|
|
@ -217,7 +217,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#pushbullet_apikey, #pushover_apitoken, #scripts_folder').on('change', function () {
|
$('#pushbullet_apikey, #pushover_apitoken, #scripts_folder, #join_apikey').on('change', function () {
|
||||||
// Reload modal to update certain fields
|
// Reload modal to update certain fields
|
||||||
doAjaxCall('set_notification_config', $(this), 'tabs', true, reloadModal);
|
doAjaxCall('set_notification_config', $(this), 'tabs', true, reloadModal);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -109,6 +109,5 @@ DOCUMENTATION :: END
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
% else:
|
% else:
|
||||||
<div class="text-muted">There was an error communicating with your Plex Server. Please check your <a href="settings">settings</a>.
|
<div class="text-muted">There was an error communicating with your Plex Server.</div><br>
|
||||||
</div><br>
|
|
||||||
% endif
|
% endif
|
|
@ -432,6 +432,13 @@
|
||||||
</div>
|
</div>
|
||||||
<p class="help-block">The base URL of the web server. Used for reverse proxies.</p>
|
<p class="help-block">The base URL of the web server. Used for reverse proxies.</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="http_proxy http-settings" id="http_proxy" value="1" ${config['http_proxy']}> Enable HTTP Proxy
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Respect the X-Forwarded-Proto header. Used for reverse proxies with SSL.</p>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" name="launch_browser" id="launch_browser" value="1" ${config['launch_browser']}> Launch Browser on Startup
|
<input type="checkbox" name="launch_browser" id="launch_browser" value="1" ${config['launch_browser']}> Launch Browser on Startup
|
||||||
|
@ -2234,7 +2241,6 @@ $(document).ready(function() {
|
||||||
$( ".pms-settings" ).change(function() {
|
$( ".pms-settings" ).change(function() {
|
||||||
serverChanged = true;
|
serverChanged = true;
|
||||||
$("#pms_identifier").val("");
|
$("#pms_identifier").val("");
|
||||||
$("#pms-verify-status").html("");
|
|
||||||
$("#server_changed").prop('checked', true);
|
$("#server_changed").prop('checked', true);
|
||||||
verifyServer();
|
verifyServer();
|
||||||
});
|
});
|
||||||
|
@ -2287,6 +2293,7 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#verify_server_button').on('click', function(){
|
$('#verify_server_button').on('click', function(){
|
||||||
|
$("#pms_identifier").val("");
|
||||||
verifyServer();
|
verifyServer();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2594,4 +2601,4 @@ $(document).ready(function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
|
@ -59,7 +59,7 @@ DOCUMENTATION :: END
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<li>Container: <strong>${data['transcode_container'] if data['transcode_container'] else data['container']}</strong></li>
|
<li>Container: <strong>${data['transcode_container'] if data['transcode_container'] else data['container']}</strong></li>
|
||||||
% if data['media_type'] != 'track':
|
% if data['media_type'] != 'track':
|
||||||
<li>Resolution: <strong>${data['video_resolution'] + 'p' if data['video_resolution'] != 'sd' else data['video_resolution']}</strong></li>
|
<li>Resolution: <strong>${data['transcode_height'] if data['transcode_height'] else data['height']}p</strong></li>
|
||||||
% endif
|
% endif
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -101,7 +101,7 @@ DOCUMENTATION :: END
|
||||||
<ul class="list-unstyled">
|
<ul class="list-unstyled">
|
||||||
<li>Container: <strong>${data['container']}</strong></li>
|
<li>Container: <strong>${data['container']}</strong></li>
|
||||||
% if data['media_type'] != 'track':
|
% if data['media_type'] != 'track':
|
||||||
<li>Resolution: <strong>${data['height']}p</strong></li>
|
<li>Resolution: <strong>${data['video_resolution'] + 'p' if data['video_resolution'] != 'sd' else data['video_resolution']}</strong></li>
|
||||||
% endif
|
% endif
|
||||||
<li>Bitrate: <strong>${data['bitrate']} kbps</strong></li>
|
<li>Bitrate: <strong>${data['bitrate']} kbps</strong></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -195,7 +195,7 @@ def proxy(base=None, local='X-Forwarded-Host', remote='X-Forwarded-For',
|
||||||
if not base:
|
if not base:
|
||||||
base = request.headers.get('Host', '127.0.0.1')
|
base = request.headers.get('Host', '127.0.0.1')
|
||||||
port = request.local.port
|
port = request.local.port
|
||||||
if port != 80:
|
if port != 80 and not base.endswith(':%s' % port):
|
||||||
base += ':%s' % port
|
base += ':%s' % port
|
||||||
|
|
||||||
if base.find("://") == -1:
|
if base.find("://") == -1:
|
||||||
|
|
|
@ -32,7 +32,7 @@ HASH_FUNCTION = 'sha256' # Must be in hashlib.
|
||||||
# Linear to the hashing time. Adjust to be high but take a reasonable
|
# Linear to the hashing time. Adjust to be high but take a reasonable
|
||||||
# amount of time on your server. Measure with:
|
# amount of time on your server. Measure with:
|
||||||
# python -m timeit -s 'import passwords as p' 'p.make_hash("something")'
|
# python -m timeit -s 'import passwords as p' 'p.make_hash("something")'
|
||||||
COST_FACTOR = 29000
|
COST_FACTOR = 10000
|
||||||
|
|
||||||
|
|
||||||
def make_hash(password):
|
def make_hash(password):
|
||||||
|
|
|
@ -72,7 +72,7 @@ def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None):
|
||||||
rv = u = _pseudorandom(salt + _pack_int(block))
|
rv = u = _pseudorandom(salt + _pack_int(block))
|
||||||
for i in xrange(iterations - 1):
|
for i in xrange(iterations - 1):
|
||||||
u = _pseudorandom(''.join(map(chr, u)))
|
u = _pseudorandom(''.join(map(chr, u)))
|
||||||
rv = starmap(xor, izip(rv, u))
|
rv = list(starmap(xor, izip(rv, u)))
|
||||||
buf.extend(rv)
|
buf.extend(rv)
|
||||||
return ''.join(map(chr, buf))[:keylen]
|
return ''.join(map(chr, buf))[:keylen]
|
||||||
|
|
||||||
|
|
|
@ -219,26 +219,30 @@ class ActivityProcessor(object):
|
||||||
args = [session['user_id']]
|
args = [session['user_id']]
|
||||||
|
|
||||||
result = self.db.select(query=query, args=args)
|
result = self.db.select(query=query, args=args)
|
||||||
|
|
||||||
new_session = {'id': result[0]['id'],
|
|
||||||
'rating_key': result[0]['rating_key'],
|
|
||||||
'view_offset': result[0]['view_offset'],
|
|
||||||
'user_id': result[0]['user_id'],
|
|
||||||
'reference_id': result[0]['reference_id']}
|
|
||||||
|
|
||||||
if len(result) == 1:
|
new_session = prev_session = last_id = None
|
||||||
prev_session = None
|
if len(result) > 1:
|
||||||
else:
|
new_session = {'id': result[0]['id'],
|
||||||
|
'rating_key': result[0]['rating_key'],
|
||||||
|
'view_offset': result[0]['view_offset'],
|
||||||
|
'user_id': result[0]['user_id'],
|
||||||
|
'reference_id': result[0]['reference_id']}
|
||||||
|
|
||||||
prev_session = {'id': result[1]['id'],
|
prev_session = {'id': result[1]['id'],
|
||||||
'rating_key': result[1]['rating_key'],
|
'rating_key': result[1]['rating_key'],
|
||||||
'view_offset': result[1]['view_offset'],
|
'view_offset': result[1]['view_offset'],
|
||||||
'user_id': result[1]['user_id'],
|
'user_id': result[1]['user_id'],
|
||||||
'reference_id': result[1]['reference_id']}
|
'reference_id': result[1]['reference_id']}
|
||||||
|
else:
|
||||||
|
# Get the last insert row id
|
||||||
|
result = self.db.select(query='SELECT last_insert_rowid() AS last_id')
|
||||||
|
last_id = result[0]['last_id'] if result else None
|
||||||
|
|
||||||
query = 'UPDATE session_history SET reference_id = ? WHERE id = ? '
|
query = 'UPDATE session_history SET reference_id = ? WHERE id = ? '
|
||||||
# If rating_key is the same in the previous session, then set the reference_id to the previous row, else set the reference_id to the new id
|
# If rating_key is the same in the previous session, then set the reference_id to the previous row, else set the reference_id to the new id
|
||||||
if (prev_session is not None) and (prev_session['rating_key'] == new_session['rating_key'] \
|
if prev_session == new_session == None:
|
||||||
and prev_session['view_offset'] <= new_session['view_offset']):
|
args = [last_id, last_id]
|
||||||
|
elif prev_session['rating_key'] == new_session['rating_key'] and prev_session['view_offset'] <= new_session['view_offset']:
|
||||||
args = [prev_session['reference_id'], new_session['id']]
|
args = [prev_session['reference_id'], new_session['id']]
|
||||||
else:
|
else:
|
||||||
args = [new_session['id'], new_session['id']]
|
args = [new_session['id'], new_session['id']]
|
||||||
|
|
|
@ -198,7 +198,7 @@ class DataFactory(object):
|
||||||
top_tv = []
|
top_tv = []
|
||||||
try:
|
try:
|
||||||
query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \
|
query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \
|
||||||
't.media_type, t.content_rating, t.labels, ' \
|
't.media_type, t.content_rating, t.labels, t.started, ' \
|
||||||
'MAX(t.started) AS last_watch, COUNT(t.id) AS total_plays, SUM(t.d) AS total_duration ' \
|
'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) - ' \
|
'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) ' \
|
' (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) ' \
|
||||||
|
@ -210,7 +210,7 @@ class DataFactory(object):
|
||||||
' AND session_history.media_type = "episode" ' \
|
' AND session_history.media_type = "episode" ' \
|
||||||
' GROUP BY %s) AS t ' \
|
' GROUP BY %s) AS t ' \
|
||||||
'GROUP BY t.grandparent_title ' \
|
'GROUP BY t.grandparent_title ' \
|
||||||
'ORDER BY %s DESC ' \
|
'ORDER BY %s DESC, started DESC ' \
|
||||||
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -246,7 +246,7 @@ class DataFactory(object):
|
||||||
popular_tv = []
|
popular_tv = []
|
||||||
try:
|
try:
|
||||||
query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \
|
query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \
|
||||||
't.media_type, t.content_rating, t.labels, ' \
|
't.media_type, t.content_rating, t.labels, t.started, ' \
|
||||||
'COUNT(DISTINCT t.user_id) AS users_watched, ' \
|
'COUNT(DISTINCT t.user_id) AS users_watched, ' \
|
||||||
'MAX(t.started) AS last_watch, COUNT(t.id) as total_plays, SUM(t.d) AS total_duration ' \
|
'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) - ' \
|
'FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - ' \
|
||||||
|
@ -259,7 +259,7 @@ class DataFactory(object):
|
||||||
' AND session_history.media_type = "episode" ' \
|
' AND session_history.media_type = "episode" ' \
|
||||||
' GROUP BY %s) AS t ' \
|
' GROUP BY %s) AS t ' \
|
||||||
'GROUP BY t.grandparent_title ' \
|
'GROUP BY t.grandparent_title ' \
|
||||||
'ORDER BY users_watched DESC, %s DESC ' \
|
'ORDER BY users_watched DESC, %s DESC, started DESC ' \
|
||||||
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -293,7 +293,7 @@ class DataFactory(object):
|
||||||
top_movies = []
|
top_movies = []
|
||||||
try:
|
try:
|
||||||
query = 'SELECT t.id, t.full_title, t.rating_key, t.thumb, t.section_id, ' \
|
query = 'SELECT t.id, t.full_title, t.rating_key, t.thumb, t.section_id, ' \
|
||||||
't.media_type, t.content_rating, t.labels, ' \
|
't.media_type, t.content_rating, t.labels, t.started, ' \
|
||||||
'MAX(t.started) AS last_watch, COUNT(t.id) AS total_plays, SUM(t.d) AS total_duration ' \
|
'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) - ' \
|
'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) ' \
|
' (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) ' \
|
||||||
|
@ -305,7 +305,7 @@ class DataFactory(object):
|
||||||
' AND session_history.media_type = "movie" ' \
|
' AND session_history.media_type = "movie" ' \
|
||||||
' GROUP BY %s) AS t ' \
|
' GROUP BY %s) AS t ' \
|
||||||
'GROUP BY t.full_title ' \
|
'GROUP BY t.full_title ' \
|
||||||
'ORDER BY %s DESC ' \
|
'ORDER BY %s DESC, started DESC ' \
|
||||||
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -341,7 +341,7 @@ class DataFactory(object):
|
||||||
popular_movies = []
|
popular_movies = []
|
||||||
try:
|
try:
|
||||||
query = 'SELECT t.id, t.full_title, t.rating_key, t.thumb, t.section_id, ' \
|
query = 'SELECT t.id, t.full_title, t.rating_key, t.thumb, t.section_id, ' \
|
||||||
't.media_type, t.content_rating, t.labels, ' \
|
't.media_type, t.content_rating, t.labels, t.started, ' \
|
||||||
'COUNT(DISTINCT t.user_id) AS users_watched, ' \
|
'COUNT(DISTINCT t.user_id) AS users_watched, ' \
|
||||||
'MAX(t.started) AS last_watch, COUNT(t.id) as total_plays, SUM(t.d) AS total_duration ' \
|
'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) - ' \
|
'FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - ' \
|
||||||
|
@ -354,7 +354,7 @@ class DataFactory(object):
|
||||||
' AND session_history.media_type = "movie" ' \
|
' AND session_history.media_type = "movie" ' \
|
||||||
' GROUP BY %s) AS t ' \
|
' GROUP BY %s) AS t ' \
|
||||||
'GROUP BY t.full_title ' \
|
'GROUP BY t.full_title ' \
|
||||||
'ORDER BY users_watched DESC, %s DESC ' \
|
'ORDER BY users_watched DESC, %s DESC, started DESC ' \
|
||||||
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -388,7 +388,7 @@ class DataFactory(object):
|
||||||
top_music = []
|
top_music = []
|
||||||
try:
|
try:
|
||||||
query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \
|
query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \
|
||||||
't.media_type, t.content_rating, t.labels, ' \
|
't.media_type, t.content_rating, t.labels, t.started, ' \
|
||||||
'MAX(t.started) AS last_watch, COUNT(t.id) AS total_plays, SUM(t.d) AS total_duration ' \
|
'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) - ' \
|
'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) ' \
|
' (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) ' \
|
||||||
|
@ -400,7 +400,7 @@ class DataFactory(object):
|
||||||
' AND session_history.media_type = "track" ' \
|
' AND session_history.media_type = "track" ' \
|
||||||
' GROUP BY %s) AS t ' \
|
' GROUP BY %s) AS t ' \
|
||||||
'GROUP BY t.grandparent_title ' \
|
'GROUP BY t.grandparent_title ' \
|
||||||
'ORDER BY %s DESC ' \
|
'ORDER BY %s DESC, started DESC ' \
|
||||||
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -436,7 +436,7 @@ class DataFactory(object):
|
||||||
popular_music = []
|
popular_music = []
|
||||||
try:
|
try:
|
||||||
query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \
|
query = 'SELECT t.id, t.grandparent_title, t.grandparent_rating_key, t.grandparent_thumb, t.section_id, ' \
|
||||||
't.media_type, t.content_rating, t.labels, ' \
|
't.media_type, t.content_rating, t.labels, t.started, ' \
|
||||||
'COUNT(DISTINCT t.user_id) AS users_watched, ' \
|
'COUNT(DISTINCT t.user_id) AS users_watched, ' \
|
||||||
'MAX(t.started) AS last_watch, COUNT(t.id) as total_plays, SUM(t.d) AS total_duration ' \
|
'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) - ' \
|
'FROM (SELECT *, SUM(CASE WHEN stopped > 0 THEN (stopped - started) - ' \
|
||||||
|
@ -449,7 +449,7 @@ class DataFactory(object):
|
||||||
' AND session_history.media_type = "track" ' \
|
' AND session_history.media_type = "track" ' \
|
||||||
' GROUP BY %s) AS t ' \
|
' GROUP BY %s) AS t ' \
|
||||||
'GROUP BY t.grandparent_title ' \
|
'GROUP BY t.grandparent_title ' \
|
||||||
'ORDER BY users_watched DESC, %s DESC ' \
|
'ORDER BY users_watched DESC, %s DESC, started DESC ' \
|
||||||
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -482,7 +482,7 @@ class DataFactory(object):
|
||||||
elif stat == 'top_users':
|
elif stat == 'top_users':
|
||||||
top_users = []
|
top_users = []
|
||||||
try:
|
try:
|
||||||
query = 'SELECT t.user, t.user_id, t.user_thumb, t.custom_thumb, ' \
|
query = 'SELECT t.user, t.user_id, t.user_thumb, t.custom_thumb, t.started, ' \
|
||||||
'(CASE WHEN t.friendly_name IS NULL THEN t.username ELSE t.friendly_name END) ' \
|
'(CASE WHEN t.friendly_name IS NULL THEN t.username ELSE t.friendly_name END) ' \
|
||||||
' AS friendly_name, ' \
|
' AS friendly_name, ' \
|
||||||
'MAX(t.started) AS last_watch, COUNT(t.id) AS total_plays, SUM(t.d) AS total_duration ' \
|
'MAX(t.started) AS last_watch, COUNT(t.id) AS total_plays, SUM(t.d) AS total_duration ' \
|
||||||
|
@ -496,7 +496,7 @@ class DataFactory(object):
|
||||||
' >= datetime("now", "-%s days", "localtime") ' \
|
' >= datetime("now", "-%s days", "localtime") ' \
|
||||||
' GROUP BY %s) AS t ' \
|
' GROUP BY %s) AS t ' \
|
||||||
'GROUP BY t.user_id ' \
|
'GROUP BY t.user_id ' \
|
||||||
'ORDER BY %s DESC ' \
|
'ORDER BY %s DESC, started DESC ' \
|
||||||
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -536,7 +536,7 @@ class DataFactory(object):
|
||||||
top_platform = []
|
top_platform = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
query = 'SELECT t.platform, ' \
|
query = 'SELECT t.platform, t.started, ' \
|
||||||
'MAX(t.started) AS last_watch, COUNT(t.id) AS total_plays, SUM(t.d) AS total_duration ' \
|
'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) - ' \
|
'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) ' \
|
' (CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) ELSE 0 END) ' \
|
||||||
|
@ -547,7 +547,7 @@ class DataFactory(object):
|
||||||
' >= datetime("now", "-%s days", "localtime") ' \
|
' >= datetime("now", "-%s days", "localtime") ' \
|
||||||
' GROUP BY %s) AS t ' \
|
' GROUP BY %s) AS t ' \
|
||||||
'GROUP BY t.platform ' \
|
'GROUP BY t.platform ' \
|
||||||
'ORDER BY %s DESC ' \
|
'ORDER BY %s DESC, started DESC ' \
|
||||||
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
||||||
result = monitor_db.select(query)
|
result = monitor_db.select(query)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
@ -786,12 +786,13 @@ class XBMC(object):
|
||||||
raise Exception
|
raise Exception
|
||||||
else:
|
else:
|
||||||
logger.info(u"PlexPy Notifiers :: XBMC notification sent.")
|
logger.info(u"PlexPy Notifiers :: XBMC notification sent.")
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.warn(u"PlexPy Notifiers :: XBMC notification filed.")
|
logger.warn(u"PlexPy Notifiers :: XBMC notification failed.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def return_config_options(self):
|
def return_config_options(self):
|
||||||
config_option = [{'label': 'XBMC Host:Port',
|
config_option = [{'label': 'XBMC Host:Port',
|
||||||
'value': self.hosts,
|
'value': self.hosts,
|
||||||
|
@ -870,11 +871,12 @@ class Plex(object):
|
||||||
raise Exception
|
raise Exception
|
||||||
else:
|
else:
|
||||||
logger.info(u"PlexPy Notifiers :: Plex Home Theater notification sent.")
|
logger.info(u"PlexPy Notifiers :: Plex Home Theater notification sent.")
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.warn(u"PlexPy Notifiers :: Plex Home Theater notification filed.")
|
logger.warn(u"PlexPy Notifiers :: Plex Home Theater notification failed.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def return_config_options(self):
|
def return_config_options(self):
|
||||||
config_option = [{'label': 'Plex Home Theater Host:Port',
|
config_option = [{'label': 'Plex Home Theater Host:Port',
|
||||||
|
@ -2567,18 +2569,12 @@ class JOIN(object):
|
||||||
'title': subject.encode("utf-8"),
|
'title': subject.encode("utf-8"),
|
||||||
'text': message.encode("utf-8")}
|
'text': message.encode("utf-8")}
|
||||||
|
|
||||||
http_handler = HTTPSConnection("joinjoaomgcd.appspot.com")
|
response = requests.post('https://joinjoaomgcd.appspot.com/_ah/api/messaging/v1/sendPush',
|
||||||
http_handler.request("POST",
|
params=data)
|
||||||
"/_ah/api/messaging/v1/sendPush?%s" % urlencode(data))
|
request_status = response.status_code
|
||||||
|
|
||||||
response = http_handler.getresponse()
|
|
||||||
request_status = response.status
|
|
||||||
# logger.debug(u"PushBullet response status: %r" % request_status)
|
|
||||||
# logger.debug(u"PushBullet response headers: %r" % response.getheaders())
|
|
||||||
# logger.debug(u"PushBullet response body: %r" % response.read())
|
|
||||||
|
|
||||||
if request_status == 200:
|
if request_status == 200:
|
||||||
data = json.loads(response.read())
|
data = json.loads(response.text)
|
||||||
if data.get('success'):
|
if data.get('success'):
|
||||||
logger.info(u"PlexPy Notifiers :: Join notification sent.")
|
logger.info(u"PlexPy Notifiers :: Join notification sent.")
|
||||||
return True
|
return True
|
||||||
|
@ -2632,7 +2628,10 @@ class JOIN(object):
|
||||||
return {'': ''}
|
return {'': ''}
|
||||||
|
|
||||||
def return_config_options(self):
|
def return_config_options(self):
|
||||||
devices = '<br>'.join(['%s: %s' % (v, k) for k, v in self.get_devices().iteritems() if k])
|
devices = '<br>'.join(['%s: <span class="inline-pre">%s</span>'
|
||||||
|
% (v, k) for k, v in self.get_devices().iteritems() if k])
|
||||||
|
if not devices:
|
||||||
|
devices = 'Enter your Join API key to load your device list.'
|
||||||
|
|
||||||
config_option = [{'label': 'Join API Key',
|
config_option = [{'label': 'Join API Key',
|
||||||
'value': self.apikey,
|
'value': self.apikey,
|
||||||
|
|
|
@ -179,7 +179,7 @@ class PmsConnect(object):
|
||||||
|
|
||||||
return request
|
return request
|
||||||
|
|
||||||
def get_recently_added(self, count='0', output_format=''):
|
def get_recently_added(self, start='0', count='0', output_format=''):
|
||||||
"""
|
"""
|
||||||
Return list of recently added items.
|
Return list of recently added items.
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ class PmsConnect(object):
|
||||||
|
|
||||||
Output: array
|
Output: array
|
||||||
"""
|
"""
|
||||||
uri = '/library/recentlyAdded?X-Plex-Container-Start=0&X-Plex-Container-Size=' + count
|
uri = '/library/recentlyAdded?X-Plex-Container-Start=%s&X-Plex-Container-Size=%s' % (start, count)
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
proto=self.protocol,
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
|
@ -196,7 +196,7 @@ class PmsConnect(object):
|
||||||
|
|
||||||
return request
|
return request
|
||||||
|
|
||||||
def get_library_recently_added(self, section_id='', count='0', output_format=''):
|
def get_library_recently_added(self, section_id='', start='0', count='0', output_format=''):
|
||||||
"""
|
"""
|
||||||
Return list of recently added items.
|
Return list of recently added items.
|
||||||
|
|
||||||
|
@ -205,7 +205,7 @@ class PmsConnect(object):
|
||||||
|
|
||||||
Output: array
|
Output: array
|
||||||
"""
|
"""
|
||||||
uri = '/library/sections/' + section_id + '/recentlyAdded?X-Plex-Container-Start=0&X-Plex-Container-Size=' + count
|
uri = '/library/sections/%s/recentlyAdded?X-Plex-Container-Start=%s&X-Plex-Container-Size=%s' % (section_id, start, count)
|
||||||
request = self.request_handler.make_request(uri=uri,
|
request = self.request_handler.make_request(uri=uri,
|
||||||
proto=self.protocol,
|
proto=self.protocol,
|
||||||
request_type='GET',
|
request_type='GET',
|
||||||
|
@ -458,7 +458,7 @@ class PmsConnect(object):
|
||||||
|
|
||||||
return request
|
return request
|
||||||
|
|
||||||
def get_recently_added_details(self, section_id='', count='0'):
|
def get_recently_added_details(self, section_id='', start='0', count='0'):
|
||||||
"""
|
"""
|
||||||
Return processed and validated list of recently added items.
|
Return processed and validated list of recently added items.
|
||||||
|
|
||||||
|
@ -467,9 +467,9 @@ class PmsConnect(object):
|
||||||
Output: array
|
Output: array
|
||||||
"""
|
"""
|
||||||
if section_id:
|
if section_id:
|
||||||
recent = self.get_library_recently_added(section_id, count, output_format='xml')
|
recent = self.get_library_recently_added(section_id, start, count, output_format='xml')
|
||||||
else:
|
else:
|
||||||
recent = self.get_recently_added(count, output_format='xml')
|
recent = self.get_recently_added(start, count, output_format='xml')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
xml_head = recent.getElementsByTagName('MediaContainer')
|
xml_head = recent.getElementsByTagName('MediaContainer')
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
PLEXPY_VERSION = "master"
|
PLEXPY_VERSION = "master"
|
||||||
PLEXPY_RELEASE_VERSION = "1.4.0"
|
PLEXPY_RELEASE_VERSION = "1.4.1"
|
||||||
|
|
|
@ -36,7 +36,7 @@ from plexpy.plextv import PlexTV
|
||||||
SESSION_KEY = '_cp_username'
|
SESSION_KEY = '_cp_username'
|
||||||
|
|
||||||
def user_login(username=None, password=None):
|
def user_login(username=None, password=None):
|
||||||
if not username and not password:
|
if not username or not password:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Try to login to Plex.tv to check if the user has a vaild account
|
# Try to login to Plex.tv to check if the user has a vaild account
|
||||||
|
@ -119,7 +119,7 @@ def check_auth(*args, **kwargs):
|
||||||
if not condition():
|
if not condition():
|
||||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT)
|
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT)
|
||||||
else:
|
else:
|
||||||
raise cherrypy.HTTPRedirect("auth/logout")
|
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "auth/logout")
|
||||||
|
|
||||||
def requireAuth(*conditions):
|
def requireAuth(*conditions):
|
||||||
"""A decorator that appends conditions to the auth.require config
|
"""A decorator that appends conditions to the auth.require config
|
||||||
|
@ -204,14 +204,14 @@ class AuthController(object):
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def index(self):
|
def index(self):
|
||||||
raise cherrypy.HTTPRedirect("login")
|
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "auth/login")
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def login(self, username=None, password=None, remember_me='0', admin_login='0'):
|
def login(self, username=None, password=None, remember_me='0', admin_login='0'):
|
||||||
if not cherrypy.config.get('tools.sessions.on'):
|
if not cherrypy.config.get('tools.sessions.on'):
|
||||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT)
|
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT)
|
||||||
|
|
||||||
if username is None or password is None:
|
if not username and not password:
|
||||||
return self.get_loginform()
|
return self.get_loginform()
|
||||||
|
|
||||||
(vaild_login, user_group) = check_credentials(username, password, admin_login)
|
(vaild_login, user_group) = check_credentials(username, password, admin_login)
|
||||||
|
@ -257,4 +257,4 @@ class AuthController(object):
|
||||||
if _session and _session['user']:
|
if _session and _session['user']:
|
||||||
cherrypy.request.login = None
|
cherrypy.request.login = None
|
||||||
self.on_logout(_session['user'], _session['user_group'])
|
self.on_logout(_session['user'], _session['user_group'])
|
||||||
raise cherrypy.HTTPRedirect("login")
|
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "auth/login")
|
|
@ -82,9 +82,9 @@ class WebInterface(object):
|
||||||
@requireAuth()
|
@requireAuth()
|
||||||
def index(self):
|
def index(self):
|
||||||
if plexpy.CONFIG.FIRST_RUN_COMPLETE:
|
if plexpy.CONFIG.FIRST_RUN_COMPLETE:
|
||||||
raise cherrypy.HTTPRedirect("home")
|
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "home")
|
||||||
else:
|
else:
|
||||||
raise cherrypy.HTTPRedirect("welcome")
|
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "welcome")
|
||||||
|
|
||||||
|
|
||||||
##### Welcome #####
|
##### Welcome #####
|
||||||
|
@ -118,7 +118,7 @@ class WebInterface(object):
|
||||||
# The setup wizard just refreshes the page on submit so we must redirect to home if config set.
|
# The setup wizard just refreshes the page on submit so we must redirect to home if config set.
|
||||||
if plexpy.CONFIG.FIRST_RUN_COMPLETE:
|
if plexpy.CONFIG.FIRST_RUN_COMPLETE:
|
||||||
plexpy.initialize_scheduler()
|
plexpy.initialize_scheduler()
|
||||||
raise cherrypy.HTTPRedirect("home")
|
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "home")
|
||||||
else:
|
else:
|
||||||
return serve_template(templatename="welcome.html", title="Welcome", config=config)
|
return serve_template(templatename="welcome.html", title="Welcome", config=config)
|
||||||
|
|
||||||
|
@ -1170,7 +1170,7 @@ class WebInterface(object):
|
||||||
@requireAuth()
|
@requireAuth()
|
||||||
@addtoapi()
|
@addtoapi()
|
||||||
def get_user_logins(self, user_id=None, **kwargs):
|
def get_user_logins(self, user_id=None, **kwargs):
|
||||||
""" Get the data on PlexPy user login table.
|
""" Get the data on PlexPy user login table.
|
||||||
|
|
||||||
```
|
```
|
||||||
Required parameters:
|
Required parameters:
|
||||||
|
@ -1189,15 +1189,15 @@ class WebInterface(object):
|
||||||
"recordsTotal": 2344,
|
"recordsTotal": 2344,
|
||||||
"recordsFiltered": 10,
|
"recordsFiltered": 10,
|
||||||
"data":
|
"data":
|
||||||
[{"browser": "Safari 7.0.3",
|
[{"browser": "Safari 7.0.3",
|
||||||
"friendly_name": "Jon Snow",
|
"friendly_name": "Jon Snow",
|
||||||
"host": "http://plexpy.castleblack.com",
|
"host": "http://plexpy.castleblack.com",
|
||||||
"ip_address": "xxx.xxx.xxx.xxx",
|
"ip_address": "xxx.xxx.xxx.xxx",
|
||||||
"os": "Mac OS X",
|
"os": "Mac OS X",
|
||||||
"timestamp": 1462591869,
|
"timestamp": 1462591869,
|
||||||
"user": "LordCommanderSnow",
|
"user": "LordCommanderSnow",
|
||||||
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A",
|
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A",
|
||||||
"user_group": "guest",
|
"user_group": "guest",
|
||||||
"user_id": 133788
|
"user_id": 133788
|
||||||
},
|
},
|
||||||
{...},
|
{...},
|
||||||
|
@ -1987,24 +1987,14 @@ class WebInterface(object):
|
||||||
def getLog(self, start=0, length=100, **kwargs):
|
def getLog(self, start=0, length=100, **kwargs):
|
||||||
start = int(start)
|
start = int(start)
|
||||||
length = int(length)
|
length = int(length)
|
||||||
search_value = ""
|
order_dir = kwargs.get('order[0][dir]', "desc")
|
||||||
search_regex = ""
|
order_column = kwargs.get('order[0][column]', "0")
|
||||||
order_column = 0
|
search_value = kwargs.get('search[value]', "")
|
||||||
order_dir = "desc"
|
search_regex = kwargs.get('search[regex]', "") # Remove?
|
||||||
|
sortcolumn = 0
|
||||||
if 'order[0][dir]' in kwargs:
|
|
||||||
order_dir = kwargs.get('order[0][dir]', "desc")
|
|
||||||
|
|
||||||
if 'order[0][column]' in kwargs:
|
|
||||||
order_column = kwargs.get('order[0][column]', "0")
|
|
||||||
|
|
||||||
if 'search[value]' in kwargs:
|
|
||||||
search_value = kwargs.get('search[value]', "")
|
|
||||||
|
|
||||||
if 'search[regex]' in kwargs:
|
|
||||||
search_regex = kwargs.get('search[regex]', "")
|
|
||||||
|
|
||||||
filt = []
|
filt = []
|
||||||
|
filtered = []
|
||||||
fa = filt.append
|
fa = filt.append
|
||||||
with open(os.path.join(plexpy.CONFIG.LOG_DIR, logger.FILENAME)) as f:
|
with open(os.path.join(plexpy.CONFIG.LOG_DIR, logger.FILENAME)) as f:
|
||||||
for l in f.readlines():
|
for l in f.readlines():
|
||||||
|
@ -2017,22 +2007,24 @@ class WebInterface(object):
|
||||||
# Add traceback message to previous msg.
|
# Add traceback message to previous msg.
|
||||||
tl = (len(filt) - 1)
|
tl = (len(filt) - 1)
|
||||||
n = len(l) - len(l.lstrip(' '))
|
n = len(l) - len(l.lstrip(' '))
|
||||||
l = ' ' * (2*n) + l[n:]
|
l = ' ' * (2 * n) + l[n:]
|
||||||
filt[tl][2] += '<br>' + l
|
filt[tl][2] += '<br>' + l
|
||||||
continue
|
continue
|
||||||
|
|
||||||
filtered = []
|
|
||||||
if search_value == '':
|
if search_value == '':
|
||||||
filtered = filt
|
filtered = filt
|
||||||
else:
|
else:
|
||||||
filtered = [row for row in filt for column in row if search_value.lower() in column.lower()]
|
filtered = [row for row in filt for column in row if search_value.lower() in column.lower()]
|
||||||
|
|
||||||
sortcolumn = 0
|
|
||||||
if order_column == '1':
|
if order_column == '1':
|
||||||
sortcolumn = 2
|
sortcolumn = 2
|
||||||
elif order_column == '2':
|
elif order_column == '2':
|
||||||
sortcolumn = 1
|
sortcolumn = 1
|
||||||
filtered.sort(key=lambda x: x[sortcolumn], reverse=order_dir == "desc")
|
|
||||||
|
filtered.sort(key=lambda x: x[sortcolumn])
|
||||||
|
|
||||||
|
if order_dir == 'desc':
|
||||||
|
filtered = filtered[::-1]
|
||||||
|
|
||||||
rows = filtered[start:(start + length)]
|
rows = filtered[start:(start + length)]
|
||||||
|
|
||||||
|
@ -2215,7 +2207,7 @@ class WebInterface(object):
|
||||||
log_dir=plexpy.CONFIG.LOG_DIR, verbose=plexpy.VERBOSE)
|
log_dir=plexpy.CONFIG.LOG_DIR, verbose=plexpy.VERBOSE)
|
||||||
logger.info(u"Verbose toggled, set to %s", plexpy.VERBOSE)
|
logger.info(u"Verbose toggled, set to %s", plexpy.VERBOSE)
|
||||||
logger.debug(u"If you read this message, debug logging is available")
|
logger.debug(u"If you read this message, debug logging is available")
|
||||||
raise cherrypy.HTTPRedirect("logs")
|
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "logs")
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@requireAuth()
|
@requireAuth()
|
||||||
|
@ -2262,6 +2254,7 @@ class WebInterface(object):
|
||||||
"http_port": plexpy.CONFIG.HTTP_PORT,
|
"http_port": plexpy.CONFIG.HTTP_PORT,
|
||||||
"http_password": http_password,
|
"http_password": http_password,
|
||||||
"http_root": plexpy.CONFIG.HTTP_ROOT,
|
"http_root": plexpy.CONFIG.HTTP_ROOT,
|
||||||
|
"http_proxy": checked(plexpy.CONFIG.HTTP_PROXY),
|
||||||
"launch_browser": checked(plexpy.CONFIG.LAUNCH_BROWSER),
|
"launch_browser": checked(plexpy.CONFIG.LAUNCH_BROWSER),
|
||||||
"enable_https": checked(plexpy.CONFIG.ENABLE_HTTPS),
|
"enable_https": checked(plexpy.CONFIG.ENABLE_HTTPS),
|
||||||
"https_create_cert": checked(plexpy.CONFIG.HTTPS_CREATE_CERT),
|
"https_create_cert": checked(plexpy.CONFIG.HTTPS_CREATE_CERT),
|
||||||
|
@ -2374,7 +2367,7 @@ class WebInterface(object):
|
||||||
"ip_logging_enable", "movie_logging_enable", "tv_logging_enable", "music_logging_enable",
|
"ip_logging_enable", "movie_logging_enable", "tv_logging_enable", "music_logging_enable",
|
||||||
"notify_consecutive", "notify_upload_posters", "notify_recently_added", "notify_recently_added_grandparent",
|
"notify_consecutive", "notify_upload_posters", "notify_recently_added", "notify_recently_added_grandparent",
|
||||||
"monitor_pms_updates", "monitor_remote_access", "get_file_sizes", "log_blacklist", "http_hash_password",
|
"monitor_pms_updates", "monitor_remote_access", "get_file_sizes", "log_blacklist", "http_hash_password",
|
||||||
"allow_guest_access", "cache_images"
|
"allow_guest_access", "cache_images", "http_proxy"
|
||||||
]
|
]
|
||||||
for checked_config in checked_configs:
|
for checked_config in checked_configs:
|
||||||
if checked_config not in kwargs:
|
if checked_config not in kwargs:
|
||||||
|
@ -2857,7 +2850,7 @@ class WebInterface(object):
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
def checkGithub(self):
|
def checkGithub(self):
|
||||||
versioncheck.checkGithub()
|
versioncheck.checkGithub()
|
||||||
raise cherrypy.HTTPRedirect("home")
|
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "home")
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
|
@ -3336,7 +3329,7 @@ class WebInterface(object):
|
||||||
@cherrypy.tools.json_out()
|
@cherrypy.tools.json_out()
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
@addtoapi("get_recently_added")
|
@addtoapi("get_recently_added")
|
||||||
def get_recently_added_details(self, count='0', section_id='', **kwargs):
|
def get_recently_added_details(self, start='0', count='0', section_id='', **kwargs):
|
||||||
""" Get all items that where recelty added to plex.
|
""" Get all items that where recelty added to plex.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -3344,6 +3337,7 @@ class WebInterface(object):
|
||||||
count (str): Number of items to return
|
count (str): Number of items to return
|
||||||
|
|
||||||
Optional parameters:
|
Optional parameters:
|
||||||
|
start (str): The item number to start at
|
||||||
section_id (str): The id of the Plex library section
|
section_id (str): The id of the Plex library section
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -3373,7 +3367,7 @@ class WebInterface(object):
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
pms_connect = pmsconnect.PmsConnect()
|
pms_connect = pmsconnect.PmsConnect()
|
||||||
result = pms_connect.get_recently_added_details(count=count, section_id=section_id)
|
result = pms_connect.get_recently_added_details(start=start, count=count, section_id=section_id)
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
return result
|
return result
|
||||||
|
@ -3618,13 +3612,13 @@ class WebInterface(object):
|
||||||
pms_connect = pmsconnect.PmsConnect(token=plexpy.CONFIG.PMS_TOKEN)
|
pms_connect = pmsconnect.PmsConnect(token=plexpy.CONFIG.PMS_TOKEN)
|
||||||
result = pms_connect.get_current_activity()
|
result = pms_connect.get_current_activity()
|
||||||
|
|
||||||
data_factory = datafactory.DataFactory()
|
|
||||||
for session in result['sessions']:
|
|
||||||
if not session['ip_address']:
|
|
||||||
ip_address = data_factory.get_session_ip(session['session_key'])
|
|
||||||
session['ip_address'] = ip_address
|
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
|
data_factory = datafactory.DataFactory()
|
||||||
|
for session in result['sessions']:
|
||||||
|
if not session['ip_address']:
|
||||||
|
ip_address = data_factory.get_session_ip(session['session_key'])
|
||||||
|
session['ip_address'] = ip_address
|
||||||
|
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
logger.warn(u"Unable to retrieve data for get_activity.")
|
logger.warn(u"Unable to retrieve data for get_activity.")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue