From 89f581f63ec4579bfd341f6ec740320830cd10bf Mon Sep 17 00:00:00 2001
From: Jonathan Wong
Date: Thu, 3 Dec 2015 18:40:18 -0800
Subject: [PATCH 01/32] Only schedule job for recently added or monitor remote
access if setting enabled
---
data/interfaces/default/settings.html | 5 +++++
plexpy/__init__.py | 18 ++++++++++++----
plexpy/activity_pinger.py | 31 +++++++++++++--------------
plexpy/config.py | 1 +
plexpy/webserve.py | 11 +++++++++-
5 files changed, 45 insertions(+), 21 deletions(-)
diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html
index fd3dd1c5..e8d3dfa4 100644
--- a/data/interfaces/default/settings.html
+++ b/data/interfaces/default/settings.html
@@ -499,6 +499,11 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
Enable Music Notifications
+
+
+ Enable Recently Added Notifications
+
+
- 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()
+ Keep records of all movie, TV show, or music items played from your Plex Media Server.
@@ -755,7 +755,7 @@ DOCUMENTATION :: END
- - ${top_stat['rows'][loop.index]['platform_type']}
+ - ${top_stat['rows'][loop.index]['player']}
diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py
index 544df9f7..ca218bf3 100644
--- a/plexpy/datafactory.py
+++ b/plexpy/datafactory.py
@@ -108,6 +108,9 @@ class DataFactory(object):
# Rename Mystery platform names
platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])
+ # Sanitize player name
+ player = helpers.sanitize(item["player"])
+
row = {"reference_id": item["reference_id"],
"id": item["id"],
"date": item["date"],
@@ -119,7 +122,7 @@ class DataFactory(object):
"user": item["user"],
"friendly_name": item["friendly_name"],
"platform": platform,
- "player": item["player"],
+ "player": player,
"ip_address": item["ip_address"],
"media_type": item["media_type"],
"rating_key": item["rating_key"],
@@ -545,7 +548,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 \
@@ -571,6 +574,9 @@ class DataFactory(object):
thumb = item[7]
else:
thumb = item[8]
+
+ # Sanitize player name
+ player = helpers.sanitize(item["player"])
row = {'row_id': item[0],
'user': item[1],
@@ -582,7 +588,7 @@ class DataFactory(object):
'thumb': thumb,
'grandparent_thumb': item[8],
'last_watch': item[9],
- 'platform_type': item[10],
+ 'player': player,
}
last_watched.append(row)
diff --git a/plexpy/helpers.py b/plexpy/helpers.py
index e5f5cc42..64b796db 100644
--- a/plexpy/helpers.py
+++ b/plexpy/helpers.py
@@ -430,3 +430,9 @@ def process_json_kwargs(json_kwargs):
params = json.loads(json_kwargs)
return params
+
+def sanitize(string):
+ if string:
+ return str(string).replace('<','<').replace('>','>')
+ else:
+ return ''
\ No newline at end of file
diff --git a/plexpy/users.py b/plexpy/users.py
index bccda2f7..e932e6c8 100644
--- a/plexpy/users.py
+++ b/plexpy/users.py
@@ -89,13 +89,16 @@ class Users(object):
# Rename Mystery platform names
platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])
+ # Sanitize player name
+ player = helpers.sanitize(item["player"])
+
row = {"id": item['id'],
"plays": item['plays'],
"last_seen": item['last_seen'],
"friendly_name": item['friendly_name'],
"ip_address": item['ip_address'],
"platform": platform,
- "player": item['player'],
+ "player": player,
"last_watched": item['last_watched'],
"thumb": thumb,
"media_type": item['media_type'],
@@ -180,12 +183,15 @@ class Users(object):
# Rename Mystery platform names
platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])
+ # Sanitize player name
+ player = helpers.sanitize(item["player"])
+
row = {"id": item['id'],
"last_seen": item['last_seen'],
"ip_address": item['ip_address'],
"play_count": item['play_count'],
"platform": platform,
- "player": item['player'],
+ "player": player,
"last_watched": item['last_watched'],
"thumb": thumb,
"media_type": item['media_type'],
@@ -531,7 +537,10 @@ class Users(object):
# Rename Mystery platform names
platform_type = common.PLATFORM_NAME_OVERRIDES.get(item[2], item[2])
- row = {'player_name': item[0],
+ # Sanitize player name
+ player = helpers.sanitize(item[0])
+
+ row = {'player_name': player,
'platform_type': platform_type,
'total_plays': item[1],
'result_id': result_id
diff --git a/plexpy/webserve.py b/plexpy/webserve.py
index 89065bf5..a3b237e8 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
from mako.lookup import TemplateLookup
@@ -738,6 +735,9 @@ class WebInterface(object):
if not session['ip_address']:
ip_address = data_factory.get_session_ip(session['session_key'])
session['ip_address'] = ip_address
+ # Sanitize player name
+ session['player'] = helpers.sanitize(session['player'])
+
except:
return serve_template(templatename="current_activity.html", data=None)
From 24c8c4319d4bdd78daf1708fe8d38d7fbb041ca7 Mon Sep 17 00:00:00 2001
From: Jonathan Wong
Date: Sat, 5 Dec 2015 23:44:02 -0800
Subject: [PATCH 12/32] v1.2.8
---
CHANGELOG.md | 16 ++++++++++++++++
plexpy/version.py | 2 +-
2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 09ba5125..7128836a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,22 @@
# Changelog
+## 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/plexpy/version.py b/plexpy/version.py
index dcea93a3..e5f27395 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.8"
From 1157fda96cce50f90b0ad300538a16ad208424c2 Mon Sep 17 00:00:00 2001
From: Jonathan Wong
Date: Sun, 6 Dec 2015 00:52:43 -0800
Subject: [PATCH 13/32] Hide Pushalot notifier message logging
---
plexpy/notifiers.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
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")
From b0fa0d534ea6cd2eb39d5b6d7620a31c1849552d Mon Sep 17 00:00:00 2001
From: Tim
Date: Sun, 6 Dec 2015 14:09:38 +0200
Subject: [PATCH 14/32] Better sanitization on templates and datatables output.
---
plexpy/database.py | 9 +-
plexpy/datafactory.py | 244 +++++++++++++++++++++---------------------
plexpy/datatables.py | 8 +-
plexpy/graphs.py | 88 +++++++--------
plexpy/helpers.py | 10 +-
plexpy/users.py | 41 ++++---
plexpy/webserve.py | 2 +-
7 files changed, 206 insertions(+), 196 deletions(-)
diff --git a/plexpy/database.py b/plexpy/database.py
index 75528c86..82ece52a 100644
--- a/plexpy/database.py
+++ b/plexpy/database.py
@@ -58,7 +58,14 @@ 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 = self.dict_factory
+
+ def dict_factory(self, cursor, row):
+ d = {}
+ for idx, col in enumerate(cursor.description):
+ d[col[0]] = row[idx]
+
+ return d
def action(self, query, args=None, return_last_id=False):
if query is None:
diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py
index ca218bf3..d857b69a 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"]:
@@ -143,7 +143,7 @@ class DataFactory(object):
}
rows.append(row)
-
+
dict = {'recordsFiltered': query['filteredCount'],
'recordsTotal': query['totalCount'],
'data': rows,
@@ -186,19 +186,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)
@@ -234,18 +234,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)
@@ -278,19 +278,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)
@@ -326,18 +326,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)
@@ -370,19 +370,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)
@@ -418,18 +418,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)
@@ -463,17 +463,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': '',
@@ -512,12 +512,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': '',
@@ -570,24 +570,24 @@ 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']
+
# Sanitize player name
player = helpers.sanitize(item["player"])
- 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],
+ 'grandparent_thumb': item['grandparent_thumb'],
+ 'last_watch': item['last_watch'],
'player': player,
}
last_watched.append(row)
@@ -615,26 +615,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
@@ -684,25 +684,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)
@@ -968,7 +968,7 @@ class DataFactory(object):
})
key_list = grandparents
-
+
return key_list
def update_rating_key(self, old_key_list='', new_key_list='', media_type=''):
@@ -990,48 +990,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.'
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..e6692d63 100644
--- a/plexpy/graphs.py
+++ b/plexpy/graphs.py
@@ -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_duration']
+ series_2_value = item['movie_duration']
+ series_3_value = item['music_duration']
break
else:
series_1_value = 0
@@ -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_duration']
+ series_2_value = item['movie_duration']
+ series_3_value = item['music_duration']
break
else:
series_1_value = 0
@@ -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_duration']
+ series_2_value = item['movie_duration']
+ series_3_value = item['music_duration']
break
else:
series_1_value = 0
@@ -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_duration']
+ series_2_value = item['movie_duration']
+ series_3_value = item['music_duration']
break
else:
series_1_value = 0
@@ -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_duration'])
+ series_2.append(item['movie_duration'])
+ series_3.append(item['music_duration'])
series_1_output = {'name': 'TV',
'data': series_1}
@@ -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_duration'])
+ series_2.append(item['movie_duration'])
+ series_3.append(item['music_duration'])
series_1_output = {'name': 'TV',
'data': series_1}
@@ -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_duration']
+ series_2_value = item['ds_duration']
+ series_3_value = item['tc_duration']
break
else:
series_1_value = 0
@@ -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_duration'])
+ series_2.append(item['ds_duration'])
+ series_3.append(item['tc_duration'])
series_1_output = {'name': 'Direct Play',
'data': series_1}
@@ -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_duration'])
+ series_2.append(item['ds_duration'])
+ series_3.append(item['tc_duration'])
series_1_output = {'name': 'Direct Play',
'data': series_1}
@@ -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_duration'])
+ series_2.append(item['ds_duration'])
+ series_3.append(item['tc_duration'])
series_1_output = {'name': 'Direct Play',
'data': series_1}
@@ -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_duration'])
+ series_2.append(item['ds_duration'])
+ series_3.append(item['tc_duration'])
series_1_output = {'name': 'Direct Play',
'data': series_1}
diff --git a/plexpy/helpers.py b/plexpy/helpers.py
index 64b796db..b4f86f49 100644
--- a/plexpy/helpers.py
+++ b/plexpy/helpers.py
@@ -155,7 +155,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')
@@ -165,7 +165,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
@@ -204,7 +204,7 @@ def piratesize(size):
split = size.split(" ")
factor = float(split[0])
unit = split[1].upper()
-
+
if unit == 'MiB':
size = factor * 1048576
elif unit == 'MB':
@@ -433,6 +433,6 @@ def process_json_kwargs(json_kwargs):
def sanitize(string):
if string:
- return str(string).replace('<','<').replace('>','>')
+ return unicode(string).replace('<','<').replace('>','>')
else:
- return ''
\ No newline at end of file
+ return ''
diff --git a/plexpy/users.py b/plexpy/users.py
index e932e6c8..995e19cf 100644
--- a/plexpy/users.py
+++ b/plexpy/users.py
@@ -271,17 +271,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:
@@ -295,17 +295,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,
@@ -492,9 +492,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
@@ -535,17 +535,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'])
- # Sanitize player name
- player = helpers.sanitize(item[0])
-
- row = {'player_name': player,
+ 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/webserve.py b/plexpy/webserve.py
index a3b237e8..ad9a6393 100644
--- a/plexpy/webserve.py
+++ b/plexpy/webserve.py
@@ -41,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
From e00c23bc491cd4fcfd6fcc72b3cc77a2ec52323c Mon Sep 17 00:00:00 2001
From: Tim
Date: Sun, 6 Dec 2015 14:39:50 +0200
Subject: [PATCH 15/32] Escape input on friendy_name change.
---
data/interfaces/default/edit_user.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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);
}
From d07add383fb9d4996b26df7c0641af429e40dada Mon Sep 17 00:00:00 2001
From: Tim
Date: Sun, 6 Dec 2015 14:52:19 +0200
Subject: [PATCH 16/32] v1.2.9
---
CHANGELOG.md | 5 +++++
plexpy/version.py | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7128836a..706bd277 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Changelog
+## v1.2.9 (2015-12-06)
+
+* Fix and improve text sanitization.
+
+
## v1.2.8 (2015-12-06)
* Fix sanitize player names
diff --git a/plexpy/version.py b/plexpy/version.py
index e5f27395..8733e484 100644
--- a/plexpy/version.py
+++ b/plexpy/version.py
@@ -1,2 +1,2 @@
PLEXPY_VERSION = "master"
-PLEXPY_RELEASE_VERSION = "1.2.8"
+PLEXPY_RELEASE_VERSION = "1.2.9"
From a18ba24f4a5950b6846f74a8fa1d497f8e3f268f Mon Sep 17 00:00:00 2001
From: Tim
Date: Sun, 6 Dec 2015 16:07:57 +0200
Subject: [PATCH 17/32] Fix count type graphs.
---
plexpy/graphs.py | 132 +++++++++++++++++++++++------------------------
1 file changed, 66 insertions(+), 66 deletions(-)
diff --git a/plexpy/graphs.py b/plexpy/graphs.py
index e6692d63..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 ' \
@@ -77,9 +77,9 @@ class Graphs(object):
series_3_value = 0
for item in result:
if date_string == item['date_played']:
- series_1_value = item['tv_duration']
- series_2_value = item['movie_duration']
- series_3_value = item['music_duration']
+ 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") ' \
@@ -166,9 +166,9 @@ class Graphs(object):
series_3_value = 0
for item in result:
if day_item == item['dayofweek']:
- series_1_value = item['tv_duration']
- series_2_value = item['movie_duration']
- series_3_value = item['music_duration']
+ 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") ' \
@@ -241,9 +241,9 @@ class Graphs(object):
series_3_value = 0
for item in result:
if hour_item == item['hourofday']:
- series_1_value = item['tv_duration']
- series_2_value = item['movie_duration']
- series_3_value = item['music_duration']
+ 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")) ' \
@@ -317,9 +317,9 @@ class Graphs(object):
series_3_value = 0
for item in result:
if date_string == item['datestring']:
- series_1_value = item['tv_duration']
- series_2_value = item['movie_duration']
- series_3_value = item['music_duration']
+ 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 ' \
@@ -387,9 +387,9 @@ class Graphs(object):
for item in result:
categories.append(common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform']))
- series_1.append(item['tv_duration'])
- series_2.append(item['movie_duration'])
- series_3.append(item['music_duration'])
+ 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 ' \
@@ -454,9 +454,9 @@ class Graphs(object):
for item in result:
categories.append(item['friendly_name'])
- series_1.append(item['tv_duration'])
- series_2.append(item['movie_duration'])
- series_3.append(item['music_duration'])
+ 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") >= ' \
@@ -541,9 +541,9 @@ class Graphs(object):
series_3_value = 0
for item in result:
if date_string == item['date_played']:
- series_1_value = item['dp_duration']
- series_2_value = item['ds_duration']
- series_3_value = item['tc_duration']
+ 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 ' \
@@ -627,9 +627,9 @@ class Graphs(object):
for item in result:
categories.append(item['resolution'])
- series_1.append(item['dp_duration'])
- series_2.append(item['ds_duration'])
- series_3.append(item['tc_duration'])
+ 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 ' \
@@ -724,9 +724,9 @@ class Graphs(object):
for item in result:
categories.append(item['resolution'])
- series_1.append(item['dp_duration'])
- series_2.append(item['ds_duration'])
- series_3.append(item['tc_duration'])
+ 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 ' \
@@ -802,9 +802,9 @@ class Graphs(object):
for item in result:
categories.append(common.PLATFORM_NAME_OVERRIDES.get(item['platform'], item['platform']))
- series_1.append(item['dp_duration'])
- series_2.append(item['ds_duration'])
- series_3.append(item['tc_duration'])
+ 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 ' \
@@ -883,9 +883,9 @@ class Graphs(object):
for item in result:
categories.append(item['username'])
- series_1.append(item['dp_duration'])
- series_2.append(item['ds_duration'])
- series_3.append(item['tc_duration'])
+ 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}
From 5bdf79606ed79c805ec7684f989804c57350249e Mon Sep 17 00:00:00 2001
From: Tim
Date: Sun, 6 Dec 2015 16:16:03 +0200
Subject: [PATCH 18/32] v1.2.10
---
CHANGELOG.md | 5 +++++
plexpy/version.py | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 706bd277..5f8ba79f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Changelog
+## v1.2.10 (2015-12-06)
+
+* Fix broken count graphs regression.
+
+
## v1.2.9 (2015-12-06)
* Fix and improve text sanitization.
diff --git a/plexpy/version.py b/plexpy/version.py
index 8733e484..056e1fdb 100644
--- a/plexpy/version.py
+++ b/plexpy/version.py
@@ -1,2 +1,2 @@
PLEXPY_VERSION = "master"
-PLEXPY_RELEASE_VERSION = "1.2.9"
+PLEXPY_RELEASE_VERSION = "1.2.10"
From ba68a9b52b33edc210a1fa73a6ac09956aea669c Mon Sep 17 00:00:00 2001
From: Tim
Date: Sun, 6 Dec 2015 16:32:07 +0200
Subject: [PATCH 19/32] Fix changelog modal output.
---
data/interfaces/default/settings.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/data/interfaces/default/settings.html b/data/interfaces/default/settings.html
index 6cef6983..66a5be3d 100644
--- a/data/interfaces/default/settings.html
+++ b/data/interfaces/default/settings.html
@@ -1130,7 +1130,7 @@ available_notification_agents = sorted(notifiers.available_notification_agents()
Changelog
- ${versioncheck.read_changelog()}
+ ${versioncheck.read_changelog() | n}
From a5bd7e6563bfaf875ce94ce6561fe0c04af4acdf Mon Sep 17 00:00:00 2001
From: Tim
Date: Sun, 6 Dec 2015 17:19:09 +0200
Subject: [PATCH 20/32] More dict key fixes.
---
plexpy/activity_pinger.py | 3 ++-
plexpy/activity_processor.py | 16 ++++++++--------
plexpy/notification_handler.py | 18 +++++++++---------
3 files changed, 19 insertions(+), 18 deletions(-)
diff --git a/plexpy/activity_pinger.py b/plexpy/activity_pinger.py
index 2eb63bbe..6cf12443 100644
--- a/plexpy/activity_pinger.py
+++ b/plexpy/activity_pinger.py
@@ -127,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
diff --git a/plexpy/activity_processor.py b/plexpy/activity_processor.py
index fb1a2376..3f7927fc 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
diff --git a/plexpy/notification_handler.py b/plexpy/notification_handler.py
index 2f91a5df..eaa8b777 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
From 0bdaedd486a12e50eabbd19e1cd6f2add38dc8c5 Mon Sep 17 00:00:00 2001
From: Tim
Date: Sun, 6 Dec 2015 17:32:25 +0200
Subject: [PATCH 21/32] Fix more regresssions.
---
CHANGELOG.md | 5 +++++
plexpy/version.py | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5f8ba79f..29023d4a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Changelog
+## v1.2.11 (2015-12-06)
+
+* Fix more regressions (sorry).
+
+
## v1.2.10 (2015-12-06)
* Fix broken count graphs regression.
diff --git a/plexpy/version.py b/plexpy/version.py
index 056e1fdb..ea0bcabd 100644
--- a/plexpy/version.py
+++ b/plexpy/version.py
@@ -1,2 +1,2 @@
PLEXPY_VERSION = "master"
-PLEXPY_RELEASE_VERSION = "1.2.10"
+PLEXPY_RELEASE_VERSION = "1.2.11"
From 37b92f3d881dc27265444a1016acebf583758fa0 Mon Sep 17 00:00:00 2001
From: Tim
Date: Sun, 6 Dec 2015 18:09:18 +0200
Subject: [PATCH 22/32] Move dict_factory out of database class.
---
plexpy/database.py | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/plexpy/database.py b/plexpy/database.py
index 82ece52a..73cdea8c 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,14 +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 = self.dict_factory
-
- def dict_factory(self, cursor, row):
- d = {}
- for idx, col in enumerate(cursor.description):
- d[col[0]] = row[idx]
-
- return d
+ self.connection.row_factory = dict_factory
def action(self, query, args=None, return_last_id=False):
if query is None:
From 560acf62fe29650a64344e982eaa765fbcc1cbbc Mon Sep 17 00:00:00 2001
From: Tim
Date: Sun, 6 Dec 2015 18:45:02 +0200
Subject: [PATCH 23/32] No need to sanitize same items more than once.
---
plexpy/datafactory.py | 10 ++--------
plexpy/users.py | 10 ++--------
plexpy/webserve.py | 2 --
3 files changed, 4 insertions(+), 18 deletions(-)
diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py
index d857b69a..b3d73cde 100644
--- a/plexpy/datafactory.py
+++ b/plexpy/datafactory.py
@@ -108,9 +108,6 @@ class DataFactory(object):
# Rename Mystery platform names
platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])
- # Sanitize player name
- player = helpers.sanitize(item["player"])
-
row = {"reference_id": item["reference_id"],
"id": item["id"],
"date": item["date"],
@@ -122,7 +119,7 @@ class DataFactory(object):
"user": item["user"],
"friendly_name": item["friendly_name"],
"platform": platform,
- "player": player,
+ "player": item['player'],
"ip_address": item["ip_address"],
"media_type": item["media_type"],
"rating_key": item["rating_key"],
@@ -575,9 +572,6 @@ class DataFactory(object):
else:
thumb = item['grandparent_thumb']
- # Sanitize player name
- player = helpers.sanitize(item["player"])
-
row = {'row_id': item['id'],
'user': item['user'],
'friendly_name': item['friendly_name'],
@@ -588,7 +582,7 @@ class DataFactory(object):
'thumb': thumb,
'grandparent_thumb': item['grandparent_thumb'],
'last_watch': item['last_watch'],
- 'player': player,
+ 'player': item['player']
}
last_watched.append(row)
diff --git a/plexpy/users.py b/plexpy/users.py
index 995e19cf..b1d613cd 100644
--- a/plexpy/users.py
+++ b/plexpy/users.py
@@ -89,16 +89,13 @@ class Users(object):
# Rename Mystery platform names
platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])
- # Sanitize player name
- player = helpers.sanitize(item["player"])
-
row = {"id": item['id'],
"plays": item['plays'],
"last_seen": item['last_seen'],
"friendly_name": item['friendly_name'],
"ip_address": item['ip_address'],
"platform": platform,
- "player": player,
+ "player": item["player"],
"last_watched": item['last_watched'],
"thumb": thumb,
"media_type": item['media_type'],
@@ -183,15 +180,12 @@ class Users(object):
# Rename Mystery platform names
platform = common.PLATFORM_NAME_OVERRIDES.get(item["platform"], item["platform"])
- # Sanitize player name
- player = helpers.sanitize(item["player"])
-
row = {"id": item['id'],
"last_seen": item['last_seen'],
"ip_address": item['ip_address'],
"play_count": item['play_count'],
"platform": platform,
- "player": player,
+ "player": item['player'],
"last_watched": item['last_watched'],
"thumb": thumb,
"media_type": item['media_type'],
diff --git a/plexpy/webserve.py b/plexpy/webserve.py
index ad9a6393..e4831cbc 100644
--- a/plexpy/webserve.py
+++ b/plexpy/webserve.py
@@ -735,8 +735,6 @@ class WebInterface(object):
if not session['ip_address']:
ip_address = data_factory.get_session_ip(session['session_key'])
session['ip_address'] = ip_address
- # Sanitize player name
- session['player'] = helpers.sanitize(session['player'])
except:
return serve_template(templatename="current_activity.html", data=None)
From 3fa5f80fc42d79f9ee42da2b6434ce9d36cb5b6d Mon Sep 17 00:00:00 2001
From: Tim
Date: Sun, 6 Dec 2015 20:01:38 +0200
Subject: [PATCH 24/32] v1.2.12
---
CHANGELOG.md | 5 +++++
plexpy/version.py | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 29023d4a..138f0ebe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Changelog
+## v1.2.12 (2015-12-06)
+
+* Fix for "too many open files" error.
+
+
## v1.2.11 (2015-12-06)
* Fix more regressions (sorry).
diff --git a/plexpy/version.py b/plexpy/version.py
index ea0bcabd..022a6bf9 100644
--- a/plexpy/version.py
+++ b/plexpy/version.py
@@ -1,2 +1,2 @@
PLEXPY_VERSION = "master"
-PLEXPY_RELEASE_VERSION = "1.2.11"
+PLEXPY_RELEASE_VERSION = "1.2.12"
From cc9d09bd54220152163dbca55c52eebfeedb446b Mon Sep 17 00:00:00 2001
From: Tim
Date: Sun, 6 Dec 2015 20:49:00 +0200
Subject: [PATCH 25/32] Allow HTML encoded content to be displayed in
notification setting descriptions.
---
data/interfaces/default/notification_config.html | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
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['label']}
-
${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
From 1fb7473dc5b60178e4013d88303f938412bafad0 Mon Sep 17 00:00:00 2001
From: Tim
Date: Sun, 6 Dec 2015 21:04:33 +0200
Subject: [PATCH 26/32] Sanitize sync row items.
---
plexpy/plextv.py | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
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,
From 53de8cda30aff854a0daf960c4fdd5fdfcc9e5a6 Mon Sep 17 00:00:00 2001
From: Jonathan Wong
Date: Sun, 6 Dec 2015 11:13:46 -0800
Subject: [PATCH 27/32] Match newline between tags for notification text
---
plexpy/notification_handler.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/plexpy/notification_handler.py b/plexpy/notification_handler.py
index eaa8b777..4d4560de 100644
--- a/plexpy/notification_handler.py
+++ b/plexpy/notification_handler.py
@@ -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
From a3782f915013441489c48f9b4d25099b0ce1b0b1 Mon Sep 17 00:00:00 2001
From: Jonathan Wong
Date: Sun, 6 Dec 2015 11:30:33 -0800
Subject: [PATCH 28/32] Fix dict key for IP address in current activity
---
plexpy/datafactory.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plexpy/datafactory.py b/plexpy/datafactory.py
index b3d73cde..8d797fae 100644
--- a/plexpy/datafactory.py
+++ b/plexpy/datafactory.py
@@ -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
From 8db891cfe63b4b5ec5d704b7bbeca4420bc8d479 Mon Sep 17 00:00:00 2001
From: Jonathan Wong
Date: Sun, 6 Dec 2015 11:38:23 -0800
Subject: [PATCH 29/32] v1.2.13
---
CHANGELOG.md | 6 ++++++
plexpy/version.py | 2 +-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 138f0ebe..b03ca181 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+## v1.2.12 (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.
diff --git a/plexpy/version.py b/plexpy/version.py
index 022a6bf9..446464e0 100644
--- a/plexpy/version.py
+++ b/plexpy/version.py
@@ -1,2 +1,2 @@
PLEXPY_VERSION = "master"
-PLEXPY_RELEASE_VERSION = "1.2.12"
+PLEXPY_RELEASE_VERSION = "1.2.13"
From b9c95d49a60198832460610bb1adf5831c090c01 Mon Sep 17 00:00:00 2001
From: Tim
Date: Mon, 7 Dec 2015 22:22:47 +0200
Subject: [PATCH 30/32] Fix regression on select_single queries.
---
plexpy/activity_processor.py | 4 ++--
plexpy/database.py | 2 +-
plexpy/users.py | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/plexpy/activity_processor.py b/plexpy/activity_processor.py
index 3f7927fc..0a68f4d0 100644
--- a/plexpy/activity_processor.py
+++ b/plexpy/activity_processor.py
@@ -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/database.py b/plexpy/database.py
index 73cdea8c..f4bbdcf5 100644
--- a/plexpy/database.py
+++ b/plexpy/database.py
@@ -111,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/users.py b/plexpy/users.py
index b1d613cd..c7adfedd 100644
--- a/plexpy/users.py
+++ b/plexpy/users.py
@@ -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:
From de39f7691cd3b02a070be92e34dec07472e4c6fc Mon Sep 17 00:00:00 2001
From: Tim
Date: Mon, 7 Dec 2015 22:31:00 +0200
Subject: [PATCH 31/32] v1.2.14
---
CHANGELOG.md | 7 ++++++-
plexpy/version.py | 2 +-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b03ca181..e1c58a21 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,11 @@
# Changelog
-## v1.2.12 (2015-12-06)
+## 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.
diff --git a/plexpy/version.py b/plexpy/version.py
index 446464e0..dfb6543c 100644
--- a/plexpy/version.py
+++ b/plexpy/version.py
@@ -1,2 +1,2 @@
PLEXPY_VERSION = "master"
-PLEXPY_RELEASE_VERSION = "1.2.13"
+PLEXPY_RELEASE_VERSION = "1.2.14"
From e98afdb6c98a181f580e5339945381e172c3169c Mon Sep 17 00:00:00 2001
From: Hellowlol
Date: Wed, 9 Dec 2015 00:23:45 +0100
Subject: [PATCH 32/32] fix conflict
---
plexpy/helpers.py | 16 +++
plexpy/pmsconnect.py | 233 ++++++++++++++++++++++++++++++++++++++++++-
plexpy/webserve.py | 16 ++-
3 files changed, 259 insertions(+), 6 deletions(-)
diff --git a/plexpy/helpers.py b/plexpy/helpers.py
index b4f86f49..793494f6 100644
--- a/plexpy/helpers.py
+++ b/plexpy/helpers.py
@@ -28,7 +28,23 @@ import os
import json
import xmltodict
import math
+from functools import wraps
+def profile_func(func):
+ @wraps(func)
+ def inner(*args, **kwargs):
+ from plexpy import logger
+ start = time.time()
+ res = func(*args, **kwargs)
+ logger.debug('%s took %s' % (func.__name__, time.time() - start))
+ return res
+ return inner
+
+def tobool(s):
+ if s in [1, '1', 'on', 'yes', 'Yes']:
+ return True
+ else:
+ return False
def multikeysort(items, columns):
comparers = [((itemgetter(col[1:].strip()), -1) if col.startswith('-') else (itemgetter(col.strip()), 1)) for col in columns]
diff --git a/plexpy/pmsconnect.py b/plexpy/pmsconnect.py
index d52ff65a..37935b87 100644
--- a/plexpy/pmsconnect.py
+++ b/plexpy/pmsconnect.py
@@ -13,11 +13,16 @@
# You should have received a copy of the GNU General Public License
# along with PlexPy. If not, see .
+import urllib2
+import time
+import concurrent.futures as cf
+from requests_futures.sessions import FuturesSession
+
from plexpy import logger, helpers, users, http_handler, common
from urlparse import urlparse
+from request import request_json
import plexpy
-import urllib2
def get_server_friendly_name():
logger.info("Requesting name from server...")
@@ -692,11 +697,229 @@ class PmsConnect(object):
output = {'metadata': metadata_list}
return output
- """
- Return processed and validated session list.
- Output: array
- """
+ def get_watched_status(self, sort=None, sections='all', all_params=False, get_file_size=True,
+ exclude_path=None, watched_older_then=None, hide_watched=0, ignore_section='',
+ *args, **kwargs):
+
+ """
+ Returns a list of all unwatched shows
+
+ named args: Used for enabled and disabling sorting/filtering
+ 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?
+
+ use_watched_older_then_sort = True
+ if watched_older_then is None:
+ use_watched_older_then_sort = False
+ watched_older_then = time.time()
+ else:
+ watched_older_then = int(watched_older_then)
+
+ if plexpy.CONFIG.PMS_URL:
+ url_parsed = urlparse(plexpy.CONFIG.PMS_URL)
+ hostname = url_parsed.hostname
+ port = url_parsed.port
+ self.protocol = url_parsed.scheme
+ else:
+ hostname = plexpy.CONFIG.PMS_IP
+ port = plexpy.CONFIG.PMS_PORT
+ self.protocol = 'http'
+
+ b_url = '%s://%s:%s' % (self.protocol, hostname, port)
+
+ h = {'Accept': 'application/json',
+ 'X-Plex-Token': plexpy.CONFIG.PMS_TOKEN
+ }
+
+ hide_watched = 'unwatched' if hide_watched else 'all'
+
+ def fetch(s=''):
+ result = request_json(b_url + s, headers=h)
+ if result:
+ return result
+
+ def maybe_number(v):
+ try:
+ v = int(v)
+ except:
+ try:
+ v = float(v)
+ except:
+ pass
+ return v
+
+ result = fetch('/library/sections/all').get('_children')
+
+ # Final results for data grab
+ f_result = []
+
+ # List of items and urls passed to req.f
+ files_urls = []
+
+ # dicts from req.f is stored here
+ futures_results = []
+
+ # Start fetching data
+ if result:
+ for sections in result:
+ if ignore_section != sections['title']:
+ section = fetch('/library/sections/%s/%s' % (sections['key'], hide_watched))
+ for item in section['_children']:
+
+ d = {'title': item.get('title'),
+ 'type': item['type'],
+ 'ratingKey': item['ratingKey'],
+ 'updatedAt': item.get('updatedAt', 0),
+ 'addedAt': item.get('addedAt', 0),
+ 'viewCount': item.get('viewCount', 0),
+ 'files': [],
+ 'lastViewedAt': item.get('lastViewedAt', 0),
+ 'viewOffset': item.get('viewOffset', 0),
+ 'spaceUsed': 0, # custom
+ 'isSeen': 0, # custom
+ '_elementType': item['_elementType']
+ }
+
+ # Only movies has the files listed here
+ if item['_elementType'] == 'Video':
+ d['viewCount'] = item.get('viewCount', 0)
+ if item.get('viewCount', 0) > 0:
+ d['isSeen'] = 1
+
+
+ # grab the file names and size
+ if '_children' in item:
+ for c in item['_children']:
+ if '_children' in c:
+ for part in c['_children']:
+ f = part.get('file')
+ s = part.get('size', 0)
+ d['spaceUsed'] += s
+ if f and s:
+ d['files'].append({'size': s, 'file': f})
+
+ f_result.append(d)
+
+
+ #elif item['_elementType'] == 'Track':
+ # # I dont think it returns a "Track" as all
+ # pass
+
+ elif item['_elementType'] == 'Directory':
+
+ if item['type'] == 'show' or item['type'] == 'artist':
+ d['viewedLeafCount'] = item.get('viewedLeafCount', 0)
+ d['leafCount'] = item.get('leafCount', 0)
+ d['_elementType'] = item['_elementType']
+ if item['type'] == 'show':
+ # We are gonna assume a entire show is watched
+ # Might be false it someone watched the same ep
+ # over and over
+ if d['viewedLeafCount'] >= d['leafCount'] and d['viewedLeafCount'] > 0:
+ d['viewCount'] = item['viewedLeafCount'] # only set to easy filter
+ d['isSeen'] = 1
+
+ elif item['type'] == 'artist':
+ d['isSeen'] = 1 if d['viewCount'] > 0 else 0
+
+ if get_file_size: # Runs faster without
+ files_urls.append((item, b_url + '/library/metadata/' + str(item['ratingKey']) + '/allLeaves'))
+ else:
+ f_result.append(d)
+
+ #elif item['type'] == 'artist':
+ # pass # Handled above left for future stuff
+
+
+ t_result = f_result
+
+ if get_file_size:
+ future_sess = FuturesSession(max_workers=8)
+ futs = {} # Future holder
+ for zomg in files_urls:
+ t = future_sess.get(zomg[1], timeout=5, headers=h)
+ futs[t] = zomg[0]
+
+ for fut_obj in cf.as_completed(futs):
+ try:
+ res = fut_obj.result()
+ jsn = res.json()
+ f_item = futs[fut_obj]
+ # Add the same dict as movies..
+ d = {'title': f_item.get('title'),
+ 'type': f_item['type'],
+ 'ratingKey': f_item['ratingKey'],
+ 'updatedAt': f_item.get('updatedAt', 0),
+ 'addedAt': f_item.get('addedAt', 0),
+ 'viewCount': f_item.get('viewCount', 0),
+ 'files': [],
+ 'lastViewedAt': f_item.get('lastViewedAt', 0),
+ 'viewOffset': f_item.get('viewOffset', 0),
+ 'spaceUsed': 0, # custom
+ 'isSeen': 0, # custom
+ }
+ if f_item['_elementType'] == 'Directory':
+ if f_item['type'] in ['show', 'artist']:
+ if f_item['type'] == 'show':
+ # We are gonna assume the user has watched if
+ d['isSeen'] = 1 if f_item['leafCount'] >= f_item['viewedLeafCount'] and f_item['viewedLeafCount'] > 0 else 0
+ d['viewCount'] = f_item['viewedLeafCount']
+ elif f_item['type'] == 'artist':
+ d['isSeen'] = 1 if d['viewCount'] > 0 else 0
+ if '_children' in jsn:
+ for e in jsn['_children']:
+ if '_children' in e:
+ for c in e['_children']:
+ if '_children' in c:
+ for cc in c['_children']:
+ f = cc.get('file')
+ s = cc.get('size', 0)
+ d['spaceUsed'] += s
+ if f and s:
+ d['files'].append({'size': s, 'file': f})
+ futures_results.append(d)
+
+ except Exception as error:
+ logger.error('Error while trying to get/process a future %s' % error)
+
+
+ t_result = t_result + futures_results
+
+ # If any user given kwargs was given filter on them.
+ if kwargs:
+ logger.debug('kwargs was given %s filtering the dicts based on them' % kwargs)
+ if not all_params:
+ t_result = [d for d in t_result for k,v in kwargs.iteritems() if d.get(k) == maybe_number(kwargs.get(k))]
+ else:
+ logger.debug('All kwargs is required to be in the list')
+ all_params_result = []
+
+ # Please fix, i would like to do this
+ # faster but i don't know how to..
+ for item in t_result:
+ if all([item.get(k) == maybe_number(kwargs.get(k)) for k,v in kwargs.iteritems() for i in t_result]):
+ all_params_result.append(item)
+
+ if all_params_result:
+ t_result = all_params_result
+
+ if use_watched_older_then_sort:
+ 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/webserve.py b/plexpy/webserve.py
index e4831cbc..6fe789f7 100644
--- a/plexpy/webserve.py
+++ b/plexpy/webserve.py
@@ -14,7 +14,7 @@
# along with PlexPy. If not, see .
from plexpy import logger, notifiers, plextv, pmsconnect, common, log_reader, datafactory, graphs, users, helpers
-from plexpy.helpers import checked, radio
+from plexpy.helpers import checked, radio, profile_func, tobool
from mako.lookup import TemplateLookup
from mako import exceptions
@@ -923,6 +923,20 @@ class WebInterface(object):
else:
logger.warn('Unable to retrieve data.')
+ @profile_func
+ @cherrypy.tools.json_out()
+ @cherrypy.expose
+ def get_watched(self, sort=None, sections='all', all_params=False,
+ get_file_size=True, exclude_path=None, watched_older_then=None,
+ hide_watched=0, ignore_section='', **kwargs):
+ """ See get_watched_status for docs"""
+ all_params = tobool(all_params)
+ get_file_size = tobool(get_file_size)
+ hide_watched = tobool(hide_watched)
+ pms_connect = pmsconnect.PmsConnect()
+ t = pms_connect.get_watched_status(sort=sort, sections=sections, all_params=all_params, get_file_size=get_file_size, exclude_path=exclude_path, hide_watched=hide_watched, watched_older_then=None, ignore_section=ignore_section, **kwargs)
+ return t
+
@cherrypy.expose
def get_episode_list_json(self, rating_key='', **kwargs):