mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-15 09:42:57 -07:00
Merge branch 'dev'
This commit is contained in:
commit
c3378e1653
19 changed files with 169 additions and 125 deletions
1
API.md
1
API.md
|
@ -1061,6 +1061,7 @@ Required parameters:
|
|||
count (str): Number of items to return
|
||||
|
||||
Optional parameters:
|
||||
start (str): The item number to start at
|
||||
section_id (str): The id of the Plex library section
|
||||
|
||||
Returns:
|
||||
|
|
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -1,5 +1,25 @@
|
|||
# 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)
|
||||
|
||||
* New: An HTML form login page with sessions support.
|
||||
|
|
|
@ -2955,3 +2955,13 @@ a.no-highlight:hover {
|
|||
min-width: 150px;
|
||||
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:
|
||||
<a href="#">
|
||||
% 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 (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>
|
||||
|
@ -105,7 +105,7 @@ DOCUMENTATION :: END
|
|||
<div class="dashboard-activity-poster-face" style="background-image: url(${data['art']});"></div>
|
||||
% endif
|
||||
<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>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -107,6 +107,11 @@
|
|||
$('#dashboard-checking-activity').remove();
|
||||
|
||||
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 sessions = current_activity.sessions;
|
||||
|
||||
|
@ -150,6 +155,7 @@
|
|||
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='
|
||||
+ s.bif_thumb + '&width=500&height=280&fallback=art);"></div>').fadeIn(1000, function () { bif_poster.remove() }));
|
||||
blurArtwork(key);
|
||||
}
|
||||
|
||||
// if transcoding, update the transcode state
|
||||
|
@ -210,14 +216,18 @@
|
|||
getCurrentActivity();
|
||||
}, 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
|
||||
$('#currentActivity').on('click', '.btn-activity-info', function (e) {
|
||||
e.preventDefault();
|
||||
$($(this).attr('data-target')).toggle();
|
||||
var id = $(this).closest('.dashboard-instance').data('id');
|
||||
var filterVal = $('#stream-' + id).is(':visible') ? 'blur(5px)' : '';
|
||||
$($(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);
|
||||
var key = $(this).data('id');
|
||||
blurArtwork(key);
|
||||
});
|
||||
|
||||
// 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
|
||||
doAjaxCall('set_notification_config', $(this), 'tabs', true, reloadModal);
|
||||
return false;
|
||||
|
|
|
@ -109,6 +109,5 @@ DOCUMENTATION :: END
|
|||
</div>
|
||||
</div>
|
||||
% else:
|
||||
<div class="text-muted">There was an error communicating with your Plex Server. Please check your <a href="settings">settings</a>.
|
||||
</div><br>
|
||||
<div class="text-muted">There was an error communicating with your Plex Server.</div><br>
|
||||
% endif
|
|
@ -432,6 +432,13 @@
|
|||
</div>
|
||||
<p class="help-block">The base URL of the web server. Used for reverse proxies.</p>
|
||||
</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">
|
||||
<label>
|
||||
<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() {
|
||||
serverChanged = true;
|
||||
$("#pms_identifier").val("");
|
||||
$("#pms-verify-status").html("");
|
||||
$("#server_changed").prop('checked', true);
|
||||
verifyServer();
|
||||
});
|
||||
|
@ -2287,6 +2293,7 @@ $(document).ready(function() {
|
|||
}
|
||||
|
||||
$('#verify_server_button').on('click', function(){
|
||||
$("#pms_identifier").val("");
|
||||
verifyServer();
|
||||
});
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ DOCUMENTATION :: END
|
|||
<ul class="list-unstyled">
|
||||
<li>Container: <strong>${data['transcode_container'] if data['transcode_container'] else data['container']}</strong></li>
|
||||
% 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
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -101,7 +101,7 @@ DOCUMENTATION :: END
|
|||
<ul class="list-unstyled">
|
||||
<li>Container: <strong>${data['container']}</strong></li>
|
||||
% 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
|
||||
<li>Bitrate: <strong>${data['bitrate']} kbps</strong></li>
|
||||
</ul>
|
||||
|
|
|
@ -195,7 +195,7 @@ def proxy(base=None, local='X-Forwarded-Host', remote='X-Forwarded-For',
|
|||
if not base:
|
||||
base = request.headers.get('Host', '127.0.0.1')
|
||||
port = request.local.port
|
||||
if port != 80:
|
||||
if port != 80 and not base.endswith(':%s' % port):
|
||||
base += ':%s' % port
|
||||
|
||||
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
|
||||
# amount of time on your server. Measure with:
|
||||
# python -m timeit -s 'import passwords as p' 'p.make_hash("something")'
|
||||
COST_FACTOR = 29000
|
||||
COST_FACTOR = 10000
|
||||
|
||||
|
||||
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))
|
||||
for i in xrange(iterations - 1):
|
||||
u = _pseudorandom(''.join(map(chr, u)))
|
||||
rv = starmap(xor, izip(rv, u))
|
||||
rv = list(starmap(xor, izip(rv, u)))
|
||||
buf.extend(rv)
|
||||
return ''.join(map(chr, buf))[:keylen]
|
||||
|
||||
|
|
|
@ -220,25 +220,29 @@ class ActivityProcessor(object):
|
|||
|
||||
result = self.db.select(query=query, args=args)
|
||||
|
||||
new_session = prev_session = last_id = None
|
||||
if len(result) > 1:
|
||||
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:
|
||||
prev_session = None
|
||||
else:
|
||||
prev_session = {'id': result[1]['id'],
|
||||
'rating_key': result[1]['rating_key'],
|
||||
'view_offset': result[1]['view_offset'],
|
||||
'user_id': result[1]['user_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 = ? '
|
||||
# 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'] \
|
||||
and prev_session['view_offset'] <= new_session['view_offset']):
|
||||
if prev_session == new_session == None:
|
||||
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']]
|
||||
else:
|
||||
args = [new_session['id'], new_session['id']]
|
||||
|
|
|
@ -198,7 +198,7 @@ class DataFactory(object):
|
|||
top_tv = []
|
||||
try:
|
||||
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 ' \
|
||||
'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) ' \
|
||||
|
@ -210,7 +210,7 @@ class DataFactory(object):
|
|||
' AND session_history.media_type = "episode" ' \
|
||||
' GROUP BY %s) AS t ' \
|
||||
'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)
|
||||
result = monitor_db.select(query)
|
||||
except Exception as e:
|
||||
|
@ -246,7 +246,7 @@ class DataFactory(object):
|
|||
popular_tv = []
|
||||
try:
|
||||
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, ' \
|
||||
'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) - ' \
|
||||
|
@ -259,7 +259,7 @@ class DataFactory(object):
|
|||
' AND session_history.media_type = "episode" ' \
|
||||
' GROUP BY %s) AS t ' \
|
||||
'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)
|
||||
result = monitor_db.select(query)
|
||||
except Exception as e:
|
||||
|
@ -293,7 +293,7 @@ class DataFactory(object):
|
|||
top_movies = []
|
||||
try:
|
||||
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 ' \
|
||||
'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) ' \
|
||||
|
@ -305,7 +305,7 @@ class DataFactory(object):
|
|||
' AND session_history.media_type = "movie" ' \
|
||||
' GROUP BY %s) AS t ' \
|
||||
'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)
|
||||
result = monitor_db.select(query)
|
||||
except Exception as e:
|
||||
|
@ -341,7 +341,7 @@ class DataFactory(object):
|
|||
popular_movies = []
|
||||
try:
|
||||
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, ' \
|
||||
'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) - ' \
|
||||
|
@ -354,7 +354,7 @@ class DataFactory(object):
|
|||
' AND session_history.media_type = "movie" ' \
|
||||
' GROUP BY %s) AS t ' \
|
||||
'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)
|
||||
result = monitor_db.select(query)
|
||||
except Exception as e:
|
||||
|
@ -388,7 +388,7 @@ class DataFactory(object):
|
|||
top_music = []
|
||||
try:
|
||||
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 ' \
|
||||
'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) ' \
|
||||
|
@ -400,7 +400,7 @@ class DataFactory(object):
|
|||
' AND session_history.media_type = "track" ' \
|
||||
' GROUP BY %s) AS t ' \
|
||||
'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)
|
||||
result = monitor_db.select(query)
|
||||
except Exception as e:
|
||||
|
@ -436,7 +436,7 @@ class DataFactory(object):
|
|||
popular_music = []
|
||||
try:
|
||||
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, ' \
|
||||
'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) - ' \
|
||||
|
@ -449,7 +449,7 @@ class DataFactory(object):
|
|||
' AND session_history.media_type = "track" ' \
|
||||
' GROUP BY %s) AS t ' \
|
||||
'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)
|
||||
result = monitor_db.select(query)
|
||||
except Exception as e:
|
||||
|
@ -482,7 +482,7 @@ class DataFactory(object):
|
|||
elif stat == 'top_users':
|
||||
top_users = []
|
||||
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) ' \
|
||||
' AS friendly_name, ' \
|
||||
'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") ' \
|
||||
' GROUP BY %s) AS t ' \
|
||||
'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)
|
||||
result = monitor_db.select(query)
|
||||
except Exception as e:
|
||||
|
@ -536,7 +536,7 @@ class DataFactory(object):
|
|||
top_platform = []
|
||||
|
||||
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 ' \
|
||||
'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) ' \
|
||||
|
@ -547,7 +547,7 @@ class DataFactory(object):
|
|||
' >= datetime("now", "-%s days", "localtime") ' \
|
||||
' GROUP BY %s) AS t ' \
|
||||
'GROUP BY t.platform ' \
|
||||
'ORDER BY %s DESC ' \
|
||||
'ORDER BY %s DESC, started DESC ' \
|
||||
'LIMIT %s ' % (time_range, group_by, sort_type, stats_count)
|
||||
result = monitor_db.select(query)
|
||||
except Exception as e:
|
||||
|
|
|
@ -786,12 +786,13 @@ class XBMC(object):
|
|||
raise Exception
|
||||
else:
|
||||
logger.info(u"PlexPy Notifiers :: XBMC notification sent.")
|
||||
return True
|
||||
|
||||
except Exception:
|
||||
logger.warn(u"PlexPy Notifiers :: XBMC notification filed.")
|
||||
logger.warn(u"PlexPy Notifiers :: XBMC notification failed.")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def return_config_options(self):
|
||||
config_option = [{'label': 'XBMC Host:Port',
|
||||
'value': self.hosts,
|
||||
|
@ -870,12 +871,13 @@ class Plex(object):
|
|||
raise Exception
|
||||
else:
|
||||
logger.info(u"PlexPy Notifiers :: Plex Home Theater notification sent.")
|
||||
return True
|
||||
|
||||
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 True
|
||||
|
||||
def return_config_options(self):
|
||||
config_option = [{'label': 'Plex Home Theater Host:Port',
|
||||
'value': self.client_hosts,
|
||||
|
@ -2567,18 +2569,12 @@ class JOIN(object):
|
|||
'title': subject.encode("utf-8"),
|
||||
'text': message.encode("utf-8")}
|
||||
|
||||
http_handler = HTTPSConnection("joinjoaomgcd.appspot.com")
|
||||
http_handler.request("POST",
|
||||
"/_ah/api/messaging/v1/sendPush?%s" % urlencode(data))
|
||||
|
||||
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())
|
||||
response = requests.post('https://joinjoaomgcd.appspot.com/_ah/api/messaging/v1/sendPush',
|
||||
params=data)
|
||||
request_status = response.status_code
|
||||
|
||||
if request_status == 200:
|
||||
data = json.loads(response.read())
|
||||
data = json.loads(response.text)
|
||||
if data.get('success'):
|
||||
logger.info(u"PlexPy Notifiers :: Join notification sent.")
|
||||
return True
|
||||
|
@ -2632,7 +2628,10 @@ class JOIN(object):
|
|||
return {'': ''}
|
||||
|
||||
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',
|
||||
'value': self.apikey,
|
||||
|
|
|
@ -179,7 +179,7 @@ class PmsConnect(object):
|
|||
|
||||
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.
|
||||
|
||||
|
@ -188,7 +188,7 @@ class PmsConnect(object):
|
|||
|
||||
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,
|
||||
proto=self.protocol,
|
||||
request_type='GET',
|
||||
|
@ -196,7 +196,7 @@ class PmsConnect(object):
|
|||
|
||||
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.
|
||||
|
||||
|
@ -205,7 +205,7 @@ class PmsConnect(object):
|
|||
|
||||
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,
|
||||
proto=self.protocol,
|
||||
request_type='GET',
|
||||
|
@ -458,7 +458,7 @@ class PmsConnect(object):
|
|||
|
||||
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.
|
||||
|
||||
|
@ -467,9 +467,9 @@ class PmsConnect(object):
|
|||
Output: array
|
||||
"""
|
||||
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:
|
||||
recent = self.get_recently_added(count, output_format='xml')
|
||||
recent = self.get_recently_added(start, count, output_format='xml')
|
||||
|
||||
try:
|
||||
xml_head = recent.getElementsByTagName('MediaContainer')
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
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'
|
||||
|
||||
def user_login(username=None, password=None):
|
||||
if not username and not password:
|
||||
if not username or not password:
|
||||
return None
|
||||
|
||||
# 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():
|
||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT)
|
||||
else:
|
||||
raise cherrypy.HTTPRedirect("auth/logout")
|
||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "auth/logout")
|
||||
|
||||
def requireAuth(*conditions):
|
||||
"""A decorator that appends conditions to the auth.require config
|
||||
|
@ -204,14 +204,14 @@ class AuthController(object):
|
|||
|
||||
@cherrypy.expose
|
||||
def index(self):
|
||||
raise cherrypy.HTTPRedirect("login")
|
||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "auth/login")
|
||||
|
||||
@cherrypy.expose
|
||||
def login(self, username=None, password=None, remember_me='0', admin_login='0'):
|
||||
if not cherrypy.config.get('tools.sessions.on'):
|
||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT)
|
||||
|
||||
if username is None or password is None:
|
||||
if not username and not password:
|
||||
return self.get_loginform()
|
||||
|
||||
(vaild_login, user_group) = check_credentials(username, password, admin_login)
|
||||
|
@ -257,4 +257,4 @@ class AuthController(object):
|
|||
if _session and _session['user']:
|
||||
cherrypy.request.login = None
|
||||
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()
|
||||
def index(self):
|
||||
if plexpy.CONFIG.FIRST_RUN_COMPLETE:
|
||||
raise cherrypy.HTTPRedirect("home")
|
||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "home")
|
||||
else:
|
||||
raise cherrypy.HTTPRedirect("welcome")
|
||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "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.
|
||||
if plexpy.CONFIG.FIRST_RUN_COMPLETE:
|
||||
plexpy.initialize_scheduler()
|
||||
raise cherrypy.HTTPRedirect("home")
|
||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "home")
|
||||
else:
|
||||
return serve_template(templatename="welcome.html", title="Welcome", config=config)
|
||||
|
||||
|
@ -1987,24 +1987,14 @@ class WebInterface(object):
|
|||
def getLog(self, start=0, length=100, **kwargs):
|
||||
start = int(start)
|
||||
length = int(length)
|
||||
search_value = ""
|
||||
search_regex = ""
|
||||
order_column = 0
|
||||
order_dir = "desc"
|
||||
|
||||
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]', "")
|
||||
search_regex = kwargs.get('search[regex]', "") # Remove?
|
||||
sortcolumn = 0
|
||||
|
||||
filt = []
|
||||
filtered = []
|
||||
fa = filt.append
|
||||
with open(os.path.join(plexpy.CONFIG.LOG_DIR, logger.FILENAME)) as f:
|
||||
for l in f.readlines():
|
||||
|
@ -2017,22 +2007,24 @@ class WebInterface(object):
|
|||
# Add traceback message to previous msg.
|
||||
tl = (len(filt) - 1)
|
||||
n = len(l) - len(l.lstrip(' '))
|
||||
l = ' ' * (2*n) + l[n:]
|
||||
l = ' ' * (2 * n) + l[n:]
|
||||
filt[tl][2] += '<br>' + l
|
||||
continue
|
||||
|
||||
filtered = []
|
||||
if search_value == '':
|
||||
filtered = filt
|
||||
else:
|
||||
filtered = [row for row in filt for column in row if search_value.lower() in column.lower()]
|
||||
|
||||
sortcolumn = 0
|
||||
if order_column == '1':
|
||||
sortcolumn = 2
|
||||
elif order_column == '2':
|
||||
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)]
|
||||
|
||||
|
@ -2215,7 +2207,7 @@ class WebInterface(object):
|
|||
log_dir=plexpy.CONFIG.LOG_DIR, verbose=plexpy.VERBOSE)
|
||||
logger.info(u"Verbose toggled, set to %s", plexpy.VERBOSE)
|
||||
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
|
||||
@requireAuth()
|
||||
|
@ -2262,6 +2254,7 @@ class WebInterface(object):
|
|||
"http_port": plexpy.CONFIG.HTTP_PORT,
|
||||
"http_password": http_password,
|
||||
"http_root": plexpy.CONFIG.HTTP_ROOT,
|
||||
"http_proxy": checked(plexpy.CONFIG.HTTP_PROXY),
|
||||
"launch_browser": checked(plexpy.CONFIG.LAUNCH_BROWSER),
|
||||
"enable_https": checked(plexpy.CONFIG.ENABLE_HTTPS),
|
||||
"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",
|
||||
"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",
|
||||
"allow_guest_access", "cache_images"
|
||||
"allow_guest_access", "cache_images", "http_proxy"
|
||||
]
|
||||
for checked_config in checked_configs:
|
||||
if checked_config not in kwargs:
|
||||
|
@ -2857,7 +2850,7 @@ class WebInterface(object):
|
|||
@requireAuth(member_of("admin"))
|
||||
def checkGithub(self):
|
||||
versioncheck.checkGithub()
|
||||
raise cherrypy.HTTPRedirect("home")
|
||||
raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "home")
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
|
@ -3336,7 +3329,7 @@ class WebInterface(object):
|
|||
@cherrypy.tools.json_out()
|
||||
@requireAuth(member_of("admin"))
|
||||
@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.
|
||||
|
||||
```
|
||||
|
@ -3344,6 +3337,7 @@ class WebInterface(object):
|
|||
count (str): Number of items to return
|
||||
|
||||
Optional parameters:
|
||||
start (str): The item number to start at
|
||||
section_id (str): The id of the Plex library section
|
||||
|
||||
Returns:
|
||||
|
@ -3373,7 +3367,7 @@ class WebInterface(object):
|
|||
```
|
||||
"""
|
||||
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:
|
||||
return result
|
||||
|
@ -3618,13 +3612,13 @@ class WebInterface(object):
|
|||
pms_connect = pmsconnect.PmsConnect(token=plexpy.CONFIG.PMS_TOKEN)
|
||||
result = pms_connect.get_current_activity()
|
||||
|
||||
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
|
||||
|
||||
if result:
|
||||
return result
|
||||
else:
|
||||
logger.warn(u"Unable to retrieve data for get_activity.")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue