diff --git a/CHANGELOG.md b/CHANGELOG.md index 09ba5125..e1c58a21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,53 @@ # Changelog +## v1.2.14 (2015-12-07) + +* Fix regression with PlexWatch db importer and buffer warnings. + + +## v1.2.13 (2015-12-06) + +* Fix match newlines between tags in notification text. +* Fix current activity not showing on PMS 0.9.12. + + +## v1.2.12 (2015-12-06) + +* Fix for "too many open files" error. + + +## v1.2.11 (2015-12-06) + +* Fix more regressions (sorry). + + +## v1.2.10 (2015-12-06) + +* Fix broken count graphs regression. + + +## v1.2.9 (2015-12-06) + +* Fix and improve text sanitization. + + +## v1.2.8 (2015-12-06) + +* Fix sanitize player names +* Fix recently added notification delay +* Fix recently added metadata queries +* Fix multiple lines in notification body text +* Fix UTF-8 encoding in Prowl notifications subject line +* Change to only log IPv4 addresses +* Add global toggle for recently added notifcations +* Add feature to delete users +* Add channel support for Telegram notification agent +* Add icon for Apple tvOS +* Add icon for Microsoft Edge + + ## v1.2.7 (2015-11-27) + * Fix IP address option in notifications diff --git a/data/interfaces/default/edit_user.html b/data/interfaces/default/edit_user.html index 1e19b66e..502ab11a 100644 --- a/data/interfaces/default/edit_user.html +++ b/data/interfaces/default/edit_user.html @@ -115,7 +115,7 @@ DOCUMENTATION :: END success: function(data) { $("#edit-user-status-message").html(data); if ($.trim(friendly_name) !== '') { - $(".set-username").html(friendly_name); + $('.set-username').html(document.createTextNode(friendly_name)); } $("#user-profile-thumb").attr('src', thumb); } diff --git a/data/interfaces/default/home_stats.html b/data/interfaces/default/home_stats.html index 10832c41..e3773f3f 100644 --- a/data/interfaces/default/home_stats.html +++ b/data/interfaces/default/home_stats.html @@ -39,7 +39,7 @@ user_id Returns the user id for the associated stat. friendly_name Returns the friendly name of the user for the associated stat. == Only if 'stat_id' is 'top_platform' or 'last_watched' == -platform_type Returns the platform name for the associated stat. +player Returns the player name for the associated stat. == Only if 'stat_id' is 'last_watched' == last_watch Returns the time the media item was last watched. @@ -709,7 +709,7 @@ DOCUMENTATION :: END - - ${top_stat['rows'][0]['platform_type']} + - ${top_stat['rows'][0]['player']}

@@ -755,7 +755,7 @@ DOCUMENTATION :: END - - ${top_stat['rows'][loop.index]['platform_type']} + - ${top_stat['rows'][loop.index]['player']}

diff --git a/data/interfaces/default/images/platforms/msedge.png b/data/interfaces/default/images/platforms/msedge.png new file mode 100644 index 00000000..3aa8dd02 Binary files /dev/null and b/data/interfaces/default/images/platforms/msedge.png differ diff --git a/data/interfaces/default/js/script.js b/data/interfaces/default/js/script.js index 4332ecc2..5d82f90c 100644 --- a/data/interfaces/default/js/script.js +++ b/data/interfaces/default/js/script.js @@ -203,6 +203,8 @@ function getPlatformImagePath(platformName) { return 'interfaces/default/images/platforms/safari.png'; } else if (platformName.indexOf("Internet Explorer") > -1) { return 'interfaces/default/images/platforms/ie.png'; + } else if (platformName.indexOf("Microsoft Edge") > -1) { + return 'interfaces/default/images/platforms/msedge.png'; } else if (platformName.indexOf("Unknown Browser") > -1) { return 'interfaces/default/images/platforms/dafault.png'; } else if (platformName.indexOf("Windows-XBMC") > -1) { diff --git a/data/interfaces/default/notification_config.html b/data/interfaces/default/notification_config.html index 7d420259..9d8ab9a1 100644 --- a/data/interfaces/default/notification_config.html +++ b/data/interfaces/default/notification_config.html @@ -25,7 +25,7 @@ from plexpy import helpers % endif -

${item['description']}

+

${item['description'] | n}

% elif item['input_type'] == 'button':
@@ -34,14 +34,14 @@ from plexpy import helpers
-

${item['description']}

+

${item['description'] | n}

% elif item['input_type'] == 'checkbox':
-

${item['description']}

+

${item['description'] | n}

% elif item['input_type'] == 'select': @@ -60,7 +60,7 @@ from plexpy import helpers -

${item['description']}

+

${item['description'] | n}

% endif % endfor diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html index fd3dd1c5..66a5be3d 100644 --- a/data/interfaces/default/settings.html +++ b/data/interfaces/default/settings.html @@ -313,7 +313,8 @@ available_notification_agents = sorted(notifiers.available_notification_agents() -

Set the complete folder path where your Plex Server logs are, shortcuts are not recognized.
Click here for help. This is required if you enable IP logging.

+

Set the complete folder path where your Plex Server logs are, shortcuts are not recognized.
+ Click here for help. This is required if you enable IP logging (for PMS 0.9.12 and below).

@@ -414,23 +415,21 @@ available_notification_agents = sorted(notifiers.available_notification_agents()

History Logging

+

Keep records of all movie, TV show, or music items played from your Plex Media Server.

-

Keep records of all movie items played from your Plex Media Server.

-

Keep records of all TV show items played from your Plex Media Server.

-

Keep records of all music items played from your Plex Media Server.

@@ -499,6 +498,11 @@ available_notification_agents = sorted(notifiers.available_notification_agents() Enable Music Notifications
+
+ +

Current Activity Notifications

@@ -529,7 +533,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents() -

Enable to only get one notification for recently added Episodes or Tracks. Movies are unaffected.

+

Enable to only get one TV Show or Artist notification for recently added Episodes or Tracks. Movies are unaffected.

@@ -1126,7 +1130,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
diff --git a/data/interfaces/default/welcome.html b/data/interfaces/default/welcome.html index a841eb92..93a5b518 100644 --- a/data/interfaces/default/welcome.html +++ b/data/interfaces/default/welcome.html @@ -106,13 +106,13 @@ from plexpy import common

Monitoring

Keep records of all movie, TV show, or music items played from your Plex Media Server.

- Log Movies + Enable Movie Logging
- Log TV Shows + Enable TV Show Logging
- Log Music + Enable Music Logging
diff --git a/plexpy/__init__.py b/plexpy/__init__.py index 88fb73f7..bbd234da 100644 --- a/plexpy/__init__.py +++ b/plexpy/__init__.py @@ -285,10 +285,20 @@ def initialize_scheduler(): hours=12, minutes=0, seconds=0) schedule_job(pmsconnect.get_server_friendly_name, 'Refresh Plex Server Name', hours=12, minutes=0, seconds=0) - schedule_job(activity_pinger.check_recently_added, 'Check for recently added items', - hours=0, minutes=0, seconds=seconds) - schedule_job(activity_pinger.check_server_response, 'Check for server response', - hours=0, minutes=0, seconds=seconds) + + if CONFIG.NOTIFY_RECENTLY_ADDED: + schedule_job(activity_pinger.check_recently_added, 'Check for recently added items', + hours=0, minutes=0, seconds=seconds) + else: + schedule_job(activity_pinger.check_recently_added, 'Check for recently added items', + hours=0, minutes=0, seconds=0) + + if CONFIG.MONITOR_REMOTE_ACCESS: + schedule_job(activity_pinger.check_server_response, 'Check for server response', + hours=0, minutes=0, seconds=seconds) + else: + schedule_job(activity_pinger.check_server_response, 'Check for server response', + hours=0, minutes=0, seconds=0) # If we're not using websockets then fall back to polling if not CONFIG.MONITORING_USE_WEBSOCKET or POLLING_FAILOVER: diff --git a/plexpy/activity_pinger.py b/plexpy/activity_pinger.py index 4063a7dd..6cf12443 100644 --- a/plexpy/activity_pinger.py +++ b/plexpy/activity_pinger.py @@ -33,7 +33,11 @@ def check_active_sessions(ws_request=False): monitor_process = activity_processor.ActivityProcessor() # logger.debug(u"PlexPy Monitor :: Checking for active streams.") + global int_ping_count + if session_list: + int_ping_count = 0 + media_container = session_list['sessions'] # Check our temp table for what we must do with the new streams @@ -123,7 +127,8 @@ def check_active_sessions(ws_request=False): kwargs=dict(stream_data=stream, notify_action='buffer')).start() logger.debug(u"PlexPy Monitor :: Stream buffering. Count is now %s. Last triggered %s." - % (buffer_values[0][0], buffer_values[0][1])) + % (buffer_values[0]['buffer_count'], + buffer_values[0]['buffer_last_triggered'])) # Check if the user has reached the offset in the media we defined as the "watched" percent # Don't trigger if state is buffer as some clients push the progress to the end when @@ -165,6 +170,16 @@ def check_active_sessions(ws_request=False): else: logger.debug(u"PlexPy Monitor :: Unable to read session list.") + int_ping_count += 1 + logger.warn(u"PlexPy Monitor :: Unable to get an internal response from the server, ping attempt %s." \ + % str(int_ping_count)) + + if int_ping_count == 3: + # Fire off notifications + threading.Thread(target=notification_handler.notify_timeline, + kwargs=dict(notify_action='intdown')).start() + + def check_recently_added(): with monitor_lock: @@ -182,21 +197,22 @@ def check_recently_added(): for item in recently_added: metadata = [] - if item['media_type'] == 'movie': - metadata_list = pms_connect.get_metadata_details(item['rating_key']) - if metadata_list: - metadata = [metadata_list['metadata']] - else: - logger.error(u"PlexPy Monitor :: Unable to retrieve metadata for rating_key %s" \ - % str(item['rating_key'])) + if 0 < time_threshold - int(item['added_at']) <= time_interval: + if item['media_type'] == 'movie': + metadata_list = pms_connect.get_metadata_details(item['rating_key']) + if metadata_list: + metadata = [metadata_list['metadata']] + else: + logger.error(u"PlexPy Monitor :: Unable to retrieve metadata for rating_key %s" \ + % str(item['rating_key'])) - else: - metadata_list = pms_connect.get_metadata_children_details(item['rating_key']) - if metadata_list: - metadata = metadata_list['metadata'] else: - logger.error(u"PlexPy Monitor :: Unable to retrieve children metadata for rating_key %s" \ - % str(item['rating_key'])) + metadata_list = pms_connect.get_metadata_children_details(item['rating_key']) + if metadata_list: + metadata = metadata_list['metadata'] + else: + logger.error(u"PlexPy Monitor :: Unable to retrieve children metadata for rating_key %s" \ + % str(item['rating_key'])) if metadata: if not plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT: @@ -231,20 +247,10 @@ def check_server_response(): pms_connect = pmsconnect.PmsConnect() server_response = pms_connect.get_server_response() - global int_ping_count global ext_ping_count - # Check for internal server response - if not server_response: - int_ping_count += 1 - logger.warn(u"PlexPy Monitor :: Unable to get an internal response from the server, ping attempt %s." \ - % str(int_ping_count)) - # Reset internal ping counter - else: - int_ping_count = 0 - # Check for remote access - if server_response and plexpy.CONFIG.MONITOR_REMOTE_ACCESS: + if server_response: mapping_state = server_response['mapping_state'] mapping_error = server_response['mapping_error'] @@ -263,11 +269,6 @@ def check_server_response(): else: ext_ping_count = 0 - if int_ping_count == 3: - # Fire off notifications - threading.Thread(target=notification_handler.notify_timeline, - kwargs=dict(notify_action='intdown')).start() - if ext_ping_count == 3: # Fire off notifications threading.Thread(target=notification_handler.notify_timeline, diff --git a/plexpy/activity_processor.py b/plexpy/activity_processor.py index fb1a2376..0a68f4d0 100644 --- a/plexpy/activity_processor.py +++ b/plexpy/activity_processor.py @@ -180,18 +180,18 @@ class ActivityProcessor(object): result = self.db.select(query=query, args=args) - new_session = {'id': result[0][0], - 'rating_key': result[0][1], - 'user_id': result[0][2], - 'reference_id': result[0][3]} + new_session = {'id': result[0]['id'], + 'rating_key': result[0]['rating_key'], + '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][0], - 'rating_key': result[1][1], - 'user_id': result[1][2], - 'reference_id': result[1][3]} + prev_session = {'id': result[1]['id'], + 'rating_key': result[1]['rating_key'], + 'user_id': result[1]['user_id'], + 'reference_id': result[1]['reference_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 @@ -400,7 +400,7 @@ class ActivityProcessor(object): 'WHERE session_key = ?', [session_key]) if buffer_count: - return buffer_count + return buffer_count['buffer_count'] return 0 @@ -417,6 +417,6 @@ class ActivityProcessor(object): 'WHERE session_key = ?', [session_key]) if last_time: - return last_time + return last_time['buffer_last_triggered'] return None \ No newline at end of file diff --git a/plexpy/config.py b/plexpy/config.py index 381fe064..491f2081 100644 --- a/plexpy/config.py +++ b/plexpy/config.py @@ -151,6 +151,7 @@ _CONFIG_DEFINITIONS = { 'NMA_ON_EXTDOWN': (int, 'NMA', 0), 'NMA_ON_INTDOWN': (int, 'NMA', 0), 'NOTIFY_CONSECUTIVE': (int, 'Monitoring', 1), + 'NOTIFY_RECENTLY_ADDED': (int, 'Monitoring', 0), 'NOTIFY_RECENTLY_ADDED_GRANDPARENT': (int, 'Monitoring', 0), 'NOTIFY_RECENTLY_ADDED_DELAY': (int, 'Monitoring', 60), 'NOTIFY_WATCHED_PERCENT': (int, 'Monitoring', 85), diff --git a/plexpy/database.py b/plexpy/database.py index 75528c86..f4bbdcf5 100644 --- a/plexpy/database.py +++ b/plexpy/database.py @@ -46,6 +46,13 @@ def get_cache_size(): return 0 return int(plexpy.CONFIG.CACHE_SIZEMB) +def dict_factory(cursor, row): + d = {} + for idx, col in enumerate(cursor.description): + d[col[0]] = row[idx] + + return d + class MonitorDatabase(object): @@ -58,7 +65,7 @@ class MonitorDatabase(object): self.connection.execute("PRAGMA journal_mode = %s" % plexpy.CONFIG.JOURNAL_MODE) # 64mb of cache memory, probably need to make it user configurable self.connection.execute("PRAGMA cache_size=-%s" % (get_cache_size() * 1024)) - self.connection.row_factory = sqlite3.Row + self.connection.row_factory = dict_factory def action(self, query, args=None, return_last_id=False): if query is None: @@ -104,7 +111,7 @@ class MonitorDatabase(object): def select_single(self, query, args=None): - sql_results = self.action(query, args).fetchone()[0] + sql_results = self.action(query, args).fetchone() if sql_results is None or sql_results == "": return "" diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py index 544df9f7..8d797fae 100644 --- a/plexpy/datafactory.py +++ b/plexpy/datafactory.py @@ -28,7 +28,7 @@ class DataFactory(object): def get_history(self, kwargs=None, custom_where=None, grouping=0, watched_percent=85): data_tables = datatables.DataTables() - + group_by = ['session_history.reference_id'] if grouping else ['session_history.id'] columns = ['session_history.reference_id', @@ -37,8 +37,8 @@ class DataFactory(object): 'MIN(started) AS started', 'MAX(stopped) AS stopped', 'SUM(CASE WHEN stopped > 0 THEN (stopped - started) ELSE 0 END) - \ - SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS duration', - 'SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS paused_counter', + SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS duration', + 'SUM(CASE WHEN paused_counter IS NULL THEN 0 ELSE paused_counter END) AS paused_counter', 'session_history.user_id', 'session_history.user', '(CASE WHEN users.friendly_name IS NULL THEN users.username ELSE users.friendly_name END) as friendly_name', @@ -88,7 +88,7 @@ class DataFactory(object): 'error': 'Unable to execute database query.'} history = query['result'] - + rows = [] for item in history: if item["media_type"] == 'episode' and item["parent_thumb"]: @@ -119,7 +119,7 @@ class DataFactory(object): "user": item["user"], "friendly_name": item["friendly_name"], "platform": platform, - "player": item["player"], + "player": item['player'], "ip_address": item["ip_address"], "media_type": item["media_type"], "rating_key": item["rating_key"], @@ -140,7 +140,7 @@ class DataFactory(object): } rows.append(row) - + dict = {'recordsFiltered': query['filteredCount'], 'recordsTotal': query['totalCount'], 'data': rows, @@ -183,19 +183,19 @@ class DataFactory(object): return None for item in result: - row = {'title': item[1], - 'total_plays': item[2], - 'total_duration': item[3], + row = {'title': item['grandparent_title'], + 'total_plays': item['total_plays'], + 'total_duration': item['total_duration'], 'users_watched': '', - 'rating_key': item[4], - 'last_play': item[5], - 'grandparent_thumb': item[6], + 'rating_key': item['grandparent_rating_key'], + 'last_play': item['last_watch'], + 'grandparent_thumb': item['grandparent_thumb'], 'thumb': '', 'user': '', 'friendly_name': '', 'platform_type': '', 'platform': '', - 'row_id': item[0] + 'row_id': item['id'] } top_tv.append(row) @@ -231,18 +231,18 @@ class DataFactory(object): return None for item in result: - row = {'title': item[1], - 'users_watched': item[2], - 'rating_key': item[3], - 'last_play': item[4], - 'total_plays': item[5], - 'grandparent_thumb': item[7], + row = {'title': item['grandparent_title'], + 'users_watched': item['users_watched'], + 'rating_key': item['grandparent_rating_key'], + 'last_play': item['last_watch'], + 'total_plays': item['total_plays'], + 'grandparent_thumb': item['grandparent_thumb'], 'thumb': '', 'user': '', 'friendly_name': '', 'platform_type': '', 'platform': '', - 'row_id': item[0] + 'row_id': item['id'] } popular_tv.append(row) @@ -275,19 +275,19 @@ class DataFactory(object): return None for item in result: - row = {'title': item[1], - 'total_plays': item[2], - 'total_duration': item[3], + row = {'title': item['full_title'], + 'total_plays': item['total_plays'], + 'total_duration': item['total_duration'], 'users_watched': '', - 'rating_key': item[4], - 'last_play': item[5], + 'rating_key': item['rating_key'], + 'last_play': item['last_watch'], 'grandparent_thumb': '', - 'thumb': item[6], + 'thumb': item['thumb'], 'user': '', 'friendly_name': '', 'platform_type': '', 'platform': '', - 'row_id': item[0] + 'row_id': item['id'] } top_movies.append(row) @@ -323,18 +323,18 @@ class DataFactory(object): return None for item in result: - row = {'title': item[1], - 'users_watched': item[2], - 'rating_key': item[3], - 'last_play': item[4], - 'total_plays': item[5], + row = {'title': item['full_title'], + 'users_watched': item['users_watched'], + 'rating_key': item['rating_key'], + 'last_play': item['last_watch'], + 'total_plays': item['total_plays'], 'grandparent_thumb': '', - 'thumb': item[7], + 'thumb': item['thumb'], 'user': '', 'friendly_name': '', 'platform_type': '', 'platform': '', - 'row_id': item[0] + 'row_id': item['id'] } popular_movies.append(row) @@ -367,19 +367,19 @@ class DataFactory(object): return None for item in result: - row = {'title': item[1], - 'total_plays': item[2], - 'total_duration': item[3], + row = {'title': item['grandparent_title'], + 'total_plays': item['total_plays'], + 'total_duration': item['total_duration'], 'users_watched': '', - 'rating_key': item[4], - 'last_play': item[5], - 'grandparent_thumb': item[6], + 'rating_key': item['grandparent_rating_key'], + 'last_play': item['last_watch'], + 'grandparent_thumb': item['grandparent_thumb'], 'thumb': '', 'user': '', 'friendly_name': '', 'platform_type': '', 'platform': '', - 'row_id': item[0] + 'row_id': item['id'] } top_music.append(row) @@ -415,18 +415,18 @@ class DataFactory(object): return None for item in result: - row = {'title': item[1], - 'users_watched': item[2], - 'rating_key': item[3], - 'last_play': item[4], - 'total_plays': item[5], - 'grandparent_thumb': item[7], + row = {'title': item['grandparent_title'], + 'users_watched': item['users_watched'], + 'rating_key': item['grandparent_rating_key'], + 'last_play': item['last_watch'], + 'total_plays': item['total_plays'], + 'grandparent_thumb': item['grandparent_thumb'], 'thumb': '', 'user': '', 'friendly_name': '', 'platform_type': '', 'platform': '', - 'row_id': item[0] + 'row_id': item['id'] } popular_music.append(row) @@ -460,17 +460,17 @@ class DataFactory(object): return None for item in result: - if not item[5] or item[5] == '': + if not item['thumb'] or item['thumb'] == '': user_thumb = common.DEFAULT_USER_THUMB else: - user_thumb = item[5] + user_thumb = item['thumb'] - row = {'user': item[0], - 'user_id': item[6], - 'friendly_name': item[1], - 'total_plays': item[2], - 'total_duration': item[3], - 'last_play': item[4], + row = {'user': item['user'], + 'user_id': item['user_id'], + 'friendly_name': item['friendly_name'], + 'total_plays': item['total_plays'], + 'total_duration': item['total_duration'], + 'last_play': item['last_watch'], 'user_thumb': user_thumb, 'grandparent_thumb': '', 'users_watched': '', @@ -509,12 +509,12 @@ class DataFactory(object): for item in result: # Rename Mystery platform names - platform_type = common.PLATFORM_NAME_OVERRIDES.get(item[0], item[0]) + platform_type = common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform']) - row = {'platform': item[0], - 'total_plays': item[1], - 'total_duration': item[2], - 'last_play': item[3], + row = {'platform': item['platform'], + 'total_plays': item['total_plays'], + 'total_duration': item['total_duration'], + 'last_play': item['last_watch'], 'platform_type': platform_type, 'title': '', 'thumb': '', @@ -545,7 +545,7 @@ class DataFactory(object): 'session_history_metadata.thumb, ' \ 'session_history_metadata.grandparent_thumb, ' \ 'MAX(session_history.started) as last_watch, ' \ - 'session_history.player as platform, ' \ + 'session_history.player, ' \ '((CASE WHEN session_history.view_offset IS NULL THEN 0.1 ELSE \ session_history.view_offset * 1.0 END) / \ (CASE WHEN session_history_metadata.duration IS NULL THEN 1.0 ELSE \ @@ -567,22 +567,22 @@ class DataFactory(object): return None for item in result: - if not item[8] or item[8] == '': - thumb = item[7] + if not item['grandparent_thumb'] or item['grandparent_thumb'] == '': + thumb = item['thumb'] else: - thumb = item[8] + thumb = item['grandparent_thumb'] - row = {'row_id': item[0], - 'user': item[1], - 'friendly_name': item[2], - 'user_id': item[3], - 'user_thumb': item[4], - 'title': item[5], - 'rating_key': item[6], + row = {'row_id': item['id'], + 'user': item['user'], + 'friendly_name': item['friendly_name'], + 'user_id': item['user_id'], + 'user_thumb': item['user_thumb'], + 'title': item['full_title'], + 'rating_key': item['rating_key'], 'thumb': thumb, - 'grandparent_thumb': item[8], - 'last_watch': item[9], - 'platform_type': item[10], + 'grandparent_thumb': item['grandparent_thumb'], + 'last_watch': item['last_watch'], + 'player': item['player'] } last_watched.append(row) @@ -609,26 +609,26 @@ class DataFactory(object): stream_output = {} for item in result: - stream_output = {'container': item[0], - 'bitrate': item[1], - 'video_resolution': item[2], - 'width': item[3], - 'height': item[4], - 'aspect_ratio': item[5], - 'video_framerate': item[6], - 'video_codec': item[7], - 'audio_codec': item[8], - 'audio_channels': item[9], - 'transcode_video_dec': item[10], - 'transcode_video_codec': item[11], - 'transcode_height': item[12], - 'transcode_width': item[13], - 'transcode_audio_dec': item[14], - 'transcode_audio_codec': item[15], - 'transcode_audio_channels': item[16], - 'media_type': item[17], - 'title': item[18], - 'grandparent_title': item[19] + stream_output = {'container': item['container'], + 'bitrate': item['bitrate'], + 'video_resolution': item['video_resolution'], + 'width': item['width'], + 'height': item['height'], + 'aspect_ratio': item['aspect_ratio'], + 'video_framerate': item['video_framerate'], + 'video_codec': item['video_codec'], + 'audio_codec': item['audio_codec'], + 'audio_channels': item['audio_channels'], + 'transcode_video_dec': item['video_decision'], + 'transcode_video_codec': item['transcode_video_codec'], + 'transcode_height': item['transcode_height'], + 'transcode_width': item['transcode_width'], + 'transcode_audio_dec': item['audio_decision'], + 'transcode_audio_codec': item['transcode_audio_codec'], + 'transcode_audio_channels': item['transcode_audio_channels'], + 'media_type': item['media_type'], + 'title': item['title'], + 'grandparent_title': item['grandparent_title'] } return stream_output @@ -678,25 +678,25 @@ class DataFactory(object): return None for row in result: - if row[1] == 'episode' and row[8]: - thumb = row[8] - elif row[1] == 'episode': - thumb = row[9] + if row['media_type'] == 'episode' and row['parent_thumb']: + thumb = row['parent_thumb'] + elif row['media_type'] == 'episode': + thumb = row['grandparent_thumb'] else: - thumb = row[7] + thumb = row['thumb'] - recent_output = {'row_id': row[0], - 'type': row[1], - 'rating_key': row[2], - 'title': row[4], - 'parent_title': row[5], - 'grandparent_title': row[6], + recent_output = {'row_id': row['id'], + 'type': row['media_type'], + 'rating_key': row['rating_key'], + 'title': row['title'], + 'parent_title': row['parent_title'], + 'grandparent_title': row['grandparent_title'], 'thumb': thumb, - 'index': row[10], - 'parent_index': row[11], - 'year': row[12], - 'time': row[13], - 'user': row[14] + 'index': row['media_index'], + 'parent_index': row['parent_media_index'], + 'year': row['year'], + 'time': row['started'], + 'user': row['user'] } recently_watched.append(recent_output) @@ -962,7 +962,7 @@ class DataFactory(object): }) key_list = grandparents - + return key_list def update_rating_key(self, old_key_list='', new_key_list='', media_type=''): @@ -984,48 +984,48 @@ class DataFactory(object): mapping = {} if old_key_list and new_key_list: mapping = get_pairs(old_key_list, new_key_list) - + if mapping: logger.info(u"PlexPy DataFactory :: Updating rating keys in the database.") for old_key, new_key in mapping.iteritems(): # check rating_key (3 tables) - monitor_db.action('UPDATE session_history SET rating_key = ? WHERE rating_key = ?', + monitor_db.action('UPDATE session_history SET rating_key = ? WHERE rating_key = ?', [new_key, old_key]) - monitor_db.action('UPDATE session_history_media_info SET rating_key = ? WHERE rating_key = ?', + monitor_db.action('UPDATE session_history_media_info SET rating_key = ? WHERE rating_key = ?', [new_key, old_key]) - monitor_db.action('UPDATE session_history_metadata SET rating_key = ? WHERE rating_key = ?', + monitor_db.action('UPDATE session_history_metadata SET rating_key = ? WHERE rating_key = ?', [new_key, old_key]) # check parent_rating_key (2 tables) - monitor_db.action('UPDATE session_history SET parent_rating_key = ? WHERE parent_rating_key = ?', + monitor_db.action('UPDATE session_history SET parent_rating_key = ? WHERE parent_rating_key = ?', [new_key, old_key]) - monitor_db.action('UPDATE session_history_metadata SET parent_rating_key = ? WHERE parent_rating_key = ?', + monitor_db.action('UPDATE session_history_metadata SET parent_rating_key = ? WHERE parent_rating_key = ?', [new_key, old_key]) # check grandparent_rating_key (2 tables) - monitor_db.action('UPDATE session_history SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?', + monitor_db.action('UPDATE session_history SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?', [new_key, old_key]) - monitor_db.action('UPDATE session_history_metadata SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?', + monitor_db.action('UPDATE session_history_metadata SET grandparent_rating_key = ? WHERE grandparent_rating_key = ?', [new_key, old_key]) # check thumb (1 table) monitor_db.action('UPDATE session_history_metadata SET thumb = replace(thumb, ?, ?) \ - WHERE thumb LIKE "/library/metadata/%s/thumb/%%"' % old_key, + WHERE thumb LIKE "/library/metadata/%s/thumb/%%"' % old_key, [old_key, new_key]) # check parent_thumb (1 table) monitor_db.action('UPDATE session_history_metadata SET parent_thumb = replace(parent_thumb, ?, ?) \ - WHERE parent_thumb LIKE "/library/metadata/%s/thumb/%%"' % old_key, + WHERE parent_thumb LIKE "/library/metadata/%s/thumb/%%"' % old_key, [old_key, new_key]) # check grandparent_thumb (1 table) monitor_db.action('UPDATE session_history_metadata SET grandparent_thumb = replace(grandparent_thumb, ?, ?) \ - WHERE grandparent_thumb LIKE "/library/metadata/%s/thumb/%%"' % old_key, + WHERE grandparent_thumb LIKE "/library/metadata/%s/thumb/%%"' % old_key, [old_key, new_key]) # check art (1 table) monitor_db.action('UPDATE session_history_metadata SET art = replace(art, ?, ?) \ - WHERE art LIKE "/library/metadata/%s/art/%%"' % old_key, + WHERE art LIKE "/library/metadata/%s/art/%%"' % old_key, [old_key, new_key]) return 'Updated rating key in database.' @@ -1046,6 +1046,6 @@ class DataFactory(object): ip_address = 'N/A' for item in result: - ip_address = item[0] + ip_address = item['ip_address'] return ip_address diff --git a/plexpy/datatables.py b/plexpy/datatables.py index 03aca36c..3cf8f0ca 100644 --- a/plexpy/datatables.py +++ b/plexpy/datatables.py @@ -178,12 +178,18 @@ class DataTables(object): filtered = self.ssp_db.select(query, args=args) # Build grand totals - totalcount = self.ssp_db.select('SELECT COUNT(id) from %s' % table_name)[0][0] + totalcount = self.ssp_db.select('SELECT COUNT(id) as total_count from %s' % table_name)[0]['total_count'] # Get draw counter draw_counter = int(parameters['draw']) + # Paginate results result = filtered[parameters['start']:(parameters['start'] + parameters['length'])] + + # Sanitize on the way out + result = [{k: helpers.sanitize(v) if isinstance(v, basestring) else v for k, v in row.iteritems()} + for row in result] + output = {'result': result, 'draw': draw_counter, 'filteredCount': len(filtered), diff --git a/plexpy/graphs.py b/plexpy/graphs.py index 1240fad5..ae6aa65a 100644 --- a/plexpy/graphs.py +++ b/plexpy/graphs.py @@ -44,11 +44,11 @@ class Graphs(object): else: query = 'SELECT date(started, "unixepoch", "localtime") as date_played, ' \ 'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_count, ' \ 'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_count, ' \ 'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_duration ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_count ' \ 'FROM session_history ' \ 'WHERE datetime(stopped, "unixepoch", "localtime") >= datetime("now", "-%s days", "localtime") ' \ 'GROUP BY date_played ' \ @@ -76,10 +76,10 @@ class Graphs(object): series_2_value = 0 series_3_value = 0 for item in result: - if date_string == item[0]: - series_1_value = item[1] - series_2_value = item[2] - series_3_value = item[3] + if date_string == item['date_played']: + series_1_value = item['tv_count'] + series_2_value = item['movie_count'] + series_3_value = item['music_count'] break else: series_1_value = 0 @@ -138,11 +138,11 @@ class Graphs(object): 'when 5 then "Friday" ' \ 'else "Saturday" end as dayofweek, ' \ 'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_count, ' \ 'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_count, ' \ 'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_duration ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_count ' \ 'FROM session_history ' \ 'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \ 'datetime("now", "-' + time_range + ' days", "localtime") ' \ @@ -165,10 +165,10 @@ class Graphs(object): series_2_value = 0 series_3_value = 0 for item in result: - if day_item == item[1]: - series_1_value = item[2] - series_2_value = item[3] - series_3_value = item[4] + if day_item == item['dayofweek']: + series_1_value = item['tv_count'] + series_2_value = item['movie_count'] + series_3_value = item['music_count'] break else: series_1_value = 0 @@ -211,11 +211,11 @@ class Graphs(object): else: query = 'select strftime("%H", datetime(started, "unixepoch", "localtime")) as hourofday, ' \ 'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_count, ' \ 'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_count, ' \ 'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_duration ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_count ' \ 'FROM session_history ' \ 'WHERE datetime(stopped, "unixepoch", "localtime") >= ' \ 'datetime("now", "-' + time_range + ' days", "localtime") ' \ @@ -240,10 +240,10 @@ class Graphs(object): series_2_value = 0 series_3_value = 0 for item in result: - if hour_item == item[0]: - series_1_value = item[1] - series_2_value = item[2] - series_3_value = item[3] + if hour_item == item['hourofday']: + series_1_value = item['tv_count'] + series_2_value = item['movie_count'] + series_3_value = item['music_count'] break else: series_1_value = 0 @@ -283,11 +283,11 @@ class Graphs(object): else: query = 'SELECT strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) as datestring, ' \ 'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_count, ' \ 'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_count, ' \ 'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_duration ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_count ' \ 'FROM session_history ' \ 'WHERE datetime(started, "unixepoch", "localtime") >= datetime("now", "-12 months", "localtime") ' \ 'GROUP BY strftime("%Y-%m", datetime(started, "unixepoch", "localtime")) ' \ @@ -316,10 +316,10 @@ class Graphs(object): series_2_value = 0 series_3_value = 0 for item in result: - if date_string == item[0]: - series_1_value = item[1] - series_2_value = item[2] - series_3_value = item[3] + if date_string == item['datestring']: + series_1_value = item['tv_count'] + series_2_value = item['movie_count'] + series_3_value = item['music_count'] break else: series_1_value = 0 @@ -364,11 +364,11 @@ class Graphs(object): else: query = 'SELECT platform, ' \ 'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_count, ' \ 'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_count, ' \ 'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_count, ' \ 'SUM(case when stopped > 0 then (stopped - started) ' \ ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \ 'FROM session_history ' \ @@ -386,10 +386,10 @@ class Graphs(object): series_3 = [] for item in result: - categories.append(common.PLATFORM_NAME_OVERRIDES.get(item[0], item[0])) - series_1.append(item[1]) - series_2.append(item[2]) - series_3.append(item[3]) + categories.append(common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform'])) + series_1.append(item['tv_count']) + series_2.append(item['movie_count']) + series_3.append(item['music_count']) series_1_output = {'name': 'TV', 'data': series_1} @@ -430,11 +430,11 @@ class Graphs(object): '(case when users.friendly_name is null then users.username else ' \ 'users.friendly_name end) as friendly_name,' \ 'SUM(case when media_type = "episode" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tv_count, ' \ 'SUM(case when media_type = "movie" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as movie_count, ' \ 'SUM(case when media_type = "track" and stopped > 0 then (stopped - started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as music_count, ' \ 'SUM(case when stopped > 0 then (stopped - started) ' \ ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \ 'FROM session_history ' \ @@ -453,10 +453,10 @@ class Graphs(object): series_3 = [] for item in result: - categories.append(item[0]) - series_1.append(item[1]) - series_2.append(item[2]) - series_3.append(item[3]) + categories.append(item['friendly_name']) + series_1.append(item['tv_count']) + series_2.append(item['movie_count']) + series_3.append(item['music_count']) series_1_output = {'name': 'TV', 'data': series_1} @@ -501,15 +501,15 @@ class Graphs(object): 'SUM(case when (session_history_media_info.video_decision = "direct play" ' \ 'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play")) ' \ 'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_count, ' \ 'SUM(case when (session_history_media_info.video_decision = "copy" ' \ 'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy")) ' \ 'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_count, ' \ 'SUM(case when (session_history_media_info.video_decision = "transcode" ' \ 'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode")) ' \ 'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_duration ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_count ' \ 'FROM session_history ' \ 'JOIN session_history_media_info ON session_history.id = session_history_media_info.id ' \ 'WHERE datetime(session_history.stopped, "unixepoch", "localtime") >= ' \ @@ -540,10 +540,10 @@ class Graphs(object): series_2_value = 0 series_3_value = 0 for item in result: - if date_string == item[0]: - series_1_value = item[1] - series_2_value = item[2] - series_3_value = item[3] + if date_string == item['date_played']: + series_1_value = item['dp_count'] + series_2_value = item['ds_count'] + series_3_value = item['tc_count'] break else: series_1_value = 0 @@ -598,15 +598,15 @@ class Graphs(object): 'SUM(case when (session_history_media_info.video_decision = "direct play" ' \ 'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play")) ' \ 'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_count, ' \ 'SUM(case when (session_history_media_info.video_decision = "copy" ' \ 'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy")) ' \ 'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_count, ' \ 'SUM(case when (session_history_media_info.video_decision = "transcode" ' \ 'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode")) ' \ 'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_count, ' \ 'SUM(case when stopped > 0 then (stopped - started) ' \ ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \ 'FROM session_history ' \ @@ -626,10 +626,10 @@ class Graphs(object): series_3 = [] for item in result: - categories.append(item[0]) - series_1.append(item[1]) - series_2.append(item[2]) - series_3.append(item[3]) + categories.append(item['resolution']) + series_1.append(item['dp_count']) + series_2.append(item['ds_count']) + series_3.append(item['tc_count']) series_1_output = {'name': 'Direct Play', 'data': series_1} @@ -695,15 +695,15 @@ class Graphs(object): 'SUM(case when (session_history_media_info.video_decision = "direct play" ' \ 'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play")) ' \ 'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_count, ' \ 'SUM(case when (session_history_media_info.video_decision = "copy" ' \ 'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy")) ' \ 'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_count, ' \ 'SUM(case when (session_history_media_info.video_decision = "transcode" ' \ 'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode")) ' \ 'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_count, ' \ 'SUM(case when stopped > 0 then (stopped - started) ' \ ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \ 'FROM session_history ' \ @@ -723,10 +723,10 @@ class Graphs(object): series_3 = [] for item in result: - categories.append(item[0]) - series_1.append(item[1]) - series_2.append(item[2]) - series_3.append(item[3]) + categories.append(item['resolution']) + series_1.append(item['dp_count']) + series_2.append(item['ds_count']) + series_3.append(item['tc_count']) series_1_output = {'name': 'Direct Play', 'data': series_1} @@ -773,15 +773,15 @@ class Graphs(object): 'SUM(case when (session_history_media_info.video_decision = "direct play" ' \ 'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play")) ' \ 'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_count, ' \ 'SUM(case when (session_history_media_info.video_decision = "copy" ' \ 'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy")) ' \ 'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_count, ' \ 'SUM(case when (session_history_media_info.video_decision = "transcode" ' \ 'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode")) ' \ 'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_count, ' \ 'SUM(case when session_history.stopped > 0 ' \ 'then (session_history.stopped - session_history.started) ' \ ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \ @@ -801,10 +801,10 @@ class Graphs(object): series_3 = [] for item in result: - categories.append(common.PLATFORM_NAME_OVERRIDES.get(item[0], item[0])) - series_1.append(item[1]) - series_2.append(item[2]) - series_3.append(item[3]) + categories.append(common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform'])) + series_1.append(item['dp_count']) + series_2.append(item['ds_count']) + series_3.append(item['tc_count']) series_1_output = {'name': 'Direct Play', 'data': series_1} @@ -853,15 +853,15 @@ class Graphs(object): 'SUM(case when (session_history_media_info.video_decision = "direct play" ' \ 'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "direct play")) ' \ 'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as dp_count, ' \ 'SUM(case when (session_history_media_info.video_decision = "copy" ' \ 'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "copy")) ' \ 'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as ds_count, ' \ 'SUM(case when (session_history_media_info.video_decision = "transcode" ' \ 'or (session_history_media_info.video_decision = "" and session_history_media_info.audio_decision = "transcode")) ' \ 'and session_history.stopped > 0 then (session_history.stopped - session_history.started) ' \ - ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_duration, ' \ + ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as tc_count, ' \ 'SUM(case when session_history.stopped > 0 ' \ 'then (session_history.stopped - session_history.started) ' \ ' - (case when paused_counter is NULL then 0 else paused_counter end) else 0 end) as total_duration ' \ @@ -882,10 +882,10 @@ class Graphs(object): series_3 = [] for item in result: - categories.append(item[0]) - series_1.append(item[1]) - series_2.append(item[2]) - series_3.append(item[3]) + categories.append(item['username']) + series_1.append(item['dp_count']) + series_2.append(item['ds_count']) + series_3.append(item['tc_count']) series_1_output = {'name': 'Direct Play', 'data': series_1} diff --git a/plexpy/helpers.py b/plexpy/helpers.py index bdddc69c..793494f6 100644 --- a/plexpy/helpers.py +++ b/plexpy/helpers.py @@ -171,7 +171,7 @@ def human_duration(s): h = int((s % 84600) / 3600) m = int(((s % 84600) % 3600) / 60) s = int(((s % 84600) % 3600) % 60) - + hd_list = [] if d > 0: hd_list.append(str(d) + ' days') @@ -181,7 +181,7 @@ def human_duration(s): hd_list.append(str(m) + ' mins') if s > 0: hd_list.append(str(s) + ' secs') - + hd = ' '.join(hd_list) return hd @@ -220,7 +220,7 @@ def piratesize(size): split = size.split(" ") factor = float(split[0]) unit = split[1].upper() - + if unit == 'MiB': size = factor * 1048576 elif unit == 'MB': @@ -446,3 +446,9 @@ def process_json_kwargs(json_kwargs): params = json.loads(json_kwargs) return params + +def sanitize(string): + if string: + return unicode(string).replace('<','<').replace('>','>') + else: + return '' diff --git a/plexpy/notification_handler.py b/plexpy/notification_handler.py index 2f91a5df..4d4560de 100644 --- a/plexpy/notification_handler.py +++ b/plexpy/notification_handler.py @@ -214,13 +214,13 @@ def get_notify_state(session): args=[session['session_key'], session['rating_key'], session['user']]) notify_states = [] for item in result: - notify_state = {'on_play': item[0], - 'on_stop': item[1], - 'on_pause': item[2], - 'on_resume': item[3], - 'on_buffer': item[4], - 'on_watched': item[5], - 'agent_id': item[6]} + notify_state = {'on_play': item['on_play'], + 'on_stop': item['on_stop'], + 'on_pause': item['on_pause'], + 'on_resume': item['on_resume'], + 'on_buffer': item['on_buffer'], + 'on_watched': item['on_watched'], + 'agent_id': item['agent_id']} notify_states.append(notify_state) return notify_states @@ -234,8 +234,8 @@ def get_notify_state_timeline(timeline): args=[timeline['rating_key']]) notify_states = [] for item in result: - notify_state = {'on_created': item[0], - 'agent_id': item[1]} + notify_state = {'on_created': item['on_created'], + 'agent_id': item['agent_id']} notify_states.append(notify_state) return notify_states @@ -315,13 +315,13 @@ def build_notify_text(session=None, timeline=None, state=None): # Check for exclusion tags if metadata['media_type'] == 'movie': # Regex pattern to remove the text in the tags we don't want - pattern = re.compile('[^>]+.|[^>]+.', re.IGNORECASE|re.DOTALL) + pattern = re.compile('\n*[^>]+.\n*|\n*[^>]+.\n*', re.IGNORECASE|re.DOTALL) elif metadata['media_type'] == 'show' or metadata['media_type'] == 'episode': # Regex pattern to remove the text in the tags we don't want - pattern = re.compile('[^>]+.|[^>]+.', re.IGNORECASE|re.DOTALL) + pattern = re.compile('\n*[^>]+.\n*|\n*?[^>]+.\n*', re.IGNORECASE|re.DOTALL) elif metadata['media_type'] == 'artist' or metadata['media_type'] == 'track': # Regex pattern to remove the text in the tags we don't want - pattern = re.compile('[^>]+.|[^>]+.', re.IGNORECASE|re.DOTALL) + pattern = re.compile('\n*[^>]+.\n*|\n*[^>]+.\n*', re.IGNORECASE|re.DOTALL) else: pattern = None diff --git a/plexpy/notifiers.py b/plexpy/notifiers.py index d8124917..c0fb44e6 100644 --- a/plexpy/notifiers.py +++ b/plexpy/notifiers.py @@ -879,9 +879,9 @@ class PUSHALOT(object): pushalot_authorizationtoken = plexpy.CONFIG.PUSHALOT_APIKEY - logger.debug(u"Pushalot event: " + event) - logger.debug(u"Pushalot message: " + message) - logger.debug(u"Pushalot api: " + pushalot_authorizationtoken) + #logger.debug(u"Pushalot event: " + event) + #logger.debug(u"Pushalot message: " + message) + #logger.debug(u"Pushalot api: " + pushalot_authorizationtoken) http_handler = HTTPSConnection("pushalot.com") diff --git a/plexpy/plextv.py b/plexpy/plextv.py index 1c6682a1..8d4796c6 100644 --- a/plexpy/plextv.py +++ b/plexpy/plextv.py @@ -342,13 +342,13 @@ class PlexTV(object): rating_key = clean_uri.rpartition('%2F')[-1] - sync_details = {"device_name": device_name, - "platform": device_platform, - "username": device_username, - "friendly_name": device_friendly_name, + sync_details = {"device_name": helpers.sanitize(device_name), + "platform": helpers.sanitize(device_platform), + "username": helpers.sanitize(device_username), + "friendly_name": helpers.sanitize(device_friendly_name), "user_id": device_user_id, - "root_title": sync_root_title, - "title": sync_title, + "root_title": helpers.sanitize(sync_root_title), + "title": helpers.sanitize(sync_title), "metadata_type": sync_metadata_type, "content_type": sync_content_type, "rating_key": rating_key, diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py index f7183b1b..98bb1a1e 100644 --- a/plexpy/pmsconnect.py +++ b/plexpy/pmsconnect.py @@ -711,6 +711,12 @@ class PmsConnect(object): Output: List for dicts + kwargs: Used for filtering inside the dicts. Adding type="movie" will only list movies + + + Output: List for dicts + + # Adding all_params=1 Makes the call insane slow. """ # Add a cache? @@ -910,6 +916,15 @@ class PmsConnect(object): return t_result + t_result = [i for i in t_result if not i['viewCount'] or i['lastViewedAt'] <= watched_older_then] + + if sort: + logger.debug('Sorted on %s' % sort) + t_result = sorted(t_result, key=lambda k: k[sort], reverse=True) + + return t_result + + def get_current_activity(self): session_data = self.get_sessions(output_format='xml') diff --git a/plexpy/users.py b/plexpy/users.py index bccda2f7..c7adfedd 100644 --- a/plexpy/users.py +++ b/plexpy/users.py @@ -95,7 +95,7 @@ class Users(object): "friendly_name": item['friendly_name'], "ip_address": item['ip_address'], "platform": platform, - "player": item['player'], + "player": item["player"], "last_watched": item['last_watched'], "thumb": thumb, "media_type": item['media_type'], @@ -265,17 +265,17 @@ class Users(object): if user_id: monitor_db = database.MonitorDatabase() query = 'select username, ' \ - '(CASE WHEN friendly_name IS NULL THEN username ELSE friendly_name END),' \ + '(CASE WHEN friendly_name IS NULL THEN username ELSE friendly_name END) as friendly_name,' \ 'do_notify, keep_history, custom_avatar_url as thumb ' \ 'FROM users WHERE user_id = ?' result = monitor_db.select(query, args=[user_id]) if result: user_detail = {'user_id': user_id, - 'user': result[0][0], - 'friendly_name': result[0][1], - 'thumb': result[0][4], - 'do_notify': helpers.checked(result[0][2]), - 'keep_history': helpers.checked(result[0][3]) + 'user': result[0]['username'], + 'friendly_name': result[0]['friendly_name'], + 'thumb': result[0]['thumb'], + 'do_notify': helpers.checked(result[0]['do_notify']), + 'keep_history': helpers.checked(result[0]['keep_history']) } return user_detail else: @@ -289,17 +289,17 @@ class Users(object): elif user: monitor_db = database.MonitorDatabase() query = 'select user_id, ' \ - '(CASE WHEN friendly_name IS NULL THEN username ELSE friendly_name END),' \ + '(CASE WHEN friendly_name IS NULL THEN username ELSE friendly_name END) as friendly_name,' \ 'do_notify, keep_history, custom_avatar_url as thumb ' \ 'FROM users WHERE username = ?' result = monitor_db.select(query, args=[user]) if result: - user_detail = {'user_id': result[0][0], + user_detail = {'user_id': result[0]['user_id'], 'user': user, - 'friendly_name': result[0][1], - 'thumb': result[0][4], - 'do_notify': helpers.checked(result[0][2]), - 'keep_history': helpers.checked(result[0][3])} + 'friendly_name': result[0]['friendly_name'], + 'thumb': result[0]['thumb'], + 'do_notify': helpers.checked(result[0]['do_notify']), + 'keep_history': helpers.checked(result[0]['keep_history'])} return user_detail else: user_detail = {'user_id': None, @@ -319,7 +319,7 @@ class Users(object): query = 'select user_id FROM users WHERE username = ?' result = monitor_db.select_single(query, args=[user]) if result: - return result + return result['user_id'] else: return None except: @@ -486,9 +486,9 @@ class Users(object): result = monitor_db.select(query, args=[user]) for item in result: - if item[0]: - total_time = item[0] - total_plays = item[1] + if item['total_time']: + total_time = item['total_time'] + total_plays = item['total_plays'] else: total_time = 0 total_plays = 0 @@ -529,14 +529,14 @@ class Users(object): for item in result: # Rename Mystery platform names - platform_type = common.PLATFORM_NAME_OVERRIDES.get(item[2], item[2]) + platform_type = common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform']) - row = {'player_name': item[0], + row = {'player_name': item['player'], 'platform_type': platform_type, - 'total_plays': item[1], + 'total_plays': item['player_count'], 'result_id': result_id } player_stats.append(row) result_id += 1 - return player_stats \ No newline at end of file + return player_stats diff --git a/plexpy/version.py b/plexpy/version.py index dcea93a3..dfb6543c 100644 --- a/plexpy/version.py +++ b/plexpy/version.py @@ -1,2 +1,2 @@ PLEXPY_VERSION = "master" -PLEXPY_RELEASE_VERSION = "1.2.7" +PLEXPY_RELEASE_VERSION = "1.2.14" diff --git a/plexpy/webserve.py b/plexpy/webserve.py index ba8cf41a..6fe789f7 100644 --- a/plexpy/webserve.py +++ b/plexpy/webserve.py @@ -1,7 +1,4 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# This file is part of PlexPy. +# This file is part of PlexPy. # # PlexPy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -16,7 +13,7 @@ # You should have received a copy of the GNU General Public License # along with PlexPy. If not, see . -from plexpy import logger, notifiers, plextv, pmsconnect, common, log_reader, datafactory, graphs, users +from plexpy import logger, notifiers, plextv, pmsconnect, common, log_reader, datafactory, graphs, users, helpers from plexpy.helpers import checked, radio, profile_func, tobool from mako.lookup import TemplateLookup @@ -44,7 +41,7 @@ def serve_template(templatename, **kwargs): interface_dir = os.path.join(str(plexpy.PROG_DIR), 'data/interfaces/') template_dir = os.path.join(str(interface_dir), plexpy.CONFIG.INTERFACE) - _hplookup = TemplateLookup(directories=[template_dir]) + _hplookup = TemplateLookup(directories=[template_dir], default_filters=['unicode', 'h']) server_name = plexpy.CONFIG.PMS_NAME @@ -448,6 +445,7 @@ class WebInterface(object): "logging_ignore_interval": plexpy.CONFIG.LOGGING_IGNORE_INTERVAL, "pms_is_remote": checked(plexpy.CONFIG.PMS_IS_REMOTE), "notify_consecutive": checked(plexpy.CONFIG.NOTIFY_CONSECUTIVE), + "notify_recently_added": checked(plexpy.CONFIG.NOTIFY_RECENTLY_ADDED), "notify_recently_added_grandparent": checked(plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_GRANDPARENT), "notify_recently_added_delay": plexpy.CONFIG.NOTIFY_RECENTLY_ADDED_DELAY, "notify_watched_percent": plexpy.CONFIG.NOTIFY_WATCHED_PERCENT, @@ -494,7 +492,7 @@ class WebInterface(object): "tv_notify_on_pause", "movie_notify_on_pause", "music_notify_on_pause", "refresh_users_on_startup", "ip_logging_enable", "movie_logging_enable", "tv_logging_enable", "music_logging_enable", "pms_is_remote", "home_stats_type", "group_history_tables", "notify_consecutive", - "notify_recently_added_grandparent", "monitor_remote_access" + "notify_recently_added", "notify_recently_added_grandparent", "monitor_remote_access" ] for checked_config in checked_configs: if checked_config not in kwargs: @@ -519,6 +517,14 @@ class WebInterface(object): if (kwargs['monitoring_interval'] != str(plexpy.CONFIG.MONITORING_INTERVAL)) or \ (kwargs['refresh_users_interval'] != str(plexpy.CONFIG.REFRESH_USERS_INTERVAL)): reschedule = True + + if 'notify_recently_added' in kwargs and \ + (kwargs['notify_recently_added'] != plexpy.CONFIG.NOTIFY_RECENTLY_ADDED): + reschedule = True + + if 'monitor_remote_access' in kwargs and \ + (kwargs['monitor_remote_access'] != plexpy.CONFIG.MONITOR_REMOTE_ACCESS): + reschedule = True if 'pms_ip' in kwargs: if kwargs['pms_ip'] != plexpy.CONFIG.PMS_IP: @@ -729,6 +735,7 @@ class WebInterface(object): if not session['ip_address']: ip_address = data_factory.get_session_ip(session['session_key']) session['ip_address'] = ip_address + except: return serve_template(templatename="current_activity.html", data=None)