diff --git a/API.md b/API.md index 427dbc6b..9a62bc95 100644 --- a/API.md +++ b/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: @@ -1357,7 +1358,7 @@ Returns: ### get_user_logins -Get the data on PlexPy user login table. +Get the data on PlexPy user login table. ``` Required parameters: @@ -1376,15 +1377,15 @@ Returns: "recordsTotal": 2344, "recordsFiltered": 10, "data": - [{"browser": "Safari 7.0.3", - "friendly_name": "Jon Snow", - "host": "http://plexpy.castleblack.com", - "ip_address": "xxx.xxx.xxx.xxx", - "os": "Mac OS X", - "timestamp": 1462591869, - "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_group": "guest", + [{"browser": "Safari 7.0.3", + "friendly_name": "Jon Snow", + "host": "http://plexpy.castleblack.com", + "ip_address": "xxx.xxx.xxx.xxx", + "os": "Mac OS X", + "timestamp": 1462591869, + "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_group": "guest", "user_id": 133788 }, {...}, diff --git a/CHANGELOG.md b/CHANGELOG.md index 95712e2b..6ac584ed 100644 --- a/CHANGELOG.md +++ b/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. diff --git a/data/interfaces/default/css/plexpy.css b/data/interfaces/default/css/plexpy.css index e40415e7..ffc79a39 100644 --- a/data/interfaces/default/css/plexpy.css +++ b/data/interfaces/default/css/plexpy.css @@ -2954,4 +2954,14 @@ a.no-highlight:hover { .datatable-wrap { 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; } \ No newline at end of file diff --git a/data/interfaces/default/current_activity_instance.html b/data/interfaces/default/current_activity_instance.html index 5a51b99b..9e69faff 100644 --- a/data/interfaces/default/current_activity_instance.html +++ b/data/interfaces/default/current_activity_instance.html @@ -71,7 +71,7 @@ DOCUMENTATION :: END % else: % endif -
+
% 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']):
@@ -105,7 +105,7 @@ DOCUMENTATION :: END
% endif
-
diff --git a/data/interfaces/default/index.html b/data/interfaces/default/index.html index 36d2887e..4100048b 100644 --- a/data/interfaces/default/index.html +++ b/data/interfaces/default/index.html @@ -107,6 +107,11 @@ $('#dashboard-checking-activity').remove(); var current_activity = $.parseJSON(xhr.responseText); + if (!(current_activity)) { + $('#currentActivity').html('
There was an error communicating with your Plex Server.
'); + 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($('
').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 diff --git a/data/interfaces/default/notification_config.html b/data/interfaces/default/notification_config.html index d56049b9..35f43fa6 100644 --- a/data/interfaces/default/notification_config.html +++ b/data/interfaces/default/notification_config.html @@ -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; diff --git a/data/interfaces/default/recently_added.html b/data/interfaces/default/recently_added.html index a1f6440a..6f3a2a59 100644 --- a/data/interfaces/default/recently_added.html +++ b/data/interfaces/default/recently_added.html @@ -109,6 +109,5 @@ DOCUMENTATION :: END
% else: -
There was an error communicating with your Plex Server. Please check your settings. -

+
There was an error communicating with your Plex Server.

% endif \ No newline at end of file diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index 9d1f54d2..169ab3d6 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -432,6 +432,13 @@

The base URL of the web server. Used for reverse proxies.

+
+ +

Respect the X-Forwarded-Proto header. Used for reverse proxies with SSL.

+
+
@@ -101,7 +101,7 @@ DOCUMENTATION :: END diff --git a/lib/cherrypy/lib/cptools.py b/lib/cherrypy/lib/cptools.py index 9be571ba..4c8991f8 100644 --- a/lib/cherrypy/lib/cptools.py +++ b/lib/cherrypy/lib/cptools.py @@ -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: diff --git a/lib/hashing_passwords.py b/lib/hashing_passwords.py index 1c2c963b..1b86ec4e 100644 --- a/lib/hashing_passwords.py +++ b/lib/hashing_passwords.py @@ -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): diff --git a/lib/pbkdf2.py b/lib/pbkdf2.py index b7a7dd42..29455db2 100644 --- a/lib/pbkdf2.py +++ b/lib/pbkdf2.py @@ -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] diff --git a/plexpy/activity_processor.py b/plexpy/activity_processor.py index 2484cf61..1687d098 100644 --- a/plexpy/activity_processor.py +++ b/plexpy/activity_processor.py @@ -219,26 +219,30 @@ class ActivityProcessor(object): args = [session['user_id']] 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: - prev_session = None - else: + 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']} + 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']] diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index 7941a6e8..b90e1b76 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -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: diff --git a/plexpy/notifiers.py b/plexpy/notifiers.py index 0fa445f3..983b34a2 100644 --- a/plexpy/notifiers.py +++ b/plexpy/notifiers.py @@ -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,11 +871,12 @@ 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', @@ -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 = '
'.join(['%s: %s' % (v, k) for k, v in self.get_devices().iteritems() if k]) + devices = '
'.join(['%s: %s' + % (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, diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py index e0a4c6f5..f94117a1 100644 --- a/plexpy/pmsconnect.py +++ b/plexpy/pmsconnect.py @@ -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') diff --git a/plexpy/version.py b/plexpy/version.py index f2340614..44483b96 100644 --- a/plexpy/version.py +++ b/plexpy/version.py @@ -1,2 +1,2 @@ PLEXPY_VERSION = "master" -PLEXPY_RELEASE_VERSION = "1.4.0" +PLEXPY_RELEASE_VERSION = "1.4.1" diff --git a/plexpy/webauth.py b/plexpy/webauth.py index a2c6f4f5..805bec7c 100644 --- a/plexpy/webauth.py +++ b/plexpy/webauth.py @@ -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") \ No newline at end of file + raise cherrypy.HTTPRedirect(plexpy.HTTP_ROOT + "auth/login") \ No newline at end of file diff --git a/plexpy/webserve.py b/plexpy/webserve.py index cc01fac1..008ee775 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -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) @@ -1170,7 +1170,7 @@ class WebInterface(object): @requireAuth() @addtoapi() 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: @@ -1189,15 +1189,15 @@ class WebInterface(object): "recordsTotal": 2344, "recordsFiltered": 10, "data": - [{"browser": "Safari 7.0.3", - "friendly_name": "Jon Snow", - "host": "http://plexpy.castleblack.com", - "ip_address": "xxx.xxx.xxx.xxx", - "os": "Mac OS X", - "timestamp": 1462591869, - "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_group": "guest", + [{"browser": "Safari 7.0.3", + "friendly_name": "Jon Snow", + "host": "http://plexpy.castleblack.com", + "ip_address": "xxx.xxx.xxx.xxx", + "os": "Mac OS X", + "timestamp": 1462591869, + "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_group": "guest", "user_id": 133788 }, {...}, @@ -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]', "") + order_dir = kwargs.get('order[0][dir]', "desc") + order_column = kwargs.get('order[0][column]', "0") + search_value = kwargs.get('search[value]', "") + 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] += '
' + 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() - 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: + 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 else: logger.warn(u"Unable to retrieve data for get_activity.")